evaluating pattern matching
This commit is contained in:
parent
7f94cfe8cd
commit
a85203bc94
4 changed files with 102 additions and 9 deletions
15
src/ast.ts
15
src/ast.ts
|
|
@ -126,8 +126,18 @@ export function prettyPrint(ast: AST, indent = 0): string {
|
|||
const i = ' '.repeat(indent);
|
||||
|
||||
switch (ast.kind) {
|
||||
case 'literal':
|
||||
return `${i}${ast.value}`;
|
||||
case 'literal': {
|
||||
const val = ast.value;
|
||||
switch (val.kind) {
|
||||
case 'int':
|
||||
case 'float':
|
||||
return `${i}${val.value}`;
|
||||
case 'string':
|
||||
return `${i}"${val.value}"`;
|
||||
default:
|
||||
return `${i}${val.kind}`;
|
||||
}
|
||||
}
|
||||
|
||||
case 'variable':
|
||||
return `${i}${ast.name}`;
|
||||
|
|
@ -171,7 +181,6 @@ export function prettyPrint(ast: AST, indent = 0): string {
|
|||
.join('\n');
|
||||
return `${i}match ${expr}\n${cases}`;
|
||||
|
||||
|
||||
default:
|
||||
return `${i}${ast.kind}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { AST } from './ast';
|
||||
import type { AST, Pattern } from './ast';
|
||||
import type { Env } from './env';
|
||||
import type { Value } from './types';
|
||||
|
||||
|
|
@ -136,6 +136,24 @@ export function evaluate(ast: AST, env: Env): Value {
|
|||
return evaluate(func.body, callEnv);
|
||||
}
|
||||
|
||||
case 'match': {
|
||||
const value = evaluate(ast.expr, env);
|
||||
|
||||
for (const matchCase of ast.cases) {
|
||||
const bindings = matchPattern(value, matchCase.pattern);
|
||||
|
||||
if (bindings !== null) {
|
||||
const newEnv = new Map(env);
|
||||
for (const [name, val] of Object.entries(bindings)) {
|
||||
newEnv.set(name, val);
|
||||
}
|
||||
return evaluate(matchCase.result, newEnv);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Non-exhaustive pattern match');
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error('Syntax Error');
|
||||
}
|
||||
|
|
@ -206,3 +224,66 @@ function evaluateBuiltIn(op: string, left: Value, right: Value): Value {
|
|||
|
||||
throw new Error(`Unknown built-in: ${op}`);
|
||||
}
|
||||
|
||||
type Bindings = { [key: string]: Value };
|
||||
|
||||
function matchPattern(value: Value, pattern: Pattern): Bindings | null {
|
||||
switch (pattern.kind) {
|
||||
case 'wildcard':
|
||||
return {};
|
||||
|
||||
case 'var':
|
||||
return { [pattern.name]: value };
|
||||
|
||||
case 'literal':
|
||||
if (value.kind === 'int' || value.kind === 'float' || value.kind === 'string') {
|
||||
if (value.value === pattern.value) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
case 'constructor': {
|
||||
if (value.kind !== 'constructor') return null;
|
||||
if (value.name !== pattern.name) return null;
|
||||
if (value.args.length !== pattern.args.length) return null;
|
||||
|
||||
const bindings: Bindings = {};
|
||||
for (let i = 0; i < pattern.args.length; i++) {
|
||||
const argBindings = matchPattern(value.args[i], pattern.args[i]);
|
||||
if (argBindings === null) return null;
|
||||
Object.assign(bindings, argBindings);
|
||||
}
|
||||
return bindings;
|
||||
}
|
||||
|
||||
case 'list': {
|
||||
if (value.kind !== 'list') return null;
|
||||
if (value.elements.length !== pattern.elements.length) return null;
|
||||
|
||||
const bindings: Bindings = {};
|
||||
for (let i = 0; i < pattern.elements.length; i++) {
|
||||
const elemBindings = matchPattern(value.elements[i], pattern.elements[i]);
|
||||
if (elemBindings === null) return null;
|
||||
Object.assign(bindings, elemBindings);
|
||||
}
|
||||
return bindings;
|
||||
}
|
||||
|
||||
case 'record': {
|
||||
if (value.kind !== 'record') return null;
|
||||
|
||||
const bindings: Bindings = {};
|
||||
for (const [fieldName, fieldPattern] of Object.entries(pattern.fields)) {
|
||||
const fieldValue = value.fields[fieldName];
|
||||
if (fieldValue === undefined) return null;
|
||||
|
||||
const fieldBindings = matchPattern(fieldValue, fieldPattern);
|
||||
if (fieldBindings === null) return null;
|
||||
Object.assign(bindings, fieldBindings);
|
||||
}
|
||||
return bindings;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ function e(str: string) {
|
|||
console.log(ast);
|
||||
console.log(prettyPrint(ast));
|
||||
const env: Env = new Map();
|
||||
// console.log(evaluate(ast, env));
|
||||
console.log(evaluate(ast, env));
|
||||
}
|
||||
|
||||
e('add1 = (x \\ x + 1); add1 3');
|
||||
|
|
@ -26,5 +26,8 @@ e('add1 = (x \\ x + 1); 3 > add1');
|
|||
e('[1, 2] & [3, 4]');
|
||||
e('"abc" & "def"');
|
||||
// e('n | 0 \\ 1 | _ \\ 99');
|
||||
e('m | Some x \\ 1 | None \\ 0');
|
||||
e('head = list \\ list | [x, _] \\ Some x | [] \\ None; head');
|
||||
// e('m | Some x \\ 1 | None \\ 0');
|
||||
// e('head = list \\ list | [x, _] \\ Some x | [] \\ None; head');
|
||||
e('n = 5; n | 5 \\ "five" | _ \\ "other"');
|
||||
e('list = [1, 2, 3]; list | [x, y, z] \\ x + y + z');
|
||||
e('point = {x = 5, y = 10}; point | {x = px, y = py} \\ px + py');
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ export class Parser {
|
|||
// Record
|
||||
if (token.kind === 'open-brace') {
|
||||
this.advance();
|
||||
const fields: { [key: string]: Pattern }= [];
|
||||
const fields: { [key: string]: Pattern } = {};
|
||||
let first = true;
|
||||
|
||||
while (this.current().kind !== 'close-brace') {
|
||||
|
|
@ -193,7 +193,7 @@ export class Parser {
|
|||
}
|
||||
|
||||
this.expect('close-brace');
|
||||
return { kind: 'list', fields };
|
||||
return { kind: 'record', fields };
|
||||
}
|
||||
|
||||
// Parens
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue