|
// Node objects are immutable |
|
// Also immutable are: properties, attributes, children, terminals, and any arrays |
|
// Immutable trees can be cached as valid with regard to a particular grammar! |
|
let freeze = (node) => Object.freeze(Object.seal(node)); |
|
|
|
// Helpers for constructing agAST trees |
|
let t = { |
|
// Tokens are really just nodes with non-ref children |
|
token: (type, str, attributes = {}) => t.node(type, [t.str([str])], {}, attributes), |
|
node: (type, children = [], properties = {}, attributes = {}) => |
|
freeze({ |
|
// nodes are truly monomorphic, making tools faster |
|
...type, |
|
children: freeze(children), |
|
properties: freeze(properties), |
|
attributes: freeze(attributes), |
|
}), |
|
id: ([str]) => { |
|
const { 0: language, 1: type } = str.split(':'); |
|
return { language, type }; |
|
}, |
|
|
|
// These are the types possible in the children array: |
|
ref: ([property]) => freeze({ type: 'Reference', value: property }), |
|
gap: ([property]) => freeze({ type: 'Gap', value: property }), |
|
str: ([str]) => freeze({ type: 'String', value: str }), |
|
trivia: ([str]) => freeze({ type: 'Trivia', value: str }), |
|
esc: (raw, cooked) => freeze({ type: 'Escape', value: { raw, cooked } }), |
|
}; |
|
|
|
// Input: for (\u0061sync of []); |
|
let tree = t.node( |
|
// Language:Production |
|
t.id`JS:ForOfStatement`, |
|
|
|
// The children array creates a total ordering of all nodes and tokens |
|
// It ensures that any document can be printed without needing a grammar |
|
[ |
|
t.ref`for`, |
|
t.trivia` `, |
|
t.ref`open`, |
|
t.ref`left`, |
|
t.trivia` `, |
|
t.ref`of`, |
|
t.trivia` `, |
|
t.ref`right`, |
|
t.ref`close`, |
|
t.ref`semi`, |
|
], |
|
// The properties object allows for fast named lookups of nodes and tokens |
|
{ |
|
for: t.token(t.id`JS:Keyword`, 'for'), |
|
open: t.token(t.id`JS:Punctuator`, '('), |
|
left: t.node( |
|
t.id`JS:Identifier`, |
|
[ |
|
// Escapes have internal structure, but it is essentially |
|
// embedded "onto" them not "into" them |
|
t.esc('\\u0061', 'a'), |
|
t.str`sync`, |
|
], |
|
), |
|
of: t.token(t.id`JS:Keyword`, 'of'), |
|
right: t.node(t.id`JS:ArrayExpression`, [t.ref`open`, t.ref`close`], { |
|
open: t.token(t.id`JS:Punctuator`, '['), |
|
close: t.token(t.id`JS:Punctuator`, ']'), |
|
}), |
|
close: t.token(t.id`JS:Punctuator`, ')'), |
|
semi: t.token(t.id`JS:Punctuator`, ';'), |
|
}, |
|
); |
|
|
|
console.log(JSON.stringify(tree, undefined, 2)); |