Index: .poseidon ================================================================== --- .poseidon +++ .poseidon @@ -37,12 +37,14 @@ polemy\failure.d polemy\fresh.d polemy\layer.d polemy\lex.d polemy\parse.d + polemy\repl.d polemy\runtime.d polemy\value.d + polemy\valueconv.d tricks\test.d tricks\tricks.d Index: doc/candydoc/modules.ddoc ================================================================== --- doc/candydoc/modules.ddoc +++ doc/candydoc/modules.ddoc @@ -7,8 +7,10 @@ $(MODULE polemy.layer) $(MODULE polemy.fresh) $(MODULE polemy.lex) $(MODULE polemy.parse) $(MODULE polemy.ast) + $(MODULE polemy.value) + $(MODULE polemy.valueconv) $(MODULE polemy.eval) $(MODULE polemy.runtime) - $(MODULE polemy.value) + $(MODULE polemy.repl) Index: main.d ================================================================== --- main.d +++ main.d @@ -3,94 +3,14 @@ * License: NYSL 0.9982 (http://www.kmonos.net/nysl/) * * Entry point for Polemy interpreter. */ module main; +import polemy.repl; import std.stdio; import std.algorithm; import std.array; -import polemy.value; -import polemy.failure; -import polemy.layer; -import polemy.parse; -import polemy.ast; -import polemy.eval; -import polemy.runtime; - -enum VersionNoMajor = 0; -enum VersionNoMinor = 1; -enum VersionNoRev = 0; - -/// Read-Eval-Print-Loop - -class REPL -{ -Evaluator ev; - /// Load the prelude environment - this() - { - ev = new Evaluator; - enrollRuntimeLibrary(ev); - } - - /// Print the version number etc. - void greet() - { - writefln("Welcome to Polemy %d.%d.%d", VersionNoMajor, VersionNoMinor, VersionNoRev); - } - - /// Run one file on the global scope - void runFile(string filename) - { - ev.evalFile(filename); - } - - /// Repeat the singleInteraction - void replLoop() - { - while( singleInteraction() ) {} - } - - /// Read one line from stdin, and do some reaction - bool singleInteraction() - { - writef(">> ", lineno); - string line = readln(); - if( line.startsWith("exit") || line.startsWith("quit") ) - return false; - try { - if( tryRun(line) ) - writeln(lastVal); - } catch(Throwable e) { - writeln(e); - } - return true; - } - -private: - Table ctx; - string buf; - Value lastVal; - int lineno = 1; - int nextlineno = 1; - - bool tryRun( string s ) - { - scope(failure) - { buf = ""; lineno = nextlineno; } - - buf ~= s; - nextlineno ++; - try - { lastVal = ev.evalString(buf, "", lineno); } - catch( UnexpectedEOF ) - { return false; } // wait - buf = ""; - lineno = nextlineno; - return true; - } -} /// Advance args[] to point the argument list fed to the script. /// Returns the name of the source file to run, or returns "" if /// no filename was given. Also, returns to libs[] the list of /// library source to load. @@ -120,15 +40,15 @@ void main( string[] args ) { string[] libs; string src = parseArgv(args, libs); - auto r = new REPL; + auto r = new REPL(args); if( src.empty ) r.greet(); foreach(lb; libs) r.runFile(lb); if( src.empty ) r.replLoop(); else r.runFile(src); } Index: polemy/_common.d ================================================================== --- polemy/_common.d +++ polemy/_common.d @@ -12,7 +12,8 @@ public import std.bigint; public import std.conv : text; public import std.exception; public import std.range; public import std.stdio : DBG = writeln; +public import std.traits; public import std.typecons; public import std.typetuple; Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -9,10 +9,12 @@ import polemy.failure; import polemy.ast; import polemy.parse; import polemy.value; import polemy.layer; +import polemy.value; +import polemy.valueconv; /// Objects for maitaining global environment and evaluation of expression on it class Evaluator { public: Index: polemy/layer.d ================================================================== --- polemy/layer.d +++ polemy/layer.d @@ -4,11 +4,10 @@ * * Definition of constants related to the layers. */ module polemy.layer; import polemy._common; -import polemy.failure; /// Type for the layer ID alias string Layer; @@ -18,14 +17,18 @@ ValueLayer = "@value", /// Predefined layer for normal run MacroLayer = "@macro", /// Predefined layer for macro run (@lay() changes layer) RawMacroLayer = "(rawmacro)", /// Predefined layer for macro run (@lay() becomes AST) } +/// True if it is macro-like layer that basically generates syntax tree + bool isMacroishLayer( Layer lay ) { return lay==MacroLayer || lay==RawMacroLayer; } + +/// True if in the specified layer @lay(...) has no effect and merely produces a syntax tree bool isNoLayerChangeLayer( Layer lay ) { return lay==RawMacroLayer; } ADDED polemy/repl.d Index: polemy/repl.d ================================================================== --- polemy/repl.d +++ polemy/repl.d @@ -0,0 +1,90 @@ +/** + * Authors: k.inaba + * License: NYSL 0.9982 (http://www.kmonos.net/nysl/) + * + * Read-Eval-Print-Loop + */ +module polemy.repl; +import polemy.failure; +import polemy.layer; +import polemy.eval; +import polemy.runtime; +import polemy.value; +import polemy.valueconv; +import std.stdio; +import std.string; + +enum VersionNoMajor = 0; /// Version Number +enum VersionNoMinor = 1; /// Version Number +enum VersionNoRev = 0; /// Version Number + +/// Read-Eval-Print-Loop + +class REPL +{ + /// Load the prelude environment + this(string[] args) + { + ev = new Evaluator; + ev.globalContext.set("argv", ValueLayer, d2polemy(args)); + enrollRuntimeLibrary(ev); + } + + /// Print the version number etc. + void greet() + { + writefln("Welcome to Polemy %d.%d.%d", VersionNoMajor, VersionNoMinor, VersionNoRev); + } + + /// Run one file on the global scope + void runFile(string filename) + { + ev.evalFile(filename); + } + + /// Repeat the singleInteraction + void replLoop() + { + while( singleInteraction() ) {} + } + + /// Read one line from stdin, and do some reaction + bool singleInteraction() + { + writef(">> ", lineno); + string line = readln(); + if( line.startsWith("exit") || line.startsWith("quit") ) + return false; + try { + if( tryRun(line) ) + writeln(lastVal); + } catch(Throwable e) { + writeln(e); + } + return true; + } + +private: + Evaluator ev; + Table ctx; + string buf; + Value lastVal; + int lineno = 1; + int nextlineno = 1; + + bool tryRun( string s ) + { + scope(failure) + { buf = ""; lineno = nextlineno; } + + buf ~= s; + nextlineno ++; + try + { lastVal = ev.evalString(buf, "", lineno); } + catch( UnexpectedEOF ) + { return false; } // wait + buf = ""; + lineno = nextlineno; + return true; + } +} ADDED polemy/runtime.d Index: polemy/runtime.d ================================================================== --- polemy/runtime.d +++ polemy/runtime.d @@ -0,0 +1,59 @@ +/** + * Authors: k.inaba + * License: NYSL 0.9982 http://www.kmonos.net/nysl/ + * + * Runtime library for Polemy programming language. + */ +module polemy.runtime; +import polemy._common; +import polemy.layer; +import polemy.value; +import polemy.eval; +import std.stdio; + +/// enroll the native implementations of primitive functions + +void enrollRuntimeLibrary( Evaluator e ) +{ + e.addPrimitive("+", ValueLayer, (IntValue lhs, IntValue rhs){return new IntValue(lhs.data + rhs.data);} ); + e.addPrimitive("-", ValueLayer, (IntValue lhs, IntValue rhs){return new IntValue(lhs.data - rhs.data);} ); + e.addPrimitive("*", ValueLayer, (IntValue lhs, IntValue rhs){return new IntValue(lhs.data * rhs.data);} ); + e.addPrimitive("/", ValueLayer, (IntValue lhs, IntValue rhs){return new IntValue(lhs.data / rhs.data);} ); + e.addPrimitive("%", ValueLayer, (IntValue lhs, IntValue rhs){return new IntValue(lhs.data % rhs.data);} ); + e.addPrimitive("||", ValueLayer, (IntValue lhs, IntValue rhs){return new IntValue(lhs.data!=0 || rhs.data!=0);} ); + e.addPrimitive("&&", ValueLayer, (IntValue lhs, IntValue rhs){return new IntValue(lhs.data!=0 && rhs.data!=0);} ); + e.addPrimitive("<", ValueLayer, (Value lhs, Value rhs){return new IntValue(lhs < rhs);} ); + e.addPrimitive(">", ValueLayer, (Value lhs, Value rhs){return new IntValue(lhs > rhs);} ); + e.addPrimitive("<=", ValueLayer, (Value lhs, Value rhs){return new IntValue(lhs <= rhs);} ); + e.addPrimitive(">=", ValueLayer, (Value lhs, Value rhs){return new IntValue(lhs >= rhs);} ); + e.addPrimitive("==", ValueLayer, (Value lhs, Value rhs){return new IntValue(lhs == rhs);} ); + e.addPrimitive("!=", ValueLayer, (Value lhs, Value rhs){return new IntValue(lhs != rhs);} ); + e.addPrimitive("print", ValueLayer, (Value a){ + writeln(a); + return new IntValue(0); + }); + e.addPrimitive("if", ValueLayer, (IntValue x, FunValue ft, FunValue fe){ + auto toRun = (x.data==0 ? fe : ft); + // [TODO] fill positional information + return toRun.invoke(ValueLayer, toRun.definitionContext(), null); + }); + e.addPrimitive("_isint", ValueLayer, (Value v){return new IntValue(cast(IntValue)v !is null);} ); + e.addPrimitive("_isstr", ValueLayer, (Value v){return new IntValue(cast(StrValue)v !is null);} ); + e.addPrimitive("_isfun", ValueLayer, (Value v){return new IntValue(cast(FunValue)v !is null);} ); + e.addPrimitive("_isundefined", ValueLayer, (Value v){return new IntValue(cast(UndefinedValue)v !is null);} ); + e.addPrimitive("_istable", ValueLayer, (Value v){return new IntValue(cast(Table)v !is null);} ); + e.addPrimitive(".", ValueLayer, (Table t, StrValue s){ + return (t.has(s.data, ValueLayer) ? t.get(s.data, ValueLayer) : new UndefinedValue); + }); + e.addPrimitive(".?", ValueLayer, (Table t, StrValue s){ + return new IntValue(t.has(s.data, ValueLayer)); + }); + e.addPrimitive(".=", ValueLayer, (Table t, StrValue s, Value v){ + auto t2 = new Table(t, Table.Kind.NotPropagateSet); + t2.set(s.data, ValueLayer, v); + return t2; + }); + e.addPrimitive("{}", ValueLayer, (){ + return new Table; + }); +} Index: polemy/value.d ================================================================== --- polemy/value.d +++ polemy/value.d @@ -7,11 +7,10 @@ module polemy.value; import polemy._common; import polemy.failure; import polemy.ast; import polemy.layer; -import std.string; /// Runtime values of Polemy abstract class Value { @@ -21,10 +20,11 @@ /// class IntValue : Value { BigInt data; + this(bool n) { this.data = n?1:0; } this(int n) { this.data = n; } this(long n) { this.data = n; } this(BigInt n) { this.data = n; } this(string n) { this.data = BigInt(n); } override string toString() const { return toDecimalString(cast(BigInt)data); } @@ -218,205 +218,5 @@ assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) ); assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) ); // [TODO] define the semantics and test @layers } - -immutable(LexPosition) extractPos( Table t ) -{ - Layer theLayer = ValueLayer; - 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, Value vvvv ) -{ - Table t = cast(Table)vvvv; - if( t is null ) - throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (not a table)"); - - 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 Int(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 Str(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 Var(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 Lay(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 Let(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, "args")) - return new App(pos, tableToAST(theLayer,f), tableToASTList(theLayer,a)); - throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"app", fun:???, args:???}`); - case "fun": - if(auto p = t.access!Table(theLayer, "params")) - if(auto b = t.access!Table(theLayer, "funbody")) - { - 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, "layers")) - { - Layer[] ls; - foreach(lll; tableAsConsList(theLayer, ll)) - if(auto l = cast(StrValue)lll) - ls ~= l.data; - else - throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(lll)); - ps ~= new Parameter(ss.data, ls); - continue; - } - else - { - Layer[] emp; - ps ~= new Parameter(ss.data, emp); - continue; - } - throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(v)); - } - auto bb = tableToAST(theLayer, b); - return new Fun(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)); - } -} - -Table makeCons(Value a, Value d) -{ - Table t = new Table; - t.set("car", ValueLayer, a); - t.set("cdr", ValueLayer, d); - return t; -} - -Table fromPos(LexPosition pos) -{ - Table t = new Table; - if( pos !is null ) { - t.set("filename", ValueLayer, new StrValue(pos.filename)); - t.set("lineno", ValueLayer, new IntValue(pos.lineno)); - t.set("column", ValueLayer, new IntValue(pos.column)); - } else { - t.set("filename", ValueLayer, new StrValue("nullpos")); - t.set("lineno", ValueLayer, new IntValue(0)); - t.set("column", ValueLayer, new IntValue(0)); - } - return t; -} - -/// Convert AST to Table so that it can be used in Polemy -/// TODO: generalize to DValue2PolemyValue - -Value ast2table(T)(T e, Value delegate(AST) rec) -{ - assert( typeid(e) == typeid(T) ); - - static if(is(T==BigInt) || is(T==long) || is(T==int)) - return new IntValue(e); - else - static if(is(T==string)) - return new StrValue(e); - else - static if(is(T S : S[])) - { - Table lst = new Table; - foreach_reverse(a; e) - static if(is(S : AST)) - lst = makeCons(rec(a), lst); - else - lst = makeCons(ast2table(a,rec), lst); - return lst; - } - else - static if(is(T : AST)) - { - auto t = new Table; - t.set("pos", ValueLayer, fromPos(e.pos)); - t.set("is" , ValueLayer, new StrValue(typeid(e).name.split(".")[$-1].tolower())); - foreach(i,m; e.tupleof) - static if(is(typeof(m) : AST)) - t.set(e.tupleof[i].stringof[2..$], ValueLayer, rec(m)); - else - t.set(e.tupleof[i].stringof[2..$], ValueLayer, ast2table(m,rec)); - return t; - } - else - static if(is(T == class)) - { - auto t = new Table; - foreach(i,m; e.tupleof) - static if(is(typeof(m) : AST)) - t.set(e.tupleof[i].stringof[2..$], ValueLayer, rec(m)); - else - t.set(e.tupleof[i].stringof[2..$], ValueLayer, ast2table(m,rec)); - return t; - } - else - static assert(false, "unknown type <"~T.stringof~"> during AST encoding"); -} ADDED polemy/valueconv.d Index: polemy/valueconv.d ================================================================== --- polemy/valueconv.d +++ polemy/valueconv.d @@ -0,0 +1,205 @@ +/** + * Authors: k.inaba + * License: NYSL 0.9982 http://www.kmonos.net/nysl/ + * + * Convert values between Polemy and D + */ +module polemy.valueconv; +import polemy._common; +import polemy.failure; +import polemy.ast; +import polemy.layer; +import polemy.value; +import std.string; + +LexPosition extractPos( Table t ) +{ + Layer theLayer = ValueLayer; + 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 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, Value vvvv ) +{ + Table t = cast(Table)vvvv; + if( t is null ) + throw genex!RuntimeException(cast(LexPosition)null, "Invalid AST (not a table)"); + + 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 Int(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 Str(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 Var(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 Lay(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 Let(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, "args")) + return new App(pos, tableToAST(theLayer,f), tableToASTList(theLayer,a)); + throw genex!RuntimeException(cast(LexPosition)null, `Invalid AST {is:"app", fun:???, args:???}`); + case "fun": + if(auto p = t.access!Table(theLayer, "params")) + if(auto b = t.access!Table(theLayer, "funbody")) + { + 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, "layers")) + { + Layer[] ls; + foreach(lll; tableAsConsList(theLayer, ll)) + if(auto l = cast(StrValue)lll) + ls ~= l.data; + else + throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(lll)); + ps ~= new Parameter(ss.data, ls); + continue; + } + else + { + Layer[] emp; + ps ~= new Parameter(ss.data, emp); + continue; + } + throw genex!RuntimeException(cast(LexPosition)null, sprintf!`Invalid AST {bad fun params %s}`(v)); + } + auto bb = tableToAST(theLayer, b); + return new Fun(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)); + } +} + +/// Cons of two pairs + +Table makeCons(Value a, Value d) +{ + Table t = new Table; + t.set("car", ValueLayer, a); + t.set("cdr", ValueLayer, d); + return t; +} + +/// Experimental!!! Convert D value (except AST) to Polemy Value + +Value d2polemy(T)(T e) +{ + return ast2table(e, delegate Value(AST){ assert(false); }); +} + +/// Convert AST to Table so that it can be used in Polemy + +Value ast2table(T)(T e, Value delegate(AST) rec) +{ + static if(is(T==BigInt) || isIntegral!(T)) + return new IntValue(e); + else + static if(is(Unqual!(T)==string)) + return new StrValue(e); + else + static if(is(T S : S[])) + { + Table lst = new Table; + foreach_reverse(a; e) + static if(is(S : AST)) + lst = makeCons(rec(a), lst); + else + lst = makeCons(ast2table(a,rec), lst); + return lst; + } + else + static if(is(T : AST)) + { + assert( typeid(e) == typeid(T), text("abstracted: ", typeid(e), " vs ", typeid(T)) ); + auto t = new Table; + t.set("pos", ValueLayer, ast2table(e.pos,rec)); + t.set("is" , ValueLayer, new StrValue(typeid(e).name.split(".")[$-1].tolower())); + foreach(i,m; e.tupleof) + static if(is(typeof(m) : AST)) + t.set(e.tupleof[i].stringof[2..$], ValueLayer, rec(m)); + else + t.set(e.tupleof[i].stringof[2..$], ValueLayer, ast2table(m,rec)); + return t; + } + else + static if(is(T == class)) + { + auto t = new Table; + foreach(i,m; e.tupleof) + static if(is(typeof(m) : AST)) + t.set(e.tupleof[i].stringof[2..$], ValueLayer, rec(m)); + else + t.set(e.tupleof[i].stringof[2..$], ValueLayer, ast2table(m,rec)); + return t; + } + else + static assert(false, "unknown type <"~T.stringof~"> during AST encoding"); +}