diff --git a/src/ast.ts b/src/ast.ts index 0b02a8a..292ecac 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1,4 +1,4 @@ -import type { Value } from './types'; +import type { Value, Closure } from './types'; export type Literal = { kind: 'literal' @@ -31,4 +31,16 @@ export type If = { else: AST } -export type AST = Literal | BinaryOp | Variable | Let | If +export type Lambda = { + kind: 'lambda' + params: string[] + body: AST +} + +export type Apply = { + kind: 'apply' + func: AST + args: AST[] +} + +export type AST = Literal | BinaryOp | Variable | Let | If | Lambda | Apply diff --git a/src/interpreter.ts b/src/interpreter.ts index 4cefab6..254a55b 100644 --- a/src/interpreter.ts +++ b/src/interpreter.ts @@ -1,6 +1,6 @@ import type { AST } from './ast'; import type { Env } from './env'; -import type { Value } from './types'; +import type { Value, Closure } from './types'; export function evaluate(ast: AST, env: Env): Value { switch (ast.kind) { @@ -34,6 +34,38 @@ export function evaluate(ast: AST, env: Env): Value { } else { return evaluate(ast.else, env); } + + case 'lambda': + return { + kind: 'closure', + params: ast.params, + body: ast.body, + env + } + + case 'apply': + const func = evaluate(ast.func, env); + if (func.kind !== 'closure') + throw new Error('Not a closure'); + + const params = func.params; + const callEnv = new Map(func.env); + const args = ast.args; + + if (args.length !== params.length) + throw new Error('Parameter argument mismatch'); + + for (let i = 0; i < args.length; i++) { + const arg = evaluate(args[i], env); + const param = params[i]; + callEnv.set(param, arg); + } + + return evaluate(func.body, callEnv); + + default: + throw new Error('Syntax Error'); + } } diff --git a/src/main.ts b/src/main.ts index 6411cca..4cc2cf4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -28,3 +28,47 @@ const env: Env = new Map(); const res = evaluate(ast, env); console.log(res); + +const ast2: AST = { + kind: 'let', + name: 'add', + value: { + kind: 'lambda', + params: ['x', 'y'], + body: { + kind: 'binaryop', + operator: '+', + left: { + kind: 'variable', + name: 'x', + }, + right: { + kind: 'variable', + name: 'y', + } + } + }, + body: { + kind: 'apply', + func: { + kind: 'variable', + name: 'add' + }, + args: [ + { + kind: 'literal', + value: { kind: 'int', value: 2 } + }, + { + kind: 'literal', + value: { kind: 'int', value: 3 } + }, + ] + } +}; + +const env2: Env = new Map(); + +const res2 = evaluate(ast2, env2); + +console.log(res2); diff --git a/src/types.ts b/src/types.ts index 4a6a8ff..c2e8fe5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,6 @@ +import type { AST } from './ast' +import type { Env } from './env' + export type IntValue = { kind: 'int' value: number @@ -18,4 +21,11 @@ export type BoolValue = { value: boolean } -export type Value = IntValue | FloatValue | StringValue | BoolValue; +export type Closure = { + kind: 'closure' + params: string[] + body: AST + env: Env +} + +export type Value = IntValue | FloatValue | StringValue | BoolValue | Closure;