diff --git a/eval.ts b/eval.ts index f052036..86a69be 100644 --- a/eval.ts +++ b/eval.ts @@ -1,4 +1,4 @@ -import { ByteCode } from 'interfaces'; +type ByteCode = [BC, any]; export enum BC { LoadConstant, FunctionLookup, @@ -13,6 +13,7 @@ export enum BC { Store, // arg is variable name, saves TOS Return, // done with this bytecode Yield, // done for now, please continue when callback is true + CloneTop, // push on another of the top of the stack } interface Scope { [key: string]: any; @@ -149,7 +150,7 @@ class DefineNode extends ASTNode { return v; } tree(){ - return 'define\n '+this.value+' to be\n'+indent(this.value.tree(), 1); + return 'define\n '+this.value+' to be\n '+indent(this.value.tree(), 1); } compile(){ return [].concat(this.value.compile(), [[BC.StoreNew, this.name]]); @@ -248,6 +249,47 @@ class ForeverNode extends ASTNode { } } +// while nodes return the first false value the condition evaluates to +class WhileNode extends ASTNode { + constructor(location: Location, + public condition: ASTNode, + public body: ASTNode){ + super(location); + } + eval(env: Environment){ + while (true){ + var a = this.condition.eval(env) + if (!a){ break; } + this.body.eval(env); + } + return a; + } + tree() { return 'while '+this.condition.tree()+'\n' + indent(this.body.tree()); } + compile():ByteCode[]{ + //TODO + var condition = this.condition.compile(); + var body = this.body.compile(); + var popTop = [[BC.Pop, null]]; + var goBack = [[BC.Jump, -(1 + // this instruction + 1 + // popTop + body.length + popTop.length + + 1 + // jumpIfNot + 1 + // cloneTop + condition.length)]] + return [].concat(condition, + [[BC.CloneTop, null]], + [[BC.JumpIfNot, (popTop.length + + body.length + + popTop.length + + goBack.length)]], + popTop, + body, + popTop, // discard the result of the body since while loops + // return the falsy condition value + goBack); + } +} + export class CompiledFunctionObject { constructor(public params: string[], public code: ByteCode[], public env: Environment, public name: string){} deepCopyCreate():CompiledFunctionObject{ @@ -398,6 +440,7 @@ parser.nodes.DefineNode = DefineNode; parser.nodes.YieldNode = YieldNode; parser.nodes.DefnNode = DefnNode; parser.nodes.ForeverNode = ForeverNode; +parser.nodes.WhileNode = WhileNode; parser.nodes.NullNode = NullNode; export function runBytecodeOneStep(counterStack: number[], bytecodeStack: ByteCode[][], @@ -445,6 +488,7 @@ export function runBytecodeOneStep(counterStack: number[], bytecodeStack: ByteCo stack.push(result); } } else { + console.log(func); if (func.params.length !== arg){ throw Error('Function called with wrong arity! Takes ' + func.params.length + ' args, given ' + args.length); @@ -507,6 +551,9 @@ export function runBytecodeOneStep(counterStack: number[], bytecodeStack: ByteCo case BC.StoreNew: env.define(arg, stack[stack.length-1]); break; + case BC.CloneTop: + stack.push(stack[stack.length-1]); + break; default: throw Error('unrecognized bytecode: '+bc+' enumLookup:'+enumLookup(BC, bc)); } @@ -554,6 +601,7 @@ var funcs = { }, 0); }, '*': function(a:number, b:number){ return a * b; }, + '<': function(a:number, b:number){ return a < b; }, } export function dis(bytecode: ByteCode[], indent=0){ @@ -693,3 +741,6 @@ function main(){ // (a 1 2)`) //trace(`(define a (lambda (x y) 1 2 (+ x y))) // (a 2 3)`); +trace(`(define a 0) + (while (< a 10) + (define a (+ a 1)))`); diff --git a/shiplang.grammar b/shiplang.grammar index 5498c05..cca8326 100644 --- a/shiplang.grammar +++ b/shiplang.grammar @@ -42,6 +42,7 @@ sexp / defnSexp / defineSexp / foreverSexp + / whileSexp / yieldSexp / functionCallSexp / atom @@ -56,6 +57,9 @@ doSexp foreverSexp = "(" _* "forever" _* head:sexp _* rest:(_* sexp)* _* ")" { return new parser.nodes.ForeverNode(location(), doIfMultipleStatements(location(), [head].concat(rest.map(function(x){return x[1];})))); } +whileSexp + = "(" _* "while" _* cond:sexp _* head:sexp _* rest:(_* sexp)* _* ")" { return new parser.nodes.WhileNode(location(), cond, doIfMultipleStatements(location(), [head].concat(rest.map(function(x){return x[1];})))); } + lambdaSexp = "(" _* "lambda" _* params:functionParams _* head:sexp _* rest:(_* sexp)* _* ")" { return new parser.nodes.LambdaNode(location(), params, doIfMultipleStatements(location(), [head].concat(rest.map(function(x){return x[1];})))); }