dal-ast-js 0.0.1-dev → 0.0.3-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
@@ -42,7 +42,7 @@ var TOKENS$1 = TOKENS;
42
42
  class DalLexer {
43
43
  constructor (source) {
44
44
  this.currPos = 0;
45
- this.source = [...source];
45
+ this.source = source;
46
46
  this.scannedTokens = [];
47
47
 
48
48
  // Current line and colno
@@ -371,6 +371,9 @@ class ArgsParser {
371
371
  value = true;
372
372
  } else if (value === "false") {
373
373
  value = false;
374
+ } else if (value === "null") {
375
+ type = "null";
376
+ value = null;
374
377
  }
375
378
  }
376
379
 
@@ -587,11 +590,16 @@ class DalParser {
587
590
  * Process design keyword.
588
591
  *
589
592
  * cmd(args1, arg2...argN)
593
+ *
594
+ * Type:
595
+ * _cmd -> "registeredCmd"
596
+ * cmd -> "cmd"
590
597
  */
591
598
  processCmd (cmd) {
592
599
  const parsedArgs = new ArgsParser(this.getArgs()).run();
600
+ const type = (cmd[0] === "_")?"registeredCmd":"cmd";
593
601
  const node = {
594
- "type": "cmd",
602
+ "type": type,
595
603
  "command": cmd,
596
604
  "args": parsedArgs
597
605
  };
@@ -682,6 +690,7 @@ class DalAstGenerator {
682
690
  }
683
691
 
684
692
  run (source) {
693
+ this.ast = null;
685
694
  try {
686
695
  const lexer = new DalLexer(source);
687
696
  const parser = new DalParser(lexer.scannedTokens);
package/dist/index.esm.js CHANGED
@@ -40,7 +40,7 @@ var TOKENS = TOKENS$1;
40
40
  class DalLexer {
41
41
  constructor (source) {
42
42
  this.currPos = 0;
43
- this.source = [...source];
43
+ this.source = source;
44
44
  this.scannedTokens = [];
45
45
 
46
46
  // Current line and colno
@@ -369,6 +369,9 @@ class ArgsParser {
369
369
  value = true;
370
370
  } else if (value === "false") {
371
371
  value = false;
372
+ } else if (value === "null") {
373
+ type = "null";
374
+ value = null;
372
375
  }
373
376
  }
374
377
 
@@ -585,11 +588,16 @@ class DalParser {
585
588
  * Process design keyword.
586
589
  *
587
590
  * cmd(args1, arg2...argN)
591
+ *
592
+ * Type:
593
+ * _cmd -> "registeredCmd"
594
+ * cmd -> "cmd"
588
595
  */
589
596
  processCmd (cmd) {
590
597
  const parsedArgs = new ArgsParser(this.getArgs()).run();
598
+ const type = (cmd[0] === "_")?"registeredCmd":"cmd";
591
599
  const node = {
592
- "type": "cmd",
600
+ "type": type,
593
601
  "command": cmd,
594
602
  "args": parsedArgs
595
603
  };
@@ -680,6 +688,7 @@ class DalAstGenerator {
680
688
  }
681
689
 
682
690
  run (source) {
691
+ this.ast = null;
683
692
  try {
684
693
  const lexer = new DalLexer(source);
685
694
  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.1-dev",
3
+ "version": "0.0.3-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
@@ -28,7 +28,7 @@ import TOKENS from "./TOKENS";
28
28
  export class DalLexer {
29
29
  constructor (source) {
30
30
  this.currPos = 0;
31
- this.source = [...source];
31
+ this.source = source;
32
32
  this.scannedTokens = [];
33
33
 
34
34
  // Current line and colno
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
  }
@@ -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/database_example/reverse_name_persist.dal")
13
13
  const source = await readFile(filePath)
14
14
  const lexer = new DalLexer(source.toString());
15
15
 
@@ -0,0 +1,30 @@
1
+ import sqlite3
2
+
3
+ def connectToDatabase():
4
+ return sqlite3.connect("my_database.db")
5
+
6
+ def createCursor(connection):
7
+ return connection.cursor()
8
+
9
+ def createTable(cursor):
10
+ cursor.execute("""
11
+ CREATE TABLE IF NOT EXISTS users (
12
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
13
+ name TEXT NOT NULL
14
+ )
15
+ """)
16
+
17
+ def commitConnection(connection):
18
+ connection.commit()
19
+
20
+ def receiveName():
21
+ return input("\nAdd Name: ")
22
+
23
+ def writeToDatabase(cursor, name):
24
+ cursor.execute(
25
+ "INSERT INTO users (name) VALUES (?)",
26
+ (name,),
27
+ )
28
+
29
+ def reverse(name):
30
+ return name[::-1]
@@ -0,0 +1,51 @@
1
+ design ("reverse_name_persist")
2
+
3
+ behavior b_createDatabaseConnection {
4
+ create(name=connection, type="object", role="connection")
5
+ _connectToDatabase(connection)
6
+ set(worldState, ["connection"], connection)
7
+ select(b_createCursor)
8
+ }
9
+
10
+ behavior b_createCursor {
11
+ create(name=cursor, type="object", role="cursor")
12
+ get(connection, worldState, ["connection"])
13
+ _createCursor(cursor, connection)
14
+ set(worldState, ["cursor"], cursor)
15
+ select(b_createTable)
16
+ }
17
+
18
+ behavior b_createTable {
19
+ get(cursor, worldState, ["cursor"])
20
+ _createTable(null, cursor)
21
+ select(b_commitConnection)
22
+ }
23
+
24
+ behavior b_commitConnection {
25
+ get(connection, worldState, ["connection"])
26
+ _commitConnection(null, connection)
27
+ select(b_receiveName)
28
+ }
29
+
30
+ behavior b_receiveName {
31
+ create(name=name, type="string", role="name")
32
+ _receiveName(name)
33
+ set(worldState, ["name"], name)
34
+ select(b_reverse)
35
+ }
36
+
37
+ behavior b_reverse {
38
+ get(name, worldState, ["name"])
39
+ _reverse(reversedName, name)
40
+ set(worldState, ["reversedName"], reversedName)
41
+ select(b_writeToDatabase)
42
+ }
43
+
44
+ behavior b_writeToDatabase {
45
+ get(cursor, worldState, ["cursor"])
46
+ get(reversedName, worldState, ["reversedName"])
47
+ _writeToDatabase(null, cursor, reversedName)
48
+ select(b_commitConnection)
49
+ }
50
+
51
+ run(b_createDatabaseConnection)
@@ -0,0 +1,46 @@
1
+ from registered import *
2
+
3
+ def b_createDatabaseConnection():
4
+ connection = connectToDatabase()
5
+ worldState["connection"] = connection
6
+ return "b_createCursor"
7
+
8
+ def b_createCursor():
9
+ connection = worldState["connection"]
10
+ cursor = createCursor(connection)
11
+ worldState["cursor"] = cursor
12
+ return "b_createTable"
13
+
14
+ def b_createTable():
15
+ cursor = worldState["cursor"]
16
+ createTable(cursor)
17
+ return "b_commitConnection"
18
+
19
+ def b_commitConnection():
20
+ connection = worldState["connection"]
21
+ commitConnection(connection)
22
+ return "b_receiveName"
23
+
24
+ def b_receiveName():
25
+ name = receiveName()
26
+ worldState["name"] = name
27
+ return "b_reverse"
28
+
29
+ def b_reverse():
30
+ name = worldState["name"]
31
+ name = reverse(name)
32
+ worldState["name"] = name
33
+ return "b_writeToDatabase"
34
+
35
+ def b_writeToDatabase():
36
+ cursor = worldState["cursor"]
37
+ name = worldState["name"]
38
+ writeToDatabase(cursor, name)
39
+ return "b_commitConnection"
40
+
41
+
42
+ if __name__ == '__main__':
43
+ nextBehavior = 'b_createDatabaseConnection'
44
+ worldState = {}
45
+ while nextBehavior:
46
+ nextBehavior = globals()[nextBehavior]()