dal-ast-js 0.0.2-dev → 0.0.4-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,25 @@
1
- # program-synthesizer-python
2
- Currently, this tool builds an Abstract Syntax Tree representation of a script written in the Design Abstraction Language (DAL).
1
+ # DAL Abstract Syntax Tree Library
2
+ This tool currently takes a design defined in a Design Abstraction Language (DAL) and produces an Abstract Syntax Tree (AST).
3
3
 
4
- # Usage
5
- Currently, I am using test cases to establish functionality.
4
+ To achieve this it implements the following tools:
5
+ - Lexer
6
+ - Parser
7
+
8
+ This tool will be extended to also do the following:
9
+ - Syntactic Validation:
10
+ - Provide metadata about the result of the lexing and parsing to identify syntax errors. This will be used by the workbench to visually provide feedback in realtime.
11
+ - Semantic Validation:
12
+ - Validate the design by ensuring that it is internally consistent in realtime.
13
+
14
+ This initial commit contains a working workflow but it is clearly not complete. I wanted to commit a working example to the repo to establish the workflow with the engine/workbench before I move forward.
15
+
16
+ Some relevant files:
17
+ - [Library Manager Design](./tests/designs/library_manager.dal)
18
+ - [Lexer Output](./tests/output/test_tokens.json)
19
+ - [Generated AST](./tests/output/ast_direct_gen.json)
20
+
21
+ # Test
22
+ While it does have test cases to verify functionality, currently this is more functional than complete.
6
23
 
7
24
  Install libraries:
8
25
  ```sh
@@ -19,8 +36,15 @@ To run specific test case:
19
36
  npm run test tests/Lexer.test.js
20
37
  ```
21
38
 
22
- This library will likely be integrated into the workbench directly and will be maintained there.
39
+ Run the lexer.test.js while providing it with a design file with a .dal extension. This will produce an AST output. Currently, I am reading the designs from the designs folder and writing the ast's to the output folder.
40
+
41
+ This will be streamlined by integrating everything into the workbench. It will automatically produce the AST and synthesize the program by invoking the python [synthesizer][synth].
42
+
43
+ # Providing feedback
44
+
45
+ You can use GitHub issues to [report a bug][bug-report] or [request a feature][feature-req].
23
46
 
24
- # Synthesizer
47
+ [bug-report]: https://github.com/vishalpalaniappan/dal-ast-js/issues
48
+ [feature-req]: https://github.com/vishalpalaniappan/dal-ast-js/issues
49
+ [synth]: https://github.com/vishalpalaniappan/dal-synthesizer-python
25
50
 
26
- This repo also contains a synthesizer that can take the generated AST and synthesize it into an implementation.
package/dist/index.cjs CHANGED
@@ -8,7 +8,8 @@ let TOKENS = {
8
8
  "LBRACE": "{",
9
9
  "RBRACE": "}",
10
10
  "LBRACKET": "[",
11
- "RBRACKET": "]"
11
+ "RBRACKET": "]",
12
+ "HASH": "#"
12
13
  };
13
14
  TOKENS = Object.freeze(TOKENS);
14
15
 
