Skip to content
Snippets Groups Projects
Commit 19864632 authored by Vetle Larsen's avatar Vetle Larsen
Browse files

Trying to run, but something doesnt work. Error at line 90 in typecheckvisitor

parent 7f5c9e53
No related branches found
No related tags found
No related merge requests found
...@@ -20,3 +20,5 @@ function main() { ...@@ -20,3 +20,5 @@ function main() {
} }
} }
main(); main();
//let blabla = b / f + g + 1[km/s];
{ {
"name": "INF222-Obligatory2-Skeleton", "name": "inf-222-v-24-obligatory-2-skeleton",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
......
...@@ -15,11 +15,13 @@ function lookup<T>(x: string, env: Environment<T>): T | undefined { ...@@ -15,11 +15,13 @@ function lookup<T>(x: string, env: Environment<T>): T | undefined {
function isDefined<T>(x: string, env: Environment<T>): boolean { function isDefined<T>(x: string, env: Environment<T>): boolean {
// 1. Return a Boolean value that represents whether an environment specified in parameter `x` has a variable specified in the parameter `x`. // 1. Return a Boolean value that represents whether an environment specified in parameter `x` has a variable specified in the parameter `x`.
// TODO: YOUR CODE HERE // Hint: You can find which methods to use at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map // TODO: YOUR CODE HERE // Hint: You can find which methods to use at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
return env.env.has(x);
} }
function declare<T>(x: string, v: T, env: Environment<T>): void { function declare<T>(x: string, v: T, env: Environment<T>): void {
// 1. Add a variable represented by the parameter `x` into the environment represented by the parameters `env`. // 1. Add a variable represented by the parameter `x` into the environment represented by the parameters `env`.
// TODO: YOUR CODE HERE // Hint: You can find which methods to use at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map // TODO: YOUR CODE HERE // Hint: You can find which methods to use at: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
env.env.set(x, v);
} }
export function newEnv<T>(): Environment<T> { export function newEnv<T>(): Environment<T> {
......
...@@ -32,16 +32,16 @@ export class EvaluateVisitor implements AbstractVisitor { ...@@ -32,16 +32,16 @@ export class EvaluateVisitor implements AbstractVisitor {
// 1.1.2. Visit the child `expr` of the node, and store the result in a variable. // 1.1.2. Visit the child `expr` of the node, and store the result in a variable.
let expr = this.visit(node.expr); let expr = this.visit(node.expr);
// 1.1.3. Add a variable and the stored evaluation result to the environment. // 1.1.3. Add a variable and the stored evaluation result to the environment.
// TODO: YOUR CODE HERE this.env.declare(node.assignee.name, expr, this.env);
// 1.1.4. Return the stored evaluation result. // 1.1.4. Return the stored evaluation result.
return expr; return expr;
// 1.2. If it is an identifier, then: // 1.2. If it is an identifier, then:
case "Identifier": case "Identifier":
// 1.2.1. Cast the node to type `Identifier`. // 1.2.1. Cast the node to type `Identifier`.
// TODO: YOUR CODE HERE node = node as Identifier;
// 1.2.2. Lookup the value of the variable from the environment, and store that value in a variable. // 1.2.2. Lookup the value of the variable from the environment, and store that value in a variable.
let lookedUp = /* YOUR CODE HERE */; let lookedUp = this.env.lookup(node.name, this.env);
// 1.2.3. If the value is not undefined, then: // 1.2.3. If the value is not undefined, then:
if (lookedUp !== undefined) { if (lookedUp !== undefined) {
// 1.2.3.1. Return the value. // 1.2.3.1. Return the value.
...@@ -53,13 +53,14 @@ export class EvaluateVisitor implements AbstractVisitor { ...@@ -53,13 +53,14 @@ export class EvaluateVisitor implements AbstractVisitor {
// 1.3. If it is a group expression, then: // 1.3. If it is a group expression, then:
case "GroupExpr": case "GroupExpr":
// 1.3.1. Cast the node to type `GroupExpr`. // 1.3.1. Cast the node to type `GroupExpr`.
node = node as GroupExpr;
// 1.3.2. Return the result of visiting the child `subExpr` of the node. // 1.3.2. Return the result of visiting the child `subExpr` of the node.
// TODO: YOUR CODE HERE this.visit(node.subExpr);
// 1.4. If it is a measured number, then: // 1.4. If it is a measured number, then:
case "MeasuredNumber": case "MeasuredNumber":
// 1.4.1. Cast the node to type `MeasuredNumber`. // 1.4.1. Cast the node to type `MeasuredNumber`.
// TODO: YOUR CODE HERE node = node as MeasuredNumber;
// 1.4.2. Depending on the physical unit of the node: // 1.4.2. Depending on the physical unit of the node:
switch (node.unit.value) { switch (node.unit.value) {
// 1.4.2.1. If it is grams: // 1.4.2.1. If it is grams:
...@@ -73,16 +74,67 @@ export class EvaluateVisitor implements AbstractVisitor { ...@@ -73,16 +74,67 @@ export class EvaluateVisitor implements AbstractVisitor {
}; };
// 1.4.2.2. If it is kilograms: // 1.4.2.2. If it is kilograms:
case "kg": case "kg":
// TODO: YOUR CODE HERE return {
value: node.numericalValue*1000,
unit: { ...node.unit, value: "g" },
};
// 1.4.2.3. If it is hours: // 1.4.2.3. If it is hours:
case "h": case "h":
// TODO: YOUR CODE HERE return {
value: node.numericalValue*3600,
unit: { ...node.unit, value: "s" },
};
// 1.4.2._. If it is ???what???: // 1.4.2._. If it is ???what???:
case /* TODO: YOUR CODE HERE */: case "min":
// TODO: YOUR CODE HERE return {
value: node.numericalValue*60,
// TODO: YOUR CODE HERE FOR THE OTHER CASES unit: { ...node.unit, value: "s" },
};
case "s":
return {
value: node.numericalValue,
unit: { ...node.unit, value: "s" },
}
case "m":
return {
value: node.numericalValue,
unit: { ...node.unit, value: "m" },
}
case "km":
return {
value: node.numericalValue*1000,
unit: { ...node.unit, value: "m" },
}
case "km/h":
return {
value: node.numericalValue/3.6,
unit: { ...node.unit, value: "m/s" },
}
case "km/s":
return {
value: node.numericalValue*1000,
unit: { ...node.unit, value: "m/s" },
}
case "km/min":
return {
value: node.numericalValue*16.6667,
unit: { ...node.unit, value: "m/s" },
}
case "m/h":
return {
value: node.numericalValue*0.277778,
unit: { ...node.unit, value: "m/s"},
}
case "m/s":
return {
value: node.numericalValue,
unit: { ...node.unit, value: "m/s" },
}
case "m/min":
return {
value: node.numericalValue*0.0166667,
unit: { ...node.unit, value: "m/s" },
}
// 1.4.2.14. Otherwise: // 1.4.2.14. Otherwise:
default: default:
// 1.4.2.14.1. Throw an error. // 1.4.2.14.1. Throw an error.
...@@ -92,25 +144,28 @@ export class EvaluateVisitor implements AbstractVisitor { ...@@ -92,25 +144,28 @@ export class EvaluateVisitor implements AbstractVisitor {
// 1.5. If it is an addition-like expression, then: // 1.5. If it is an addition-like expression, then:
case "Additive": case "Additive":
// 1.5.1. Cast the node to type `Additive`. // 1.5.1. Cast the node to type `Additive`.
// TODO: YOUR CODE HERE node = node as Additive;
// 1.5.2. Visit the `left` child of this node, and store the result in a variable. // 1.5.2. Visit the `left` child of this node, and store the result in a variable.
// TODO: YOUR CODE HERE let left = this.visit(node.left)
// 1.5.3. Visit the `right` child of this node, and store the result in a variable. // 1.5.3. Visit the `right` child of this node, and store the result in a variable.
// TODO: YOUR CODE HERE let right = this.visit(node.right)
// 1.5.4. Depending on the `op` of this node: // 1.5.4. Depending on the `op` of this node:
// 1.5.4.1. If it is `+`, then: // 1.5.4.1. If it is `+`, then:
if (node.op.value === "+") { if (node.op.value === "+") {
// 1.5.4.1.1. Return an object that represents a value and a physical unit: // 1.5.4.1.1. Return an object that represents a value and a physical unit:
return { return {
// 1.5.4.1.1.1. The property `value` is the summation of the left and the right child of the node. // 1.5.4.1.1.1. The property `value` is the summation of the left and the right child of the node.
value: /* TODO: YOUR CODE HERE */, value: left.value + right.value,
// 1.5.4.1.1.2. The property `unit` is the unit of the left child of the node. // 1.5.4.1.1.2. The property `unit` is the unit of the left child of the node.
// 1.5.4.1.1.2.1. Alternatively, it could have been the value of the right child of the node, as both of them have the same unit. // 1.5.4.1.1.2.1. Alternatively, it could have been the value of the right child of the node, as both of them have the same unit.
unit: additiveLeft.unit, unit: left.unit,
}; };
// 1.5.4.2. Otherwise, if it is `-`, then: // 1.5.4.2. Otherwise, if it is `-`, then:
} else if (node.op.value === "-") { } else if (node.op.value === "-") {
// TODO: YOUR CODE HERE return {
value: left.value - right.value,
unit: left.unit,
};
} }
// 1.5.4.3. Otherwise, the operation should be prohibited, and an error is reported. // 1.5.4.3. Otherwise, the operation should be prohibited, and an error is reported.
else { else {
...@@ -119,7 +174,26 @@ export class EvaluateVisitor implements AbstractVisitor { ...@@ -119,7 +174,26 @@ export class EvaluateVisitor implements AbstractVisitor {
// 1.6. If it is a multiplication-like expression, then: // 1.6. If it is a multiplication-like expression, then:
case "Multiplicative": case "Multiplicative":
// TODO: YOUR CODE HERE node = node as Multiplicative;
let leftNode = this.visit(node.left);
let rightNode = this.visit(node.right);
if (node.op.value === "*") {
return {
value: leftNode.value * rightNode.value,
unit: leftNode.unit,
};
}
else if (node.op.value === "/") {
return {
value: leftNode.value / rightNode.value,
unit: leftNode.unit,
};
}
else {
throw new Error("System failure");
}
// 1.7. Otherwise: // 1.7. Otherwise:
default: default:
......
...@@ -24,18 +24,20 @@ export function interpret(program: string): Environment<EvaluatedResult> { ...@@ -24,18 +24,20 @@ export function interpret(program: string): Environment<EvaluatedResult> {
// 4.3. For each statement in the AST: // 4.3. For each statement in the AST:
for (let statement of parsed) { for (let statement of parsed) {
// 4.3.1. Visit it using the typechecking visitor. // 4.3.1. Visit it using the typechecking visitor.
// TODO: YOUR CODE HERE typeCheckVisitor.visit(statement);
} }
// 5. Evaluate the AST. // 5. Evaluate the AST.
// 5.1. Initialize a new environment that will store _values_ of variables. // 5.1. Initialize a new environment that will store _values_ of variables.
// TODO: YOUR CODE HERE let valueCheckEnv = newEnv<EvaluatedResult>();
// 5.2. Initialize an evaluating visitor. // 5.2. Initialize an evaluating visitor.
// TODO: YOUR CODE HERE let valueCheckVisitor = new EvaluateVisitor(valueCheckEnv);
// 5.3. For each statement in the AST: // 5.3. For each statement in the AST:
// 5.3.1. Visit it using the evaluating visitor. // 5.3.1. Visit it using the evaluating visitor.
// TODO: YOUR CODE HERE for (let statement of parsed) {
valueCheckVisitor.visit(statement);
}
// 6. Return the environment that stores the values of variables. // 6. Return the environment that stores the values of variables.
return /* TODO: YOUR CODE HERE */ ; return valueCheckEnv ;
} }
...@@ -37,12 +37,12 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -37,12 +37,12 @@ export function parseProgram(tokens: Token[]): AstNode[] {
function advance(): void { function advance(): void {
// 1. Increment the value of `currentPosition` by 1. // 1. Increment the value of `currentPosition` by 1.
// TODO: YOUR CODE HERE currentPosition++;
} }
function peek() { function peek() {
// 1. Return the element of array `tokens` at a position immediately after the current position. // 1. Return the element of array `tokens` at a position immediately after the current position.
// TODO: YOUR CODE HERE return tokens[currentPosition + 1];
} }
function error() { function error() {
...@@ -77,19 +77,31 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -77,19 +77,31 @@ export function parseProgram(tokens: Token[]): AstNode[] {
// 1.1.1. Advance the position of the parser. // 1.1.1. Advance the position of the parser.
// 1.2. Otherwise, i.e., if it is not an equality sign, report an error. // 1.2. Otherwise, i.e., if it is not an equality sign, report an error.
// TODO: YOUR CODE HERE if (peek().kind === "Equals") {
advance();
}
else throw error();
} }
function Semicolon(): void { function Semicolon(): void {
// TODO: YOUR CODE HERE if (peek().kind === "Separator") {
advance();
}
else throw error();
} }
function /* TODO: YOUR CODE HERE */(): void { function OpeningBracket(): void {
// TODO: YOUR CODE HERE if (peek().kind === "OpeningBracket"){
advance();
}
else throw error();
} }
function /* TODO: YOUR CODE HERE */(): void { function ClosingBracket(): void {
// TODO: YOUR CODE HERE if (peek().kind === "ClosingBracket"){
advance();
}
else throw error();
} }
...@@ -103,10 +115,13 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -103,10 +115,13 @@ export function parseProgram(tokens: Token[]): AstNode[] {
// 1.1. If it is a numeric literal, then: // 1.1. If it is a numeric literal, then:
// 1.1.1. Advance the position of the parser. // 1.1.1. Advance the position of the parser.
// 1.2. Otherwise, i.e., if it is not a numeric literal, report an error. // 1.2. Otherwise, i.e., if it is not a numeric literal, report an error.
// TODO: YOUR CODE HERE if (peek().kind === "Number"){
advance();
}
else throw error();
// 2. Return the numerical value of the token. // 2. Return the numerical value of the token.
return parseInt(/* TODO: YOUR CODE HERE */, 10); return parseInt(getCurrentToken().value, 10);
} }
function Identifier(): Identifier { function Identifier(): Identifier {
...@@ -118,13 +133,13 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -118,13 +133,13 @@ export function parseProgram(tokens: Token[]): AstNode[] {
// 2.2. Return an object with two properties: `name` and `nodeType`. // 2.2. Return an object with two properties: `name` and `nodeType`.
return { return {
// 2.2.1. The property `name` should be the value of the current token. // 2.2.1. The property `name` should be the value of the current token.
// TODO: YOUR CODE HERE name: getCurrentToken().value,
// 2.2.2. The property `nodeType` should be `Identifier`. // 2.2.2. The property `nodeType` should be `Identifier`.
nodeType: "Identifier" nodeType: "Identifier"
}; };
} }
// 3. Throw an error otherwise (i.e., if the kind of the peek'ed token is not `Identifier`. // 3. Throw an error otherwise (i.e., if the kind of the peek'ed token is not `Identifier`.
throw error(); else throw error();
} }
function PhysicalUnit(): PhysicalUnit { function PhysicalUnit(): PhysicalUnit {
...@@ -142,30 +157,39 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -142,30 +157,39 @@ export function parseProgram(tokens: Token[]): AstNode[] {
// 3.1.2.1. Return an object with the following properties: // 3.1.2.1. Return an object with the following properties:
return { return {
// 3.1.2.1.1. Property `value` should be the value of the token, casted to type `Time` // 3.1.2.1.1. Property `value` should be the value of the token, casted to type `Time`
// TODO: YOUR CODE HERE value: unitValue as Time,
// 3.1.2.1.2. Property `kind` should be `PhysicalUnitEnum.Time` // 3.1.2.1.2. Property `kind` should be `PhysicalUnitEnum.Time`
// TODO: YOUR CODE HERE kind: PhysicalUnitEnum.Time,
// 3.1.2.1.3. Property `nodeType` should be `PhysicalUnit` // 3.1.2.1.3. Property `nodeType` should be `PhysicalUnit`
// TODO: YOUR CODE HERE nodeType: "PhysicalUnit"
}; };
// 3.1.3. Otherwise, if that value is one of the mass units, then: // 3.1.3. Otherwise, if that value is one of the mass units, then:
} else if (/* TODO: YOUR CODE HERE */) { } else if (["g", "kg"]) {
// 3.1.3.1. Return an object with the following properties: // 3.1.3.1. Return an object with the following properties:
return {
// 3.1.3.1.1. Property `value` should be the value of the token, casted to type `Mass` // 3.1.3.1.1. Property `value` should be the value of the token, casted to type `Mass`
value: unitValue as Mass,
// 3.1.3.1.2. Property `kind` should be `PhysicalUnitEnum.Mass` // 3.1.3.1.2. Property `kind` should be `PhysicalUnitEnum.Mass`
kind: PhysicalUnitEnum.Mass,
// 3.1.3.1.3. Property `nodeType` should be `PhysicalUnit` // 3.1.3.1.3. Property `nodeType` should be `PhysicalUnit`
nodeType: "PhysicalUnit"
// TODO: YOUR CODE HERE };
// 3.1.4. Otherwise, if that value is one of the (???what???) units, then: // 3.1.4. Otherwise, if that value is one of the (???what???) units, then:
} else if (/* TODO: YOUR CODE HERE */) { } else if (["m", "km"]) {
// TODO: YOUR CODE HERE return {
value: unitValue as Distance,
kind: PhysicalUnitEnum.Distance,
nodeType: "PhysicalUnit"
}
} }
// 3.1.5. Otherwise, if that value is one of the (???what???) units, then: // 3.1.5. Otherwise, if that value is one of the (???what???) units, then:
else if ( else if (["km/h", "km/s", "km/min", "m/h", "m/s", "m/min"]) {
// TODO: YOUR CODE HERE return {
) { value: unitValue as Velocity,
// TODO: YOUR CODE HERE kind: PhysicalUnitEnum.Velocity,
nodeType: "PhysicalUnit"
}
} }
} }
// 3.2. Otherwise, i.e., if the kind of the token is not `PhysicalUnit`, throw an error. // 3.2. Otherwise, i.e., if the kind of the token is not `PhysicalUnit`, throw an error.
...@@ -190,28 +214,35 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -190,28 +214,35 @@ export function parseProgram(tokens: Token[]): AstNode[] {
// 2. Expect an identifier, and store it in a variable. // 2. Expect an identifier, and store it in a variable.
let assignee = Identifier(); let assignee = Identifier();
// 3. Expect an equality symbol `=`. // 3. Expect an equality symbol `=`.
// TODO: YOUR CODE HERE Equals();
// 4. Expect an expression, and store it in a variable. // 4. Expect an expression, and store it in a variable.
// TODO: YOUR CODE HERE let expr = Expr();
// 5. Expect a semicolon. // 5. Expect a semicolon.
// TODO: YOUR CODE HERE Semicolon();
// 6. Return an AST node that represents an assignment statement -- it is an object with: // 6. Return an AST node that represents an assignment statement -- it is an object with:
return { return {
// 6.1. A property that represents the assignee (i.e., the variable that has been assigned to). // 6.1. A property that represents the assignee (i.e., the variable that has been assigned to).
assignee, assignee,
// 6.2. A property that represents the expression on the right-hand side of the assignment statement. // 6.2. A property that represents the expression on the right-hand side of the assignment statement.
// TODO: YOUR CODE HERE expr,
// 6.3. A property `nodeType` which is (???what???). // 6.3. A property `nodeType` which is (???what???).
// TODO: YOUR CODE HERE nodeType : "AssignmentStatement"
}; };
} }
function Expr(): Expr { function Expr(): Expr {
// TODO: YOUR CODE HERE return Additive();
} }
function GroupExpr(): GroupExpr { function GroupExpr(): GroupExpr {
// TODO: YOUR CODE HERE OpeningBracket();
let subExpr = Expr();
ClosingBracket();
return {
subExpr,
nodeType: "GroupExpr"
};
} }
function PrimitiveExpr(): PrimitiveExpr { function PrimitiveExpr(): PrimitiveExpr {
...@@ -227,10 +258,10 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -227,10 +258,10 @@ export function parseProgram(tokens: Token[]): AstNode[] {
case "Number": case "Number":
// 2.2.1. Expect a measured number. // 2.2.1. Expect a measured number.
// 2.2.2. Return an AST node that represents it. // 2.2.2. Return an AST node that represents it.
// TODO: YOUR CODE HERE return MeasuredNumber();
// 2.3. If it is a (???what???): // 2.3. If it is a (???what???):
case /* TODO: YOUR CODE HERE */: case "OpeningBracket":
// TODO: YOUR CODE HERE return GroupExpr();
// 2.4. Otherwise, if it is none of the above: // 2.4. Otherwise, if it is none of the above:
default: default:
// 2.4.1. Throw an error. // 2.4.1. Throw an error.
...@@ -239,7 +270,14 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -239,7 +270,14 @@ export function parseProgram(tokens: Token[]): AstNode[] {
} }
function MeasuredNumber(): MeasuredNumber { function MeasuredNumber(): MeasuredNumber {
// TODO: YOUR CODE HERE let numericalValue = NumericalLiteral();
let unit = PhysicalUnit();
return {
numericalValue,
unit,
nodeType: "MeasuredNumber"
};
} }
function OpAddSub(): Operator { function OpAddSub(): Operator {
...@@ -247,7 +285,7 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -247,7 +285,7 @@ export function parseProgram(tokens: Token[]): AstNode[] {
// 1.1. If it is either `+` or `-`, then: // 1.1. If it is either `+` or `-`, then:
if (peek().kind == "OpAddSub") { if (peek().kind == "OpAddSub") {
// 1.1.1. Advance the position of the parser. // 1.1.1. Advance the position of the parser.
// TODO: YOUR CODE HERE advance();
} }
// 1.2. Otherwise, i.e., if it is not `+` or `-`, report an error. // 1.2. Otherwise, i.e., if it is not `+` or `-`, report an error.
else throw error(); else throw error();
...@@ -264,7 +302,10 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -264,7 +302,10 @@ export function parseProgram(tokens: Token[]): AstNode[] {
}; };
// 2.2. If it is `-`, then: // 2.2. If it is `-`, then:
case "-": case "-":
// TODO: YOUR CODE HERE return {
value: "-",
nodeType: "OpAddSub"
}
// 2.3. Otherwise, i.e., if it is neither `+` nor `-`: // 2.3. Otherwise, i.e., if it is neither `+` nor `-`:
default: default:
// 2.3.1. Throw an error. // 2.3.1. Throw an error.
...@@ -273,29 +314,58 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -273,29 +314,58 @@ export function parseProgram(tokens: Token[]): AstNode[] {
} }
function OpMulDiv(): Operator { function OpMulDiv(): Operator {
// TODO: YOUR CODE HERE // 1. Peek the next input token.
// 1.1. If it is either `+` or `-`, then:
if (peek().kind == "OpMulDiv") {
// 1.1.1. Advance the position of the parser.
advance();
}
// 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: "OpMulDiv"
};
// 2.2. If it is `-`, then:
case "/":
return {
value: "/",
nodeType: "OpMulDiv"
}
// 2.3. Otherwise, i.e., if it is neither `+` nor `-`:
default:
// 2.3.1. Throw an error.
throw error();
}
} }
function Additive(): PrimitiveExpr | Multiplicative | Additive { function Additive(): PrimitiveExpr | Multiplicative | Additive {
// 1. Expect a multiplicative expression, and store it in a variable. // 1. Expect a multiplicative expression, and store it in a variable.
let left: PrimitiveExpr | Multiplicative | Additive = Multiplicative(); let left: PrimitiveExpr | Multiplicative | Additive = Multiplicative(); //May be a problem
// 2. Emulate repetition in the grammar `{ ... }*` by peeking the next input token. See also item 2.2. below. // 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 `-`. // 2.1. Check whether that peek'ed token is `+` or `-`.
while (peek().kind === "OpAddSub") { while (peek().kind === "OpAddSub") {
// 3. Expect (i.e., consume) that peek'ed token, and store it in a variable. // 3. Expect (i.e., consume) that peek'ed token, and store it in a variable.
let op = OpAddSub(); let op = OpAddSub();
// 4. Expect a multiplicative expression, and store it in a variable. // 4. Expect a multiplicative expression, and store it in a variable.
let right = /* TODO: YOUR CODE HERE */; let right = Multiplicative();
// 5. Update the expression stored in item 1. to be an AST node that represents an additive expression -- it is an object with: // 5. Update the expression stored in item 1. to be an AST node that represents an additive expression -- it is an object with:
left = { left = {
// 5.1. A property that represents the left operand of the multiplicative expression. // 5.1. A property that represents the left operand of the multiplicative expression.
left, left,
// 5.2. A property that represents the operator of the multiplicative expression. // 5.2. A property that represents the operator of the multiplicative expression.
// TODO: YOUR CODE HERE op,
// 5.3. A property that represents the right operand of the multiplicative expression. // 5.3. A property that represents the right operand of the multiplicative expression.
right, right,
// 5.4. A property `nodeType` which is (???what???). // 5.4. A property `nodeType` which is (???what???).
nodeType: /* TODO: YOUR CODE HERE */, nodeType: "Additive"
}; };
// 2.2. Make an iteration of the loop to process the possible remaining part of the addition-like expression. // 2.2. Make an iteration of the loop to process the possible remaining part of the addition-like expression.
} }
...@@ -304,7 +374,18 @@ export function parseProgram(tokens: Token[]): AstNode[] { ...@@ -304,7 +374,18 @@ export function parseProgram(tokens: Token[]): AstNode[] {
} }
function Multiplicative(): Multiplicative | PrimitiveExpr { function Multiplicative(): Multiplicative | PrimitiveExpr {
// TODO: YOUR CODE HERE let left: Multiplicative | PrimitiveExpr = PrimitiveExpr(); //May be a problem
while (peek().kind === "OpMulDiv") {
let op = OpMulDiv();
let right = PrimitiveExpr();
left = {
left,
op,
right,
nodeType: "Multiplicative"
};
}
return left;
} }
} }
...@@ -22,28 +22,32 @@ export class TypeCheckVisitor implements AbstractVisitor { ...@@ -22,28 +22,32 @@ export class TypeCheckVisitor implements AbstractVisitor {
// 1.1. If it is a measured number, then: // 1.1. If it is a measured number, then:
case "MeasuredNumber": case "MeasuredNumber":
// 1.1.1. Cast the node to type `MeasuredNumber`. // 1.1.1. Cast the node to type `MeasuredNumber`.
// TODO: YOUR CODE HERE node = node as MeasuredNumber;
// 1.1.2. Return the physical unit of the node. // 1.1.2. Return the physical unit of the node.
return node.unit.kind; return node.unit.kind;
// 1.2. If it is a (???what???), then: // 1.2. If it is a (???what???), then:
case /* TODO: YOUR CODE HERE */: case "GroupExpr":
// TODO: YOUR CODE HERE node = node as GroupExpr;
return this.visit(node.subExpr);
// 1.3. If it is an assignment statement, then: // 1.3. If it is an assignment statement, then:
case "AssignmentStatement": case "AssignmentStatement":
// TODO: YOUR CODE HERE node = node as Assignment;
let right = this.visit(node.expr);
this.env.declare(node.assignee.name, right, this.env);
return right;
// 1.4. If it is an identifier, then: // 1.4. If it is an identifier, then:
case "Identifier": case "Identifier":
// 1.4.1. Cast the node to type `Identifier`. // 1.4.1. Cast the node to type `Identifier`.
// TODO: YOUR CODE HERE node = node as Identifier;
// 1.4.2. Lookup the type (i.e., its physical unit) of the variable in the environment. // 1.4.2. Lookup the type (i.e., its physical unit) of the variable in the environment.
// TODO: YOUR CODE HERE let fromEnv = this.env.lookup(node.name, this.env);
// 1.4.3. If the looked up type is not undefined, then: // 1.4.3. If the looked up type is not undefined, then:
if (fromEnv !== undefined) { if (fromEnv !== undefined) {
// 1.4.3.1. Return the looked up type. // 1.4.3.1. Return the looked up type.
// TODO: YOUR CODE HERE return fromEnv;
} else { } else {
// 1.4.4. Otherwise, i.e., if the looked up type is undefined, then report an error. // 1.4.4. Otherwise, i.e., if the looked up type is undefined, then report an error.
throw new Error("Variable " + node.name + " is not defined"); throw new Error("Variable " + node.name + " is not defined");
...@@ -51,20 +55,23 @@ export class TypeCheckVisitor implements AbstractVisitor { ...@@ -51,20 +55,23 @@ export class TypeCheckVisitor implements AbstractVisitor {
// 1.5. If it is an addition-like expression, then: // 1.5. If it is an addition-like expression, then:
case "Additive": { case "Additive": {
// TODO: YOUR CODE HERE node = node as Additive;
return this.visit(node.left);
} }
// 1.6. It it is a multiplication-like expression, then: // 1.6. It it is a multiplication-like expression, then:
case "Multiplicative": { case "Multiplicative": {
// TODO: YOUR CODE HERE node = node as Multiplicative;
let left = this.visit(node.left);
let right = this.visit(node.right);
// 1.6.4. Depending on the operation `op` of the node: // 1.6.4. Depending on the operation `op` of the node:
// 1.6.4.1. If it is `*`, then: // 1.6.4.1. If it is `*`, then:
if (node.op.value === "*") { if (node.op.value === "*") {
// 1.6.4.1.1. If the physical quantity of `left` is (???what???) and the physical quantity of `right` is (???what???), or vice versa, then: // 1.6.4.1.1. If the physical quantity of `left` is (???what???) and the physical quantity of `right` is (???what???), or vice versa, then:
if ( /** TODO: YOUR CODE HERE **/ ) { if (left === PhysicalUnitEnum.Velocity && right === PhysicalUnitEnum.Time || left === PhysicalUnitEnum.Time && right === PhysicalUnitEnum.Velocity) {
// 1.6.4.1.1.1. Return the physical unit representing length. // 1.6.4.1.1.1. Return the physical unit representing length.
// TODO: YOUR CODE HERE return PhysicalUnitEnum.Distance;
} else { } else {
// 1.6.4.1.2. Otherwise, report an error. // 1.6.4.1.2. Otherwise, report an error.
throw new Error( throw new Error(
...@@ -76,8 +83,13 @@ export class TypeCheckVisitor implements AbstractVisitor { ...@@ -76,8 +83,13 @@ export class TypeCheckVisitor implements AbstractVisitor {
} }
// 1.6.4.2. Otherwise, if it is `/`, then: // 1.6.4.2. Otherwise, if it is `/`, then:
} else if (node.op.value === "/") { } else if (node.op.value === "/") {
// TODO: YOUR CODE HERE if (left === PhysicalUnitEnum.Distance && right === PhysicalUnitEnum.Time) {
} return PhysicalUnitEnum.Velocity;
}
if (right === 0) {
throw new Error("Division by zero");
}
}
// 1.6.4.3. Otherwise, if it any other operator, report an error. // 1.6.4.3. Otherwise, if it any other operator, report an error.
throw new Error("Failure!"); throw new Error("Failure!");
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment