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 +31 -7
- package/dist/index.cjs +11 -2
- package/dist/index.esm.js +11 -2
- package/docs/validation_planning +12 -0
- package/package.json +1 -1
- package/src/ArgsParser.js +3 -0
- package/src/DalAstGenerator.js +1 -0
- package/src/DesignValidator.js +8 -0
- package/src/Lexer.js +1 -1
- package/src/Parser.js +6 -1
- package/src/docs/commands.json +56 -0
- package/tests/Lexer.test.js +1 -1
- package/tests/designs/database_example/my_database.db +0 -0
- package/tests/designs/database_example/registered.py +30 -0
- package/tests/designs/database_example/reverse_name_persist.dal +51 -0
- package/tests/designs/database_example/synthesized.py +46 -0
- package/tests/output/ast.json +71 -429
- package/tests/output/test_tokens.json +802 -2442
- package/synthesizer/Synthesizer.py +0 -88
- package/synthesizer/asts/lib_manager_ast.json +0 -842
- package/synthesizer/dal_ast_synthesizer.py +0 -31
- package/synthesizer/docs/commands.json +0 -8
- package/synthesizer/getSynthesizedNode.py +0 -362
- package/synthesizer/helper.py +0 -13
- package/synthesizer/output/LoggingHelper.py +0 -57
- package/synthesizer/output/synthesized.py +0 -76
- package/synthesizer/output_helpers/LoggingHelper.py +0 -57
- package/synthesizer/output_helpers/readme.md +0 -3
- package/synthesizer/readme.md +0 -9
- package/tests/designs/test3.dal +0 -16
package/README.md
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
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
|
-
|
|
5
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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":
|
|
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 =
|
|
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":
|
|
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
package/src/ArgsParser.js
CHANGED
package/src/DalAstGenerator.js
CHANGED
package/src/DesignValidator.js
CHANGED
|
@@ -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
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":
|
|
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
|
+
}
|
package/tests/Lexer.test.js
CHANGED
|
@@ -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/
|
|
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
|
|
|
Binary file
|
|
@@ -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]()
|