@@ -84,6 +85,10 @@ class DalLexer {
84
85
 
85
86
  const token = this.getToken(character);
86
87
  if (token) {
88
+ if (token === "HASH") {
89
+ this.scanToNewLine();
90
+ return;
91
+ }
87
92
  this.addAccumulatedIdentifierToken();
88
93
  this.addToken(token);
89
94
  if (token === "QUOTE") {
@@ -94,6 +99,20 @@ class DalLexer {
94
99
  this.addToAccumulator(character);
95
100
  }
96
101
 
102
+ /**
103
+ * Scan to new line to ignore comment.
104
+ * @returns {null}
105
+ */
106
+ scanToNewLine () {
107
+ do {
108
+ const character = this.source[this.currPos];
109
+ if (character == "\n") {
110
+ console.log("SCANNED NEW LINE");
111
+ return;
112
+ }
113
+ } while (++this.currPos < this.source.length);
114
+ }
115
+
97
116
  /**
98
117
  * Finds the identifier given the character.
99
118
  * @param {String} character Character from scan.
@@ -371,6 +390,9 @@ class ArgsParser {
371
390
  value = true;
372
391
  } else if (value === "false") {
373
392
  value = false;
393
+ } else if (value === "null") {
394
+ type = "null";
395
+ value = null;
374
396
  }
375
397
  }
376
398
 
@@ -587,11 +609,16 @@ class DalParser {
587
609
  * Process design keyword.
588
610
  *
589
611
  * cmd(args1, arg2...argN)
612
+ *
613
+ * Type:
614
+ * _cmd -> "registeredCmd"
615
+ * cmd -> "cmd"
590
616
  */
591
617
  processCmd (cmd) {
592
618
  const parsedArgs = new ArgsParser(this.getArgs()).run();
619
+ const type = (cmd[0] === "_")?"registeredCmd":"cmd";
593
620
  const node = {
594
- "type": "cmd",
621
+ "type": type,
595
622
  "command": cmd,
596
623
  "args": parsedArgs
597
624
  };
@@ -682,6 +709,7 @@ class DalAstGenerator {
682
709
  }
683
710
 
684
711
  run (source) {
712
+ this.ast = null;
685
713
  try {
686
714
  const lexer = new DalLexer(source);
687
715
  const parser = new DalParser(lexer.scannedTokens);
package/dist/index.esm.js CHANGED
@@ -6,7 +6,8 @@ let TOKENS$1 = {
6
6
  "LBRACE": "{",
7
7
  "RBRACE": "}",
8
8
  "LBRACKET": "[",
9
- "RBRACKET": "]"
9
+ "RBRACKET": "]",
10
+ "HASH": "#"
10
11
  };
11
12
  TOKENS$1 = Object.freeze(TOKENS$1);
12
13
 
@@ -82,6 +83,10 @@ class DalLexer {
82
83
 
83
84
  const token = this.getToken(character);
84
85
  if (token) {
86
+ if (token === "HASH") {
87
+ this.scanToNewLine();
88
+ return;
89
+ }
85
90
  this.addAccumulatedIdentifierToken();
86
91
  this.addToken(token);
87
92
  if (token === "QUOTE") {
@@ -92,6 +97,20 @@ class DalLexer {
92
97
  this.addToAccumulator(character);
93
98
  }
94
99
 
100
+ /**
101
+ * Scan to new line to ignore comment.
102
+ * @returns {null}
103
+ */
104
+ scanToNewLine () {
105
+ do {
106
+ const character = this.source[this.currPos];
107
+ if (character == "\n") {
108
+ console.log("SCANNED NEW LINE");
109
+ return;
110
+ }
111
+ } while (++this.currPos < this.source.length);
112
+ }
113
+
95
114
  /**
96
115
  * Finds the identifier given the character.
97
116
  * @param {String} character Character from scan.
@@ -369,6 +388,9 @@ class ArgsParser {
369
388
  value = true;
370
389
  } else if (value === "false") {
371
390
  value = false;
391
+ } else if (value === "null") {
392
+ type = "null";
393
+ value = null;
372
394
  }
373
395
  }
374
396
 
@@ -585,11 +607,16 @@ class DalParser {
585
607
  * Process design keyword.
586
608
  *
587
609
  * cmd(args1, arg2...argN)
610
+ *
611
+ * Type:
612
+ * _cmd -> "registeredCmd"
613
+ * cmd -> "cmd"
588
614
  */
589
615
  processCmd (cmd) {
590
616
  const parsedArgs = new ArgsParser(this.getArgs()).run();
617
+ const type = (cmd[0] === "_")?"registeredCmd":"cmd";
591
618
  const node = {
592
- "type": "cmd",
619
+ "type": type,
593
620
  "command": cmd,
594
621
  "args": parsedArgs
595
622
  };
@@ -680,6 +707,7 @@ class DalAstGenerator {
680
707
  }
681
708
 
682
709
  run (source) {
710
+ this.ast = null;
683
711
  try {
684
712
  const lexer = new DalLexer(source);
685
713
  const parser = new DalParser(lexer.scannedTokens);
@@ -0,0 +1,12 @@
1
+ ## Syntax Validation Planning
2
+ - For a given keyword, it is easy to identify if the tokens match the pattern that is established in the grammar.
3
+ - How should I detect when blocks are not closed?
4
+ - I think there is actually a simple answer to this, since I am creating a stack for each nested block I encounter. I can tell at the end which stacks were not closed.
5
+ - I also cannot encounter an opening brace when I am inside a block without a new entry in the stack being created.
6
+ - Since I've already gathered the line numbers, its easy to visually identify exactly where the syntax error is.
7
+
8
+ It would be great to have a formatting class that will pretty print the script as well.
9
+
10
+ ## Semantic Validation Planning
11
+ - I think this is very simple because it just requires setting up the rules and then seeing if they are enforced.
12
+ - If the rule isn't specified, that is also relevant information.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dal-ast-js",
3
- "version": "0.0.2-dev",
3
+ "version": "0.0.4-dev",
4
4
  "main": "dist/index.cjs",
5
5
  "module": "dist/index.esm.js",
6
6
  "type": "module",
package/src/ArgsParser.js CHANGED
@@ -141,6 +141,9 @@ export class ArgsParser {
141
141
  value = true;
142
142
  } else if (value === "false") {
143
143
  value = false;
144
+ } else if (value === "null") {
145
+ type = "null";
146
+ value = null;
144
147
  }
145
148
  }
146
149
 
@@ -23,6 +23,7 @@ export class DalAstGenerator {
23
23
  }
24
24
 
25
25
  run (source) {
26
+ this.ast = null;
26
27
  try {
27
28
  const lexer = new DalLexer(source);
28
29
  const parser = new DalParser(lexer.scannedTokens);
@@ -4,6 +4,10 @@
4
4
  // This requires knowledge about the actual transformations semantics, so I will establish that before moving forward with the validation.
5
5
  // The synthesis development can happen in parallel because it doesn't impact this process.
6
6
 
7
+ /**
8
+ * Note: This is not a working implementation, it is just the skeleton to
9
+ * traverse the tree. This will developed further in followup PRs.
10
+ */
7
11
  export class DesignValidator {
8
12
 
9
13
  constructor (ast) {
@@ -23,6 +27,10 @@ export class DesignValidator {
23
27
  }
24
28
  this.processTree(child)
25
29
  }
30
+ // Process else blocks in if statements.
31
+ if ("else" in node) {
32
+ this.processTree(node["else"]);
33
+ }
26
34
  } else {
27
35
  this.processNode(node);
28
36
  }
package/src/Lexer.js CHANGED
@@ -70,6 +70,10 @@ export class DalLexer {
70
70
 
71
71
  const token = this.getToken(character);
72
72
  if (token) {
73
+ if (token === "HASH") {
74
+ this.scanToNewLine();
75
+ return;
76
+ }
73
77
  this.addAccumulatedIdentifierToken();
74
78
  this.addToken(token);
75
79
  if (token === "QUOTE") {
@@ -80,6 +84,20 @@ export class DalLexer {
80
84
  this.addToAccumulator(character);
81
85
  }
82
86
 
87
+ /**
88
+ * Scan to new line to ignore comment.
89
+ * @returns {null}
90
+ */
91
+ scanToNewLine () {
92
+ do {
93
+ const character = this.source[this.currPos];
94
+ if (character == "\n") {
95
+ console.log("SCANNED NEW LINE");
96
+ return;
97
+ }
98
+ } while (++this.currPos < this.source.length);
99
+ }
100
+
83
101
  /**
84
102
  * Finds the identifier given the character.
85
103
  * @param {String} character Character from scan.
package/src/Parser.js CHANGED
@@ -208,11 +208,16 @@ export class DalParser {
208
208
  * Process design keyword.
209
209
  *
210
210
  * cmd(args1, arg2...argN)
211
+ *
212
+ * Type:
213
+ * _cmd -> "registeredCmd"
214
+ * cmd -> "cmd"
211
215
  */
212
216
  processCmd (cmd) {
213
217
  const parsedArgs = new ArgsParser(this.getArgs()).run();
218
+ const type = (cmd[0] === "_")?"registeredCmd":"cmd";
214
219
  const node = {
215
- "type": "cmd",
220
+ "type": type,
216
221
  "command": cmd,
217
222
  "args": parsedArgs
218
223
  }
package/src/TOKENS.js CHANGED
@@ -6,7 +6,8 @@ let TOKENS = {
6
6
  "LBRACE": "{",
7
7
  "RBRACE": "}",
8
8
  "LBRACKET": "[",
9
- "RBRACKET": "]"
9
+ "RBRACKET": "]",
10
+ "HASH": "#"
10
11
  }
11
12
  TOKENS = Object.freeze(TOKENS);
12
13
 
@@ -0,0 +1,56 @@
1
+ {
2
+ "set": {
3
+ "description": "Set the value of the target with the given keys",
4
+ "syntax": "set(<target>,<keys>,<value>)",
5
+ "synthesized": "target[<keys>] = <value>"
6
+ },
7
+ "create": {
8
+ "description": "Design metadata, declares participants.",
9
+ "syntax": "create(<name>,<type>,<role>)"
10
+ },
11
+ "log": {
12
+ "description": "Adds a log statement for the given participant.",
13
+ "syntax": "log(<behavior>, <name>, <type>, <value>)",
14
+ "synthesized": "semanticLogger.logParticipant(<behavior>, <name>, <type>, <value>)"
15
+ },
16
+ "insert": {
17
+ "description": "Inserts the value at the given index in the target with the provided keys",
18
+ "syntax": "insert(<target>, <keys>, <value>, <index>)",
19
+ "synthesized": " <target>[<keys>].insert(<index>, <value>)"
20
+ },
21
+ "get": {
22
+ "description": "Get the value from the source provided the keys and save in target.",
23
+ "syntax": "get(<target>, <source>, <keys>)",
24
+ "synthesized": "<target> = <source>[<keys>]"
25
+ },
26
+ "select": {
27
+ "description": "Selects the next behavior to exhibit.",
28
+ "syntax": "select(<nextBehavior>)",
29
+ "synthesized": "return \"<nextBehavior>\""
30
+ },
31
+ "display": {
32
+ "description": "Prints the promopt to screen (prompt is an f string)",
33
+ "example": "display(\"f'Got book named {name} and it has first letter {firstLetter}'\")",
34
+ "syntax": "display(<prompt>)",
35
+ "synthesized": "print(\"<prompt>\")"
36
+ },
37
+ "isEqual": {
38
+ "description": "Checks the equality of cmp1 and cmp2 and stores result.",
39
+ "syntax": "isEqual(<cmp1>, <cmp2>, <result>)",
40
+ "synthesized": "<result> = <cmp1> == <cmp2>"
41
+ },
42
+ "getFromPos": {
43
+ "description": "Get the value at pos from source and saves it in target.",
44
+ "syntax": "getFromPos(<target>, <source>, <pos>)",
45
+ "synthesized": "<target> = <source>[<pos>]"
46
+ },
47
+ "removeFromPos": {
48
+ "description": "Removes the value at pos from source and saves it in target.",
49
+ "syntax": "getFromPos(<target>, <source>, <pos>)",
50
+ "synthesized": "<target> = <source>.pop(<pos>)"
51
+ },
52
+ "run": {
53
+ "description": "Synthesizes the block which runs the desig.",
54
+ "syntax": "See geCmdRunAst in getSynthesizedNode.py"
55
+ }
56
+ }
@@ -9,7 +9,7 @@ import { DalAstGenerator } from "../src/DalAstGenerator";
9
9
 
10
10
  describe("Lexer", () => {
11
11
  it("basic source", async () => {
12
- const filePath = resolve(__dirname, "./designs/library_manager.dal")
12
+ const filePath = resolve(__dirname, "./designs/reverse_name_persist.dal")
13
13
  const source = await readFile(filePath)
14
14
  const lexer = new DalLexer(source.toString());
15
15
 
@@ -32,7 +32,7 @@ describe("Lexer", () => {
32
32
  });
33
33
 
34
34
  it("tests direct ast generation", async () => {
35
- const filePath = resolve(__dirname, "./designs/library_manager.dal")
35
+ const filePath = resolve(__dirname, "./designs/reverse_name_persist.dal")
36
36
  const source = await readFile(filePath)
37
37
  const ast = new DalAstGenerator().run(source.toString());
38
38
  const ast_output_path = resolve(__dirname, "./output/ast_direct_gen.json")
@@ -0,0 +1,52 @@
1
+ design ("reverse_name_persist")
2
+
3
+ # Example comment that is ignored.
4
+ behavior b_createDatabaseConnection {
5
+ create(name=connection, type="object", role="connection")
6
+ _connectToDatabase(connection)
7
+ set(worldState, ["connection"], connection)
8
+ select(b_createCursor)
9
+ }
10
+
11
+ behavior b_createCursor {
12
+ create(name=cursor, type="object", role="cursor")
13
+ get(connection, worldState, ["connection"])
14
+ _createCursor(cursor, connection)
15
+ set(worldState, ["cursor"], cursor)
16
+ select(b_createTable)
17
+ }
18
+
19
+ behavior b_createTable {
20
+ get(cursor, worldState, ["cursor"])
21
+ _createTable(null, cursor)
22
+ select(b_commitConnection)
23
+ }
24
+
25
+ behavior b_commitConnection {
26
+ get(connection, worldState, ["connection"])
27
+ _commitConnection(null, connection)
28
+ select(b_receiveName)
29
+ }
30
+
31
+ behavior b_receiveName {
32
+ create(name=name, type="string", role="name")
33
+ _receiveName(name)
34
+ set(worldState, ["name"], name)
35
+ select(b_reverse)
36
+ }
37
+
38
+ behavior b_reverse {
39
+ get(name, worldState, ["name"])
40
+ _reverse(reversedName, name)
41
+ set(worldState, ["reversedName"], reversedName)
42
+ select(b_writeToDatabase)
43
+ }
44
+
45
+ behavior b_writeToDatabase {
46
+ get(cursor, worldState, ["cursor"])
47
+ get(reversedName, worldState, ["reversedName"])
48
+ _writeToDatabase(null, cursor, reversedName)
49
+ select(b_commitConnection)
50
+ }
51
+
52
+ run(b_createDatabaseConnection)