From b16c075e6a11ac697bd3f82e43f69f02b4604c76 Mon Sep 17 00:00:00 2001 From: "W. Cho" Date: Fri, 22 Dec 2023 08:54:46 +0900 Subject: [PATCH] use evaluated type instead with some minor testing code refactoring --- src/evaluator/environment/index.test.ts | 31 +-- src/evaluator/environment/index.ts | 12 +- src/evaluator/index.test.ts | 303 ++++++++++++++++++------ src/evaluator/index.ts | 123 ++++++---- src/index.test.ts | 12 +- src/index.ts | 2 +- 6 files changed, 337 insertions(+), 146 deletions(-) diff --git a/src/evaluator/environment/index.test.ts b/src/evaluator/environment/index.test.ts index cef2761..91741c3 100644 --- a/src/evaluator/environment/index.test.ts +++ b/src/evaluator/environment/index.test.ts @@ -1,49 +1,52 @@ +import type { Evaluated } from "../evaluated"; import Environment from "./"; describe("set()", () => { it("set name and value", () => { const env = new Environment(); + const varName = "foo"; + const varValue = {} as Evaluated; - expect(() => env.set("foo", 42)).not.toThrow(); + expect(() => env.set(varName, varValue)).not.toThrow(); }); }); describe("get()", () => { it("get value after setting the value", () => { const env = new Environment(); + const varName = "foo"; + const varValue = {} as Evaluated; - env.set("foo", 42); - const value = env.get("foo"); + env.set(varName, varValue); - expect(value).toBe(42); + expect(env.get(varName)).toBe(varValue); }); it("get null if not found", () => { const env = new Environment(); + const varNameNotSet = "foo"; - const value = env.get("foo"); - - expect(value).toBe(null); + expect(env.get(varNameNotSet)).toBe(null); }); }); describe("linked environment", () => { it("set super environment and get via sub environment", () => { + const varNameInSuper = "foo"; + const varValueInSuper = {} as Evaluated; + const superEnv = new Environment(); - superEnv.set("foo", 42); + superEnv.set(varNameInSuper, varValueInSuper); const subEnv = new Environment(superEnv); - const value = subEnv.get("foo"); - - expect(value).toBe(42); + expect(subEnv.get(varNameInSuper)).toBe(varValueInSuper); }); it("get null if not found even in super environment", () => { const superEnv = new Environment(); const subEnv = new Environment(superEnv); - const value = subEnv.get("foo"); - - expect(value).toBe(null); + const varNameSetNowhere = "foo"; + expect(subEnv.get(varNameSetNowhere)).toBe(null); }); }); diff --git a/src/evaluator/environment/index.ts b/src/evaluator/environment/index.ts index c95652f..a27a929 100644 --- a/src/evaluator/environment/index.ts +++ b/src/evaluator/environment/index.ts @@ -1,6 +1,8 @@ +import type { Evaluated } from "../evaluated"; + export interface EnvironmentType { - get: (name: string) => unknown; - set: (name: string, value: any) => unknown; + get: (name: string) => Evaluated | null; + set: (name: string, value: Evaluated) => void; } export default class Environment implements EnvironmentType { @@ -12,7 +14,7 @@ export default class Environment implements EnvironmentType { this.table = new Map; } - get(name: string): unknown { + get(name: string): Evaluated | null { // return if found in current environment const fetched = this.table.get(name); if (fetched !== undefined) { @@ -26,9 +28,7 @@ export default class Environment implements EnvironmentType { return this.superEnvironment.get(name); } - set(name: string, value: any): unknown { + set(name: string, value: Evaluated): void { this.table.set(name, value); - - return value; } } diff --git a/src/evaluator/index.test.ts b/src/evaluator/index.test.ts index 4737010..2cc6ec0 100644 --- a/src/evaluator/index.test.ts +++ b/src/evaluator/index.test.ts @@ -1,15 +1,56 @@ import Lexer from "../lexer"; import Parser from "../parser"; +import type { EvaluatedPrimitive, EvaluatedFunction, EvaluatedEmpty } from "./evaluated"; import Evaluator from "./"; import Environment from "./environment"; +const evaluateInput = (input: string) => { + const lexer = new Lexer(input); + const parser = new Parser(lexer); + const program = parser.parseProgram(); + + const evaluator = new Evaluator(); + const environment = new Environment(); + const evaluated = evaluator.evaluate(program, environment); + + return evaluated; +}; + +const testEvaluatingPrimitive = ({ input, expected }: { input: string, expected: any }): void => { + const evaluated = evaluateInput(input) as EvaluatedPrimitive; + + expect(evaluated.value).toBe(expected); +}; + +const testEvaluatingFunction = ({ input, expectedParamsLength }: { input: string, expectedParamsLength: number }): void => { + const evaluated = evaluateInput(input) as EvaluatedFunction; + + expect(evaluated).toHaveProperty("parameters"); + expect(evaluated.parameters.length).toBe(expectedParamsLength); + expect(evaluated).toHaveProperty("body"); + expect(evaluated).toHaveProperty("environment"); +}; + +const testEvaluatingEmpty = ({ input }: { input: string }): void => { + const evaluated = evaluateInput(input) as EvaluatedEmpty; + + expect(evaluated.type).toBe("empty"); +}; + describe("evaluate()", () => { - describe("simple expression", () => { + describe("single numbers", () => { const cases = [ { input: "5", expected: 5 }, { input: "-5", expected: -5 }, { input: "--5", expected: 5 }, { input: "+5", expected: 5 }, + ]; + + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("simple arithmetic expressions", () => { + const cases = [ { input: "100+25", expected: 125 }, { input: "100-25", expected: 75 }, { input: "100*25", expected: 2500 }, @@ -17,15 +58,72 @@ describe("evaluate()", () => { { input: "100+25+4", expected: 129 }, { input: "100+25-4", expected: 121 }, { input: "100+25*4", expected: 200 }, + ]; + + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("left associativity of arithmetic operation", () => { + const cases = [ + { input: "100-25-4", expected: 71 }, + { input: "100/25/4", expected: 1 }, + ]; + + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("grouped arithmetic expressions", () => { + const cases = [ + { input: "100-(25-4)", expected: 79 }, + { input: "12-(34-56)", expected: 34 }, + { input: "12*(12/6)", expected: 24 }, + { input: "12+((30+4)-3*(12/(56-50)))", expected: 40 }, + ]; + + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("arithmetic expressions with floating point number", () => { + const cases = [ + { input: "0.75 + 1.25", expected: 2 }, + { input: "2.5 / 0.5", expected: 5 }, + ]; + + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("single boolean", () => { + const cases = [ { input: "참", expected: true }, { input: "거짓", expected: false }, - { input: "'foo bar'", expected: "foo bar" }, + ]; + + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("logical not expressions", () => { + const cases = [ { input: "!참", expected: false }, { input: "!거짓", expected: true }, { input: "!!참", expected: true }, { input: "!!거짓", expected: false }, + ]; - /* test case for comparison expressions */ + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("single string", () => { + const cases = [ + { input: "''", expected: "" }, + { input: "'foo bar'", expected: "foo bar" }, + { input: "'한글 단어'", expected: "한글 단어" }, + ]; + + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("boolean comparison", () => { + const cases = [ { input: "참 == 참", expected: true }, { input: "거짓 == 참", expected: false }, { input: "참 == 거짓", expected: false }, @@ -34,6 +132,13 @@ describe("evaluate()", () => { { input: "거짓 != 참", expected: true }, { input: "참 != 거짓", expected: true }, { input: "거짓 != 거짓", expected: false }, + ]; + + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("number comparison", () => { + const cases = [ { input: "2 > 1", expected: true }, { input: "1 > 1", expected: false }, { input: "1 > 2", expected: false }, @@ -46,37 +151,44 @@ describe("evaluate()", () => { { input: "2 <= 1", expected: false }, { input: "1 <= 1", expected: true }, { input: "1 <= 2", expected: true }, - { input: "!(1 == 1)", expected: false }, - { input: "!!(1 == 1)", expected: true }, - - /* test case for left associativity */ - { input: "100-25-4", expected: 71 }, - { input: "100/25/4", expected: 1 }, + ]; - /* test case for grouped expression */ - { input: "100-(25-4)", expected: 79 }, - { input: "12-(34-56)", expected: 34 }, - { input: "12*(12/6)", expected: 24 }, - { input: "12+((30+4)-3*(12/(56-50)))", expected: 40 }, + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); - /* test case for floating point numbers */ - { input: "0.75 + 1.25", expected: 2 }, - { input: "2.5 / 0.5", expected: 5 }, + describe("string comparison", () => { + const cases = [ + { input: "'사과' == '사과'", expected: true }, + { input: "'사과' != '사과'", expected: false }, + { input: "'사과' == '바나나'", expected: false }, + { input: "'사과' != '바나나'", expected: true }, + { input: "'B' > 'A'", expected: true }, + { input: "'A' > 'A'", expected: false }, + { input: "'A' > 'B'", expected: false }, + { input: "'B' >= 'A'", expected: true }, + { input: "'A' >= 'A'", expected: true }, + { input: "'A' >= 'B'", expected: false }, + { input: "'B' < 'A'", expected: false }, + { input: "'A' < 'A'", expected: false }, + { input: "'A' < 'B'", expected: true }, + { input: "'B' <= 'A'", expected: false }, + { input: "'A' <= 'A'", expected: true }, + { input: "'A' <= 'B'", expected: true }, ]; - it.each(cases)("evaluate $input", ({ input, expected }) => { - const lexer = new Lexer(input); - const parser = new Parser(lexer); - const program = parser.parseProgram(); - const evaluator = new Evaluator(); - const environment = new Environment(); - const evaluated = evaluator.evaluate(program, environment); + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); - expect(evaluated).toBe(expected); - }); + describe("logical not operation to boolean expression", () => { + const cases = [ + { input: "!(1 == 1)", expected: false }, + { input: "!!(1 == 1)", expected: true }, + ]; + + it.each(cases)("evaluate $input", testEvaluatingPrimitive); }); - describe("branch statements", () => { + describe("branch statements yielding something", () => { const cases = [ { name: "simple if statement with boolean literal predicate", @@ -110,79 +222,118 @@ describe("evaluate()", () => { }, ]; - it.each(cases)("evaluate $input", ({ input, expected }) => { - const lexer = new Lexer(input); - const parser = new Parser(lexer); - const program = parser.parseProgram(); - const evaluator = new Evaluator(); - const environment = new Environment(); - const evaluated = evaluator.evaluate(program, environment); + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("branch statements yielding nothing", () => { + const cases = [ + { + name: "simple if statement with boolean literal predicate", + input: "만약 거짓 { 3 }", + }, + { + name: "simple if statement with boolean expression predicate", + input: "만약 1 == 2 { 4 }", + }, + { + name: "simple if statement with variable comparison predicate", + input: "사과 = 3 바나나 = 4 만약 사과 > 바나나 { 5 }", + }, + ]; - expect(evaluated).toBe(expected); - }); + it.each(cases)("evaluate $input", testEvaluatingEmpty); }); - describe("variable statements", () => { + describe("nested branch statements yielding something", () => { const cases = [ - { name: "integer variable with number literal", input: "foo = 42 foo", expected: 42 }, - { name: "integer variable with arithmetic expression", input: "foo = 42 * (8 / 4) + (1 - (2 - 1)) foo", expected: 84 }, - { name: "two integer variables with number literal", input: "foo = 42 bar = foo + 1 bar", expected: 43 }, - { name: "arithmetic expression with variables", input: "foo = 42 bar = 43 baz = 44 qux = (bar * (baz - foo))", expected: 86 }, - { name: "Korean integer variable with number literal", input: "변수 = 42 변수", expected: 42 }, + { + name: "nested if statements", + input: "만약 참 { 만약 참 { 만약 참 { 1 } } }", + expected: 1, + }, + { + name: "nested if-else statements", + input: "만약 거짓 { 0 } 아니면 { 만약 참 { 만약 거짓 { 1 } 아니면 { 2 } } 아니면 { 3 } }", + expected: 2, + }, + ]; + + it.each(cases)("evaluate $input", testEvaluatingPrimitive); + }); + + describe("nested branch statements yielding nothing", () => { + const cases = [ + { + name: "nested if statements", + input: "만약 참 { 만약 참 { 만약 거짓 { 1 } } }", + }, + { + name: "nested if and if-else statements", + input: "만약 거짓 { 만약 참 { 만약 거짓 { 0 } 아니면 { 1 } } 아니면 { 2 } }", + }, ]; - it.each(cases)("evaluate $name", ({ input, expected }) => { - const lexer = new Lexer(input); - const parser = new Parser(lexer); - const program = parser.parseProgram(); - const evaluator = new Evaluator(); - const environment = new Environment(); - const evaluated = evaluator.evaluate(program, environment); + it.each(cases)("evaluate $input", testEvaluatingEmpty); + }); + + describe("variable statements", () => { + const cases = [ + { + name: "integer variable with number literal", + input: "foo = 42 foo", + expected: 42 + }, + { + name: "integer variable with arithmetic expression", + input: "foo = 42 * (8 / 4) + (1 - (2 - 1)) foo", + expected: 84 + }, + { + name: "two integer variables with number literal", + input: "foo = 42 bar = foo + 1 bar", + expected: 43 + }, + { + name: "arithmetic expression with variables", + input: "foo = 42 bar = 43 baz = 44 qux = (bar * (baz - foo))", + expected: 86 + }, + { + name: "Korean integer variable with number literal", + input: "변수 = 42 변수", + expected: 42 + }, + ]; - expect(evaluated).toBe(expected); - }); + it.each(cases)("evaluate $name", testEvaluatingPrimitive); }); describe("function expressions", () => { const cases = [ { name: "simple function expression", - input: "함수 () { 1 }" + input: "함수 () { 1 }", + expectedParamsLength: 0, + }, + { + name: "simple function expression", + input: "함수 (사과, 바나나, 포도) { 1 }", + expectedParamsLength: 3, }, ]; - it.each(cases)("evaluate $name", ({ input }) => { - const lexer = new Lexer(input); - const parser = new Parser(lexer); - const program = parser.parseProgram(); - const evaluator = new Evaluator(); - const environment = new Environment(); - const evaluated = evaluator.evaluate(program, environment); - - expect(evaluated).not.toBeUndefined(); - expect(evaluated).toHaveProperty("parameters"); - expect(evaluated).toHaveProperty("body"); - expect(evaluated).toHaveProperty("environment"); - }); + it.each(cases)("evaluate $name", testEvaluatingFunction); }); describe("call expressions", () => { const cases = [ { name: "function call with function literal", - input: "함수(바나나) { 바나나 + 1 }(42)" + input: "함수(바나나) { 바나나 + 1 }(42)", + expected: 43, }, ]; - it.each(cases)("evaluate $name", ({ input }) => { - const lexer = new Lexer(input); - const parser = new Parser(lexer); - const program = parser.parseProgram(); - const evaluator = new Evaluator(); - const environment = new Environment(); - const evaluated = evaluator.evaluate(program, environment); - - expect(evaluated).toBe(43); - }); + it.each(cases)("evaluate $name", testEvaluatingPrimitive); }); }); diff --git a/src/evaluator/index.ts b/src/evaluator/index.ts index 3a58b83..490b595 100644 --- a/src/evaluator/index.ts +++ b/src/evaluator/index.ts @@ -1,103 +1,122 @@ import type { Program, Block, BranchStatement, Node, Expression } from "../parser"; +import { + makeEvaluatedNumber, + makeEvaluatedBoolean, + makeEvaluatedString, + makeEvaluatedFunction, + makeEvaluatedEmpty, +} from "./evaluated"; +import type { + Evaluated, + EvaluatedNumber, + EvaluatedBoolean, + EvaluatedFunction, +} from "./evaluated"; import Environment from "./environment"; -// TODO: fix any return type to specific ones (by implement value system) export default class Evaluator { - private evaluateProgram(node: Program, env: Environment): any { - let evaluated; - - for (const statement of node.statements) { - evaluated = this.evaluate(statement, env); + private evaluateProgram(node: Program, env: Environment): Evaluated { + if (node.statements.length === 0) { + return makeEvaluatedEmpty(); } + const evaluatedStatements = node.statements.map(statement => this.evaluate(statement, env)); + const evaluated = evaluatedStatements[evaluatedStatements.length-1]; + return evaluated; } - private evaluateBlock(node: Block, env: Environment): any { - let evaluated; - - for (const statement of node.statements) { - evaluated = this.evaluate(statement, env); + private evaluateBlock(node: Block, env: Environment): Evaluated { + if (node.statements.length === 0) { + throw new Error(`block cannot be empty`); } + const evaluatedStatements = node.statements.map(statement => this.evaluate(statement, env)); + const evaluated = evaluatedStatements[evaluatedStatements.length-1]; + return evaluated; } - private evaluatePrefixNumberExpression(prefix: string, operand: number): any { + private evaluatePrefixNumberExpression(prefix: string, operand: EvaluatedNumber): EvaluatedNumber { if (prefix === "+") { return operand; } if (prefix === "-") { - return -operand; + return makeEvaluatedNumber(-operand.value); } throw new Error(`bad prefix ${prefix}`); } - private evaluatePrefixBooleanExpression(prefix: string, operand: boolean): any { + private evaluatePrefixBooleanExpression(prefix: string, operand: EvaluatedBoolean): EvaluatedBoolean { if (prefix === "!") { - return !operand; + return makeEvaluatedBoolean(!operand.value); } throw new Error(`bad prefix ${prefix}`); } - private evaluateBranchStatement(node: BranchStatement, env: Environment): any { + private evaluateBranchStatement(node: BranchStatement, env: Environment): Evaluated { const predicate = this.evaluate(node.predicate, env); + if (predicate.type !== "boolean") { + throw new Error(`expected boolean expression predicate, but received ${predicate.type}`); + } - if (predicate) { + if (predicate.value) { const consequence = this.evaluate(node.consequence, env); return consequence; } // early return if no else block if (typeof node.alternative === "undefined") { - return undefined; + return makeEvaluatedEmpty(); } const alternative = this.evaluate(node.alternative, env); return alternative; } - private evaluateInfixExpression(infix: string, left: unknown, right: unknown): any { + private evaluateInfixExpression(infix: string, left: Evaluated, right: Evaluated): Evaluated { // type matching order is important: more inclusive case first if ( - (typeof left === "boolean" && typeof right === "boolean") || - (typeof left === "number" && typeof right === "number") + (left.type === "boolean" && right.type === "boolean") || + (left.type === "number" && right.type === "number") || + (left.type === "string" && right.type === "string") ) { if (infix === "==") { - return left === right; + return makeEvaluatedBoolean(left.value == right.value); } if (infix === "!=") { - return left !== right; + return makeEvaluatedBoolean(left.value != right.value); } if (infix === ">") { - return left > right; + return makeEvaluatedBoolean(left.value > right.value); } if (infix === "<") { - return left < right; + return makeEvaluatedBoolean(left.value < right.value); } if (infix === ">=") { - return left >= right; + return makeEvaluatedBoolean(left.value >= right.value); } if (infix === "<=") { - return left <= right; + return makeEvaluatedBoolean(left.value <= right.value); } } - if (typeof left === "number" && typeof right === "number") { + if (left.type === "number" && right.type === "number") { if (infix === "+") { - return left + right; + return makeEvaluatedNumber(left.value + right.value); } if (infix === "-") { - return left - right; + return makeEvaluatedNumber(left.value - right.value); } if (infix === "*") { - return left * right; + return makeEvaluatedNumber(left.value * right.value); } if (infix === "/") { - return left / right; + // TODO: guard division by zero + return makeEvaluatedNumber(left.value / right.value); } throw new Error(`bad infix ${infix} for number operands`); @@ -106,73 +125,88 @@ export default class Evaluator { throw new Error(`bad infix ${infix}, with left '${left}' and right '${right}'`); } - evaluate(node: Node, env: Environment): any { + evaluate(node: Node, env: Environment): Evaluated { if (node.type === "program") { return this.evaluateProgram(node, env); } + if (node.type === "block") { return this.evaluateBlock(node, env); } + if (node.type === "branch statement") { return this.evaluateBranchStatement(node, env); } + if (node.type === "expression statement") { return this.evaluate(node.expression, env); } + if (node.type === "number node") { - return node.value; + return makeEvaluatedNumber(node.value); } + if (node.type === "boolean node") { - return node.value; + return makeEvaluatedBoolean(node.value); } + if (node.type === "string node") { - return node.value; + return makeEvaluatedString(node.value); } + if (node.type === "infix expression") { const left = this.evaluate(node.left, env); const right = this.evaluate(node.right, env); return this.evaluateInfixExpression(node.infix, left, right); } + if (node.type === "prefix expression") { const subExpression = this.evaluate(node.expression, env); + if ( (node.prefix === "+" || node.prefix === "-") && - typeof subExpression == "number" + subExpression.type == "number" ) { return this.evaluatePrefixNumberExpression(node.prefix, subExpression); } - if (node.prefix === "!" && typeof subExpression === "boolean") { + if (node.prefix === "!" && subExpression.type === "boolean") { return this.evaluatePrefixBooleanExpression(node.prefix, subExpression); } throw new Error(`bad prefix expression: prefix: '${node.prefix}' with type: '${typeof subExpression}'`); } + if (node.type === "function expression") { const parameters = node.parameter; const body = node.body; - return { parameters, body, environment: env }; + return makeEvaluatedFunction(parameters, body, env); } + if (node.type === "call") { const functionToCall = this.evaluate(node.functionToCall, env); + if (functionToCall.type !== "function") { + throw new Error(`expected function but received ${functionToCall.type}`); + } const callArguments = this.parseCallArguments(node.callArguments, env); const value = this.evaluateFunctionCall(functionToCall, callArguments); return value; } - if (node.type === "assignment") { - const varValue = this.evaluate(node.right, env); + if (node.type === "assignment") { if (node.left.type !== "identifier") { throw new Error(`expected identifier on left value, but received ${typeof node.left.type}`); } const varName = node.left.value; + const varValue = this.evaluate(node.right, env); env.set(varName, varValue); - return varValue; + return varValue; // evaluated value of assignment is the evaluated value of variable } + if (node.type === "identifier") { const varName = node.value; const value = env.get(varName); @@ -185,9 +219,10 @@ export default class Evaluator { } const exhaustiveCheck: never = node; + return exhaustiveCheck; } - private parseCallArguments(callArguments: Expression[], env: Environment): any[] { + private parseCallArguments(callArguments: Expression[], env: Environment): Evaluated[] { const values = []; for (const arg of callArguments) { const value = this.evaluate(arg, env); @@ -196,7 +231,7 @@ export default class Evaluator { return values; } - private evaluateFunctionCall(functionToCall: any, callArguments: any[]): any { + private evaluateFunctionCall(functionToCall: EvaluatedFunction, callArguments: Evaluated[]): Evaluated { const functionEnv = new Environment(functionToCall.environment); for (let i = 0; i < functionToCall.parameters.length; ++i) { const name = functionToCall.parameters[i].value; diff --git a/src/index.test.ts b/src/index.test.ts index f5981f0..7a0c5da 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,5 +1,7 @@ import { execute } from "./"; +it.todo(""); + it("execute -5", () => { expect(execute("-5")).toBe("-5"); }); @@ -17,23 +19,23 @@ it("execute 2.5/0.5", () => { }); it("execute 참", () => { - expect(execute("참")).toBe("true"); // TODO(?): display as "참" + expect(execute("참")).toBe("참"); }); it("execute 1 == 2", () => { - expect(execute("1 == 2")).toBe("false"); + expect(execute("1 == 2")).toBe("거짓"); }); it("execute 2 > 1 == 참", () => { - expect(execute("2 > 1 == 참")).toBe("true"); + expect(execute("2 > 1 == 참")).toBe("참"); }); it("execute 1 != 1 == 거짓", () => { // note that comparison is left associative - expect(execute("1 != 1 == 거짓")).toBe("true"); + expect(execute("1 != 1 == 거짓")).toBe("참"); }); it("execute 거짓 == (1 < 1+1)", () => { - expect(execute("거짓 == (1 < 1+1)")).toBe("false"); + expect(execute("거짓 == (1 < 1+1)")).toBe("거짓"); }); it("execute 만약 1 == 1 { 2 } 아니면 { 3 }", () => { diff --git a/src/index.ts b/src/index.ts index 5850d2a..278f9ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,5 +11,5 @@ export const execute = (input: string): string => { const environment = new Environment(); const evaluated = evaluator.evaluate(parsed, environment); - return String(evaluated); + return String(evaluated.representation); };