diff --git a/packages/apidom-parser-adapter-yaml-1-2/package.json b/packages/apidom-parser-adapter-yaml-1-2/package.json index d19c5f3fc..00f51481a 100644 --- a/packages/apidom-parser-adapter-yaml-1-2/package.json +++ b/packages/apidom-parser-adapter-yaml-1-2/package.json @@ -39,8 +39,8 @@ "typescript:declaration": "tsc -p declaration.tsconfig.json && rollup -c config/rollup/types.dist.js", "test": "cross-env NODE_ENV=test BABEL_ENV=cjs mocha", "perf": "cross-env NODE_ENV=test BABEL_ENV=cjs node ./test/perf/index.cjs", - "perf:parse": "cross-env NODE_ENV=test BABEL_ENV=cjs node ./test/perf/parse.cjs", "perf:lexical-analysis": "cross-env NODE_ENV=test BABEL_ENV=cjs node ./test/perf/lexical-analysis.cjs", + "perf:parse-syntactic-analysis-indirect": "cross-env NODE_ENV=test BABEL_ENV=cjs node ./test/perf/parse-syntactic-analysis-indirect.cjs", "prepack": "copyfiles -u 3 ../../LICENSES/* LICENSES && copyfiles -u 2 ../../NOTICE .", "postpack": "rimraf NOTICE LICENSES" }, diff --git a/packages/apidom-parser-adapter-yaml-1-2/src/adapter-browser.ts b/packages/apidom-parser-adapter-yaml-1-2/src/adapter-browser.ts index 06e82001d..5105f4d59 100644 --- a/packages/apidom-parser-adapter-yaml-1-2/src/adapter-browser.ts +++ b/packages/apidom-parser-adapter-yaml-1-2/src/adapter-browser.ts @@ -1,7 +1,7 @@ import { ParseResultElement } from '@swagger-api/apidom-core'; import lexicalAnalysis from './lexical-analysis/browser'; -import syntacticAnalysis from './syntactic-analysis/index'; +import syntacticAnalysis from './syntactic-analysis/indirect/index'; export { mediaTypes, namespace } from './adapter'; export { lexicalAnalysis, syntacticAnalysis }; diff --git a/packages/apidom-parser-adapter-yaml-1-2/src/adapter-node.ts b/packages/apidom-parser-adapter-yaml-1-2/src/adapter-node.ts index b63a8c0b6..24f911c4f 100644 --- a/packages/apidom-parser-adapter-yaml-1-2/src/adapter-node.ts +++ b/packages/apidom-parser-adapter-yaml-1-2/src/adapter-node.ts @@ -1,7 +1,7 @@ import { ParseResultElement } from '@swagger-api/apidom-core'; import lexicalAnalysis from './lexical-analysis/node'; -import syntacticAnalysis from './syntactic-analysis/index'; +import syntacticAnalysis from './syntactic-analysis/indirect/index'; export { mediaTypes, namespace } from './adapter'; export { lexicalAnalysis, syntacticAnalysis }; diff --git a/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/TreeCursorIterator.ts b/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/TreeCursorIterator.ts new file mode 100644 index 000000000..1fd21e9ef --- /dev/null +++ b/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/TreeCursorIterator.ts @@ -0,0 +1,121 @@ +import { TreeCursor as NodeTreeCursor } from 'tree-sitter'; +import { TreeCursor as WebTreeCursor } from 'web-tree-sitter'; + +import TreeCursorSyntaxNode from './TreeCursorSyntaxNode'; + +class TreeCursorIterator { + protected readonly cursor; + + constructor(cursor: NodeTreeCursor | WebTreeCursor) { + this.cursor = cursor; + } + + stream() { + return new TreeCursorSyntaxNode(this.cursor); + } + + yaml_directive() { + return new TreeCursorSyntaxNode(this.cursor); + } + + tag_directive() { + return new TreeCursorSyntaxNode(this.cursor); + } + + reserved_directive() { + return new TreeCursorSyntaxNode(this.cursor); + } + + document() { + return new TreeCursorSyntaxNode(this.cursor); + } + + block_node() { + return new TreeCursorSyntaxNode(this.cursor).setFieldName(this.cursor); + } + + flow_node() { + return new TreeCursorSyntaxNode(this.cursor).setFieldName(this.cursor); + } + + block_mapping() { + return new TreeCursorSyntaxNode(this.cursor); + } + + block_mapping_pair() { + return new TreeCursorSyntaxNode(this.cursor); + } + + flow_mapping() { + return new TreeCursorSyntaxNode(this.cursor); + } + + flow_pair() { + return new TreeCursorSyntaxNode(this.cursor); + } + + block_sequence() { + return new TreeCursorSyntaxNode(this.cursor); + } + + block_sequence_item() { + return new TreeCursorSyntaxNode(this.cursor); + } + + flow_sequence() { + return new TreeCursorSyntaxNode(this.cursor); + } + + plain_scalar() { + return new TreeCursorSyntaxNode(this.cursor); + } + + single_quote_scalar() { + return new TreeCursorSyntaxNode(this.cursor); + } + + double_quote_scalar() { + return new TreeCursorSyntaxNode(this.cursor); + } + + block_scalar() { + return new TreeCursorSyntaxNode(this.cursor); + } + + ERROR() { + return new TreeCursorSyntaxNode(this.cursor).setHasError(this.cursor); + } + + public *[Symbol.iterator]() { + let node: TreeCursorSyntaxNode; + + if (this.cursor.nodeType in this) { + // @ts-ignore + node = this[this.cursor.nodeType]() as TreeCursorSyntaxNode; + } else { + node = new TreeCursorSyntaxNode(this.cursor); + } + + if (this.cursor.gotoFirstChild()) { + const [firstChild] = new TreeCursorIterator(this.cursor); + + node.pushChildren(firstChild); + + while (this.cursor.gotoNextSibling()) { + const firstChildSiblings = Array.from(new TreeCursorIterator(this.cursor)); + node.pushChildren(...firstChildSiblings); + } + + node.children.reduce((previousNode: TreeCursorSyntaxNode | undefined, currentNode) => { + currentNode.setPreviousSibling(previousNode); + return currentNode; + }, undefined); + + this.cursor.gotoParent(); + } + + yield node; + } +} + +export default TreeCursorIterator; diff --git a/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/TreeCursorSyntaxNode.ts b/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/TreeCursorSyntaxNode.ts new file mode 100644 index 000000000..847a481db --- /dev/null +++ b/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/TreeCursorSyntaxNode.ts @@ -0,0 +1,105 @@ +import { TreeCursor as NodeTreeCursor, Point as NodePoint } from 'tree-sitter'; +import { TreeCursor as WebTreeCursor, Point as WebPoint } from 'web-tree-sitter'; + +class TreeCursorSyntaxNode { + public readonly type: string; + + public readonly startPosition: NodePoint | WebPoint; + + public readonly endPosition: NodePoint | WebPoint; + + public readonly startIndex: number; + + public readonly endIndex: number; + + public readonly text: string; + + public readonly isNamed: boolean; + + public readonly isMissing: boolean; + + public fieldName: string | undefined; + + public hasError = false; + + public readonly children: TreeCursorSyntaxNode[] = []; + + public previousSibling: TreeCursorSyntaxNode | undefined; + + constructor(cursor: NodeTreeCursor | WebTreeCursor) { + this.type = cursor.nodeType; + this.startPosition = cursor.startPosition; + this.endPosition = cursor.endPosition; + this.startIndex = cursor.startIndex; + this.endIndex = cursor.endIndex; + this.text = cursor.nodeText; + this.isNamed = cursor.nodeIsNamed; + this.isMissing = cursor.nodeIsMissing; + } + + get keyNode(): TreeCursorSyntaxNode | undefined { + if (this.type === 'flow_pair' || this.type === 'block_mapping_pair') { + return this.children.find((node) => node.fieldName === 'key'); + } + return undefined; + } + + get valueNode(): TreeCursorSyntaxNode | undefined { + if (this.type === 'flow_pair' || this.type === 'block_mapping_pair') { + return this.children.find((node) => node.fieldName === 'value'); + } + return undefined; + } + + get tag(): TreeCursorSyntaxNode | undefined { + let { previousSibling } = this; + + while (typeof previousSibling !== 'undefined' && previousSibling.type !== 'tag') { + ({ previousSibling } = previousSibling); + } + + return previousSibling; + } + + get anchor(): TreeCursorSyntaxNode | undefined { + let { previousSibling } = this; + + while (typeof previousSibling !== 'undefined' && previousSibling.type !== 'anchor') { + ({ previousSibling } = previousSibling); + } + + return previousSibling; + } + + get firstNamedChild(): TreeCursorSyntaxNode | undefined { + return this.children.find((node) => node.isNamed); + } + + setFieldName(cursor: NodeTreeCursor | WebTreeCursor) { + if (typeof cursor.currentFieldName === 'function') { + this.fieldName = cursor.currentFieldName(); + } else { + this.fieldName = cursor.currentFieldName; + } + return this; + } + + setHasError(cursor: NodeTreeCursor | WebTreeCursor) { + if (typeof cursor.currentNode === 'function') { + this.hasError = cursor.currentNode().hasError(); + } else { + this.hasError = cursor.currentNode.hasError(); + } + return this; + } + + setPreviousSibling(previousSibling: TreeCursorSyntaxNode | undefined) { + this.previousSibling = previousSibling; + } + + pushChildren(...children: TreeCursorSyntaxNode[]) { + this.children.push(...children); + } +} + +export default TreeCursorSyntaxNode; diff --git a/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/index.ts b/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/indirect/index.ts similarity index 80% rename from packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/index.ts rename to packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/indirect/index.ts index 2268e2186..f16ffe3a9 100644 --- a/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/index.ts +++ b/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/indirect/index.ts @@ -9,6 +9,8 @@ import YamlAstVisitor, { isNode as isAstNode, getNodeType as getAstNodeType, } from './visitors/YamlAstVisitor'; +import TreeCursorIterator from '../TreeCursorIterator'; +import TreeCursorSyntaxNode from '../TreeCursorSyntaxNode'; type Tree = WebTree | NodeTree; @@ -18,11 +20,14 @@ type Tree = WebTree | NodeTree; * Two traversals passes are needed to get from CST to ApiDOM. */ const analyze = (cst: Tree, { sourceMap = false } = {}): ParseResultElement => { + const cursor = cst.walk(); + const iterator = new TreeCursorIterator(cursor); + const rootNode = [...iterator].at(0) as TreeCursorSyntaxNode; const cstVisitor = CstVisitor(); const astVisitor = YamlAstVisitor(); const schema = JsonSchema(); - const yamlAst = visit(cst.rootNode, cstVisitor, { + const yamlAst = visit(rootNode, cstVisitor, { // @ts-ignore keyMap: cstKeyMap, nodePredicate: isCstNode, @@ -35,9 +40,7 @@ const analyze = (cst: Tree, { sourceMap = false } = {}): ParseResultElement => { return visit(yamlAst.rootNode, astVisitor, { // @ts-ignore keyMap: astKeyMap, - // @ts-ignore nodeTypeGetter: getAstNodeType, - // @ts-ignore nodePredicate: isAstNode, state: { sourceMap, diff --git a/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/visitors/CstVisitor.ts b/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/indirect/visitors/CstVisitor.ts similarity index 75% rename from packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/visitors/CstVisitor.ts rename to packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/indirect/visitors/CstVisitor.ts index e33fe5439..cdd32fe90 100644 --- a/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/visitors/CstVisitor.ts +++ b/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/indirect/visitors/CstVisitor.ts @@ -1,6 +1,4 @@ import stampit from 'stampit'; -import { SyntaxNode as NodeSyntaxNode } from 'tree-sitter'; -import { SyntaxNode as WebSyntaxNode } from 'web-tree-sitter'; import { YamlDirective, YamlStream, @@ -23,6 +21,8 @@ import { isNode as isCSTNode, } from '@swagger-api/apidom-ast'; +import TreeCursorSyntaxNode from '../../TreeCursorSyntaxNode'; + export const keyMap = { stream: ['children'], document: ['children'], @@ -37,8 +37,6 @@ export const isNode = (node: any) => Array.isArray(node) || isCSTNode(node); /* eslint-disable no-param-reassign */ -type SyntaxNode = WebSyntaxNode | NodeSyntaxNode; - const CstVisitor = stampit({ props: { schema: null, @@ -48,11 +46,7 @@ const CstVisitor = stampit({ * Private API. */ - const toPosition = (node: SyntaxNode | null): Position | null => { - if (node === null) { - return null; - } - + const toPosition = (node: TreeCursorSyntaxNode): Position => { const start = Point({ row: node.startPosition.row, column: node.startPosition.column, @@ -67,14 +61,9 @@ const CstVisitor = stampit({ return Position({ start, end }); }; - const kindNodeToYamlTag = (node: SyntaxNode) => { - let { previousSibling } = node; - - while (previousSibling !== null && previousSibling.type !== 'tag') { - ({ previousSibling } = previousSibling); - } - - const explicitName = previousSibling?.text || node.type === 'plain_scalar' ? '?' : '!'; + const kindNodeToYamlTag = (node: TreeCursorSyntaxNode) => { + const { tag: tagNode } = node; + const explicitName = tagNode?.text || node.type === 'plain_scalar' ? '?' : '!'; // eslint-disable-next-line no-nested-ternary const kind = node.type.endsWith('mapping') @@ -82,76 +71,51 @@ const CstVisitor = stampit({ : node.type.endsWith('sequence') ? YamlNodeKind.Sequence : YamlNodeKind.Scalar; - const position = toPosition(previousSibling); + const position = tagNode ? toPosition(tagNode) : null; return YamlTag({ explicitName, kind, position }); }; - const kindNodeToYamlAnchor = (node: SyntaxNode): YamlAnchor | null => { - let { previousSibling } = node; + const kindNodeToYamlAnchor = (node: TreeCursorSyntaxNode): YamlAnchor | null => { + const { anchor: anchorNode } = node; - while (previousSibling !== null && previousSibling.type !== 'anchor') { - ({ previousSibling } = previousSibling); - } + if (typeof anchorNode === 'undefined') return null; - if (previousSibling === null) { - return null; - } - - return YamlAnchor({ name: previousSibling.text, position: toPosition(previousSibling) }); + return YamlAnchor({ name: anchorNode.text, position: toPosition(anchorNode) }); }; - /** - * If web-tree-sitter will support keyNode and valueNode this can be further simplified. - */ const isKind = (ending: string) => (node: any) => typeof node?.type === 'string' && node.type.endsWith(ending); const isScalar = isKind('scalar'); const isMapping = isKind('mapping'); const isSequence = isKind('sequence'); - const getFieldFromNode = (fieldName: string, node: SyntaxNode): SyntaxNode | null => { - return `${fieldName}Node` in node - ? // @ts-ignore - node[`${fieldName}Node`] - : 'childForFieldName' in node - ? node.childForFieldName?.(fieldName) - : null; - }; - - const hasKeyValuePairEmptyKey = (node: SyntaxNode) => { + const hasKeyValuePairEmptyKey = (node: TreeCursorSyntaxNode) => { if (node.type !== 'block_mapping_pair' && node.type !== 'flow_pair') { return false; } - const keyNode = getFieldFromNode('key', node); - // keyNode was not explicitly provided; tag and anchor are missing too - return keyNode === null; + return typeof node.keyNode === 'undefined'; }; - const hasKeyValuePairEmptyValue = (node: SyntaxNode) => { + const hasKeyValuePairEmptyValue = (node: TreeCursorSyntaxNode) => { if (node.type !== 'block_mapping_pair' && node.type !== 'flow_pair') { return false; } - - const valueNode = getFieldFromNode('value', node); - // valueNode was not explicitly provided; tag and anchor are missing too - return valueNode === null; + return typeof node.valueNode === 'undefined'; }; - const createKeyValuePairEmptyKey = (node: SyntaxNode) => { + const createKeyValuePairEmptyKey = (node: TreeCursorSyntaxNode) => { const emptyPoint = Point({ row: node.startPosition.row, column: node.startPosition.column, char: node.startIndex, }); - const keyNode = getFieldFromNode('key', node); + const { keyNode } = node; const children = keyNode?.children || []; - // @ts-ignore - const tagNode: any | undefined = children.find(isKind('tag')); - // @ts-ignore - const anchorNode: any | undefined = children.find(isKind('anchor')); + const tagNode = children.find(isKind('tag')); + const anchorNode = children.find(isKind('anchor')); const tag = typeof tagNode !== 'undefined' ? YamlTag({ @@ -178,18 +142,16 @@ const CstVisitor = stampit({ }); }; - const createKeyValuePairEmptyValue = (node: SyntaxNode) => { + const createKeyValuePairEmptyValue = (node: TreeCursorSyntaxNode) => { const emptyPoint = Point({ row: node.endPosition.row, column: node.endPosition.column, char: node.endIndex, }); - const valueNode = getFieldFromNode('value', node); + const { valueNode } = node; const children = valueNode?.children || []; - // @ts-ignore - const tagNode: any | undefined = children.find(isKind('tag')); - // @ts-ignore - const anchorNode: any | undefined = children.find(isKind('anchor')); + const tagNode = children.find(isKind('tag')); + const anchorNode = children.find(isKind('anchor')); const tag = typeof tagNode !== 'undefined' ? YamlTag({ @@ -220,16 +182,12 @@ const CstVisitor = stampit({ * Public API. */ - this.enter = function enter(node: SyntaxNode) { + this.enter = function enter(node: TreeCursorSyntaxNode) { // missing anonymous literals from CST transformed into AST literal nodes - // WARNING: be aware that web-tree-sitter and tree-sitter node bindings have inconsistency - // in `SyntaxNode.isNamed` property. web-tree-sitter has it defined as method - // whether tree-sitter node binding has it defined as a boolean property. - // @ts-ignore - if ((typeof node.isNamed === 'function' && !node.isNamed()) || node.isNamed === false) { + if (node instanceof TreeCursorSyntaxNode && !node.isNamed) { const position = toPosition(node); const value = node.type || node.text; - const isMissing = node.isMissing(); + const { isMissing } = node; return Literal({ value, position, isMissing }); } @@ -238,13 +196,13 @@ const CstVisitor = stampit({ }; this.stream = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); return YamlStream({ children: node.children, position, - isMissing: node.isMissing(), + isMissing: node.isMissing, }); }, leave(stream: YamlStream) { @@ -253,7 +211,7 @@ const CstVisitor = stampit({ }; this.yaml_directive = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); const version = node?.firstNamedChild?.text || null; @@ -268,10 +226,10 @@ const CstVisitor = stampit({ }; this.tag_directive = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); - const tagHandleNode = node.child(0); - const tagPrefixNode = node.child(1); + const tagHandleNode = node.children[0]; + const tagPrefixNode = node.children[1]; const tagDirective = YamlDirective({ position, name: '%TAG', @@ -288,11 +246,11 @@ const CstVisitor = stampit({ }; this.reserved_directive = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); - const directiveNameNode = node.child(0); - const directiveParameter1Node = node.child(1); - const directiveParameter2Node = node.child(2); + const directiveNameNode = node.children[0]; + const directiveParameter1Node = node.children[1]; + const directiveParameter2Node = node.children[2]; return YamlDirective({ position, @@ -306,13 +264,13 @@ const CstVisitor = stampit({ }; this.document = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); return YamlDocument({ children: node.children, position, - isMissing: node.isMissing(), + isMissing: node.isMissing, }); }, leave(node: YamlDocument) { @@ -321,13 +279,13 @@ const CstVisitor = stampit({ }; this.block_node = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { return node.children; }, }; this.flow_node = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const [kindCandidate] = node.children.slice(-1); // kind node is present in flow node @@ -367,7 +325,7 @@ const CstVisitor = stampit({ }; this.block_mapping = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); const tag = kindNodeToYamlTag(node); const anchor = kindNodeToYamlAnchor(node); @@ -378,7 +336,7 @@ const CstVisitor = stampit({ tag, styleGroup: YamlStyleGroup.Block, style: YamlStyle.NextLine, - isMissing: node.isMissing(), + isMissing: node.isMissing, }); return this.schema.resolve(mappingNode); @@ -386,9 +344,9 @@ const CstVisitor = stampit({ }; this.block_mapping_pair = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); - const children: Array = [...node.children]; + const children: Array = [...node.children]; if (hasKeyValuePairEmptyKey(node)) { const keyNode = createKeyValuePairEmptyKey(node); @@ -403,13 +361,13 @@ const CstVisitor = stampit({ children, position, styleGroup: YamlStyleGroup.Block, - isMissing: node.isMissing(), + isMissing: node.isMissing, }); }, }; this.flow_mapping = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); const tag = kindNodeToYamlTag(node); const anchor = kindNodeToYamlAnchor(node); @@ -420,7 +378,7 @@ const CstVisitor = stampit({ tag, styleGroup: YamlStyleGroup.Flow, style: YamlStyle.Explicit, - isMissing: node.isMissing(), + isMissing: node.isMissing, }); return this.schema.resolve(mappingNode); @@ -428,9 +386,9 @@ const CstVisitor = stampit({ }; this.flow_pair = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); - const children: Array = [...node.children]; + const children: Array = [...node.children]; if (hasKeyValuePairEmptyKey(node)) { const keyNode = createKeyValuePairEmptyKey(node); @@ -445,7 +403,7 @@ const CstVisitor = stampit({ children, position, styleGroup: YamlStyleGroup.Flow, - isMissing: node.isMissing(), + isMissing: node.isMissing, }); }, }; @@ -457,7 +415,7 @@ const CstVisitor = stampit({ }; this.block_sequence = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); const tag = kindNodeToYamlTag(node); const anchor = kindNodeToYamlAnchor(node); @@ -475,7 +433,7 @@ const CstVisitor = stampit({ }; this.block_sequence_item = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { // flow or block node present; first node is always `-` literal if (node.children.length > 1) { return node.children; @@ -504,7 +462,7 @@ const CstVisitor = stampit({ }; this.flow_sequence = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); const tag = kindNodeToYamlTag(node); const anchor = kindNodeToYamlAnchor(node); @@ -528,7 +486,7 @@ const CstVisitor = stampit({ }; this.plain_scalar = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); const tag = kindNodeToYamlTag(node); const anchor = kindNodeToYamlAnchor(node); @@ -546,7 +504,7 @@ const CstVisitor = stampit({ }; this.single_quote_scalar = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); const tag = kindNodeToYamlTag(node); const anchor = kindNodeToYamlAnchor(node); @@ -564,7 +522,7 @@ const CstVisitor = stampit({ }; this.double_quote_scalar = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); const tag = kindNodeToYamlTag(node); const anchor = kindNodeToYamlAnchor(node); @@ -582,7 +540,7 @@ const CstVisitor = stampit({ }; this.block_scalar = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { const position = toPosition(node); const tag = kindNodeToYamlTag(node); const anchor = kindNodeToYamlAnchor(node); @@ -606,18 +564,18 @@ const CstVisitor = stampit({ }; this.comment = { - enter(node: SyntaxNode) { + enter(node: TreeCursorSyntaxNode) { return YamlComment({ content: node.text }); }, }; - this.ERROR = function ERROR(node: SyntaxNode, key: any, parent: any, path: string[]) { + this.ERROR = function ERROR(node: TreeCursorSyntaxNode, key: any, parent: any, path: string[]) { const position = toPosition(node); const errorNode = Error({ children: node.children, position, - isUnexpected: !node.hasError(), - isMissing: node.isMissing(), + isUnexpected: !node.hasError, + isMissing: node.isMissing, value: node.text, }); diff --git a/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/visitors/YamlAstVisitor.ts b/packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/indirect/visitors/YamlAstVisitor.ts similarity index 100% rename from packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/visitors/YamlAstVisitor.ts rename to packages/apidom-parser-adapter-yaml-1-2/src/syntactic-analysis/indirect/visitors/YamlAstVisitor.ts diff --git a/packages/apidom-parser-adapter-yaml-1-2/test/perf/index.cjs b/packages/apidom-parser-adapter-yaml-1-2/test/perf/index.cjs index 994e5c5af..923ba6f18 100644 --- a/packages/apidom-parser-adapter-yaml-1-2/test/perf/index.cjs +++ b/packages/apidom-parser-adapter-yaml-1-2/test/perf/index.cjs @@ -2,13 +2,13 @@ require('@babel/register')({ extensions: ['.js', '.ts'], rootMode: 'upward' }); const Benchmark = require('benchmark'); -const parseBench = require('./parse.cjs'); +const parseSyntacticAnalysisIndirectBench = require('./parse-syntactic-analysis-indirect.cjs'); const lexicalAnalysisBench = require('./lexical-analysis.cjs'); const suite = new Benchmark.Suite(); suite - .add(parseBench) + .add(parseSyntacticAnalysisIndirectBench) .add(lexicalAnalysisBench) // add listeners .on('cycle', function (event) { diff --git a/packages/apidom-parser-adapter-yaml-1-2/test/perf/parse.cjs b/packages/apidom-parser-adapter-yaml-1-2/test/perf/parse-syntactic-analysis-indirect.cjs similarity index 74% rename from packages/apidom-parser-adapter-yaml-1-2/test/perf/parse.cjs rename to packages/apidom-parser-adapter-yaml-1-2/test/perf/parse-syntactic-analysis-indirect.cjs index 792c16dfb..7f7054124 100644 --- a/packages/apidom-parser-adapter-yaml-1-2/test/perf/parse.cjs +++ b/packages/apidom-parser-adapter-yaml-1-2/test/perf/parse-syntactic-analysis-indirect.cjs @@ -4,18 +4,18 @@ const fs = require('node:fs'); const path = require('node:path'); const Benchmark = require('benchmark'); -const { parse } = require('../../src/adapter-node'); +const { parse: parseSyntacticAnalysisIndirect } = require('../../src/adapter-node'); const fixturePath = path.join(__dirname, 'fixtures/data.yaml'); const source = fs.readFileSync(fixturePath).toString(); const options = { - name: 'parse', + name: 'parse-syntactic-analysis-indirect', defer: true, minSamples: 600, - expected: '9.93 ops/sec ±1.11% (642 runs sampled)', + expected: '68.33 ops/sec ±0.29% (675 runs sampled)', async fn(deferred) { - await parse(source); + await parseSyntacticAnalysisIndirect(source); deferred.resolve(); }, }; diff --git a/packages/apidom-parser-adapter-yaml-1-2/test/syntactic-analysis/TreeCursorIterator.ts b/packages/apidom-parser-adapter-yaml-1-2/test/syntactic-analysis/TreeCursorIterator.ts new file mode 100644 index 000000000..8678f589e --- /dev/null +++ b/packages/apidom-parser-adapter-yaml-1-2/test/syntactic-analysis/TreeCursorIterator.ts @@ -0,0 +1,16 @@ +import { expect } from 'chai'; + +import { lexicalAnalysis } from '../../src/adapter-node'; +import TreeCursorIterator from '../../src/syntactic-analysis/TreeCursorIterator'; + +describe('syntactic-analysis', function () { + context('TreeCursorIterator', function () { + specify('should create optimized CST', async function () { + const cst = await lexicalAnalysis('[1, 2]'); + const cursor = cst.walk(); + const iterator = new TreeCursorIterator(cursor); + + expect([...iterator].at(0)).toMatchSnapshot(); + }); + }); +}); diff --git a/packages/apidom-parser-adapter-yaml-1-2/test/syntactic-analysis/__snapshots__/TreeCursorIterator.ts.snap b/packages/apidom-parser-adapter-yaml-1-2/test/syntactic-analysis/__snapshots__/TreeCursorIterator.ts.snap new file mode 100644 index 000000000..cfa783b38 --- /dev/null +++ b/packages/apidom-parser-adapter-yaml-1-2/test/syntactic-analysis/__snapshots__/TreeCursorIterator.ts.snap @@ -0,0 +1,605 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`syntactic-analysis TreeCursorIterator should create optimized CST 1`] = ` +TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [], + endIndex: 1, + endPosition: Object { + column: 1, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: false, + previousSibling: undefined, + startIndex: 0, + startPosition: Object { + column: 0, + row: 0, + }, + text: [, + type: [, + }, + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: integer_scalar, + }, + ], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: plain_scalar, + }, + ], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + fieldName: undefined, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: TreeCursorSyntaxNode { + children: Array [], + endIndex: 1, + endPosition: Object { + column: 1, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: false, + previousSibling: undefined, + startIndex: 0, + startPosition: Object { + column: 0, + row: 0, + }, + text: [, + type: [, + }, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: flow_node, + }, + TreeCursorSyntaxNode { + children: Array [], + endIndex: 3, + endPosition: Object { + column: 3, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: false, + previousSibling: TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: integer_scalar, + }, + ], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: plain_scalar, + }, + ], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + fieldName: undefined, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: TreeCursorSyntaxNode { + children: Array [], + endIndex: 1, + endPosition: Object { + column: 1, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: false, + previousSibling: undefined, + startIndex: 0, + startPosition: Object { + column: 0, + row: 0, + }, + text: [, + type: [, + }, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: flow_node, + }, + startIndex: 2, + startPosition: Object { + column: 2, + row: 0, + }, + text: ,, + type: ,, + }, + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [], + endIndex: 5, + endPosition: Object { + column: 5, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 4, + startPosition: Object { + column: 4, + row: 0, + }, + text: 2, + type: integer_scalar, + }, + ], + endIndex: 5, + endPosition: Object { + column: 5, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 4, + startPosition: Object { + column: 4, + row: 0, + }, + text: 2, + type: plain_scalar, + }, + ], + endIndex: 5, + endPosition: Object { + column: 5, + row: 0, + }, + fieldName: undefined, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: TreeCursorSyntaxNode { + children: Array [], + endIndex: 3, + endPosition: Object { + column: 3, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: false, + previousSibling: TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: integer_scalar, + }, + ], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: plain_scalar, + }, + ], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + fieldName: undefined, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: TreeCursorSyntaxNode { + children: Array [], + endIndex: 1, + endPosition: Object { + column: 1, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: false, + previousSibling: undefined, + startIndex: 0, + startPosition: Object { + column: 0, + row: 0, + }, + text: [, + type: [, + }, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: flow_node, + }, + startIndex: 2, + startPosition: Object { + column: 2, + row: 0, + }, + text: ,, + type: ,, + }, + startIndex: 4, + startPosition: Object { + column: 4, + row: 0, + }, + text: 2, + type: flow_node, + }, + TreeCursorSyntaxNode { + children: Array [], + endIndex: 6, + endPosition: Object { + column: 6, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: false, + previousSibling: TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [], + endIndex: 5, + endPosition: Object { + column: 5, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 4, + startPosition: Object { + column: 4, + row: 0, + }, + text: 2, + type: integer_scalar, + }, + ], + endIndex: 5, + endPosition: Object { + column: 5, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 4, + startPosition: Object { + column: 4, + row: 0, + }, + text: 2, + type: plain_scalar, + }, + ], + endIndex: 5, + endPosition: Object { + column: 5, + row: 0, + }, + fieldName: undefined, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: TreeCursorSyntaxNode { + children: Array [], + endIndex: 3, + endPosition: Object { + column: 3, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: false, + previousSibling: TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [ + TreeCursorSyntaxNode { + children: Array [], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: integer_scalar, + }, + ], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: plain_scalar, + }, + ], + endIndex: 2, + endPosition: Object { + column: 2, + row: 0, + }, + fieldName: undefined, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: TreeCursorSyntaxNode { + children: Array [], + endIndex: 1, + endPosition: Object { + column: 1, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: false, + previousSibling: undefined, + startIndex: 0, + startPosition: Object { + column: 0, + row: 0, + }, + text: [, + type: [, + }, + startIndex: 1, + startPosition: Object { + column: 1, + row: 0, + }, + text: 1, + type: flow_node, + }, + startIndex: 2, + startPosition: Object { + column: 2, + row: 0, + }, + text: ,, + type: ,, + }, + startIndex: 4, + startPosition: Object { + column: 4, + row: 0, + }, + text: 2, + type: flow_node, + }, + startIndex: 5, + startPosition: Object { + column: 5, + row: 0, + }, + text: ], + type: ], + }, + ], + endIndex: 6, + endPosition: Object { + column: 6, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 0, + startPosition: Object { + column: 0, + row: 0, + }, + text: [1, 2], + type: flow_sequence, + }, + ], + endIndex: 6, + endPosition: Object { + column: 6, + row: 0, + }, + fieldName: undefined, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 0, + startPosition: Object { + column: 0, + row: 0, + }, + text: [1, 2], + type: flow_node, + }, + ], + endIndex: 6, + endPosition: Object { + column: 6, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + previousSibling: undefined, + startIndex: 0, + startPosition: Object { + column: 0, + row: 0, + }, + text: [1, 2], + type: document, + }, + ], + endIndex: 6, + endPosition: Object { + column: 6, + row: 0, + }, + hasError: false, + isMissing: false, + isNamed: true, + startIndex: 0, + startPosition: Object { + column: 0, + row: 0, + }, + text: [1, 2], + type: stream, +} +`;