Newer
Older
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import { Token } from "./tokenizer";
import {
AstNode,
Identifier,
Operator,
PhysicalUnit,
MeasuredNumber,
Assignment,
Statement,
PrimitiveExpr,
PhysicalUnitEnum,
Distance,
Time,
Multiplicative,
Additive,
GroupExpr,
Expr,
Mass,
Velocity,
} from "./types";
export function parseProgram(tokens: Token[]): AstNode[] {
let currentPosition = -1;
let AstNodes: AstNode[] = [];
while (currentPosition < tokens.length - 1) {
AstNodes.push(Statement());
}
return AstNodes;
function getCurrentToken() {
// 1. Return the element of array `tokens` at the current position.
return tokens[currentPosition];
}
function advance(): void {
// 1. Increment the value of `currentPosition` by 1.
// TODO: YOUR CODE HERE
}
function peek() {
// 1. Return the element of array `tokens` at a position immediately after the current position.
// TODO: YOUR CODE HERE
}
function error() {
return new Error(
"Parsing failed at position: " +
currentPosition +
". The erroneous input token is: " +
getCurrentToken().value
);
}
/*** functions for terminal symbols of the grammar ***/
function KeywordLet(): void {
// 1. Peek the next input token.
// 1.1. If it is the keyword `let`, then:
if (peek().kind === "KeywordLet") {
// 1.1.1. Advance the position of the parser.
advance();
}
// 1.2. Otherwise, i.e., if it is not a keyword `let`, report an error.
else throw error();
}
function Equals(): void {
// 1. Peek the next input token.
// 1.1. If it is an equality symbol `=`, then:
// 1.1.1. Advance the position of the parser.
// 1.2. Otherwise, i.e., if it is not an equality sign, report an error.
// TODO: YOUR CODE HERE
}
function Semicolon(): void {
// TODO: YOUR CODE HERE
}
function /* TODO: YOUR CODE HERE */(): void {
// TODO: YOUR CODE HERE
}
function /* TODO: YOUR CODE HERE */(): void {
// TODO: YOUR CODE HERE
}
/*** functions for "special" terminal symbols that also have a value (these special terminals are: `NumericalLiteral`, `Identifier` and `PhysicalUnit`) ***/
function NumericalLiteral(): number {
// 1. Peek the next input token.
// 1.1. If it is a numeric literal, then:
// 1.1.1. Advance the position of the parser.
// 1.2. Otherwise, i.e., if it is not a numeric literal, report an error.
// TODO: YOUR CODE HERE
// 2. Return the numerical value of the token.
return parseInt(/* TODO: YOUR CODE HERE */, 10);
}
function Identifier(): Identifier {
// 1. Peek the next input token.
// 2. If its kind is `Identifier`, then:
if (peek().kind === "Identifier") {
// 2.1. Advance the position of the parser.
advance();
// 2.2. Return an object with two properties: `name` and `nodeType`.
return {
// 2.2.1. The property `name` should be the value of the current token.
// TODO: YOUR CODE HERE
// 2.2.2. The property `nodeType` should be `Identifier`.
nodeType: "Identifier"
};
}
// 3. Throw an error otherwise (i.e., if the kind of the peek'ed token is not `Identifier`.
throw error();
}
function PhysicalUnit(): PhysicalUnit {
// 1. Advance the position of the parser.
advance();
// 2. Get the current token and store it in some variable.
let unit = getCurrentToken();
// 3. Check the kind of that token.
// 3.1. If it is `PhysicalUnit`, then:
if (unit.kind === "PhysicalUnit") {
// 3.1.1. Store the value of the token in some variable.
let unitValue = unit.value;
// 3.1.2. If that value is one of the time units, then:
if (["min", "s", "h"].includes(unitValue)) {
// 3.1.2.1. Return an object with the following properties:
return {
// 3.1.2.1.1. Property `value` should be the value of the token, casted to type `Time`
// TODO: YOUR CODE HERE
// 3.1.2.1.2. Property `kind` should be `PhysicalUnitEnum.Time`
// TODO: YOUR CODE HERE
// 3.1.2.1.3. Property `nodeType` should be `PhysicalUnit`
// TODO: YOUR CODE HERE
};
// 3.1.3. Otherwise, if that value is one of the mass units, then:
} else if (/* TODO: YOUR CODE HERE */) {
// 3.1.3.1. Return an object with the following properties:
// 3.1.3.1.1. Property `value` should be the value of the token, casted to type `Mass`
// 3.1.3.1.2. Property `kind` should be `PhysicalUnitEnum.Mass`
// 3.1.3.1.3. Property `nodeType` should be `PhysicalUnit`
// TODO: YOUR CODE HERE

Mikhail.Barash
committed
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// 3.1.4. Otherwise, if that value is one of the (???what???) units, then:
} else if (/* TODO: YOUR CODE HERE */) {
// TODO: YOUR CODE HERE
}
// 3.1.5. Otherwise, if that value is one of the (???what???) units, then:
else if (
// TODO: YOUR CODE HERE
) {
// TODO: YOUR CODE HERE
}
}
// 3.2. Otherwise, i.e., if the kind of the token is not `PhysicalUnit`, throw an error.
throw error();
}
/*** functions for non-terminal symbols of the grammar ***/
function Statement(): Statement {
// 1.1. A statement is an assignment statement.
// 1.2. Return a corresponding AST node.
return AssignStatement();
}
function AssignStatement(): Assignment {
// 1. Expect (i.e., consume) keyword `let`.
KeywordLet();
// 2. Expect an identifier, and store it in a variable.
let assignee = Identifier();
// 3. Expect an equality symbol `=`.
// TODO: YOUR CODE HERE
// 4. Expect an expression, and store it in a variable.
// TODO: YOUR CODE HERE
// 5. Expect a semicolon.
// TODO: YOUR CODE HERE
// 6. Return an AST node that represents an assignment statement -- it is an object with:
return {
// 6.1. A property that represents the assignee (i.e., the variable that has been assigned to).
assignee,
// 6.2. A property that represents the expression on the right-hand side of the assignment statement.
// TODO: YOUR CODE HERE
// 6.3. A property `nodeType` which is (???what???).
// TODO: YOUR CODE HERE
};
}
function Expr(): Expr {
// TODO: YOUR CODE HERE
}
function GroupExpr(): GroupExpr {
// TODO: YOUR CODE HERE
}
function PrimitiveExpr(): PrimitiveExpr {
// 1. Peek the next input token.
// 2. Depending on what this token is:
switch (peek().kind) {
// 2.1. If it is an identifier:
case "Identifier":
// 2.1.1. Expect an identifier.
// 2.1.2. Return an AST node that represents it.
return Identifier();
// 2.2. If it is a number:
case "Number":
// 2.2.1. Expect a measured number.
// 2.2.2. Return an AST node that represents it.
// TODO: YOUR CODE HERE
// 2.3. If it is a (???what???):
case /* TODO: YOUR CODE HERE */:
// TODO: YOUR CODE HERE
// 2.4. Otherwise, if it is none of the above:
default:
// 2.4.1. Throw an error.
throw error();
}
}
function MeasuredNumber(): MeasuredNumber {
// TODO: YOUR CODE HERE
}
function OpAddSub(): Operator {
// 1. Peek the next input token.
// 1.1. If it is either `+` or `-`, then:
if (peek().kind == "OpAddSub") {
// 1.1.1. Advance the position of the parser.
// TODO: YOUR CODE HERE
}
// 1.2. Otherwise, i.e., if it is not `+` or `-`, report an error.
else throw error();
// 2. Depending on the value of the token:
switch (getCurrentToken().value) {
// 2.1. If it is `+`, then:
case "+":
// 2.1.1. Return an AST node that represents an addition-like operator -- it is an object with:
return {
// 2.1.1.1. A property `value` which is `+`.
value: "+",
// 2.1.1.2. A property `nodeType` which is `OpAddSub`.
nodeType: "OpAddSub"
};
// 2.2. If it is `-`, then:
case "-":
// TODO: YOUR CODE HERE
// 2.3. Otherwise, i.e., if it is neither `+` nor `-`:
default:
// 2.3.1. Throw an error.
throw error();
}
}
function OpMulDiv(): Operator {
// TODO: YOUR CODE HERE
}
function Additive(): PrimitiveExpr | Multiplicative | Additive {
// 1. Expect a multiplicative expression, and store it in a variable.
let left: PrimitiveExpr | Multiplicative | Additive = Multiplicative();
// 2. Emulate repetition in the grammar `{ ... }*` by peeking the next input token. See also item 2.2. below.
// 2.1. Check whether that peek'ed token is `+` or `-`.
while (peek().kind === "OpAddSub") {
// 3. Expect (i.e., consume) that peek'ed token, and store it in a variable.
let op = OpAddSub();
// 4. Expect a multiplicative expression, and store it in a variable.
let right = /* TODO: YOUR CODE HERE */;
// 5. Update the expression stored in item 1. to be an AST node that represents an additive expression -- it is an object with:
left = {
// 5.1. A property that represents the left operand of the multiplicative expression.
left,
// 5.2. A property that represents the operator of the multiplicative expression.
// TODO: YOUR CODE HERE
// 5.3. A property that represents the right operand of the multiplicative expression.
right,
// 5.4. A property `nodeType` which is (???what???).
nodeType: /* TODO: YOUR CODE HERE */,
};
// 2.2. Make an iteration of the loop to process the possible remaining part of the addition-like expression.