diff options
Diffstat (limited to 'std/encoding/_yaml/loader/loader.ts')
-rw-r--r-- | std/encoding/_yaml/loader/loader.ts | 1798 |
1 files changed, 0 insertions, 1798 deletions
diff --git a/std/encoding/_yaml/loader/loader.ts b/std/encoding/_yaml/loader/loader.ts deleted file mode 100644 index 322f5ce0d..000000000 --- a/std/encoding/_yaml/loader/loader.ts +++ /dev/null @@ -1,1798 +0,0 @@ -// Ported from js-yaml v3.13.1: -// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da -// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license. -// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. - -import { YAMLError } from "../error.ts"; -import { Mark } from "../mark.ts"; -import type { Type } from "../type.ts"; -import * as common from "../utils.ts"; -import { LoaderState, LoaderStateOptions, ResultType } from "./loader_state.ts"; - -type Any = common.Any; -type ArrayObject<T = Any> = common.ArrayObject<T>; - -const _hasOwnProperty = Object.prototype.hasOwnProperty; - -const CONTEXT_FLOW_IN = 1; -const CONTEXT_FLOW_OUT = 2; -const CONTEXT_BLOCK_IN = 3; -const CONTEXT_BLOCK_OUT = 4; - -const CHOMPING_CLIP = 1; -const CHOMPING_STRIP = 2; -const CHOMPING_KEEP = 3; - -const PATTERN_NON_PRINTABLE = - // deno-lint-ignore no-control-regex - /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; -const PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/; -const PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/; -const PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i; -const PATTERN_TAG_URI = - /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i; - -function _class(obj: unknown): string { - return Object.prototype.toString.call(obj); -} - -function isEOL(c: number): boolean { - return c === 0x0a || /* LF */ c === 0x0d /* CR */; -} - -function isWhiteSpace(c: number): boolean { - return c === 0x09 || /* Tab */ c === 0x20 /* Space */; -} - -function isWsOrEol(c: number): boolean { - return ( - c === 0x09 /* Tab */ || - c === 0x20 /* Space */ || - c === 0x0a /* LF */ || - c === 0x0d /* CR */ - ); -} - -function isFlowIndicator(c: number): boolean { - return ( - c === 0x2c /* , */ || - c === 0x5b /* [ */ || - c === 0x5d /* ] */ || - c === 0x7b /* { */ || - c === 0x7d /* } */ - ); -} - -function fromHexCode(c: number): number { - if (0x30 <= /* 0 */ c && c <= 0x39 /* 9 */) { - return c - 0x30; - } - - const lc = c | 0x20; - - if (0x61 <= /* a */ lc && lc <= 0x66 /* f */) { - return lc - 0x61 + 10; - } - - return -1; -} - -function escapedHexLen(c: number): number { - if (c === 0x78 /* x */) { - return 2; - } - if (c === 0x75 /* u */) { - return 4; - } - if (c === 0x55 /* U */) { - return 8; - } - return 0; -} - -function fromDecimalCode(c: number): number { - if (0x30 <= /* 0 */ c && c <= 0x39 /* 9 */) { - return c - 0x30; - } - - return -1; -} - -function simpleEscapeSequence(c: number): string { - /* eslint:disable:prettier */ - return c === 0x30 /* 0 */ - ? "\x00" - : c === 0x61 /* a */ - ? "\x07" - : c === 0x62 /* b */ - ? "\x08" - : c === 0x74 /* t */ - ? "\x09" - : c === 0x09 /* Tab */ - ? "\x09" - : c === 0x6e /* n */ - ? "\x0A" - : c === 0x76 /* v */ - ? "\x0B" - : c === 0x66 /* f */ - ? "\x0C" - : c === 0x72 /* r */ - ? "\x0D" - : c === 0x65 /* e */ - ? "\x1B" - : c === 0x20 /* Space */ - ? " " - : c === 0x22 /* " */ - ? "\x22" - : c === 0x2f /* / */ - ? "/" - : c === 0x5c /* \ */ - ? "\x5C" - : c === 0x4e /* N */ - ? "\x85" - : c === 0x5f /* _ */ - ? "\xA0" - : c === 0x4c /* L */ - ? "\u2028" - : c === 0x50 /* P */ - ? "\u2029" - : ""; - /* eslint:enable:prettier */ -} - -function charFromCodepoint(c: number): string { - if (c <= 0xffff) { - return String.fromCharCode(c); - } - // Encode UTF-16 surrogate pair - // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF - return String.fromCharCode( - ((c - 0x010000) >> 10) + 0xd800, - ((c - 0x010000) & 0x03ff) + 0xdc00, - ); -} - -const simpleEscapeCheck = new Array(256); // integer, for fast access -const simpleEscapeMap = new Array(256); -for (let i = 0; i < 256; i++) { - simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0; - simpleEscapeMap[i] = simpleEscapeSequence(i); -} - -function generateError(state: LoaderState, message: string): YAMLError { - return new YAMLError( - message, - new Mark( - state.filename as string, - state.input, - state.position, - state.line, - state.position - state.lineStart, - ), - ); -} - -function throwError(state: LoaderState, message: string): never { - throw generateError(state, message); -} - -function throwWarning(state: LoaderState, message: string): void { - if (state.onWarning) { - state.onWarning.call(null, generateError(state, message)); - } -} - -interface DirectiveHandlers { - [directive: string]: ( - state: LoaderState, - name: string, - ...args: string[] - ) => void; -} - -const directiveHandlers: DirectiveHandlers = { - YAML(state, _name, ...args: string[]) { - if (state.version !== null) { - return throwError(state, "duplication of %YAML directive"); - } - - if (args.length !== 1) { - return throwError(state, "YAML directive accepts exactly one argument"); - } - - const match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]); - if (match === null) { - return throwError(state, "ill-formed argument of the YAML directive"); - } - - const major = parseInt(match[1], 10); - const minor = parseInt(match[2], 10); - if (major !== 1) { - return throwError(state, "unacceptable YAML version of the document"); - } - - state.version = args[0]; - state.checkLineBreaks = minor < 2; - if (minor !== 1 && minor !== 2) { - return throwWarning(state, "unsupported YAML version of the document"); - } - }, - - TAG(state, _name, ...args: string[]): void { - if (args.length !== 2) { - return throwError(state, "TAG directive accepts exactly two arguments"); - } - - const handle = args[0]; - const prefix = args[1]; - - if (!PATTERN_TAG_HANDLE.test(handle)) { - return throwError( - state, - "ill-formed tag handle (first argument) of the TAG directive", - ); - } - - if (_hasOwnProperty.call(state.tagMap, handle)) { - return throwError( - state, - `there is a previously declared suffix for "${handle}" tag handle`, - ); - } - - if (!PATTERN_TAG_URI.test(prefix)) { - return throwError( - state, - "ill-formed tag prefix (second argument) of the TAG directive", - ); - } - - if (typeof state.tagMap === "undefined") { - state.tagMap = {}; - } - state.tagMap[handle] = prefix; - }, -}; - -function captureSegment( - state: LoaderState, - start: number, - end: number, - checkJson: boolean, -): void { - let result: string; - if (start < end) { - result = state.input.slice(start, end); - - if (checkJson) { - for ( - let position = 0, length = result.length; - position < length; - position++ - ) { - const character = result.charCodeAt(position); - if ( - !(character === 0x09 || (0x20 <= character && character <= 0x10ffff)) - ) { - return throwError(state, "expected valid JSON character"); - } - } - } else if (PATTERN_NON_PRINTABLE.test(result)) { - return throwError(state, "the stream contains non-printable characters"); - } - - state.result += result; - } -} - -function mergeMappings( - state: LoaderState, - destination: ArrayObject, - source: ArrayObject, - overridableKeys: ArrayObject<boolean>, -): void { - if (!common.isObject(source)) { - return throwError( - state, - "cannot merge mappings; the provided source object is unacceptable", - ); - } - - const keys = Object.keys(source); - for (let i = 0, len = keys.length; i < len; i++) { - const key = keys[i]; - if (!_hasOwnProperty.call(destination, key)) { - destination[key] = (source as ArrayObject)[key]; - overridableKeys[key] = true; - } - } -} - -function storeMappingPair( - state: LoaderState, - result: ArrayObject | null, - overridableKeys: ArrayObject<boolean>, - keyTag: string | null, - keyNode: Any, - valueNode: unknown, - startLine?: number, - startPos?: number, -): ArrayObject { - // The output is a plain object here, so keys can only be strings. - // We need to convert keyNode to a string, but doing so can hang the process - // (deeply nested arrays that explode exponentially using aliases). - if (Array.isArray(keyNode)) { - keyNode = Array.prototype.slice.call(keyNode); - - for (let index = 0, quantity = keyNode.length; index < quantity; index++) { - if (Array.isArray(keyNode[index])) { - return throwError(state, "nested arrays are not supported inside keys"); - } - - if ( - typeof keyNode === "object" && - _class(keyNode[index]) === "[object Object]" - ) { - keyNode[index] = "[object Object]"; - } - } - } - - // Avoid code execution in load() via toString property - // (still use its own toString for arrays, timestamps, - // and whatever user schema extensions happen to have @@toStringTag) - if (typeof keyNode === "object" && _class(keyNode) === "[object Object]") { - keyNode = "[object Object]"; - } - - keyNode = String(keyNode); - - if (result === null) { - result = {}; - } - - if (keyTag === "tag:yaml.org,2002:merge") { - if (Array.isArray(valueNode)) { - for ( - let index = 0, quantity = valueNode.length; - index < quantity; - index++ - ) { - mergeMappings(state, result, valueNode[index], overridableKeys); - } - } else { - mergeMappings(state, result, valueNode as ArrayObject, overridableKeys); - } - } else { - if ( - !state.json && - !_hasOwnProperty.call(overridableKeys, keyNode) && - _hasOwnProperty.call(result, keyNode) - ) { - state.line = startLine || state.line; - state.position = startPos || state.position; - return throwError(state, "duplicated mapping key"); - } - result[keyNode] = valueNode; - delete overridableKeys[keyNode]; - } - - return result; -} - -function readLineBreak(state: LoaderState): void { - const ch = state.input.charCodeAt(state.position); - - if (ch === 0x0a /* LF */) { - state.position++; - } else if (ch === 0x0d /* CR */) { - state.position++; - if (state.input.charCodeAt(state.position) === 0x0a /* LF */) { - state.position++; - } - } else { - return throwError(state, "a line break is expected"); - } - - state.line += 1; - state.lineStart = state.position; -} - -function skipSeparationSpace( - state: LoaderState, - allowComments: boolean, - checkIndent: number, -): number { - let lineBreaks = 0, - ch = state.input.charCodeAt(state.position); - - while (ch !== 0) { - while (isWhiteSpace(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (allowComments && ch === 0x23 /* # */) { - do { - ch = state.input.charCodeAt(++state.position); - } while (ch !== 0x0a && /* LF */ ch !== 0x0d && /* CR */ ch !== 0); - } - - if (isEOL(ch)) { - readLineBreak(state); - - ch = state.input.charCodeAt(state.position); - lineBreaks++; - state.lineIndent = 0; - - while (ch === 0x20 /* Space */) { - state.lineIndent++; - ch = state.input.charCodeAt(++state.position); - } - } else { - break; - } - } - - if ( - checkIndent !== -1 && - lineBreaks !== 0 && - state.lineIndent < checkIndent - ) { - throwWarning(state, "deficient indentation"); - } - - return lineBreaks; -} - -function testDocumentSeparator(state: LoaderState): boolean { - let _position = state.position; - let ch = state.input.charCodeAt(_position); - - // Condition state.position === state.lineStart is tested - // in parent on each call, for efficiency. No needs to test here again. - if ( - (ch === 0x2d || /* - */ ch === 0x2e) /* . */ && - ch === state.input.charCodeAt(_position + 1) && - ch === state.input.charCodeAt(_position + 2) - ) { - _position += 3; - - ch = state.input.charCodeAt(_position); - - if (ch === 0 || isWsOrEol(ch)) { - return true; - } - } - - return false; -} - -function writeFoldedLines(state: LoaderState, count: number): void { - if (count === 1) { - state.result += " "; - } else if (count > 1) { - state.result += common.repeat("\n", count - 1); - } -} - -function readPlainScalar( - state: LoaderState, - nodeIndent: number, - withinFlowCollection: boolean, -): boolean { - const kind = state.kind; - const result = state.result; - let ch = state.input.charCodeAt(state.position); - - if ( - isWsOrEol(ch) || - isFlowIndicator(ch) || - ch === 0x23 /* # */ || - ch === 0x26 /* & */ || - ch === 0x2a /* * */ || - ch === 0x21 /* ! */ || - ch === 0x7c /* | */ || - ch === 0x3e /* > */ || - ch === 0x27 /* ' */ || - ch === 0x22 /* " */ || - ch === 0x25 /* % */ || - ch === 0x40 /* @ */ || - ch === 0x60 /* ` */ - ) { - return false; - } - - let following: number; - if (ch === 0x3f || /* ? */ ch === 0x2d /* - */) { - following = state.input.charCodeAt(state.position + 1); - - if ( - isWsOrEol(following) || - (withinFlowCollection && isFlowIndicator(following)) - ) { - return false; - } - } - - state.kind = "scalar"; - state.result = ""; - let captureEnd: number, - captureStart = (captureEnd = state.position); - let hasPendingContent = false; - let line = 0; - while (ch !== 0) { - if (ch === 0x3a /* : */) { - following = state.input.charCodeAt(state.position + 1); - - if ( - isWsOrEol(following) || - (withinFlowCollection && isFlowIndicator(following)) - ) { - break; - } - } else if (ch === 0x23 /* # */) { - const preceding = state.input.charCodeAt(state.position - 1); - - if (isWsOrEol(preceding)) { - break; - } - } else if ( - (state.position === state.lineStart && testDocumentSeparator(state)) || - (withinFlowCollection && isFlowIndicator(ch)) - ) { - break; - } else if (isEOL(ch)) { - line = state.line; - const lineStart = state.lineStart; - const lineIndent = state.lineIndent; - skipSeparationSpace(state, false, -1); - - if (state.lineIndent >= nodeIndent) { - hasPendingContent = true; - ch = state.input.charCodeAt(state.position); - continue; - } else { - state.position = captureEnd; - state.line = line; - state.lineStart = lineStart; - state.lineIndent = lineIndent; - break; - } - } - - if (hasPendingContent) { - captureSegment(state, captureStart, captureEnd, false); - writeFoldedLines(state, state.line - line); - captureStart = captureEnd = state.position; - hasPendingContent = false; - } - - if (!isWhiteSpace(ch)) { - captureEnd = state.position + 1; - } - - ch = state.input.charCodeAt(++state.position); - } - - captureSegment(state, captureStart, captureEnd, false); - - if (state.result) { - return true; - } - - state.kind = kind; - state.result = result; - return false; -} - -function readSingleQuotedScalar( - state: LoaderState, - nodeIndent: number, -): boolean { - let ch, captureStart, captureEnd; - - ch = state.input.charCodeAt(state.position); - - if (ch !== 0x27 /* ' */) { - return false; - } - - state.kind = "scalar"; - state.result = ""; - state.position++; - captureStart = captureEnd = state.position; - - while ((ch = state.input.charCodeAt(state.position)) !== 0) { - if (ch === 0x27 /* ' */) { - captureSegment(state, captureStart, state.position, true); - ch = state.input.charCodeAt(++state.position); - - if (ch === 0x27 /* ' */) { - captureStart = state.position; - state.position++; - captureEnd = state.position; - } else { - return true; - } - } else if (isEOL(ch)) { - captureSegment(state, captureStart, captureEnd, true); - writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); - captureStart = captureEnd = state.position; - } else if ( - state.position === state.lineStart && - testDocumentSeparator(state) - ) { - return throwError( - state, - "unexpected end of the document within a single quoted scalar", - ); - } else { - state.position++; - captureEnd = state.position; - } - } - - return throwError( - state, - "unexpected end of the stream within a single quoted scalar", - ); -} - -function readDoubleQuotedScalar( - state: LoaderState, - nodeIndent: number, -): boolean { - let ch = state.input.charCodeAt(state.position); - - if (ch !== 0x22 /* " */) { - return false; - } - - state.kind = "scalar"; - state.result = ""; - state.position++; - let captureEnd: number, - captureStart = (captureEnd = state.position); - let tmp: number; - while ((ch = state.input.charCodeAt(state.position)) !== 0) { - if (ch === 0x22 /* " */) { - captureSegment(state, captureStart, state.position, true); - state.position++; - return true; - } - if (ch === 0x5c /* \ */) { - captureSegment(state, captureStart, state.position, true); - ch = state.input.charCodeAt(++state.position); - - if (isEOL(ch)) { - skipSeparationSpace(state, false, nodeIndent); - - // TODO(bartlomieju): rework to inline fn with no type cast? - } else if (ch < 256 && simpleEscapeCheck[ch]) { - state.result += simpleEscapeMap[ch]; - state.position++; - } else if ((tmp = escapedHexLen(ch)) > 0) { - let hexLength = tmp; - let hexResult = 0; - - for (; hexLength > 0; hexLength--) { - ch = state.input.charCodeAt(++state.position); - - if ((tmp = fromHexCode(ch)) >= 0) { - hexResult = (hexResult << 4) + tmp; - } else { - return throwError(state, "expected hexadecimal character"); - } - } - - state.result += charFromCodepoint(hexResult); - - state.position++; - } else { - return throwError(state, "unknown escape sequence"); - } - - captureStart = captureEnd = state.position; - } else if (isEOL(ch)) { - captureSegment(state, captureStart, captureEnd, true); - writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); - captureStart = captureEnd = state.position; - } else if ( - state.position === state.lineStart && - testDocumentSeparator(state) - ) { - return throwError( - state, - "unexpected end of the document within a double quoted scalar", - ); - } else { - state.position++; - captureEnd = state.position; - } - } - - return throwError( - state, - "unexpected end of the stream within a double quoted scalar", - ); -} - -function readFlowCollection(state: LoaderState, nodeIndent: number): boolean { - let ch = state.input.charCodeAt(state.position); - let terminator: number; - let isMapping = true; - let result: ResultType = {}; - if (ch === 0x5b /* [ */) { - terminator = 0x5d; /* ] */ - isMapping = false; - result = []; - } else if (ch === 0x7b /* { */) { - terminator = 0x7d; /* } */ - } else { - return false; - } - - if ( - state.anchor !== null && - typeof state.anchor != "undefined" && - typeof state.anchorMap != "undefined" - ) { - state.anchorMap[state.anchor] = result; - } - - ch = state.input.charCodeAt(++state.position); - - const tag = state.tag, - anchor = state.anchor; - let readNext = true; - let valueNode, - keyNode, - keyTag: string | null = (keyNode = valueNode = null), - isExplicitPair: boolean, - isPair = (isExplicitPair = false); - let following = 0, - line = 0; - const overridableKeys: ArrayObject<boolean> = {}; - while (ch !== 0) { - skipSeparationSpace(state, true, nodeIndent); - - ch = state.input.charCodeAt(state.position); - - if (ch === terminator) { - state.position++; - state.tag = tag; - state.anchor = anchor; - state.kind = isMapping ? "mapping" : "sequence"; - state.result = result; - return true; - } - if (!readNext) { - return throwError(state, "missed comma between flow collection entries"); - } - - keyTag = keyNode = valueNode = null; - isPair = isExplicitPair = false; - - if (ch === 0x3f /* ? */) { - following = state.input.charCodeAt(state.position + 1); - - if (isWsOrEol(following)) { - isPair = isExplicitPair = true; - state.position++; - skipSeparationSpace(state, true, nodeIndent); - } - } - - line = state.line; - // eslint-disable-next-line @typescript-eslint/no-use-before-define - composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); - keyTag = state.tag || null; - keyNode = state.result; - skipSeparationSpace(state, true, nodeIndent); - - ch = state.input.charCodeAt(state.position); - - if ((isExplicitPair || state.line === line) && ch === 0x3a /* : */) { - isPair = true; - ch = state.input.charCodeAt(++state.position); - skipSeparationSpace(state, true, nodeIndent); - // eslint-disable-next-line @typescript-eslint/no-use-before-define - composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); - valueNode = state.result; - } - - if (isMapping) { - storeMappingPair( - state, - result, - overridableKeys, - keyTag, - keyNode, - valueNode, - ); - } else if (isPair) { - (result as ArrayObject[]).push( - storeMappingPair( - state, - null, - overridableKeys, - keyTag, - keyNode, - valueNode, - ), - ); - } else { - (result as ResultType[]).push(keyNode as ResultType); - } - - skipSeparationSpace(state, true, nodeIndent); - - ch = state.input.charCodeAt(state.position); - - if (ch === 0x2c /* , */) { - readNext = true; - ch = state.input.charCodeAt(++state.position); - } else { - readNext = false; - } - } - - return throwError( - state, - "unexpected end of the stream within a flow collection", - ); -} - -function readBlockScalar(state: LoaderState, nodeIndent: number): boolean { - let chomping = CHOMPING_CLIP, - didReadContent = false, - detectedIndent = false, - textIndent = nodeIndent, - emptyLines = 0, - atMoreIndented = false; - - let ch = state.input.charCodeAt(state.position); - - let folding = false; - if (ch === 0x7c /* | */) { - folding = false; - } else if (ch === 0x3e /* > */) { - folding = true; - } else { - return false; - } - - state.kind = "scalar"; - state.result = ""; - - let tmp = 0; - while (ch !== 0) { - ch = state.input.charCodeAt(++state.position); - - if (ch === 0x2b || /* + */ ch === 0x2d /* - */) { - if (CHOMPING_CLIP === chomping) { - chomping = ch === 0x2b /* + */ ? CHOMPING_KEEP : CHOMPING_STRIP; - } else { - return throwError(state, "repeat of a chomping mode identifier"); - } - } else if ((tmp = fromDecimalCode(ch)) >= 0) { - if (tmp === 0) { - return throwError( - state, - "bad explicit indentation width of a block scalar; it cannot be less than one", - ); - } else if (!detectedIndent) { - textIndent = nodeIndent + tmp - 1; - detectedIndent = true; - } else { - return throwError(state, "repeat of an indentation width identifier"); - } - } else { - break; - } - } - - if (isWhiteSpace(ch)) { - do { - ch = state.input.charCodeAt(++state.position); - } while (isWhiteSpace(ch)); - - if (ch === 0x23 /* # */) { - do { - ch = state.input.charCodeAt(++state.position); - } while (!isEOL(ch) && ch !== 0); - } - } - - while (ch !== 0) { - readLineBreak(state); - state.lineIndent = 0; - - ch = state.input.charCodeAt(state.position); - - while ( - (!detectedIndent || state.lineIndent < textIndent) && - ch === 0x20 /* Space */ - ) { - state.lineIndent++; - ch = state.input.charCodeAt(++state.position); - } - - if (!detectedIndent && state.lineIndent > textIndent) { - textIndent = state.lineIndent; - } - - if (isEOL(ch)) { - emptyLines++; - continue; - } - - // End of the scalar. - if (state.lineIndent < textIndent) { - // Perform the chomping. - if (chomping === CHOMPING_KEEP) { - state.result += common.repeat( - "\n", - didReadContent ? 1 + emptyLines : emptyLines, - ); - } else if (chomping === CHOMPING_CLIP) { - if (didReadContent) { - // i.e. only if the scalar is not empty. - state.result += "\n"; - } - } - - // Break this `while` cycle and go to the function's epilogue. - break; - } - - // Folded style: use fancy rules to handle line breaks. - if (folding) { - // Lines starting with white space characters (more-indented lines) are not folded. - if (isWhiteSpace(ch)) { - atMoreIndented = true; - // except for the first content line (cf. Example 8.1) - state.result += common.repeat( - "\n", - didReadContent ? 1 + emptyLines : emptyLines, - ); - - // End of more-indented block. - } else if (atMoreIndented) { - atMoreIndented = false; - state.result += common.repeat("\n", emptyLines + 1); - - // Just one line break - perceive as the same line. - } else if (emptyLines === 0) { - if (didReadContent) { - // i.e. only if we have already read some scalar content. - state.result += " "; - } - - // Several line breaks - perceive as different lines. - } else { - state.result += common.repeat("\n", emptyLines); - } - - // Literal style: just add exact number of line breaks between content lines. - } else { - // Keep all line breaks except the header line break. - state.result += common.repeat( - "\n", - didReadContent ? 1 + emptyLines : emptyLines, - ); - } - - didReadContent = true; - detectedIndent = true; - emptyLines = 0; - const captureStart = state.position; - - while (!isEOL(ch) && ch !== 0) { - ch = state.input.charCodeAt(++state.position); - } - - captureSegment(state, captureStart, state.position, false); - } - - return true; -} - -function readBlockSequence(state: LoaderState, nodeIndent: number): boolean { - let line: number, - following: number, - detected = false, - ch: number; - const tag = state.tag, - anchor = state.anchor, - result: unknown[] = []; - - if ( - state.anchor !== null && - typeof state.anchor !== "undefined" && - typeof state.anchorMap !== "undefined" - ) { - state.anchorMap[state.anchor] = result; - } - - ch = state.input.charCodeAt(state.position); - - while (ch !== 0) { - if (ch !== 0x2d /* - */) { - break; - } - - following = state.input.charCodeAt(state.position + 1); - - if (!isWsOrEol(following)) { - break; - } - - detected = true; - state.position++; - - if (skipSeparationSpace(state, true, -1)) { - if (state.lineIndent <= nodeIndent) { - result.push(null); - ch = state.input.charCodeAt(state.position); - continue; - } - } - - line = state.line; - // eslint-disable-next-line @typescript-eslint/no-use-before-define - composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true); - result.push(state.result); - skipSeparationSpace(state, true, -1); - - ch = state.input.charCodeAt(state.position); - - if ((state.line === line || state.lineIndent > nodeIndent) && ch !== 0) { - return throwError(state, "bad indentation of a sequence entry"); - } else if (state.lineIndent < nodeIndent) { - break; - } - } - - if (detected) { - state.tag = tag; - state.anchor = anchor; - state.kind = "sequence"; - state.result = result; - return true; - } - return false; -} - -function readBlockMapping( - state: LoaderState, - nodeIndent: number, - flowIndent: number, -): boolean { - const tag = state.tag, - anchor = state.anchor, - result = {}, - overridableKeys = {}; - let following: number, - allowCompact = false, - line: number, - pos: number, - keyTag = null, - keyNode = null, - valueNode = null, - atExplicitKey = false, - detected = false, - ch: number; - - if ( - state.anchor !== null && - typeof state.anchor !== "undefined" && - typeof state.anchorMap !== "undefined" - ) { - state.anchorMap[state.anchor] = result; - } - - ch = state.input.charCodeAt(state.position); - - while (ch !== 0) { - following = state.input.charCodeAt(state.position + 1); - line = state.line; // Save the current line. - pos = state.position; - - // - // Explicit notation case. There are two separate blocks: - // first for the key (denoted by "?") and second for the value (denoted by ":") - // - if ((ch === 0x3f || /* ? */ ch === 0x3a) && /* : */ isWsOrEol(following)) { - if (ch === 0x3f /* ? */) { - if (atExplicitKey) { - storeMappingPair( - state, - result, - overridableKeys, - keyTag as string, - keyNode, - null, - ); - keyTag = keyNode = valueNode = null; - } - - detected = true; - atExplicitKey = true; - allowCompact = true; - } else if (atExplicitKey) { - // i.e. 0x3A/* : */ === character after the explicit key. - atExplicitKey = false; - allowCompact = true; - } else { - return throwError( - state, - "incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line", - ); - } - - state.position += 1; - ch = following; - - // - // Implicit notation case. Flow-style node as the key first, then ":", and the value. - // - // eslint-disable-next-line @typescript-eslint/no-use-before-define - } else if (composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) { - if (state.line === line) { - ch = state.input.charCodeAt(state.position); - - while (isWhiteSpace(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (ch === 0x3a /* : */) { - ch = state.input.charCodeAt(++state.position); - - if (!isWsOrEol(ch)) { - return throwError( - state, - "a whitespace character is expected after the key-value separator within a block mapping", - ); - } - - if (atExplicitKey) { - storeMappingPair( - state, - result, - overridableKeys, - keyTag as string, - keyNode, - null, - ); - keyTag = keyNode = valueNode = null; - } - - detected = true; - atExplicitKey = false; - allowCompact = false; - keyTag = state.tag; - keyNode = state.result; - } else if (detected) { - return throwError( - state, - "can not read an implicit mapping pair; a colon is missed", - ); - } else { - state.tag = tag; - state.anchor = anchor; - return true; // Keep the result of `composeNode`. - } - } else if (detected) { - return throwError( - state, - "can not read a block mapping entry; a multiline key may not be an implicit key", - ); - } else { - state.tag = tag; - state.anchor = anchor; - return true; // Keep the result of `composeNode`. - } - } else { - break; // Reading is done. Go to the epilogue. - } - - // - // Common reading code for both explicit and implicit notations. - // - if (state.line === line || state.lineIndent > nodeIndent) { - if ( - // eslint-disable-next-line @typescript-eslint/no-use-before-define - composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact) - ) { - if (atExplicitKey) { - keyNode = state.result; - } else { - valueNode = state.result; - } - } - - if (!atExplicitKey) { - storeMappingPair( - state, - result, - overridableKeys, - keyTag as string, - keyNode, - valueNode, - line, - pos, - ); - keyTag = keyNode = valueNode = null; - } - - skipSeparationSpace(state, true, -1); - ch = state.input.charCodeAt(state.position); - } - - if (state.lineIndent > nodeIndent && ch !== 0) { - return throwError(state, "bad indentation of a mapping entry"); - } else if (state.lineIndent < nodeIndent) { - break; - } - } - - // - // Epilogue. - // - - // Special case: last mapping's node contains only the key in explicit notation. - if (atExplicitKey) { - storeMappingPair( - state, - result, - overridableKeys, - keyTag as string, - keyNode, - null, - ); - } - - // Expose the resulting mapping. - if (detected) { - state.tag = tag; - state.anchor = anchor; - state.kind = "mapping"; - state.result = result; - } - - return detected; -} - -function readTagProperty(state: LoaderState): boolean { - let position: number, - isVerbatim = false, - isNamed = false, - tagHandle = "", - tagName: string, - ch: number; - - ch = state.input.charCodeAt(state.position); - - if (ch !== 0x21 /* ! */) return false; - - if (state.tag !== null) { - return throwError(state, "duplication of a tag property"); - } - - ch = state.input.charCodeAt(++state.position); - - if (ch === 0x3c /* < */) { - isVerbatim = true; - ch = state.input.charCodeAt(++state.position); - } else if (ch === 0x21 /* ! */) { - isNamed = true; - tagHandle = "!!"; - ch = state.input.charCodeAt(++state.position); - } else { - tagHandle = "!"; - } - - position = state.position; - - if (isVerbatim) { - do { - ch = state.input.charCodeAt(++state.position); - } while (ch !== 0 && ch !== 0x3e /* > */); - - if (state.position < state.length) { - tagName = state.input.slice(position, state.position); - ch = state.input.charCodeAt(++state.position); - } else { - return throwError( - state, - "unexpected end of the stream within a verbatim tag", - ); - } - } else { - while (ch !== 0 && !isWsOrEol(ch)) { - if (ch === 0x21 /* ! */) { - if (!isNamed) { - tagHandle = state.input.slice(position - 1, state.position + 1); - - if (!PATTERN_TAG_HANDLE.test(tagHandle)) { - return throwError( - state, - "named tag handle cannot contain such characters", - ); - } - - isNamed = true; - position = state.position + 1; - } else { - return throwError( - state, - "tag suffix cannot contain exclamation marks", - ); - } - } - - ch = state.input.charCodeAt(++state.position); - } - - tagName = state.input.slice(position, state.position); - - if (PATTERN_FLOW_INDICATORS.test(tagName)) { - return throwError( - state, - "tag suffix cannot contain flow indicator characters", - ); - } - } - - if (tagName && !PATTERN_TAG_URI.test(tagName)) { - return throwError( - state, - `tag name cannot contain such characters: ${tagName}`, - ); - } - - if (isVerbatim) { - state.tag = tagName; - } else if ( - typeof state.tagMap !== "undefined" && - _hasOwnProperty.call(state.tagMap, tagHandle) - ) { - state.tag = state.tagMap[tagHandle] + tagName; - } else if (tagHandle === "!") { - state.tag = `!${tagName}`; - } else if (tagHandle === "!!") { - state.tag = `tag:yaml.org,2002:${tagName}`; - } else { - return throwError(state, `undeclared tag handle "${tagHandle}"`); - } - - return true; -} - -function readAnchorProperty(state: LoaderState): boolean { - let ch = state.input.charCodeAt(state.position); - if (ch !== 0x26 /* & */) return false; - - if (state.anchor !== null) { - return throwError(state, "duplication of an anchor property"); - } - ch = state.input.charCodeAt(++state.position); - - const position = state.position; - while (ch !== 0 && !isWsOrEol(ch) && !isFlowIndicator(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (state.position === position) { - return throwError( - state, - "name of an anchor node must contain at least one character", - ); - } - - state.anchor = state.input.slice(position, state.position); - return true; -} - -function readAlias(state: LoaderState): boolean { - let ch = state.input.charCodeAt(state.position); - - if (ch !== 0x2a /* * */) return false; - - ch = state.input.charCodeAt(++state.position); - const _position = state.position; - - while (ch !== 0 && !isWsOrEol(ch) && !isFlowIndicator(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (state.position === _position) { - return throwError( - state, - "name of an alias node must contain at least one character", - ); - } - - const alias = state.input.slice(_position, state.position); - if ( - typeof state.anchorMap !== "undefined" && - !Object.prototype.hasOwnProperty.call(state.anchorMap, alias) - ) { - return throwError(state, `unidentified alias "${alias}"`); - } - - if (typeof state.anchorMap !== "undefined") { - state.result = state.anchorMap[alias]; - } - skipSeparationSpace(state, true, -1); - return true; -} - -function composeNode( - state: LoaderState, - parentIndent: number, - nodeContext: number, - allowToSeek: boolean, - allowCompact: boolean, -): boolean { - let allowBlockScalars: boolean, - allowBlockCollections: boolean, - indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this<parent - atNewLine = false, - hasContent = false, - type: Type, - flowIndent: number, - blockIndent: number; - - if (state.listener && state.listener !== null) { - state.listener("open", state); - } - - state.tag = null; - state.anchor = null; - state.kind = null; - state.result = null; - - const allowBlockStyles = - (allowBlockScalars = allowBlockCollections = - CONTEXT_BLOCK_OUT === nodeContext || CONTEXT_BLOCK_IN === nodeContext); - - if (allowToSeek) { - if (skipSeparationSpace(state, true, -1)) { - atNewLine = true; - - if (state.lineIndent > parentIndent) { - indentStatus = 1; - } else if (state.lineIndent === parentIndent) { - indentStatus = 0; - } else if (state.lineIndent < parentIndent) { - indentStatus = -1; - } - } - } - - if (indentStatus === 1) { - while (readTagProperty(state) || readAnchorProperty(state)) { - if (skipSeparationSpace(state, true, -1)) { - atNewLine = true; - allowBlockCollections = allowBlockStyles; - - if (state.lineIndent > parentIndent) { - indentStatus = 1; - } else if (state.lineIndent === parentIndent) { - indentStatus = 0; - } else if (state.lineIndent < parentIndent) { - indentStatus = -1; - } - } else { - allowBlockCollections = false; - } - } - } - - if (allowBlockCollections) { - allowBlockCollections = atNewLine || allowCompact; - } - - if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) { - const cond = CONTEXT_FLOW_IN === nodeContext || - CONTEXT_FLOW_OUT === nodeContext; - flowIndent = cond ? parentIndent : parentIndent + 1; - - blockIndent = state.position - state.lineStart; - - if (indentStatus === 1) { - if ( - (allowBlockCollections && - (readBlockSequence(state, blockIndent) || - readBlockMapping(state, blockIndent, flowIndent))) || - readFlowCollection(state, flowIndent) - ) { - hasContent = true; - } else { - if ( - (allowBlockScalars && readBlockScalar(state, flowIndent)) || - readSingleQuotedScalar(state, flowIndent) || - readDoubleQuotedScalar(state, flowIndent) - ) { - hasContent = true; - } else if (readAlias(state)) { - hasContent = true; - - if (state.tag !== null || state.anchor !== null) { - return throwError( - state, - "alias node should not have Any properties", - ); - } - } else if ( - readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext) - ) { - hasContent = true; - - if (state.tag === null) { - state.tag = "?"; - } - } - - if (state.anchor !== null && typeof state.anchorMap !== "undefined") { - state.anchorMap[state.anchor] = state.result; - } - } - } else if (indentStatus === 0) { - // Special case: block sequences are allowed to have same indentation level as the parent. - // http://www.yaml.org/spec/1.2/spec.html#id2799784 - hasContent = allowBlockCollections && - readBlockSequence(state, blockIndent); - } - } - - if (state.tag !== null && state.tag !== "!") { - if (state.tag === "?") { - for ( - let typeIndex = 0, typeQuantity = state.implicitTypes.length; - typeIndex < typeQuantity; - typeIndex++ - ) { - type = state.implicitTypes[typeIndex]; - - // Implicit resolving is not allowed for non-scalar types, and '?' - // non-specific tag is only assigned to plain scalars. So, it isn't - // needed to check for 'kind' conformity. - - if (type.resolve(state.result)) { - // `state.result` updated in resolver if matched - state.result = type.construct(state.result); - state.tag = type.tag; - if (state.anchor !== null && typeof state.anchorMap !== "undefined") { - state.anchorMap[state.anchor] = state.result; - } - break; - } - } - } else if ( - _hasOwnProperty.call(state.typeMap[state.kind || "fallback"], state.tag) - ) { - type = state.typeMap[state.kind || "fallback"][state.tag]; - - if (state.result !== null && type.kind !== state.kind) { - return throwError( - state, - `unacceptable node kind for !<${state.tag}> tag; it should be "${type.kind}", not "${state.kind}"`, - ); - } - - if (!type.resolve(state.result)) { - // `state.result` updated in resolver if matched - return throwError( - state, - `cannot resolve a node with !<${state.tag}> explicit tag`, - ); - } else { - state.result = type.construct(state.result); - if (state.anchor !== null && typeof state.anchorMap !== "undefined") { - state.anchorMap[state.anchor] = state.result; - } - } - } else { - return throwError(state, `unknown tag !<${state.tag}>`); - } - } - - if (state.listener && state.listener !== null) { - state.listener("close", state); - } - return state.tag !== null || state.anchor !== null || hasContent; -} - -function readDocument(state: LoaderState): void { - const documentStart = state.position; - let position: number, - directiveName: string, - directiveArgs: string[], - hasDirectives = false, - ch: number; - - state.version = null; - state.checkLineBreaks = state.legacy; - state.tagMap = {}; - state.anchorMap = {}; - - while ((ch = state.input.charCodeAt(state.position)) !== 0) { - skipSeparationSpace(state, true, -1); - - ch = state.input.charCodeAt(state.position); - - if (state.lineIndent > 0 || ch !== 0x25 /* % */) { - break; - } - - hasDirectives = true; - ch = state.input.charCodeAt(++state.position); - position = state.position; - - while (ch !== 0 && !isWsOrEol(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - directiveName = state.input.slice(position, state.position); - directiveArgs = []; - - if (directiveName.length < 1) { - return throwError( - state, - "directive name must not be less than one character in length", - ); - } - - while (ch !== 0) { - while (isWhiteSpace(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (ch === 0x23 /* # */) { - do { - ch = state.input.charCodeAt(++state.position); - } while (ch !== 0 && !isEOL(ch)); - break; - } - - if (isEOL(ch)) break; - - position = state.position; - - while (ch !== 0 && !isWsOrEol(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - directiveArgs.push(state.input.slice(position, state.position)); - } - - if (ch !== 0) readLineBreak(state); - - if (_hasOwnProperty.call(directiveHandlers, directiveName)) { - directiveHandlers[directiveName](state, directiveName, ...directiveArgs); - } else { - throwWarning(state, `unknown document directive "${directiveName}"`); - } - } - - skipSeparationSpace(state, true, -1); - - if ( - state.lineIndent === 0 && - state.input.charCodeAt(state.position) === 0x2d /* - */ && - state.input.charCodeAt(state.position + 1) === 0x2d /* - */ && - state.input.charCodeAt(state.position + 2) === 0x2d /* - */ - ) { - state.position += 3; - skipSeparationSpace(state, true, -1); - } else if (hasDirectives) { - return throwError(state, "directives end mark is expected"); - } - - composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true); - skipSeparationSpace(state, true, -1); - - if ( - state.checkLineBreaks && - PATTERN_NON_ASCII_LINE_BREAKS.test( - state.input.slice(documentStart, state.position), - ) - ) { - throwWarning(state, "non-ASCII line breaks are interpreted as content"); - } - - state.documents.push(state.result); - - if (state.position === state.lineStart && testDocumentSeparator(state)) { - if (state.input.charCodeAt(state.position) === 0x2e /* . */) { - state.position += 3; - skipSeparationSpace(state, true, -1); - } - return; - } - - if (state.position < state.length - 1) { - return throwError( - state, - "end of the stream or a document separator is expected", - ); - } else { - return; - } -} - -function loadDocuments(input: string, options?: LoaderStateOptions): unknown[] { - input = String(input); - options = options || {}; - - if (input.length !== 0) { - // Add tailing `\n` if not exists - if ( - input.charCodeAt(input.length - 1) !== 0x0a /* LF */ && - input.charCodeAt(input.length - 1) !== 0x0d /* CR */ - ) { - input += "\n"; - } - - // Strip BOM - if (input.charCodeAt(0) === 0xfeff) { - input = input.slice(1); - } - } - - const state = new LoaderState(input, options); - - // Use 0 as string terminator. That significantly simplifies bounds check. - state.input += "\0"; - - while (state.input.charCodeAt(state.position) === 0x20 /* Space */) { - state.lineIndent += 1; - state.position += 1; - } - - while (state.position < state.length - 1) { - readDocument(state); - } - - return state.documents; -} - -export type CbFunction = (doc: unknown) => void; -function isCbFunction(fn: unknown): fn is CbFunction { - return typeof fn === "function"; -} - -export function loadAll<T extends CbFunction | LoaderStateOptions>( - input: string, - iteratorOrOption?: T, - options?: LoaderStateOptions, -): T extends CbFunction ? void : unknown[] { - if (!isCbFunction(iteratorOrOption)) { - return loadDocuments(input, iteratorOrOption as LoaderStateOptions) as Any; - } - - const documents = loadDocuments(input, options); - const iterator = iteratorOrOption; - for (let index = 0, length = documents.length; index < length; index++) { - iterator(documents[index]); - } - - return void 0 as Any; -} - -export function load(input: string, options?: LoaderStateOptions): unknown { - const documents = loadDocuments(input, options); - - if (documents.length === 0) { - return; - } - if (documents.length === 1) { - return documents[0]; - } - throw new YAMLError( - "expected a single document in the stream, but found more", - ); -} |