Skip to content

Commit

Permalink
perf(parser-adapter-yaml-1-2): use tree-sitter cursor for CST travers…
Browse files Browse the repository at this point in the history
…al (#2955)

Refs #691
  • Loading branch information
char0n committed Jul 15, 2023
1 parent e6a3d80 commit c46ae6a
Show file tree
Hide file tree
Showing 12 changed files with 923 additions and 115 deletions.
2 changes: 1 addition & 1 deletion packages/apidom-parser-adapter-yaml-1-2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -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 };
Expand Down
Original file line number Diff line number Diff line change
@@ -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 };
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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,
Expand All @@ -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,
Expand Down
Loading

0 comments on commit c46ae6a

Please sign in to comment.