cleaning up interpreter. adding more types. fixing function application, handling infix binary ops, added currying

master
Dustin Swan 5 days ago
parent b6f7e63e49
commit f2f81b5ed6
Signed by: dustinswan
GPG Key ID: 30D46587E2100467

@ -1,6 +1,5 @@
import type { Value } from './types';
// Literals and Variables
export type Literal = {

@ -13,28 +13,11 @@ export function evaluate(ast: AST, env: Env): Value {
throw new Error(`Unknown variable: ${ast.name}`);
return val;
case 'binaryop':
const left = evaluate(ast.left, env);
const right = evaluate(ast.right, env);
return evaluateBinaryOp(ast.operator, left, right);
case 'let':
const val2 = evaluate(ast.value, env);
const newEnv = new Map(env).set(ast.name, val2);
return evaluate(ast.body, newEnv);
case 'if':
const res = evaluate(ast.condition, env);
if (res.kind !== 'bool')
throw new Error('Condition must be bool');
if (res.value) {
return evaluate(ast.then, env);
} else {
return evaluate(ast.else, env);
}
case 'lambda':
return {
kind: 'closure',
@ -44,21 +27,54 @@ export function evaluate(ast: AST, env: Env): Value {
}
case 'apply':
// Operators
if (ast.func.kind === 'variable') {
const name = ast.func.name;
const builtIns = ['+', '-', '*', '/', '>', '&'];
if (builtIns.includes(name)) {
const argValues = ast.args.map(arg => evaluate(arg, env));
if (argValues.length !== 2) {
throw new Error(`${name} expects 2 args`);
}
return evaluateBuiltIn(name, argValues[0], argValues[1]);
}
}
const func = evaluate(ast.func, env);
if (func.kind !== 'closure')
throw new Error('Not a closure');
throw new Error('Not a function');
const params = func.params;
const callEnv = new Map(func.env);
const args = ast.args;
const argValues = ast.args.map(arg => evaluate(arg, env));
if (args.length !== params.length)
throw new Error('Parameter argument mismatch');
// Too few args (Currying)
if (argValues.length < func.params.length) {
// Bind the params we have
const newEnv = new Map(func.env);
for (let i = 0; i < args.length; i++) {
const arg = evaluate(args[i], env);
const param = params[i];
callEnv.set(param, arg);
for (let i = 0; i < argValues.length; i++) {
newEnv.set(func.params[i], argValues[i]);
}
return {
kind: 'closure',
params: func.params.slice(argValues.length),
body: func.body,
env: newEnv
};
}
// Too many args
if (argValues.length > func.params.length)
throw new Error('Too many arguments');
// Exact number of args
const callEnv = new Map(func.env);
for (let i = 0; i < argValues.length; i++) {
callEnv.set(func.params[i], argValues[i]);
}
return evaluate(func.body, callEnv);
@ -97,3 +113,17 @@ function evaluateBinaryOp(op: string, left: Value, right: Value): Value {
throw new Error(`Unknown operation: ${op}`);
}
}
function evaluateBuiltIn(op: string, left: Value, right: Value): Value {
if (op === '+' || op === '-' || op === '*' || op === '/') {
return evaluateBinaryOp(op, left, right);
}
if (op === '>')
throw new Error('TODO Not implemented yet');
if (op === '&')
throw new Error('TODO Not implemented yet');
throw new Error(`Unknown built-in: ${op}`);
}

@ -1,6 +1,6 @@
// import { evaluate } from './interpreter'
import { evaluate } from './interpreter'
// import type { AST } from './ast'
// import type { Env } from './env'
import type { Env } from './env'
import { tokenize } from './lexer'
import { Parser } from './parser'
import { prettyPrint } from './ast';
@ -25,11 +25,12 @@ console.log(tokenize(str));
// const tokens2 = tokenize("let x = (y) => 5 + y in x(3)");
// const tokens2 = tokenize("let x = 5 in x * 4");
// const tokens2 = tokenize("(x, y) => x + y");
const tokens2 = tokenize('point { x = 3 }');
// const tokens2 = tokenize('point { x = 3 }');
// const tokens2 = tokenize('add1 = (x \\ x + 1); add1 3');
const tokens2 = tokenize('sum = (x y \\ x + y); sum 5 3');
const p2 = new Parser(tokens2);
const ast3 = p2.parse();
console.log(ast3);
console.log(prettyPrint(ast3));
// const env3: Env = new Map();
// console.log(ast3);
// console.log(evaluate(ast3, env3));
const env3: Env = new Map();
console.log(evaluate(ast3, env3));

@ -16,11 +16,6 @@ export type StringValue = {
value: string
}
export type BoolValue = {
kind: 'bool'
value: boolean
}
export type Closure = {
kind: 'closure'
params: string[]
@ -28,4 +23,20 @@ export type Closure = {
env: Env
}
export type Value = IntValue | FloatValue | StringValue | BoolValue | Closure;
export type ListValue = {
kind: 'list'
elements: Value[]
}
export type RecordValue = {
kind: 'record'
fields: { [key: string]: Value }
}
export type ConstructorValue = {
kind: 'constructor'
name: string
args: Value[]
}
export type Value = IntValue | FloatValue | StringValue | Closure | ListValue | RecordValue | ConstructorValue;

Loading…
Cancel
Save