From d6d6568149c28339df197ee6137d080a0c90c38a Mon Sep 17 00:00:00 2001 From: xyliew25 Date: Mon, 29 Jan 2024 18:53:52 +0800 Subject: [PATCH] Update tests - Create Context stubs - Check Environment --- src/ec-evaluator/__tests__/arithmetic.test.ts | 283 ++++++++++++++++++ src/ec-evaluator/__tests__/ec-evaluator.ts | 201 ------------- .../__tests__/local-var-assignment.test.ts | 192 ++++++++++++ src/ec-evaluator/__tests__/utils.ts | 48 +++ 4 files changed, 523 insertions(+), 201 deletions(-) create mode 100644 src/ec-evaluator/__tests__/arithmetic.test.ts delete mode 100644 src/ec-evaluator/__tests__/ec-evaluator.ts create mode 100644 src/ec-evaluator/__tests__/local-var-assignment.test.ts create mode 100644 src/ec-evaluator/__tests__/utils.ts diff --git a/src/ec-evaluator/__tests__/arithmetic.test.ts b/src/ec-evaluator/__tests__/arithmetic.test.ts new file mode 100644 index 0000000..cef631e --- /dev/null +++ b/src/ec-evaluator/__tests__/arithmetic.test.ts @@ -0,0 +1,283 @@ +import { evaluate } from "../interpreter"; +import { parse } from "../../ast/parser" +import { DECLARED_BUT_NOT_YET_ASSIGNED, isNode } from "../utils"; +import { ControlStub, EnvironmentStub, StashStub, createContextStub } from "./utils"; + +it("evaluate local variable declaration to a basic arithmetic operation correctly", () => { + const programStr = ` + public class Test { + public static void main(String[] args) { + int y = 10 % 2; + } + } + `; + + const compilationUnit = parse(programStr); + expect(compilationUnit).toBeTruthy(); + + const context = createContextStub(); + context.control.push(compilationUnit!); + + const result = evaluate(context); + + const expectedAgendaTrace = [ + "CompilationUnit", + "Assignment", + "LocalVariableDeclarationStatement", + "Pop", + "Assign", + "BinaryExpression", + "BinaryOperation", + "Literal", + "Literal" + ]; + const expectedStashTrace = ["10", "2", "0"]; + const expectedEnv = new Map([ + ["y", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + { + kind: "Literal", + literalType: { + kind: "DecimalIntegerLiteral", + value: "0", + }, + }, + ]] + ]); + + expect(result).toEqual(undefined); + expect((context.control as ControlStub).getTrace().map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); + expect((context.stash as StashStub).getTrace().map(i => i.literalType.value)).toEqual(expectedStashTrace); + expect((context.environment as EnvironmentStub).getTrace()).toEqual(expectedEnv); +}); + +it("evaluate local variable declaration to a complex arithmetic operation correctly", () => { + const programStr = ` + public class Test { + public static void main(String[] args) { + int z = 1 + (2 * 3) - 4; + } + } + `; + + const compilationUnit = parse(programStr); + expect(compilationUnit).toBeTruthy(); + + const context = createContextStub(); + context.control.push(compilationUnit!); + + const result = evaluate(context); + + const expectedAgendaTrace = [ + "CompilationUnit", + "Assignment", + "LocalVariableDeclarationStatement", + "Pop", + "Assign", + "BinaryExpression", + "BinaryOperation", + "Literal", + "BinaryExpression", + "BinaryOperation", + "BinaryExpression", + "Literal", + "BinaryOperation", + "Literal", + "Literal" + ]; + const expectedStashTrace = ["1", "2", "3", "6", "7", "4", "3"]; + const expectedEnv = new Map([ + ["z", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + { + kind: "Literal", + literalType: { + kind: "DecimalIntegerLiteral", + value: "3", + } + }, + ]], + ]); + + expect(result).toEqual(undefined); + expect((context.control as ControlStub).getTrace().map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); + expect((context.stash as StashStub).getTrace().map(i => i.literalType.value)).toEqual(expectedStashTrace); + expect((context.environment as EnvironmentStub).getTrace()).toEqual(expectedEnv); +}); + +it("evaluate multiple local variable declarations correctly", () => { + const programStr = ` + public class Test { + public static void main(String[] args) { + int x = 1; + int y = 10 % 2; + } + } + `; + + const compilationUnit = parse(programStr); + expect(compilationUnit).toBeTruthy(); + + const context = createContextStub(); + context.control.push(compilationUnit!); + + const result = evaluate(context); + + const expectedAgendaTrace = [ + "CompilationUnit", + "LocalVariableDeclarationStatement", + "LocalVariableDeclarationStatement", + "Assignment", + "LocalVariableDeclarationStatement", + "Pop", + "Assign", + "Literal", + "Assignment", + "LocalVariableDeclarationStatement", + "Pop", + "Assign", + "BinaryExpression", + "BinaryOperation", + "Literal", + "Literal" + ]; + const expectedStashTrace = ["1", "10", "2", "0"]; + const expectedEnv = new Map([ + ["x", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + { + kind: "Literal", + literalType: { + kind: "DecimalIntegerLiteral", + value: "1", + }, + }, + ]], + ["y", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + { + kind: "Literal", + literalType: { + kind: "DecimalIntegerLiteral", + value: "0", + }, + }, + ]], + ]); + + expect(result).toEqual(undefined); + expect((context.control as ControlStub).getTrace().map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); + expect((context.stash as StashStub).getTrace().map(i => i.literalType.value)).toEqual(expectedStashTrace); + expect((context.environment as EnvironmentStub).getTrace()).toEqual(expectedEnv); +}); + +it("evaluate local variable declaration to a basic arithmetic expression without brackets to enforce precedence correctly", () => { + const programStr = ` + public class Test { + public static void main(String[] args) { + int x = 1 + 2 * 3; + } + } + `; + + const compilationUnit = parse(programStr); + expect(compilationUnit).toBeTruthy(); + + const context = createContextStub(); + context.control.push(compilationUnit!); + + const result = evaluate(context); + + const expectedAgendaTrace = [ + "CompilationUnit", + "Assignment", + "LocalVariableDeclarationStatement", + "Pop", + "Assign", + "BinaryExpression", // 1 + 2 * 3 + "BinaryOperation", // + + "BinaryExpression", // 2 * 3 + "Literal", // 1 + "BinaryOperation", // * + "Literal", // 3 + "Literal" // 2 + ]; + const expectedStashTrace = ["1", "2", "3", "6", "7"]; + const expectedEnv = new Map([ + ["x", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + { + kind: "Literal", + literalType: { + kind: "DecimalIntegerLiteral", + value: "7", + }, + }, + ]], + ]); + + expect(result).toEqual(undefined); + expect((context.control as ControlStub).getTrace().map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); + expect((context.stash as StashStub).getTrace().map(i => i.literalType.value)).toEqual(expectedStashTrace); + expect((context.environment as EnvironmentStub).getTrace()).toEqual(expectedEnv); +}); + +it("evaluate local variable declaration to a complex arithmetic expression without brackets to enforce precedence correctly", () => { + const programStr = ` + public class Test { + public static void main(String[] args) { + int x = 2 / 1 - 3 * (5 % 4) + 6; + } + } + `; + + const compilationUnit = parse(programStr); + expect(compilationUnit).toBeTruthy(); + + const context = createContextStub(); + context.control.push(compilationUnit!); + + const result = evaluate(context); + + const expectedAgendaTrace = [ + "CompilationUnit", + "Assignment", + "LocalVariableDeclarationStatement", + "Pop", + "Assign", + "BinaryExpression", // 2 / 1 - 3 * (5 % 4) + 6 + "BinaryOperation", // + + "Literal", // 6 + "BinaryExpression", // 2 / 1 - 3 * (5 % 4) + "BinaryOperation", // - + "BinaryExpression", // 3 * (5 % 4) + "BinaryExpression", // 2 / 1 + "BinaryOperation", // / + "Literal", // 1 + "Literal", // 2 + "BinaryOperation", // * + "BinaryExpression", // (5 % 4) + "Literal", // 3 + "BinaryOperation", // % + "Literal", // 4 + "Literal" // 5 + ]; + const expectedStashTrace = ["2", "1", "2", "3", "5", "4", "1", "3", "-1", "6", "5"]; + const expectedEnv = new Map([ + ["x", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + { + kind: "Literal", + literalType: { + kind: "DecimalIntegerLiteral", + value: "5", + }, + }, + ]], + ]); + + expect(result).toEqual(undefined); + expect((context.control as ControlStub).getTrace().map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); + expect((context.stash as StashStub).getTrace().map(i => i.literalType.value)).toEqual(expectedStashTrace); + expect((context.environment as EnvironmentStub).getTrace()).toEqual(expectedEnv); +}); diff --git a/src/ec-evaluator/__tests__/ec-evaluator.ts b/src/ec-evaluator/__tests__/ec-evaluator.ts deleted file mode 100644 index 35a03f6..0000000 --- a/src/ec-evaluator/__tests__/ec-evaluator.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { evaluate } from "../interpreter"; -import { parse } from "../../ast/parser" -import { isNode } from "../utils"; - -it('evaluate local variable declaration to a literal correctly', () => { - const programStr = ` - public class Test { - public static void main(String[] args) { - int x = 1; - } - } - `; - const compilationUnit = parse(programStr); - if (compilationUnit) { - const [result, agendaTrace, stashTrace] = evaluate(compilationUnit);; - - const expectedAgendaTrace = [ - 'CompilationUnit', - 'Pop', - 'Assignment', - 'Literal' - ]; - const expectedStashTrace = [1]; - - expect(result).toMatchInlineSnapshot(`undefined`); - expect(agendaTrace.map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); - expect(stashTrace.map(i => i.value)).toEqual(expectedStashTrace); - } -}) - -it('evaluate local variable declaration to a basic arithmetic operation correctly', () => { - const programStr = ` - public class Test { - public static void main(String[] args) { - int y = 10 % 2; - } - } - `; - const compilationUnit = parse(programStr); - if (compilationUnit) { - const [result, agendaTrace, stashTrace] = evaluate(compilationUnit);; - - const expectedAgendaTrace = [ - 'CompilationUnit', - 'Pop', - 'Assignment', - 'BinaryExpression', - 'BinaryOperation', - 'Literal', - 'Literal' - ]; - const expectedStashTrace = [10, 2, 0]; - - expect(result).toMatchInlineSnapshot(`undefined`); - expect(agendaTrace.map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); - expect(stashTrace.map(i => i.value)).toEqual(expectedStashTrace); - } -}) - -it('evaluate local variable declaration to a complex arithmetic operation correctly', () => { - const programStr = ` - public class Test { - public static void main(String[] args) { - int z = 1 + (2 * 3) - 4; - } - } - `; - const compilationUnit = parse(programStr); - if (compilationUnit) { - const [result, agendaTrace, stashTrace] = evaluate(compilationUnit);; - - const expectedAgendaTrace = [ - 'CompilationUnit', - 'Pop', - 'Assignment', - 'BinaryExpression', - 'BinaryOperation', - 'Literal', - 'BinaryExpression', - 'BinaryOperation', - 'BinaryExpression', - 'Literal', - 'BinaryOperation', - 'Literal', - 'Literal' - ]; - const expectedStashTrace = [1, 2, 3, 6, 7, 4, 3]; - - expect(result).toMatchInlineSnapshot(`undefined`); - expect(agendaTrace.map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); - expect(stashTrace.map(i => i.value)).toEqual(expectedStashTrace); - } -}) - -it('evaluate multiple local variable declarations correctly', () => { - const programStr = ` - public class Test { - public static void main(String[] args) { - int x = 1; - int y = 10 % 2; - } - } - `; - const compilationUnit = parse(programStr); - if (compilationUnit) { - const [result, agendaTrace, stashTrace] = evaluate(compilationUnit);; - - const expectedAgendaTrace = [ - 'CompilationUnit', - 'LocalVariableDeclarationStatement', - 'LocalVariableDeclarationStatement', - 'Pop', - 'Assignment', - 'Literal', - 'Pop', - 'Assignment', - 'BinaryExpression', - 'BinaryOperation', - 'Literal', - 'Literal' - ]; - const expectedStashTrace = [1, 10, 2, 0]; - - expect(result).toMatchInlineSnapshot(`undefined`); - expect(agendaTrace.map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); - expect(stashTrace.map(i => i.value)).toEqual(expectedStashTrace); - } -}) - -it('evaluate local variable declaration to a basic arithmetic expression without brackets to enforce precedence correctly', () => { - const programStr = ` - public class Test { - public static void main(String[] args) { - int x = 1 + 2 * 3; - } - } - `; - const compilationUnit = parse(programStr); - if (compilationUnit) { - const [result, agendaTrace, stashTrace] = evaluate(compilationUnit); - - const expectedAgendaTrace = [ - 'CompilationUnit', - 'Pop', - 'Assignment', - 'BinaryExpression', // 1 + 2 * 3 - 'BinaryOperation', // + - 'BinaryExpression', // 2 * 3 - 'Literal', // 1 - 'BinaryOperation', // * - 'Literal', // 3 - 'Literal' // 2 - ]; - const expectedStashTrace = [1, 2, 3, 6, 7]; - - expect(result).toMatchInlineSnapshot(`undefined`); - expect(agendaTrace.map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); - expect(stashTrace.map(i => i.value)).toEqual(expectedStashTrace); - } -}) - -it('evaluate local variable declaration to a complex arithmetic expression without brackets to enforce precedence correctly', () => { - const programStr = ` - public class Test { - public static void main(String[] args) { - int x = 2 / 1 - 3 * (5 % 4) + 6; - } - } - `; - const compilationUnit = parse(programStr); - if (compilationUnit) { - const [result, agendaTrace, stashTrace] = evaluate(compilationUnit); - - const expectedAgendaTrace = [ - 'CompilationUnit', - 'Pop', - 'Assignment', - 'BinaryExpression', // 2 / 1 - 3 * (5 % 4) + 6 - 'BinaryOperation', // + - 'Literal', // 6 - 'BinaryExpression', // 2 / 1 - 3 * (5 % 4) - 'BinaryOperation', // - - 'BinaryExpression', // 3 * (5 % 4) - 'BinaryExpression', // 2 / 1 - 'BinaryOperation', // / - 'Literal', // 1 - 'Literal', // 2 - 'BinaryOperation', // * - 'BinaryExpression', // (5 % 4) - 'Literal', // 3 - 'BinaryOperation', // % - 'Literal', // 4 - 'Literal' // 5 - ]; - const expectedStashTrace = [2, 1, 2, 3, 5, 4, 1, 3, -1, 6, 5]; - - expect(result).toMatchInlineSnapshot(`undefined`); - expect(agendaTrace.map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedAgendaTrace); - expect(stashTrace.map(i => i.value)).toEqual(expectedStashTrace); - } -}) diff --git a/src/ec-evaluator/__tests__/local-var-assignment.test.ts b/src/ec-evaluator/__tests__/local-var-assignment.test.ts new file mode 100644 index 0000000..01b4b66 --- /dev/null +++ b/src/ec-evaluator/__tests__/local-var-assignment.test.ts @@ -0,0 +1,192 @@ +import { parse } from "../../ast/parser"; +import { evaluate } from "../interpreter"; +import { Value } from "../types"; +import { DECLARED_BUT_NOT_YET_ASSIGNED, isNode } from "../utils"; +import { ControlStub, EnvironmentStub, StashStub, createContextStub } from "./utils"; + +it("evaluate LocalVariableDeclarationStatement without variableInitializer correctly", () => { + const programStr = ` + public class Test { + public static void main(String[] args) { + int x; + } + } + `; + + const compilationUnit = parse(programStr); + expect(compilationUnit).toBeTruthy(); + + const context = createContextStub(); + context.control.push(compilationUnit!); + + const result = evaluate(context); + + const expectedControlTrace = [ + "CompilationUnit", + ]; + const expectedStashTrace: Value[] = []; + const expectedEnv = new Map([ + ["x", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + ]], + ]); + + expect(result).toEqual(undefined); + expect((context.control as ControlStub).getTrace().map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedControlTrace); + expect((context.stash as StashStub).getTrace().map(i => i.literalType.value)).toEqual(expectedStashTrace); + expect((context.environment as EnvironmentStub).getTrace()).toEqual(expectedEnv); +}); + +it("evaluate LocalVariableDeclarationStatement with variableInitializer correctly", () => { + const programStr = ` + public class Test { + public static void main(String[] args) { + int x = 1; + } + } + `; + + const compilationUnit = parse(programStr); + expect(compilationUnit).toBeTruthy(); + + const context = createContextStub(); + context.control.push(compilationUnit!); + + const result = evaluate(context); + + const expectedControlTrace = [ + "CompilationUnit", + "Assignment", + "LocalVariableDeclarationStatement", + "Pop", + "Assign", + "Literal", + ]; + const expectedStashTrace = ["1"]; + const expectedEnv = new Map([ + ["x", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + { + kind: "Literal", + literalType: { + kind: "DecimalIntegerLiteral", + value: "1", + }, + }, + ]], + ]); + + expect(result).toEqual(undefined); + expect((context.control as ControlStub).getTrace().map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedControlTrace); + expect((context.stash as StashStub).getTrace().map(i => i.literalType.value)).toEqual(expectedStashTrace); + expect((context.environment as EnvironmentStub).getTrace()).toEqual(expectedEnv); +}); + +it("evaluate Assignment correctly", () => { + const programStr = ` + public class Test { + public static void main(String[] args) { + int x; + x = 1; + } + } + `; + + const compilationUnit = parse(programStr); + expect(compilationUnit).toBeTruthy(); + + const context = createContextStub(); + context.control.push(compilationUnit!); + + const result = evaluate(context); + + const expectedControlTrace = [ + "CompilationUnit", + "Assignment", + "LocalVariableDeclarationStatement", + "Pop", + "Assign", + "Literal", + ]; + const expectedStashTrace = ["1"]; + const expectedEnv = new Map([ + ["x", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + { + kind: "Literal", + literalType: { + kind: "DecimalIntegerLiteral", + value: "1", + }, + }, + ]], + ]); + + expect(result).toEqual(undefined); + expect((context.control as ControlStub).getTrace().map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedControlTrace); + expect((context.stash as StashStub).getTrace().map(i => i.literalType.value)).toEqual(expectedStashTrace); + expect((context.environment as EnvironmentStub).getTrace()).toEqual(expectedEnv); +}); + +it("evaluate LocalVariableDeclarationStatement with local variable as variableInitializer correctly", () => { + const programStr = ` + public class Test { + public static void main(String[] args) { + int x = 1; + int y = x; + } + } + `; + + const compilationUnit = parse(programStr); + expect(compilationUnit).toBeTruthy(); + + const context = createContextStub(); + context.control.push(compilationUnit!); + + const result = evaluate(context); + + const expectedControlTrace = [ + "CompilationUnit", + "LocalVariableDeclarationStatement", + "LocalVariableDeclarationStatement", + "Assignment", + "LocalVariableDeclarationStatement", + "Pop", + "Assign", + "Literal", + "Assignment", + "LocalVariableDeclarationStatement", + "Pop", + "Assign", + "ExpressionName", + ]; + const expectedStashTrace = ["1", "1"]; + const expectedEnv = new Map([ + ["x", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + { + kind: "Literal", + literalType: { + kind: "DecimalIntegerLiteral", + value: "1", + }, + }, + ]], + ["y", [ + DECLARED_BUT_NOT_YET_ASSIGNED, + { + kind: "Literal", + literalType: { + kind: "DecimalIntegerLiteral", + value: "1", + }, + }, + ]], + ]); + + expect(result).toEqual(undefined); + expect((context.control as ControlStub).getTrace().map(i => isNode(i) ? i.kind : i.instrType)).toEqual(expectedControlTrace); + expect((context.stash as StashStub).getTrace().map(i => i.literalType.value)).toEqual(expectedStashTrace); + expect((context.environment as EnvironmentStub).getTrace()).toEqual(expectedEnv); +}); diff --git a/src/ec-evaluator/__tests__/utils.ts b/src/ec-evaluator/__tests__/utils.ts new file mode 100644 index 0000000..54a3053 --- /dev/null +++ b/src/ec-evaluator/__tests__/utils.ts @@ -0,0 +1,48 @@ +import { Environment } from "../interpreter"; +import { ControlItem, Context, Name, Value } from "../types"; +import { Stack } from "../utils"; + +export class StackStub extends Stack { + private trace: T[] = []; + + public push(...items: T[]): void { + for (const item of items) { + super.push(item); + this.trace.push(item); + } + }; + + public getTrace(): T[] { + return this.trace; + }; +}; +export class ControlStub extends StackStub {}; +export class StashStub extends StackStub {}; + +export class EnvironmentStub extends Environment { + private trace = new Map(); + + public set(key: Name, value: Value): this { + super.set(key, value); + + if (this.trace.has(key)) { + this.trace.set(key, [...this.trace.get(key)!, value]); + } else { + this.trace.set(key, [value]); + } + + return this; + } + + public getTrace(): Map { + return this.trace; + } +} + +export const createContextStub = (): Context => ({ + errors: [], + control: new ControlStub(), + stash: new StashStub(), + environment: new EnvironmentStub(), + totalSteps: Infinity, +});