mondo: proper lambda functions with local scope
This commit is contained in:
parent
642ddcdb59
commit
e04f250adb
2 changed files with 29 additions and 19 deletions
|
|
@ -214,11 +214,14 @@ export class MondoParser {
|
||||||
if (pipeIndex === -1) break;
|
if (pipeIndex === -1) break;
|
||||||
// pipe up front => lambda
|
// pipe up front => lambda
|
||||||
if (pipeIndex === 0) {
|
if (pipeIndex === 0) {
|
||||||
// . as lambda: (.fast 2) = x=>x.fast(2)
|
// . as lambda: (.fast 2) = (lambda (_) (fast _ 2))
|
||||||
// TODO: this doesn't work for (.fast 2 .speed 2)
|
const args = [{ type: 'plain', value: '_' }];
|
||||||
// probably needs proper ast representation of lambda
|
const body = this.desugar([args[0], ...children]);
|
||||||
children[pipeIndex] = { type: 'plain', value: '.' };
|
return [
|
||||||
continue;
|
{ type: 'plain', value: 'lambda' },
|
||||||
|
{ type: 'list', children: args },
|
||||||
|
{ type: 'list', children: body },
|
||||||
|
];
|
||||||
}
|
}
|
||||||
const rightSide = children.slice(pipeIndex + 2);
|
const rightSide = children.slice(pipeIndex + 2);
|
||||||
const right = children[pipeIndex + 1];
|
const right = children[pipeIndex + 1];
|
||||||
|
|
@ -343,7 +346,7 @@ export class MondoRunner {
|
||||||
errorhead(ast) {
|
errorhead(ast) {
|
||||||
return `[mondo ${ast.loc?.join(':') || ''}]`;
|
return `[mondo ${ast.loc?.join(':') || ''}]`;
|
||||||
}
|
}
|
||||||
call(ast) {
|
call(ast, scope = []) {
|
||||||
// for a node to be callable, it needs to be a list
|
// for a node to be callable, it needs to be a list
|
||||||
this.assert(ast.type === 'list', `${this.errorhead(ast)} function call: expected list, got ${ast.type}`);
|
this.assert(ast.type === 'list', `${this.errorhead(ast)} function call: expected list, got ${ast.type}`);
|
||||||
// the first element is expected to be the function name
|
// the first element is expected to be the function name
|
||||||
|
|
@ -354,10 +357,22 @@ export class MondoRunner {
|
||||||
`${this.errorhead(first)} expected function name, got ${first.type}${name ? ` "${name}"` : ''}.`,
|
`${this.errorhead(first)} expected function name, got ${first.type}${name ? ` "${name}"` : ''}.`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (name === 'lambda') {
|
||||||
|
const [_, args, body] = ast.children;
|
||||||
|
const argNames = args.children.map((child) => child.value);
|
||||||
|
// console.log('lambda', argNames, body.children);
|
||||||
|
return (x) => {
|
||||||
|
scope = {
|
||||||
|
[argNames[0]]: x, // TODO: merge scope... + support multiple args
|
||||||
|
};
|
||||||
|
return this.call(body, scope);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// process args
|
// process args
|
||||||
const args = ast.children.slice(1).map((arg) => {
|
const args = ast.children.slice(1).map((arg) => {
|
||||||
if (arg.type === 'list') {
|
if (arg.type === 'list') {
|
||||||
return this.call(arg);
|
return this.call(arg, scope);
|
||||||
}
|
}
|
||||||
if (!this.lib.leaf) {
|
if (!this.lib.leaf) {
|
||||||
throw new Error(`no handler for leaft nodes! add leaf to your lib`);
|
throw new Error(`no handler for leaft nodes! add leaf to your lib`);
|
||||||
|
|
@ -368,21 +383,12 @@ export class MondoRunner {
|
||||||
} else if (['quotes_double', 'quotes_single'].includes(arg.type)) {
|
} else if (['quotes_double', 'quotes_single'].includes(arg.type)) {
|
||||||
arg.value = arg.value.slice(1, -1);
|
arg.value = arg.value.slice(1, -1);
|
||||||
}
|
}
|
||||||
return this.lib.leaf(arg);
|
return this.lib.leaf(arg, scope);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (name === '.') {
|
|
||||||
// lambda : (.fast 2) = x=>fast(2, x)
|
|
||||||
const second = ast.children[1];
|
|
||||||
const callee = second.value;
|
|
||||||
const innerFn = this.lib[callee];
|
|
||||||
this.assert(innerFn, `${this.errorhead(second)} unknown function name "${callee}"`);
|
|
||||||
return (pat) => this.libcall(innerFn, [pat, ...args.slice(1)], callee);
|
|
||||||
}
|
|
||||||
|
|
||||||
// look up function in lib
|
// look up function in lib
|
||||||
const fn = this.lib[name];
|
const fn = this.lib[name];
|
||||||
this.assert(fn, `${this.errorhead(first)} unknown function name "${name}"`);
|
this.assert(fn, `${this.errorhead(first)} unknown function name "${name}"`);
|
||||||
return this.libcall(fn, args, name);
|
return this.libcall(fn, args, name, scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,12 @@ import { MondoRunner } from '../mondo/mondo.mjs';
|
||||||
|
|
||||||
let runner = new MondoRunner(strudelScope);
|
let runner = new MondoRunner(strudelScope);
|
||||||
|
|
||||||
strudelScope.leaf = (token) => {
|
strudelScope.leaf = (token, scope) => {
|
||||||
let { value } = token;
|
let { value } = token;
|
||||||
|
// local scope
|
||||||
|
if (token.type === 'plain' && scope[value]) {
|
||||||
|
return reify(scope[value]); // -> local scope has no location
|
||||||
|
}
|
||||||
const [from, to] = token.loc;
|
const [from, to] = token.loc;
|
||||||
if (token.type === 'plain' && strudelScope[value]) {
|
if (token.type === 'plain' && strudelScope[value]) {
|
||||||
// what if we want a string that happens to also be a variable name?
|
// what if we want a string that happens to also be a variable name?
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue