Skip to content

Instantly share code, notes, and snippets.

@conartist6
Last active October 13, 2023 16:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save conartist6/b548adbf385d1ed6e226b25b67f8b46b to your computer and use it in GitHub Desktop.
Save conartist6/b548adbf385d1ed6e226b25b67f8b46b to your computer and use it in GitHub Desktop.
Prettier agAST example
// 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));
{
"language": "JS",
"type": "ForOfStatement",
"children": [
{
"type": "Reference",
"value": "for"
},
{
"type": "Trivia",
"value": " "
},
{
"type": "Reference",
"value": "open"
},
{
"type": "Reference",
"value": "left"
},
{
"type": "Trivia",
"value": " "
},
{
"type": "Reference",
"value": "of"
},
{
"type": "Trivia",
"value": " "
},
{
"type": "Reference",
"value": "right"
},
{
"type": "Reference",
"value": "close"
},
{
"type": "Reference",
"value": "semi"
}
],
"properties": {
"for": {
"language": "JS",
"type": "Keyword",
"children": [
{
"type": "String",
"value": "for"
}
],
"properties": {},
"attributes": {}
},
"open": {
"language": "JS",
"type": "Punctuator",
"children": [
{
"type": "String",
"value": "("
}
],
"properties": {},
"attributes": {}
},
"left": {
"language": "JS",
"type": "Identifier",
"children": [
{
"type": "Escape",
"value": {
"cooked": "a",
"raw": "\\u0061"
}
},
{
"type": "String",
"value": "sync"
}
],
"properties": {},
"attributes": {}
},
"of": {
"language": "JS",
"type": "Keyword",
"children": [
{
"type": "String",
"value": "of"
}
],
"properties": {},
"attributes": {}
},
"right": {
"language": "JS",
"type": "ArrayExpression",
"children": [
{
"type": "Reference",
"value": "open"
},
{
"type": "Reference",
"value": "close"
}
],
"properties": {
"open": {
"language": "JS",
"type": "Punctuator",
"children": [
{
"type": "String",
"value": "["
}
],
"properties": {},
"attributes": {}
},
"close": {
"language": "JS",
"type": "Punctuator",
"children": [
{
"type": "String",
"value": "]"
}
],
"properties": {},
"attributes": {}
}
},
"attributes": {}
},
"close": {
"language": "JS",
"type": "Punctuator",
"children": [
{
"type": "String",
"value": ")"
}
],
"properties": {},
"attributes": {}
},
"semi": {
"language": "JS",
"type": "Punctuator",
"children": [
{
"type": "String",
"value": ";"
}
],
"properties": {},
"attributes": {}
}
},
"attributes": {}
}

Changes:

node = { language, type, ... }
(was node = { tagName: { language, production }, ... })

children = [{ type: 'Escape', value: { cooked: 'str', raw: 'rawstr' }]
(was children = [{ type: 'Escape', value: 'rawstr' }])

Removed value='async' attribute from Identifier

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment