Index: main.d ================================================================== --- main.d +++ main.d @@ -49,11 +49,19 @@ string line = readln(); if( line.startsWith("exit") || line.startsWith("quit") ) return false; try { if( tryRun(line) ) + { + // for debugging. + //try { + // writeln(tableToAST("@v", cast(Table)lastVal)); + //} catch(Throwable e) { + // writeln(e); + //} writeln(lastVal); + } } catch(Throwable e) { writeln(e); } return true; } Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -186,10 +186,120 @@ { throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e))); } ); } + +// [TODO] Optimization +Value macroEval(AST e, Table ctx, bool AlwaysMacro) +{ + Layer theLayer = "@v"; + + Table pos = new Table; + pos.set("filename", theLayer, new StrValue(e.pos.filename)); + pos.set("lineno", theLayer, new IntValue(BigInt(e.pos.lineno))); + pos.set("column", theLayer, new IntValue(BigInt(e.pos.column))); + return e.match( + (StrLiteral e) + { + Table t = new Table; + t.set("pos", theLayer, pos); + t.set("is", theLayer, new StrValue("str")); + t.set("data", theLayer, new StrValue(e.data)); + return t; + }, + (IntLiteral e) + { + Table t = new Table; + t.set("pos", theLayer, pos); + t.set("is", theLayer, new StrValue("int")); + t.set("data", theLayer, new IntValue(e.data)); + return t; + }, + (VarExpression e) + { + Table t = new Table; + t.set("pos", theLayer, pos); + t.set("is", theLayer, new StrValue("var")); + t.set("name", theLayer, new StrValue(e.var)); + return t; + }, + (LayeredExpression e) + { + if( AlwaysMacro ) + { + Table t = new Table; + t.set("pos", theLayer, pos); + t.set("is", theLayer, new StrValue("lay")); + t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro)); + return cast(Value)t; + } + else + { + return eval(e.expr, ctx, true, e.lay); + } + }, + (LetExpression e) + { + Table t = new Table; + t.set("pos", theLayer, pos); + t.set("is", theLayer, new StrValue("let")); + t.set("name", theLayer, new StrValue(e.var)); + t.set("init", theLayer, macroEval(e.init,ctx,AlwaysMacro)); + t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro)); + return t; + }, + (FuncallExpression e) + { + // [TODO] @macro invokation!!!! + // if e.fun is varname and its ctx[@macro] is set, switch to usual eval + Table t = new Table; + t.set("pos", theLayer, pos); + t.set("is", theLayer, new StrValue("app")); + t.set("fun", theLayer, macroEval(e.fun,ctx,AlwaysMacro)); + Table args = new Table; + foreach_reverse(a; e.args) { + Table cons = new Table; + cons.set("car",theLayer,macroEval(a,ctx,AlwaysMacro)); + cons.set("cdr",theLayer,args); + args = cons; + } + t.set("arg", theLayer, args); + return t; + }, + (FunLiteral e) + { + Table t = new Table; + t.set("pos", theLayer, pos); + t.set("is", theLayer, new StrValue("fun")); + t.set("body", theLayer, macroEval(e.funbody,ctx,AlwaysMacro)); + Table param = new Table; + foreach_reverse(p; e.params) + { + Table cons = new Table; + Table kv = new Table; + kv.set("name", theLayer, new StrValue(p.name)); + foreach_reverse(lay; p.layers) + { + Table cons2 = new Table; + cons2.set("car", theLayer, new StrValue(lay)); + cons2.set("cdr", theLayer, kv); + kv = cons2; + } + cons.set("car", theLayer, kv); + cons.set("cdr", theLayer, param); + param = cons; + } + t.set("param", theLayer, param); + return t; + }, + delegate Value (AST e) + { + throw genex!RuntimeException(e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(e))); + } + ); +} unittest { auto r = assert_nothrow( evalString(`var x = 21; x + x*x;`) ); assert_eq( r.val, new IntValue(BigInt(21+21*21)) ); Index: polemy/lex.d ================================================================== --- polemy/lex.d +++ polemy/lex.d @@ -12,11 +12,17 @@ /*mixin*/ template ExceptionWithPosition() { const LexPosition pos; this( const LexPosition pos, string msg, string file=null, size_t line=0, Throwable next=null ) - { super(sprintf!"[%s] %s"(pos, msg), file, line, next); this.pos = pos; } + { + if(pos is null) + super(sprintf!"[??] %s"(msg), file, line, next); + else + super(sprintf!"[%s] %s"(pos, msg), file, line, next); + this.pos = pos; + } } /// Thrown when encountered an EOF in the middle of a lexical token class UnexpectedEOF : Exception Index: polemy/value.d ================================================================== --- polemy/value.d +++ polemy/value.d @@ -5,10 +5,11 @@ * Runtime data structures for Polemy programming language. */ module polemy.value; import polemy._common; import polemy.lex; +import polemy.ast; /// Raised when something went wrong in runtime class RuntimeException : Exception { @@ -131,10 +132,25 @@ } if( prototype is null ) throw new RuntimeException(pos, sprintf!"variable %s not found"(i)); return prototype.get(i, lay, pos); } + + T access(T,S...)( Layer lay, string path, S rest ) + { + static if( rest.length == 0 ) + { + if( this.has(path, lay) ) + return cast(T) this.get(path, lay); + } + else + { + if(auto next = this.access!Table(lay,path)) + return next.access!T(lay,rest); + } + return null; + } private: Table prototype; Kind kind; Value[Layer][string] data; @@ -188,5 +204,126 @@ assert_eq( c01.get("z", "@v"), new IntValue(BigInt(0)) ); assert_eq( c0.get("z", "@v"), new IntValue(BigInt(0)) ); // [TODO] define the semantics and test @layers } + +immutable(LexPosition) extractPos( Table t ) +{ + Layer theLayer = "@v"; + if(auto tt = t.access!Table(theLayer, "pos")) + { + auto fn = tt.access!StrValue(theLayer, "filename"); + auto ln = tt.access!IntValue(theLayer, "lineno"); + auto cl = tt.access!IntValue(theLayer, "column"); + if(fn !is null && ln !is null && cl !is null) + return new immutable(LexPosition)(fn.data,cast(int)ln.data.toInt,cast(int)cl.data.toInt); + } + return null; +} + +Value[] tableAsConsList( Layer theLayer, Table t ) +{ + Value[] result; + while(t) + if(auto v = t.access!Value(theLayer, "car")) + { + result ~= v; + t = t.access!Table(theLayer, "cdr"); + } + else + break; + return result; +} + +AST[] tableToASTList( Layer theLayer, Table t ) +{ + AST[] result; + foreach(v; tableAsConsList(theLayer, t)) + if(auto t = cast(Table)v) + result ~= tableToAST(theLayer,t); + else + throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (non-table in cons-list)"); + return result; +} + +AST tableToAST( Layer theLayer, Table t ) +{ + auto nodeType = t.access!StrValue(theLayer, "is"); + if( nodeType is null ) + throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST {is:(not string)}"); + auto pos = extractPos(t); + switch(nodeType.data) + { + case "int": + if(auto v = t.access!IntValue(theLayer, "data")) + return new IntLiteral(pos, v.data); + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"int", data:(not int)}`); + case "str": + if(auto v = t.access!StrValue(theLayer, "data")) + return new StrLiteral(pos, v.data); + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"str", data:(not string)}`); + case "var": + if(auto v = t.access!StrValue(theLayer, "name")) + return new VarExpression(pos, v.data); + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"var", name:(not string)}`); + case "lay": + if(auto v = t.access!StrValue(theLayer, "layer")) + if(auto e = t.access!Table(theLayer, "expr")) + return new LayeredExpression(pos, v.data, tableToAST(theLayer,e)); + else + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", expr:(not table)}`); + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"lay", layer:(not string)}`); + case "let": + if(auto n = t.access!StrValue(theLayer, "name")) + if(auto e = t.access!Table(theLayer, "init")) + if(auto b = t.access!Table(theLayer, "expr")) + { + string nn = n.data; + auto ee = tableToAST(theLayer, e); + auto bb = tableToAST(theLayer, b); + Layer lay=""; + if(auto l = t.access!StrValue(theLayer, "layer")) + lay = l.data; + return new LetExpression(pos, nn, lay, ee, bb); + } + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"let", name:"???", init:"???", expr:"???"}`); + case "app": + if(auto f = t.access!Table(theLayer, "fun")) + if(auto a = t.access!Table(theLayer, "arg")) + return new FuncallExpression(pos, tableToAST(theLayer,f), tableToASTList(theLayer,a)); + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"app", fun:???, arg:???}`); + case "fun": + if(auto p = t.access!Table(theLayer, "param")) + if(auto b = t.access!Table(theLayer, "body")) + { + Parameter[] ps; + foreach(v; tableAsConsList(theLayer, p)) + { + if(auto tt = cast(Table)v) + if(auto ss = tt.access!StrValue(theLayer, "name")) + if(auto ll = tt.access!Table(theLayer, "layer")) + { + Layer[] ls; + foreach(lll; tableAsConsList(theLayer, ll)) + if(auto l = cast(StrValue)lll) + ls ~= l.data; + else + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {bad fun params}`); + ps ~= new Parameter(ss.data, ls); + continue; + } + else + { + Layer[] emp; + ps ~= new Parameter(ss.data, emp); + } + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {bad fun params}`); + } + auto bb = tableToAST(theLayer, b); + return new FunLiteral(pos,ps,bb); + } + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"fun", param:???, body:???}`); + default: + throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {is: "%s"} unknown`(nodeType.data)); + } +}