From 1986463294063b9fbbcdea535f489b7dbff6db5a Mon Sep 17 00:00:00 2001 From: Vetle Larsen <vetlelarzen@gmail.com> Date: Fri, 5 Apr 2024 13:49:36 +0200 Subject: [PATCH] Trying to run, but something doesnt work. Error at line 90 in typecheckvisitor --- index.ts | 2 + package-lock.json | 2 +- src/environment.ts | 2 + src/evaluateVisitor.ts | 112 ++++++++++++++++++++++----- src/interpreter.ts | 12 +-- src/parser.ts | 167 +++++++++++++++++++++++++++++----------- src/typeCheckVisitor.ts | 40 ++++++---- 7 files changed, 255 insertions(+), 82 deletions(-) diff --git a/index.ts b/index.ts index 02d5694..f6dde36 100644 --- a/index.ts +++ b/index.ts @@ -20,3 +20,5 @@ function main() { } } main(); + +//let blabla = b / f + g + 1[km/s]; diff --git a/package-lock.json b/package-lock.json index 2abd3bc..d3fd99f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "INF222-Obligatory2-Skeleton", + "name": "inf-222-v-24-obligatory-2-skeleton", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/src/environment.ts b/src/environment.ts index 7102d23..17864eb 100644 --- a/src/environment.ts +++ b/src/environment.ts @@ -15,11 +15,13 @@ function lookup<T>(x: string, env: Environment<T>): T | undefined { 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`. // 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 { // 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 + env.env.set(x, v); } export function newEnv<T>(): Environment<T> { diff --git a/src/evaluateVisitor.ts b/src/evaluateVisitor.ts index 13d0340..8961add 100644 --- a/src/evaluateVisitor.ts +++ b/src/evaluateVisitor.ts @@ -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. let expr = this.visit(node.expr); // 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. return expr; // 1.2. If it is an identifier, then: case "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. - let lookedUp = /* YOUR CODE HERE */; + let lookedUp = this.env.lookup(node.name, this.env); // 1.2.3. If the value is not undefined, then: if (lookedUp !== undefined) { // 1.2.3.1. Return the value. @@ -53,13 +53,14 @@ export class EvaluateVisitor implements AbstractVisitor { // 1.3. If it is a group expression, then: case "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. - // TODO: YOUR CODE HERE + this.visit(node.subExpr); // 1.4. If it is a measured number, then: case "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: switch (node.unit.value) { // 1.4.2.1. If it is grams: @@ -73,16 +74,67 @@ export class EvaluateVisitor implements AbstractVisitor { }; // 1.4.2.2. If it is kilograms: case "kg": - // TODO: YOUR CODE HERE + return { + value: node.numericalValue*1000, + unit: { ...node.unit, value: "g" }, + }; // 1.4.2.3. If it is hours: case "h": - // TODO: YOUR CODE HERE + return { + value: node.numericalValue*3600, + unit: { ...node.unit, value: "s" }, + }; // 1.4.2._. If it is ???what???: - case /* TODO: YOUR CODE HERE */: - // TODO: YOUR CODE HERE - - // TODO: YOUR CODE HERE FOR THE OTHER CASES - + case "min": + return { + value: node.numericalValue*60, + 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: default: // 1.4.2.14.1. Throw an error. @@ -92,25 +144,28 @@ export class EvaluateVisitor implements AbstractVisitor { // 1.5. If it is an addition-like expression, then: case "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. - // 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. - // TODO: YOUR CODE HERE + let right = this.visit(node.right) // 1.5.4. Depending on the `op` of this node: // 1.5.4.1. If it is `+`, then: if (node.op.value === "+") { // 1.5.4.1.1. Return an object that represents a value and a physical unit: return { // 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.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: } 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. else { @@ -119,7 +174,26 @@ export class EvaluateVisitor implements AbstractVisitor { // 1.6. If it is a multiplication-like expression, then: 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: default: diff --git a/src/interpreter.ts b/src/interpreter.ts index ee575ed..db4174d 100644 --- a/src/interpreter.ts +++ b/src/interpreter.ts @@ -24,18 +24,20 @@ export function interpret(program: string): Environment<EvaluatedResult> { // 4.3. For each statement in the AST: for (let statement of parsed) { // 4.3.1. Visit it using the typechecking visitor. - // TODO: YOUR CODE HERE + typeCheckVisitor.visit(statement); } // 5. Evaluate the AST. // 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. - // TODO: YOUR CODE HERE + let valueCheckVisitor = new EvaluateVisitor(valueCheckEnv); // 5.3. For each statement in the AST: // 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. - return /* TODO: YOUR CODE HERE */ ; + return valueCheckEnv ; } diff --git a/src/parser.ts b/src/parser.ts index 4d4ed82..2583851 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -37,12 +37,12 @@ export function parseProgram(tokens: Token[]): AstNode[] { function advance(): void { // 1. Increment the value of `currentPosition` by 1. - // TODO: YOUR CODE HERE + currentPosition++; } function peek() { // 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() { @@ -77,19 +77,31 @@ export function parseProgram(tokens: Token[]): AstNode[] { // 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 + if (peek().kind === "Equals") { + advance(); + } + else throw error(); } function Semicolon(): void { - // TODO: YOUR CODE HERE + if (peek().kind === "Separator") { + advance(); + } + else throw error(); } - function /* TODO: YOUR CODE HERE */(): void { - // TODO: YOUR CODE HERE + function OpeningBracket(): void { + if (peek().kind === "OpeningBracket"){ + advance(); + } + else throw error(); } - function /* TODO: YOUR CODE HERE */(): void { - // TODO: YOUR CODE HERE + function ClosingBracket(): void { + if (peek().kind === "ClosingBracket"){ + advance(); + } + else throw error(); } @@ -103,10 +115,13 @@ export function parseProgram(tokens: Token[]): AstNode[] { // 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 + if (peek().kind === "Number"){ + advance(); + } + else throw error(); // 2. Return the numerical value of the token. - return parseInt(/* TODO: YOUR CODE HERE */, 10); + return parseInt(getCurrentToken().value, 10); } function Identifier(): Identifier { @@ -118,13 +133,13 @@ export function parseProgram(tokens: Token[]): AstNode[] { // 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 + name: getCurrentToken().value, // 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(); + else throw error(); } function PhysicalUnit(): PhysicalUnit { @@ -142,30 +157,39 @@ export function parseProgram(tokens: Token[]): AstNode[] { // 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 + value: unitValue as 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` - // TODO: YOUR CODE HERE + nodeType: "PhysicalUnit" }; // 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: + return { // 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` + kind: PhysicalUnitEnum.Mass, // 3.1.3.1.3. Property `nodeType` should be `PhysicalUnit` - - // TODO: YOUR CODE HERE + nodeType: "PhysicalUnit" + }; // 3.1.4. Otherwise, if that value is one of the (???what???) units, then: - } else if (/* TODO: YOUR CODE HERE */) { - // TODO: YOUR CODE HERE + } else if (["m", "km"]) { + return { + value: unitValue as Distance, + kind: PhysicalUnitEnum.Distance, + nodeType: "PhysicalUnit" + } } // 3.1.5. Otherwise, if that value is one of the (???what???) units, then: - else if ( - // TODO: YOUR CODE HERE - ) { - // TODO: YOUR CODE HERE + else if (["km/h", "km/s", "km/min", "m/h", "m/s", "m/min"]) { + return { + value: unitValue as Velocity, + kind: PhysicalUnitEnum.Velocity, + nodeType: "PhysicalUnit" + } } } // 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[] { // 2. Expect an identifier, and store it in a variable. let assignee = Identifier(); // 3. Expect an equality symbol `=`. - // TODO: YOUR CODE HERE + Equals(); // 4. Expect an expression, and store it in a variable. - // TODO: YOUR CODE HERE + let expr = Expr(); // 5. Expect a semicolon. - // TODO: YOUR CODE HERE + Semicolon(); // 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 + expr, // 6.3. A property `nodeType` which is (???what???). - // TODO: YOUR CODE HERE + nodeType : "AssignmentStatement" }; } function Expr(): Expr { - // TODO: YOUR CODE HERE + return Additive(); } function GroupExpr(): GroupExpr { - // TODO: YOUR CODE HERE + OpeningBracket(); + let subExpr = Expr(); + ClosingBracket(); + + return { + subExpr, + nodeType: "GroupExpr" + }; } function PrimitiveExpr(): PrimitiveExpr { @@ -227,10 +258,10 @@ export function parseProgram(tokens: Token[]): AstNode[] { case "Number": // 2.2.1. Expect a measured number. // 2.2.2. Return an AST node that represents it. - // TODO: YOUR CODE HERE + return MeasuredNumber(); // 2.3. If it is a (???what???): - case /* TODO: YOUR CODE HERE */: - // TODO: YOUR CODE HERE + case "OpeningBracket": + return GroupExpr(); // 2.4. Otherwise, if it is none of the above: default: // 2.4.1. Throw an error. @@ -239,7 +270,14 @@ export function parseProgram(tokens: Token[]): AstNode[] { } function MeasuredNumber(): MeasuredNumber { - // TODO: YOUR CODE HERE + let numericalValue = NumericalLiteral(); + let unit = PhysicalUnit(); + + return { + numericalValue, + unit, + nodeType: "MeasuredNumber" + }; } function OpAddSub(): Operator { @@ -247,7 +285,7 @@ export function parseProgram(tokens: Token[]): AstNode[] { // 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 + advance(); } // 1.2. Otherwise, i.e., if it is not `+` or `-`, report an error. else throw error(); @@ -264,7 +302,10 @@ export function parseProgram(tokens: Token[]): AstNode[] { }; // 2.2. If it is `-`, then: case "-": - // TODO: YOUR CODE HERE + return { + value: "-", + nodeType: "OpAddSub" + } // 2.3. Otherwise, i.e., if it is neither `+` nor `-`: default: // 2.3.1. Throw an error. @@ -273,29 +314,58 @@ export function parseProgram(tokens: Token[]): AstNode[] { } 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 { // 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.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 */; + 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: 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 + op, // 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 */, + nodeType: "Additive" }; // 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[] { } 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; } } diff --git a/src/typeCheckVisitor.ts b/src/typeCheckVisitor.ts index 7cf66bb..7d801fc 100644 --- a/src/typeCheckVisitor.ts +++ b/src/typeCheckVisitor.ts @@ -22,28 +22,32 @@ export class TypeCheckVisitor implements AbstractVisitor { // 1.1. If it is a measured number, then: case "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. return node.unit.kind; // 1.2. If it is a (???what???), then: - case /* TODO: YOUR CODE HERE */: - // TODO: YOUR CODE HERE + case "GroupExpr": + node = node as GroupExpr; + return this.visit(node.subExpr); // 1.3. If it is an assignment statement, then: 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: case "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. - // 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: if (fromEnv !== undefined) { // 1.4.3.1. Return the looked up type. - // TODO: YOUR CODE HERE + return fromEnv; } else { // 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"); @@ -51,20 +55,23 @@ export class TypeCheckVisitor implements AbstractVisitor { // 1.5. If it is an addition-like expression, then: 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: 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.1. If it is `*`, then: 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: - 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. - // TODO: YOUR CODE HERE + return PhysicalUnitEnum.Distance; } else { // 1.6.4.1.2. Otherwise, report an error. throw new Error( @@ -76,8 +83,13 @@ export class TypeCheckVisitor implements AbstractVisitor { } // 1.6.4.2. Otherwise, if it is `/`, then: } 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. throw new Error("Failure!"); } -- GitLab