Skip to content

Instantly share code, notes, and snippets.

@kenwebb
Last active December 16, 2024 12:43
Show Gist options
  • Save kenwebb/59bf08805972a394fe1e6311bb29f68a to your computer and use it in GitHub Desktop.
Save kenwebb/59bf08805972a394fe1e6311bb29f68a to your computer and use it in GitHub Desktop.
JavaScript - Abstract Syntax Tree (AST)
Ken Webb
10 Dec 2024
READMEksw.txt
I am trying to download and install the acorn parser.
https://github.com/acornjs/acorn
cd ~/nodespace
mkdir acorn
cd acorn
git clone https://github.com/acornjs/acorn.git
cd acorn
npm install
- this worked but it took a long time (a few minutes with no or minimal progress messages)
- it generated various files including:
~/nodespace/acorn/acorn/acorn/dist/acorn.js
- this may be the file I can add to Xholon that will work in the browser
- I copied this acorn.js file into Xholon at two places:
~/gwtspace/Xholon/Xholon/src/org/public/lib/
~/gwtspace/Xholon/Xholon/war/xholon/lib/
- then I ran the runtime:
http://127.0.0.1:8080/Xholon.html?app=JavaScript+-+Abstract+Syntax+Tree+(AST)&src=lstr&gui=clsc&jslib=acorn
- this worked
- in Dev Tools:
var code = `
UOR.K=UOR.J+(DT)*(RRR.JK-SSR.JK) // L
IAR.K=IAR.J+(DT)*(SRR.JK-SSR.JK) // L
SSR.KL=UOR.K/DUR // R
RSR.K=RSR.J+(DT)*(1/DRR)*(RRR.JK-RSR.J) // L
IDR.K=(AIR)*(RSR.K) // A
ISR.K=IDR.K-IAR.K // A
PDR.KL=(ISR.K/DIR)+RSR.K // R
UOD.K=UOD.J+(DT)*(PDR.JK-SRR.JK) // L
SRR.KL=UOD.K/DUD // R
INSTP.K=STEP(20,1) // timing
RRR.KL=100.0+INSTP.K // R
`;
var ast = acorn.Parser.parse(code);
console.log(ast); // YES
- it generated 11 objects like the following:
{
"type": "ExpressionStatement",
"start": 5,
"end": 37,
"expression": {
"type": "AssignmentExpression",
"start": 5,
"end": 37,
"operator": "=",
"left": {
"type": "MemberExpression",
"start": 5,
"end": 10,
"object": {
"type": "Identifier",
"start": 5,
"end": 8,
"name": "UOR"
},
"property": {
"type": "Identifier",
"start": 9,
"end": 10,
"name": "K"
},
"computed": false,
"optional": false
},
"right": {
"type": "BinaryExpression",
"start": 11,
"end": 37,
"left": {
"type": "MemberExpression",
"start": 11,
"end": 16,
"object": {
"type": "Identifier",
"start": 11,
"end": 14,
"name": "UOR"
},
"property": {
"type": "Identifier",
"start": 15,
"end": 16,
"name": "J"
},
"computed": false,
"optional": false
},
"operator": "+",
"right": {
"type": "BinaryExpression",
"start": 17,
"end": 37,
"left": {
"type": "Identifier",
"start": 18,
"end": 20,
"name": "DT"
},
"operator": "*",
"right": {
"type": "BinaryExpression",
"start": 23,
"end": 36,
"left": {
"type": "MemberExpression",
"start": 23,
"end": 29,
"object": {
"type": "Identifier",
"start": 23,
"end": 26,
"name": "RRR"
},
"property": {
"type": "Identifier",
"start": 27,
"end": 29,
"name": "JK"
},
"computed": false,
"optional": false
},
"operator": "-",
"right": {
"type": "MemberExpression",
"start": 30,
"end": 36,
"object": {
"type": "Identifier",
"start": 30,
"end": 33,
"name": "SSR"
},
"property": {
"type": "Identifier",
"start": 34,
"end": 36,
"name": "JK"
},
"computed": false,
"optional": false
}
}
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!--Xholon Workbook http://www.primordion.com/Xholon/gwt/ MIT License, Copyright (C) Ken Webb, Mon Dec 16 2024 07:42:30 GMT-0500 (Eastern Standard Time)-->
<XholonWorkbook>
<Notes><![CDATA[
Xholon
------
Title: JavaScript - Abstract Syntax Tree (AST)
Description:
Url: http://www.primordion.com/Xholon/gwt/
InternalName: 59bf08805972a394fe1e6311bb29f68a
Keywords:
My Notes
--------
10 Dec 2024
http://127.0.0.1:8080/Xholon.html?app=JavaScript+-+Abstract+Syntax+Tree+(AST)&src=lstr&gui=clsc&jslib=acorn
Note: the URL must have "&jslib=acorn" at the end
Learn how to parse JavaScript code using an AST tool such as a parser.
I want to use this capability in my "Industrial Dynamics - Chapter 15 - Xholonish - SubtreeGenerator" workbook.
### acorn
see ~/nodespace/acorn/READMEksw.txt for details on what I did to install and use acorn
it works in Dev Tools
### References
(1) search: JavaScript Abstract Syntax Tree
(2) https://astexplorer.net/
Type JavaScript into left editor, and see the AST in the right pane.
The following code worked:
UOR.K=UOR.J+(DT)*(RRR.JK-SSR.JK) // L
IAR.K=IAR.J+(DT)*(SRR.JK-SSR.JK) // L
SSR.KL=UOR.K/DUR // R
RSR.K=RSR.J+(DT)*(1/DRR)*(RRR.JK-RSR.J) // L
IDR.K=(AIR)*(RSR.K) // A
ISR.K=IDR.K-IAR.K // A
PDR.KL=(ISR.K/DIR)+RSR.K // R
UOD.K=UOD.J+(DT)*(PDR.JK-SRR.JK) // L
SRR.KL=UOD.K/DUD // R
INSTP.K=STEP(20,1) // timing
RRR.KL=100.0+INSTP.K // R
- each line is an Expression Statement with its subtree
- it uses Parser: acorn-8.7.0
(3) https://www.digitalocean.com/community/tutorials/js-traversing-ast
Read JavaScript Source Code, Using an AST
Published on February 9, 2019
small example
Open Source ECMAScript parsers
they use the acorn parser
I should be able to use this page to help me get started.
(4) https://dev.to/marvinjude/abstract-syntax-trees-and-practical-applications-in-javascript-4a3
Abstract Syntax Trees and Practical Applications in JavaScript
Jude Agboola
Posted on Oct 21, 2023
- it uses astexplorer as a learning tool
- he uses the swc parser, rather than acorn
- the examples he works through are not that relevant to my needs
(5) https://swc.rs/
SWC
Rust-based platform for the Web
SWC is an extensible Rust-based platform for the next generation of fast developer tools.
It's used by tools like Next.js, Parcel, and Deno, as well as companies like Vercel, ByteDance, Tencent, Shopify, Trip.com, and more.
SWC can be used for both compilation and bundling.
For compilation, it takes JavaScript / TypeScript files using modern JavaScript features and outputs valid code that is supported by all major browsers.
) https://swc.rs/docs/usage/core#parse
(6) https://github.com/acornjs/acorn
A small, fast, JavaScript-based JavaScript parser
) https://github.com/acornjs/acorn/tree/master/acorn-walk/
]]></Notes>
<_-.XholonClass>
<PhysicalSystem/>
<AstParser/>
<!-- System Dynamics node types: Level Rate Auxiliary Constant -->
<SdNode>
<SdLevel/>
<SdRate/>
<SdAux/>
<SdConst/>
</SdNode>
</_-.XholonClass>
<xholonClassDetails>
<Avatar><Color>black</Color></Avatar>
<PhysicalSystem><Color>white</Color></PhysicalSystem>
<SdLevel><Color>yellowgreen</Color></SdLevel>
<SdRate><Color>orangered</Color></SdRate>
<SdAux><Color>purple</Color></SdAux>
<SdConst><Color>gold</Color></SdConst>
</xholonClassDetails>
<PhysicalSystem>
<!-- DYNAMO constants -->
DUR=1.0,
DRR=2.0,
AIR=3.0,
DIR=2.0,
DUD=2.0
<SdConst roleName="DT" vall="0.5"/>
<SdConst roleName="DUR" vall="1.0"/>
<SdConst roleName="AIR" vall="3.0"/>
<SdConst roleName="DIR" vall="2.0"/>
<SdConst roleName="DUD" vall="2.0"/>
<SdConst roleName="DRR" vall="2.0"/>
<AstParser>
<Attribute_String>
UOR.K=UOR.J+(DT)*(RRR.JK-SSR.JK) // L
IAR.K=IAR.J+(DT)*(SRR.JK-SSR.JK) // L
SSR.KL=UOR.K/DUR // R
RSR.K=RSR.J+(DT)*(1/DRR)*(RRR.JK-RSR.J) // L
IDR.K=(AIR)*(RSR.K) // A
ISR.K=IDR.K-IAR.K // A
PDR.KL=(ISR.K/DIR)+RSR.K // R
UOD.K=UOD.J+(DT)*(PDR.JK-SRR.JK) // L
SRR.KL=UOD.K/DUD // R
INSTP.K=STEP(20,1) // timing
RRR.KL=100.0+INSTP.K // R
</Attribute_String>
</AstParser>
</PhysicalSystem>
<AstParserbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
// 16 Dec 2024 Note: I had written 6 lines of code before the above line; it contained less-than and greater-than symbols; failed to load all the behavior nodes
// and, after fixing that line, all 6 lines were lost when I loaded the workbook
// LESSON - DO NOT include any text before the opening XML tag
TESTING = "AstParser"; // this is available in all my Xholon behavior nodes
NODES = this;
JKL = {J:101, K:102}; // yes - JKL is available in all my Xholon behavior nodes
// I need to manually (for now) add the following:
UOR = {J:0, K:0};
var me, code, beh = {
postConfigure: function() {
me = this.cnode.parent();
me.println(me.name());
code = me.first().text();
me.println(code);
console.log(JKL); // NODES, ABC, $wnd.ABC, DEF, GHI
const ast = $wnd.acorn.Parser.parse(code, {ecmaVersion: 2020});
const leftobjnamearr = [];
const leftpropnamearr = [];
const allportsarr = []; // an array where each item is itself an array
console.log(ast);
const body = ast.body; // an array
for (var i = 0; i < body.length; i++) {
const item = body[i];
const portsarr = [];
const extype = item.expression.type;
const exoperator = item.expression.operator;
const exleft = item.expression.left;
const exright = item.expression.right;
const leftobjname = item.expression.left.object.name;
const leftpropname = item.expression.left.property.name;
const rightoperator = item.expression.right.operator;
// add object names to portsarr
this.collectPorts(exright, portsarr);
leftobjnamearr.push(leftobjname);
leftpropnamearr.push(leftpropname);
me.println("type:" + extype + " operator:" + exoperator + " obj.prop:" + leftobjname + "." + leftpropname + " rightop:" + rightoperator);
allportsarr.push(portsarr);
}
me.println(leftobjnamearr.join(","));
const nodetype = (nodename, prop, myportsarr) => {
//const myportsarr = myportsstr.split(",");
let ntype = "SdNode";
switch (prop) {
case "K": ntype = myportsarr[0] == nodename ? "SdLevel" : "SdAux"; break;
case "KL": ntype = "SdRate"; break;
default: ntype = "SdConst"; break;
}
return ntype;
}
// <Script>initializerscriptstr</Script/>
// $wnd.xh.root().append("<Script>UOR2 = {J:1, K:2}; IAR2 = {J:3.0, K:4.0}; SSR2 = {KL:5.0}; IDR2 = {K:6.0}; console.log(UOR2, IAR2, SSR2, IDR2)</Script>"); // TEST this works!
// TODO use J K or JK KL depending on leftpropnamearr[index]
const initializerscriptstr =
leftobjnamearr.map((item, index) => item + "x={" + (leftpropnamearr[index] == "K" ? "J" : "JK") + ":11," + leftpropnamearr[index] + ":12" + "}", "").join("\n");
console.log(initializerscriptstr);
const xstr = "<Script>" + initializerscriptstr + "</Script>";
console.log(xstr);
$wnd.xh.root().append(xstr);
const nodes = "<_-.nodes>\n"
+ leftobjnamearr.map((item, index) => '<' + nodetype(item, leftpropnamearr[index], allportsarr[index]) + ' roleName="' + item
+ '" prop="' + leftpropnamearr[index]
+ '" ports="' + allportsarr[index]
+ '"/>', "").join("\n")
+ "\n</_-.nodes>";
me.println(nodes);
me.parent().append(nodes);
let newnode = me.next();
while (newnode) {
let portsarr = newnode.ports.split(",");
me.println(portsarr.length);
//if (portsarr[0]) {
for (var i = 0; i < portsarr.length; i++) {
const xpathexpr = "../SdNode[@roleName='" + portsarr[i] + "']";
me.println(xpathexpr);
const remoteNode = newnode.xpath(xpathexpr);
newnode[portsarr[i]] = remoteNode;
}
newnode[newnode.prop] = 0; // J K JK KL
switch (newnode.prop) {
case "K": newnode["J"] = 0; break;
case "KL": newnode["JK"] = 0; break;
default: break;
}
newnode = newnode.next();
}
$wnd.xh.root().append("<Script>UOR2 = {J:1, K:2}; IAR2 = {J:3.0, K:4.0}; SSR2 = {KL:5.0}; IDR2 = {K:6.0}; console.log(UOR2, IAR2, SSR2, IDR2)</Script>"); // TEST this works!
},
act: function() {
//me.println(me.name());
me.println(TESTING);
},
// anode an AST node item.expression.right
// ports arr
collectPorts: function(node, parr) {
if (node.type == "Identifier") {
parr.push(node.name);
}
else if (node.object?.type == "Identifier") {
parr.push(node.object.name);
}
if (node.left) {
this.collectPorts(node.left, parr);
}
if (node.right) {
this.collectPorts(node.right, parr)
}
}
}
//# sourceURL=AstParserbehavior.js
]]></AstParserbehavior>
<SdLevelbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var me, beh = {
postConfigure: function() {
me = this.cnode.parent();
me.println(me.name());
me.J = Number(me.J);
me.K = Number(me.K);
TESTING += me.role();
JKL.J += 11;
console.log(JKL);
},
act: function() {
// do the DYNAMO function
me.println(me.role() + "." + me.prop + ": " + me.K);
if (me.DTxxxxxxxxxx) {
me.K = me.J + Number(me.DT.vall);
}
switch (me.role()) {
case "UOR":
// UOR.K=UOR.J+(DT)*(RRR.JK-SSR.JK) // L
me.UOR.K=me.UOR.J+(me.DT.vall)*(me.RRR.JK-me.SSR.JK);
break;
case "IAR":
// IAR.K=IAR.J+(DT)*(SRR.JK-SSR.JK) // L
me.IAR.K=me.IAR.J+(me.DT.vall)*(me.SRR.JK-me.SSR.JK);
break;
case "RSR":
// RSR.K=RSR.J+(DT)*(1/DRR)*(RRR.JK-RSR.J) // L
me.RSR.K=me.RSR.J+(me.DT.vall)*(1/me.DRR.vall)*(me.RRR.JK-me.RSR.J)
break;
case "UOD":
// UOD.K=UOD.J+(DT)*(PDR.JK-SRR.JK) // L
me.UOD.K=me.UOD.J+(me.DT.vall)*(me.PDR.JK-me.SRR.JK)
break;
default: break;
}
me.println("UOR2 .J .K " + UOR2.J + " " + UOR2.K); // YES
},
preAct: function() {
me.J = me.K;
}
}
//# sourceURL=SdLevelbehavior.js
]]></SdLevelbehavior>
<SdRatebehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var me, beh = {
postConfigure: function() {
me = this.cnode.parent();
me.println(me.name());
me.JK = Number(me.JK);
me.KL = Number(me.KL);
},
act: function() {
// do the DYNAMO function
me.println(me.role() + "." + me.prop + ": " + me.KL);
switch (me.role()) {
case "SSR":
// SSR.KL=UOR.K/DUR // R
me.KL=me.UOR.K/me.DUR.vall
break;
case "PDR":
// PDR.KL=(ISR.K/DIR)+RSR.K // R
me.KL=(me.ISR.K/me.DIR.vall)+me.RSR.K
break;
case "SRR":
// SRR.KL=UOD.K/DUD // R
me.KL=me.UOD.K/me.DUD.vall
break;
default: break;
}
},
preAct: function() {
me.JK = me.KL;
}
}
//# sourceURL=SdRatebehavior.js
]]></SdRatebehavior>
<SdAuxbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var me, beh = {
postConfigure: function() {
me = this.cnode.parent();
me.println(me.name());
me.J = Number(me.J);
me.K = Number(me.K);
},
act: function() {
// do the DYNAMO function
me.println(me.role() + "." + me.prop + ": " + me.K);
switch (me.role()) {
case "IDR":
// IDR.K=(AIR)*(RSR.K) // A
me.K = me.AIR.vall * me.RSR.K
break;
case "ISR":
// ISR.K=IDR.K-IAR.K // A
me.K=me.IDR.K-me.IAR.K
break;
case "INSTP":
// INSTP.K=STEP(20,1) // timing
me.K = 1
break;
default: break;
}
},
preAct: function() {
//me.J = me.K;
}
}
//# sourceURL=SdAuxbehavior.js
]]></SdAuxbehavior>
<SvgClient><Attribute_String roleName="svgUri"><![CDATA[data:image/svg+xml,
<svg width="100" height="50" xmlns="http://www.w3.org/2000/svg">
<g>
<title>AstParser</title>
<rect id="PhysicalSystem/AstParser" fill="#98FB98" height="50" width="50" x="25" y="0"/>
<g>
<title>UOR</title>
<rect id="PhysicalSystem/XhNode[@roleName='UOR']" fill="#6AB06A" height="50" width="10" x="80" y="0"/>
</g>
</g>
</svg>
]]></Attribute_String><Attribute_String roleName="setup">${MODELNAME_DEFAULT},${SVGURI_DEFAULT}</Attribute_String></SvgClient>
</XholonWorkbook>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment