-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement YAML CST to AST transformer
Refs #1
- Loading branch information
Showing
10 changed files
with
324 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import stampit from 'stampit'; | ||
|
||
import Node from '../../Node'; | ||
|
||
interface YamlAnchor extends Node { | ||
type: 'anchor'; | ||
name: string | null; | ||
} | ||
|
||
const YamlAnchor: stampit.Stamp<YamlAnchor> = stampit(Node, { | ||
statics: { | ||
type: 'anchor', | ||
}, | ||
props: { | ||
name: null, | ||
}, | ||
init({ name = null } = {}) { | ||
this.name = name; | ||
}, | ||
}); | ||
|
||
export default YamlAnchor; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,25 @@ | ||
import stampit from 'stampit'; | ||
import { isArray } from 'ramda-adjunct'; | ||
|
||
import Node from '../../Node'; | ||
import YamlDocument from './YamlDocument'; | ||
import { isDocument } from './predicates'; | ||
|
||
interface YamlStream extends Node { | ||
type: 'stream'; | ||
readonly content: Array<YamlDocument>; | ||
children: Array<YamlDocument>; | ||
} | ||
|
||
const YamlStream: stampit.Stamp<YamlStream> = stampit(Node, { | ||
statics: { | ||
type: 'stream', | ||
}, | ||
methods: { | ||
get content(): Array<YamlDocument> { | ||
return isArray(this.children) ? this.children.filter(isDocument) : []; | ||
}, | ||
}, | ||
}); | ||
|
||
export default YamlStream; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
248 changes: 248 additions & 0 deletions
248
apidom/packages/apidom-ast/src/transformers/tree-sitter-yaml.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
import stampit from 'stampit'; | ||
import { either, flatten, lensProp, over } from 'ramda'; | ||
import { isArray, isFunction, isFalse } from 'ramda-adjunct'; | ||
import { SyntaxNode, Tree } from 'tree-sitter'; | ||
|
||
import YamlStream from '../nodes/yaml/YamlStream'; | ||
import YamlDocument from '../nodes/yaml/YamlDocument'; | ||
import YamlSequence from '../nodes/yaml/YamlSequence'; | ||
import YamlMapping from '../nodes/yaml/YamlMapping'; | ||
import YamlKeyValuePair from '../nodes/yaml/YamlKeyValuePair'; | ||
import YamlTag, { YamlNodeKind } from '../nodes/yaml/YamlTag'; | ||
import YamlAnchor from '../nodes/yaml/YamlAnchor'; | ||
import YamlScalar from '../nodes/yaml/YamlScalar'; | ||
import { YamlStyle, YamlStyleGroup } from '../nodes/yaml/YamlStyle'; | ||
import ParseResult from '../ParseResult'; | ||
import Position, { Point } from '../Position'; | ||
import Literal from '../Literal'; | ||
import { isNode, visit } from '../visitor'; | ||
|
||
export const keyMap = { | ||
stream: ['children'], | ||
document: ['children'], | ||
mapping: ['children'], | ||
keyValuePair: ['children'], | ||
sequence: ['children'], | ||
}; | ||
|
||
const Visitor = stampit({ | ||
init() { | ||
/** | ||
* Private API. | ||
*/ | ||
|
||
const toPosition = (node: SyntaxNode | null): Position | null => { | ||
if (node === null) { | ||
return null; | ||
} | ||
|
||
const start = Point({ | ||
row: node.startPosition.row, | ||
column: node.startPosition.column, | ||
char: node.startIndex, | ||
}); | ||
const end = Point({ | ||
row: node.endPosition.row, | ||
column: node.endPosition.column, | ||
char: node.endIndex, | ||
}); | ||
|
||
return Position({ start, end }); | ||
}; | ||
|
||
const toTag = (node: SyntaxNode): YamlTag | null => { | ||
let { previousSibling } = node; | ||
|
||
while (previousSibling !== null && previousSibling.type !== 'tag') { | ||
({ previousSibling } = previousSibling); | ||
} | ||
|
||
if (previousSibling === null) { | ||
return null; | ||
} | ||
|
||
// eslint-disable-next-line no-nested-ternary | ||
const kind = node.type.endsWith('mapping') | ||
? YamlNodeKind.Mapping | ||
: node.type.endsWith('sequence') | ||
? YamlNodeKind.Sequence | ||
: YamlNodeKind.Scalar; | ||
const position = toPosition(previousSibling); | ||
|
||
return YamlTag({ name: previousSibling.text, kind, position }); | ||
}; | ||
|
||
const toAnchor = (node: SyntaxNode): YamlAnchor | null => { | ||
let { previousSibling } = node; | ||
|
||
while (previousSibling !== null && previousSibling.type !== 'anchor') { | ||
({ previousSibling } = previousSibling); | ||
} | ||
|
||
if (previousSibling === null) { | ||
return null; | ||
} | ||
|
||
return YamlAnchor({ name: previousSibling.text, position: toPosition(previousSibling) }); | ||
}; | ||
|
||
const flattenChildren = over(lensProp('children'), flatten); | ||
|
||
/** | ||
* Public API. | ||
*/ | ||
|
||
this.enter = function enter(node: SyntaxNode) { | ||
// 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 ((isFunction(node.isNamed) && !node.isNamed()) || isFalse(node.isNamed)) { | ||
const position = toPosition(node); | ||
const value = node.type || node.text; | ||
const isMissing = node.isMissing(); | ||
|
||
return Literal({ value, position, isMissing }); | ||
} | ||
|
||
return undefined; | ||
}; | ||
|
||
this.stream = { | ||
enter(node: SyntaxNode) { | ||
const position = toPosition(node); | ||
|
||
return YamlStream({ | ||
children: node.children, | ||
position, | ||
isMissing: node.isMissing(), | ||
}); | ||
}, | ||
}; | ||
|
||
this.document = { | ||
enter(node: SyntaxNode) { | ||
const position = toPosition(node); | ||
|
||
return YamlDocument({ | ||
children: node.children, | ||
position, | ||
isMissing: node.isMissing(), | ||
}); | ||
}, | ||
leave(node: YamlDocument) { | ||
return flattenChildren(node); | ||
}, | ||
}; | ||
|
||
this.block_node = { | ||
enter(node: SyntaxNode) { | ||
return node.children; | ||
}, | ||
}; | ||
|
||
this.flow_node = { | ||
enter(node: SyntaxNode) { | ||
return node.children; | ||
}, | ||
}; | ||
|
||
this.tag = { | ||
enter() { | ||
return null; | ||
}, | ||
}; | ||
|
||
this.anchor = { | ||
enter() { | ||
return null; | ||
}, | ||
}; | ||
|
||
this.block_mapping = { | ||
enter(node: SyntaxNode) { | ||
const position = toPosition(node); | ||
const tag = toTag(node); | ||
const anchor = toAnchor(node); | ||
|
||
return YamlMapping({ | ||
children: node.children, | ||
position, | ||
anchor, | ||
tag, | ||
styleGroup: YamlStyleGroup.Block, | ||
style: YamlStyle.NextLine, | ||
isMissing: node.isMissing(), | ||
}); | ||
}, | ||
}; | ||
|
||
this.block_mapping_pair = { | ||
enter(node: SyntaxNode) { | ||
const position = toPosition(node); | ||
|
||
return YamlKeyValuePair({ | ||
children: node.children, | ||
position, | ||
isMissing: node.isMissing(), | ||
}); | ||
}, | ||
}; | ||
|
||
this.keyValuePair = { | ||
leave(node: YamlKeyValuePair) { | ||
return flattenChildren(node); | ||
}, | ||
}; | ||
|
||
this.flow_sequence = { | ||
enter(node: SyntaxNode) { | ||
const position = toPosition(node); | ||
const tag = toTag(node); | ||
const anchor = toAnchor(node); | ||
|
||
return YamlSequence({ | ||
children: node.children, | ||
position, | ||
anchor, | ||
tag, | ||
styleGroup: YamlStyleGroup.Flow, | ||
style: YamlStyle.Explicit, | ||
}); | ||
}, | ||
}; | ||
|
||
this.sequence = { | ||
leave(node: YamlSequence) { | ||
return flattenChildren(node); | ||
}, | ||
}; | ||
|
||
this.plain_scalar = { | ||
enter(node: SyntaxNode) { | ||
const position = toPosition(node); | ||
const tag = toTag(node); | ||
const anchor = toAnchor(node); | ||
|
||
return YamlScalar({ | ||
content: node.text, | ||
anchor, | ||
tag, | ||
position, | ||
styleGroup: YamlStyleGroup.Flow, | ||
style: YamlStyle.Plain, | ||
}); | ||
}, | ||
}; | ||
}, | ||
}); | ||
|
||
export const transform = (cst: Tree): ParseResult => { | ||
const visitor = Visitor(); | ||
const nodePredicate = either(isArray, isNode); | ||
// @ts-ignore | ||
const rootNode = visit(cst.rootNode, visitor, { keyMap, nodePredicate }); | ||
|
||
return ParseResult({ children: [rootNode] }); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
apidom/packages/apidom-ast/test/transformers/tree-sitter-json.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
// @ts-ignore | ||
import Parser from 'tree-sitter'; | ||
import { assert } from 'chai'; | ||
// @ts-ignore | ||
|
Oops, something went wrong.