forked from ucsd-cse231-s22/lecture3
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.ts
143 lines (133 loc) · 4.72 KB
/
parser.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import { TreeCursor } from 'lezer';
import {parser} from 'lezer-python';
import {Parameter, Stmt, Expr, Type} from './ast';
export function parseProgram(source : string) : Array<Stmt> {
const t = parser.parse(source).cursor();
return traverseStmts(source, t);
}
export function traverseStmts(s : string, t : TreeCursor) {
// The top node in the program is a Script node with a list of children
// that are various statements
t.firstChild();
const stmts = [];
do {
stmts.push(traverseStmt(s, t));
} while(t.nextSibling()); // t.nextSibling() returns false when it reaches
// the end of the list of children
return stmts;
}
/*
Invariant – t must focus on the same node at the end of the traversal
*/
export function traverseStmt(s : string, t : TreeCursor) : Stmt {
switch(t.type.name) {
case "ReturnStatement":
t.firstChild(); // Focus return keyword
t.nextSibling(); // Focus expression
var value = traverseExpr(s, t);
t.parent();
return { tag: "return", value };
case "AssignStatement":
t.firstChild(); // focused on name (the first child)
var name = s.substring(t.from, t.to);
t.nextSibling(); // focused on = sign. May need this for complex tasks, like +=!
t.nextSibling(); // focused on the value expression
var value = traverseExpr(s, t);
t.parent();
return { tag: "assign", name, value };
case "ExpressionStatement":
t.firstChild(); // The child is some kind of expression, the
// ExpressionStatement is just a wrapper with no information
var expr = traverseExpr(s, t);
t.parent();
return { tag: "expr", expr: expr };
case "FunctionDefinition":
t.firstChild(); // Focus on def
t.nextSibling(); // Focus on name of function
var name = s.substring(t.from, t.to);
t.nextSibling(); // Focus on ParamList
var parameters = traverseParameters(s, t)
t.nextSibling(); // Focus on Body or TypeDef
let ret : Type = "none";
let maybeTD = t;
if(maybeTD.type.name === "TypeDef") {
t.firstChild();
ret = traverseType(s, t);
t.parent();
}
t.nextSibling(); // Focus on single statement (for now)
t.firstChild(); // Focus on :
const body = [];
while(t.nextSibling()) {
body.push(traverseStmt(s, t));
}
t.parent(); // Pop to Body
t.parent(); // Pop to FunctionDefinition
return {
tag: "define",
name, parameters, body, ret
}
}
}
export function traverseType(s : string, t : TreeCursor) : Type {
switch(t.type.name) {
case "VariableName":
const name = s.substring(t.from, t.to);
if(name !== "int") {
throw new Error("Unknown type: " + name)
}
return name;
default:
throw new Error("Unknown type: " + t.type.name)
}
}
export function traverseParameters(s : string, t : TreeCursor) : Array<Parameter> {
t.firstChild(); // Focuses on open paren
const parameters = []
t.nextSibling(); // Focuses on a VariableName
while(t.type.name !== ")") {
let name = s.substring(t.from, t.to);
t.nextSibling(); // Focuses on "TypeDef", hopefully, or "," if mistake
let nextTagName = t.type.name; // NOTE(joe): a bit of a hack so the next line doesn't if-split
if(nextTagName !== "TypeDef") { throw new Error("Missed type annotation for parameter " + name)};
t.firstChild(); // Enter TypeDef
t.nextSibling(); // Focuses on type itself
let typ = traverseType(s, t);
t.parent();
t.nextSibling(); // Move on to comma or ")"
parameters.push({name, typ});
t.nextSibling(); // Focuses on a VariableName
}
t.parent(); // Pop to ParamList
return parameters;
}
export function traverseExpr(s : string, t : TreeCursor) : Expr {
switch(t.type.name) {
case "Number":
return { tag: "number", value: Number(s.substring(t.from, t.to)) };
case "VariableName":
return { tag: "id", name: s.substring(t.from, t.to) };
case "CallExpression":
t.firstChild(); // Focus name
var name = s.substring(t.from, t.to);
t.nextSibling(); // Focus ArgList
t.firstChild(); // Focus open paren
var args = traverseArguments(t, s);
var result : Expr = { tag: "call", name, arguments: args};
t.parent();
return result;
}
}
export function traverseArguments(c : TreeCursor, s : string) : Expr[] {
c.firstChild(); // Focuses on open paren
const args = [];
c.nextSibling();
while(c.type.name !== ")") {
let expr = traverseExpr(s, c);
args.push(expr);
c.nextSibling(); // Focuses on either "," or ")"
c.nextSibling(); // Focuses on a VariableName
}
c.parent(); // Pop to ArgList
return args;
}