Index: .poseidon ================================================================== --- .poseidon +++ .poseidon @@ -33,10 +33,11 @@ main.d polemy\_common.d polemy\ast.d polemy\eval.d polemy\failure.d + polemy\layer.d polemy\lex.d polemy\parse.d polemy\value.d tricks\test.d tricks\tricks.d Index: doc/candydoc/modules.ddoc ================================================================== --- doc/candydoc/modules.ddoc +++ doc/candydoc/modules.ddoc @@ -2,10 +2,11 @@ $(MODULE main) $(MODULE tricks.tricks) $(MODULE tricks.test) $(MODULE polemy._common) $(MODULE polemy.failure) + $(MODULE polemy.layer) $(MODULE polemy.lex) $(MODULE polemy.parse) $(MODULE polemy.ast) $(MODULE polemy.eval) $(MODULE polemy.value) Index: main.d ================================================================== --- main.d +++ main.d @@ -11,10 +11,11 @@ import polemy.value; import polemy.failure; import polemy.parse; import polemy.ast; import polemy.eval; +import polemy.layer; enum VersionNoMajor = 0; enum VersionNoMinor = 1; enum VersionNoRev = 0; @@ -35,11 +36,11 @@ } /// Run one file on the global scope void runFile(string filename) { - eval(parseFile(filename), ctx, false, "@v"); + eval(parseFile(filename), ctx, false, ValueLayer); } /// Repeat the singleInteraction void replLoop() { @@ -75,11 +76,11 @@ { buf = ""; lineno = nextlineno; } buf ~= s; nextlineno ++; try - { lastVal = eval(parseString(buf, "", lineno), ctx, false, "@v"); } + { lastVal = eval(parseString(buf, "", lineno), ctx, false, ValueLayer); } catch( UnexpectedEOF ) { return false; } // wait buf = ""; lineno = nextlineno; return true; Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -8,56 +8,57 @@ import polemy._common; import polemy.failure; import polemy.ast; import polemy.parse; import polemy.value; +import polemy.layer; import std.typecons; import std.stdio; /// Table createGlobalContext() { auto ctx = new Table; - ctx.set("+", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data + rhs.data);} )); - ctx.set("-", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data - rhs.data);} )); - ctx.set("*", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data * rhs.data);} )); - ctx.set("/", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data / rhs.data);} )); - ctx.set("%", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data % rhs.data);} )); - ctx.set("||", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) || (rhs.data!=0) ? 1:0));} )); - ctx.set("&&", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) && (rhs.data!=0) ? 1:0));} )); - ctx.set("<", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs < rhs ? 1: 0));} )); - ctx.set(">", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs > rhs ? 1: 0));} )); - ctx.set("<=", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs <= rhs ? 1: 0));} )); - ctx.set(">=", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs >= rhs ? 1: 0));} )); - ctx.set("==", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs == rhs ? 1: 0));} )); - ctx.set("!=", "@v", native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs != rhs ? 1: 0));} )); - ctx.set("print", "@v", native( (Value a){ + ctx.set("+", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data + rhs.data);} )); + ctx.set("-", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data - rhs.data);} )); + ctx.set("*", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data * rhs.data);} )); + ctx.set("/", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data / rhs.data);} )); + ctx.set("%", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data % rhs.data);} )); + ctx.set("||", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) || (rhs.data!=0) ? 1:0));} )); + ctx.set("&&", ValueLayer, native( (IntValue lhs, IntValue rhs){return new IntValue(BigInt((lhs.data!=0) && (rhs.data!=0) ? 1:0));} )); + ctx.set("<", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs < rhs ? 1: 0));} )); + ctx.set(">", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs > rhs ? 1: 0));} )); + ctx.set("<=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs <= rhs ? 1: 0));} )); + ctx.set(">=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs >= rhs ? 1: 0));} )); + ctx.set("==", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs == rhs ? 1: 0));} )); + ctx.set("!=", ValueLayer, native( (Value lhs, Value rhs){return new IntValue(BigInt(lhs != rhs ? 1: 0));} )); + ctx.set("print", ValueLayer, native( (Value a){ writeln(a); return new IntValue(BigInt(178)); })); - ctx.set("if", "@v", native( (IntValue x, FunValue ft, FunValue fe){ + ctx.set("if", ValueLayer, native( (IntValue x, FunValue ft, FunValue fe){ auto toRun = (x.data==0 ? fe : ft); - return toRun.invoke(null, "@v", toRun.definitionContext()); + return toRun.invoke(null, ValueLayer, toRun.definitionContext()); // return toRun.invoke(pos, lay, toRun.definitionContext()); })); - ctx.set("_isint", "@v", native( (Value v){return new IntValue(BigInt(cast(IntValue)v is null ? 0 : 1));} )); - ctx.set("_isstr", "@v", native( (Value v){return new IntValue(BigInt(cast(StrValue)v is null ? 0 : 1));} )); - ctx.set("_isfun", "@v", native( (Value v){return new IntValue(BigInt(cast(FunValue)v is null ? 0 : 1));} )); - ctx.set("_isundefined", "@v", native( (Value v){return new IntValue(BigInt(cast(UndValue)v is null ? 0 : 1));} )); - ctx.set("_istable", "@v", native( (Value v){return new IntValue(BigInt(cast(Table)v is null ? 0 : 1));} )); - ctx.set(".", "@v", native( (Table t, StrValue s){ - return (t.has(s.data, "@v") ? t.get(s.data, "@v") : new UndValue); + ctx.set("_isint", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(IntValue)v is null ? 0 : 1));} )); + ctx.set("_isstr", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(StrValue)v is null ? 0 : 1));} )); + ctx.set("_isfun", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(FunValue)v is null ? 0 : 1));} )); + ctx.set("_isundefined", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(UndValue)v is null ? 0 : 1));} )); + ctx.set("_istable", ValueLayer, native( (Value v){return new IntValue(BigInt(cast(Table)v is null ? 0 : 1));} )); + ctx.set(".", ValueLayer, native( (Table t, StrValue s){ + return (t.has(s.data, ValueLayer) ? t.get(s.data, ValueLayer) : new UndValue); + }) ); + ctx.set(".?", ValueLayer, native( (Table t, StrValue s){ + return new IntValue(BigInt(t.has(s.data, ValueLayer) ? 1 : 0)); }) ); - ctx.set(".?", "@v", native( (Table t, StrValue s){ - return new IntValue(BigInt(t.has(s.data, "@v") ? 1 : 0)); - }) ); - ctx.set(".=", "@v", native( (Table t, StrValue s, Value v){ + ctx.set(".=", ValueLayer, native( (Table t, StrValue s, Value v){ auto t2 = new Table(t, Table.Kind.NotPropagateSet); - t2.set(s.data, "@v", v); + t2.set(s.data, ValueLayer, v); return t2; }) ); - ctx.set("{}", "@v", native( (){ + ctx.set("{}", ValueLayer, native( (){ return new Table; }) ); return ctx; } @@ -78,27 +79,27 @@ /// Entry point of this module Tuple!(Value,"val",Table,"ctx") eval(AST e) { Table ctx = createGlobalContext(); - return typeof(return)(eval(e, ctx, false, "@v"), ctx); + return typeof(return)(eval(e, ctx, false, ValueLayer), ctx); } Value invokeFunction(in LexPosition pos, Value _f, AST[] args, Table callerCtx, Layer lay, bool AlwaysMacro=false) { if(auto f = cast(FunValue)_f) { Table ctx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); foreach(i,p; f.params()) if( p.layers.empty ) - if(lay=="@macro") + if(lay==MacroLayer) ctx.set(p.name, lay, macroEval(args[i], callerCtx, AlwaysMacro)); else ctx.set(p.name, lay, eval(args[i], callerCtx, true, lay)); else foreach(argLay; p.layers) - if(argLay=="@macro") + if(argLay==MacroLayer) ctx.set(p.name, argLay, macroEval(args[i], callerCtx, AlwaysMacro)); else ctx.set(p.name, argLay, eval(args[i], callerCtx, true, argLay)); return f.invoke(pos, lay, ctx); } @@ -105,73 +106,73 @@ throw genex!RuntimeException(pos, "tried to call non-function"); } Value lift(in LexPosition pos, Value v, Layer lay, Table callerCtx) { - // similar to invoke Function, but with only one argument bound to @v - Value _f = callerCtx.get(lay, "(system)", pos); + // similar to invoke Function, but with only one argument bound to ValueLayer + Value _f = callerCtx.get(lay, SystemLayer, pos); if(auto f = cast(FunValue)_f) { Table ctx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); auto ps = f.params(); if( ps.length != 1 ) - throw genex!RuntimeException(pos, "lift function must take exactly one argument at @v layer"); - if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]=="@v" ) + throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer"); + if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer ) { - ctx.set(ps[0].name, "@v", v); - return f.invoke(pos, "@v", ctx); + ctx.set(ps[0].name, ValueLayer, v); + return f.invoke(pos, ValueLayer, ctx); } else - throw genex!RuntimeException(pos, "lift function must take exactly one argument at @v layer"); + throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer"); } throw genex!RuntimeException(pos, "tried to call non-function"); } /// Entry point of this module /// If splitCtx = true, then inner variable declaration do not overwrite ctx. -/// lay is the layer ID for evaluation (standard value semantics uses "@v"). +/// lay is the layer ID for evaluation (standard value semantics uses ValueLayer). Value eval(AST e, Table ctx, bool splitCtx, Layer lay) { return e.match( (StrLiteral e) { Value v = new StrValue(e.data); - if( lay == "@v" ) + if( lay == ValueLayer ) return v; else return lift(e.pos,v,lay,ctx); }, (IntLiteral e) { Value v = new IntValue(e.data); - if( lay == "@v" ) + if( lay == ValueLayer ) return v; else // rise return lift(e.pos,v,lay,ctx); }, (VarExpression e) { - if( lay == "@v" ) + if( lay == ValueLayer ) return ctx.get(e.var, lay, e.pos); try { return ctx.get(e.var, lay, e.pos); } catch( Throwable ) { // [TODO] more precise... - return lift(e.pos, ctx.get(e.var, "@v", e.pos), lay, ctx); + return lift(e.pos, ctx.get(e.var, ValueLayer, e.pos), lay, ctx); } }, (LayeredExpression e) { - if( e.lay == "@macro" ) + if( e.lay == MacroLayer ) return macroEval(e.expr, ctx, false); else return eval(e.expr, ctx, true, e.lay); }, (LetExpression e) { // for letrec, we need this, but should avoid overwriting???? - // ctx.set(e.var, "@v", new UndefinedValue, e.pos); + // ctx.set(e.var, ValueLayer, new UndefinedValue, e.pos); if(splitCtx) ctx = new Table(ctx, Table.Kind.NotPropagateSet); Value v = eval(e.init, ctx, true, lay); ctx.set(e.var, (e.layer.length ? e.layer : lay), v, e.pos); return eval(e.expr, ctx, false, lay); @@ -197,11 +198,11 @@ } // [TODO] Optimization Value macroEval(AST e, Table ctx, bool AlwaysMacro) { - Layer theLayer = "@v"; + Layer theLayer = ValueLayer; 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))); @@ -223,11 +224,11 @@ return t; }, (VarExpression e) { try { - return ctx.get(e.var, "@macro", e.pos); + return ctx.get(e.var, MacroLayer, e.pos); } catch( Throwable ) {// [TODO] more precies... Table t = new Table; t.set("pos", theLayer, pos); t.set("is", theLayer, new StrValue("var")); t.set("name", theLayer, new StrValue(e.var)); @@ -245,11 +246,11 @@ t.set("expr", theLayer, macroEval(e.expr,ctx,AlwaysMacro)); return cast(Value)t; } else { - if( e.lay == "@macro" ) + if( e.lay == MacroLayer ) return macroEval(e.expr, ctx, false); else return eval(e.expr, ctx, true, e.lay); } }, @@ -266,11 +267,11 @@ (FuncallExpression e) { Value _f = macroEval(e.fun,ctx,AlwaysMacro); if( auto f = cast(FunValue)_f ) - return invokeFunction(e.pos, f, e.args, ctx, "@macro", AlwaysMacro); + return invokeFunction(e.pos, f, e.args, ctx, MacroLayer, AlwaysMacro); Table t = new Table; t.set("pos", theLayer, pos); t.set("is", theLayer, new StrValue("app")); t.set("fun", theLayer, _f); @@ -319,21 +320,21 @@ unittest { auto r = assert_nothrow( evalString(`var x = 21; x + x*x;`) ); assert_eq( r.val, new IntValue(BigInt(21+21*21)) ); - assert_eq( r.ctx.get("x","@v"), new IntValue(BigInt(21)) ); - assert_nothrow( r.ctx.get("x","@v") ); - assert_throw!RuntimeException( r.ctx.get("y","@v") ); + assert_eq( r.ctx.get("x",ValueLayer), new IntValue(BigInt(21)) ); + assert_nothrow( r.ctx.get("x",ValueLayer) ); + assert_throw!RuntimeException( r.ctx.get("y",ValueLayer) ); } unittest { auto r = assert_nothrow( evalString(`var x = 21; var x = x + x*x;`) ); assert_eq( r.val, new IntValue(BigInt(21+21*21)) ); - assert_eq( r.ctx.get("x","@v"), new IntValue(BigInt(21+21*21)) ); - assert_nothrow( r.ctx.get("x","@v") ); - assert_throw!RuntimeException( r.ctx.get("y","@v") ); + assert_eq( r.ctx.get("x",ValueLayer), new IntValue(BigInt(21+21*21)) ); + assert_nothrow( r.ctx.get("x",ValueLayer) ); + assert_throw!RuntimeException( r.ctx.get("y",ValueLayer) ); } unittest { assert_eq( evalString(`let x=1; let y=(let x=2); x`).val, new IntValue(BigInt(1)) ); assert_eq( evalString(`let x=1; let y=(let x=2;fun(){x}); y()`).val, new IntValue(BigInt(2)) ); @@ -366,12 +367,12 @@ unittest { assert_throw!Throwable( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};@s(1+2)`) ); assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){x-y};1+2`).val, new IntValue(BigInt(3)) ); - assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@v(@s(x)-@s(y))};1+2`).val, new IntValue(BigInt(3)) ); - assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@v(@s(x)-@s(y))};@s(1+2)`).val, new IntValue(BigInt(-1)) ); + assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@value(@s(x)-@s(y))};1+2`).val, new IntValue(BigInt(3)) ); + assert_eq( evalString(`@@s(x){x}; @s "+"=fun(x,y){@value(@s(x)-@s(y))};@s(1+2)`).val, new IntValue(BigInt(-1)) ); } unittest { assert_eq( evalString(`@@t = fun(x){x+1}; @t(123)`).val, new IntValue(BigInt(124)) ); @@ -380,6 +381,5 @@ assert_nothrow( evalString(`def foo() { def bar(y) { if(y<1) {0} else {bar(0)} }; bar(1) }; foo()`) ); } - Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -7,10 +7,11 @@ module polemy.parse; import polemy._common; import polemy.failure; import polemy.lex; import polemy.ast; +import polemy.layer; /// Parse a string and return its AST /// Throws: ParseException, LexException, UnexpectedEOF AST parseString(S, T...)(S str, T fn_ln_cn) @@ -100,13 +101,13 @@ auto e = tryEat("(") ? parseLambdaAfterOpenParen(pos) // let var ( ... : (eat("=", "after "~kwd), E(0)); // let var = ... if( moreDeclarationExists() ) - return new LetExpression(pos, var, "(system)", e, Body()); + return new LetExpression(pos, var, SystemLayer, e, Body()); else - return new LetExpression(pos, var, "(system)", e, new VarExpression(pos, var)); + return new LetExpression(pos, var, SystemLayer, e, new VarExpression(pos, var)); } else { string kwd = layer; if( layer.empty && !tryEat(kwd="let") && !tryEat(kwd="var") && !tryEat(kwd="def") ) @@ -473,14 +474,14 @@ fun(["x"], call(var("+"), var("x"), intl(1))), var("foo")) ); assert_eq(parseString(`@@type ( x ) { x }`), - let("@type", "(system)", fun(["x"], var("x")), var("@type")) ); + let("@type", SystemLayer, fun(["x"], var("x")), var("@type")) ); assert_eq(parseString(`{}`), call(var("{}"))); assert_eq(parseString(`{foo:1,"bar":2}`), call(var(".="), call(var(".="), call(var("{}")), strl("foo"), intl(1)), strl("bar"), intl(2))); assert_eq(parseString(`{}.foo`), call(var("."),call(var("{}")),strl("foo"))); assert_eq(parseString(`{}.?foo`), call(var(".?"),call(var("{}")),strl("foo"))); assert_eq(parseString(`x{y:1}`), call(var(".="),var("x"),strl("y"),intl(1))); } Index: polemy/value.d ================================================================== --- polemy/value.d +++ polemy/value.d @@ -6,10 +6,11 @@ */ module polemy.value; import polemy._common; import polemy.failure; import polemy.ast; +import polemy.layer; /// Runtime values of Polemy abstract class Value { @@ -61,33 +62,33 @@ override Value invoke(in LexPosition pos, Layer lay, Table ctx) { // TODO: only auto raised ones need memo? no? // auto memoization /* - if( lay != "@v" && lay != "@macro" ) + if( lay != ValueLayer && lay != MacroLayer ) { if( auto memolay = lay in memo ) if( auto pv = args in *memolay ) return *pv; memo[lay][args] = lift(e.pos,new UndValue,lay,ctx); } */ // @macro run!!! - if( lay == "@macro" ) + if( lay == MacroLayer ) return macroEval(ast.funbody, ctx, false); /*TODO memo*/ AST macroMemo; if( macroMemo is null ) { // .prototype!, forced macro cannot access parameters ctx.kill = true; scope(exit)ctx.kill=false; - macroMemo = tableToAST("@v",macroEval(ast.funbody, ctx, true)); + macroMemo = tableToAST(ValueLayer,macroEval(ast.funbody, ctx, true)); } auto v = eval(macroMemo, ctx, true, lay); //auto v = eval(e.funbody, ctxNeo, true, lay); // auto memoization -// if( lay != "@v" && lay != "@macro" ) +// if( lay != ValueLayer && lay != MacroLayer ) // memo[lay][args] = v; return v; } mixin SimpleClass; @@ -112,15 +113,15 @@ foreach(i, Ti; T) params_data ~= new Parameter(text(i), []); } override Value invoke(in LexPosition pos, Layer lay, Table ctx) { - if( lay != "@v" ) - throw genex!RuntimeException(pos, "only @v layer can call native function"); + if( lay != ValueLayer ) + throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function"); T typed_args; foreach(i, Ti; T) { - typed_args[i] = cast(Ti) ctx.get(text(i), "@v"); + typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer); if( typed_args[i] is null ) throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1)); } try { return dg(typed_args); @@ -129,14 +130,10 @@ } } }; } -/// Layer ID - -alias string Layer; - /// Context (variable environment) /// Simlar to prototype chain of ECMAScript etc. /// But extended with the notion of "Layer" class Table : Value @@ -247,45 +244,45 @@ Table c0 = new Table; Table c01 = new Table(c0, Table.Kind.NotPropagateSet); Table c012 = new Table(c01, Table.Kind.PropagateSet); Table c013 = new Table(c01, Table.Kind.PropagateSet); - assert_nothrow( c012.set("x", "@v", new IntValue(BigInt(12))) ); - assert_throw!RuntimeException( c013.get("x", "@v") ); - assert_nothrow( c013.set("x", "@v", new IntValue(BigInt(13))) ); - assert_eq( c013.get("x", "@v"), new IntValue(BigInt(13)) ); - assert_eq( c012.get("x", "@v"), new IntValue(BigInt(12)) ); - assert_throw!RuntimeException( c01.get("x", "@v") ); + assert_nothrow( c012.set("x", ValueLayer, new IntValue(BigInt(12))) ); + assert_throw!RuntimeException( c013.get("x", ValueLayer) ); + assert_nothrow( c013.set("x", ValueLayer, new IntValue(BigInt(13))) ); + assert_eq( c013.get("x", ValueLayer), new IntValue(BigInt(13)) ); + assert_eq( c012.get("x", ValueLayer), new IntValue(BigInt(12)) ); + assert_throw!RuntimeException( c01.get("x", ValueLayer) ); + + assert_nothrow( c01.set("y", ValueLayer, new IntValue(BigInt(1))) ); + assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(1)) ); + assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(1)) ); + assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(1)) ); - assert_nothrow( c01.set("y", "@v", new IntValue(BigInt(1))) ); - assert_eq( c013.get("y", "@v"), new IntValue(BigInt(1)) ); - assert_eq( c012.get("y", "@v"), new IntValue(BigInt(1)) ); - assert_eq( c01.get("y", "@v"), new IntValue(BigInt(1)) ); + assert_nothrow( c0.set("z", ValueLayer, new IntValue(BigInt(0))) ); + assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) ); + assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(0)) ); + assert_eq( c01.get("z", ValueLayer), new IntValue(BigInt(0)) ); + assert_eq( c0.get("z", ValueLayer), new IntValue(BigInt(0)) ); - assert_nothrow( c0.set("z", "@v", new IntValue(BigInt(0))) ); - assert_eq( c013.get("z", "@v"), new IntValue(BigInt(0)) ); - assert_eq( c012.get("z", "@v"), new IntValue(BigInt(0)) ); - assert_eq( c01.get("z", "@v"), new IntValue(BigInt(0)) ); - assert_eq( c0.get("z", "@v"), new IntValue(BigInt(0)) ); + assert_nothrow( c012.set("y", ValueLayer, new IntValue(BigInt(444))) ); + assert_eq( c013.get("y", ValueLayer), new IntValue(BigInt(444)) ); + assert_eq( c012.get("y", ValueLayer), new IntValue(BigInt(444)) ); + assert_eq( c01.get("y", ValueLayer), new IntValue(BigInt(444)) ); - assert_nothrow( c012.set("y", "@v", new IntValue(BigInt(444))) ); - assert_eq( c013.get("y", "@v"), new IntValue(BigInt(444)) ); - assert_eq( c012.get("y", "@v"), new IntValue(BigInt(444)) ); - assert_eq( c01.get("y", "@v"), new IntValue(BigInt(444)) ); - - assert_nothrow( c012.set("z", "@v", new IntValue(BigInt(555))) ); - assert_eq( c013.get("z", "@v"), new IntValue(BigInt(0)) ); - assert_eq( c012.get("z", "@v"), new IntValue(BigInt(555)) ); - assert_eq( c01.get("z", "@v"), new IntValue(BigInt(0)) ); - assert_eq( c0.get("z", "@v"), new IntValue(BigInt(0)) ); + assert_nothrow( c012.set("z", ValueLayer, new IntValue(BigInt(555))) ); + assert_eq( c013.get("z", ValueLayer), new IntValue(BigInt(0)) ); + assert_eq( c012.get("z", ValueLayer), new IntValue(BigInt(555)) ); + 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 = "@v"; + 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"); Index: readme.txt ================================================================== --- readme.txt +++ readme.txt @@ -42,93 +42,316 @@ <> > polemy starts REPL - > polemy foo.pmy - executes foo.pmy - - > polemy -l foo.pmy - after executing foo.pmy, starts REPL - - > polemy -l foo.pmy -l bar.pmy buz.pmy - executes foo.pmy, bar.bmy, and then buz.pmy - - - -<> - - Comment is "# ... \n" - - E ::= - // declaration - | ("var"|"let"|"def"|LAYER) ID "=" E (";"|"in") E - | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" (";"|"in") E - | ("var"|"let"|"def"|LAYER) ID "=" E - | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" - // literal - | INTEGER - | STRING - | "{" ENTRYS "}" - | "fun" "(" PARAMS ")" "{" E "}" + > polemy foo.pmy + executes foo.pmy + + > polemy -l foo.pmy + after executing foo.pmy, starts REPL + + > polemy -l foo.pmy -l bar.pmy buz.pmy + executes foo.pmy, bar.bmy, and then buz.pmy + + + +<> + + Comment is "# ... \n" + + E ::= + // declaration + | ("var"|"let"|"def"|LAYER) ID "=" E (";"|"in") E + | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" (";"|"in") E + | ("var"|"let"|"def"|LAYER) ID "=" E + | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" + // literal + | INTEGER + | STRING + | "{" ENTRYS "}" // table + | "fun" "(" PARAMS ")" "{" E "}" // anonymous function // function call - | E "(" ARGS")" - where ARGS ::= E "," ... "," E - PARAMS ::= ID LAYER* "," ... "," ID LAYER* - ENTRYS ::= ID ":" E "," ... "," ID ":" E - ID ::= 'a-zA-Z0-9_...'+ - LAYER ::= "@" ID - // operators - | "(" E ")" - | E "." ID - | E ".?" ID - | E BINOP E - | "if" "(" E ")" "{" E "}" - | "if" "(" E ")" "{" E "}" "else "{" E "}" - // layered exec - | LAYER "(" E ")" - + | E "(" ARGS")" + where ARGS ::= E "," ... "," E + PARAMS ::= ID LAYER* "," ... "," ID LAYER* + ENTRYS ::= ID ":" E "," ... "," ID ":" E + ID ::= 'a-zA-Z0-9_...'+ + LAYER ::= "@" ID + // operators + | "(" E ")" + | E "." ID // table field access + | E ".?" ID // table field existence check + | E "{" ENTRYS "}" // table extend (pure functionally) + | E BINOP E + | "if" "(" E ")" "{" E "}" + | "if" "(" E ")" "{" E "}" "else "{" E "}" + // layered exec + | LAYER "(" E ")" + The following are actually rewritten to function calls: - - - if (E) then{E} else{E} ==> if( E, fun(){E}, fun(){E} ) - - E BINOP E ==> BINOP(E, E) + + - if (E) then{E} else{E} ==> if( E, fun(){E}, fun(){E} ) + - E BINOP E ==> BINOP(E, E) - E.ID ==> . (E, ID) - - E.?ID ==> .?(E, ID) - - {} ==> {}() - - {ID:E, ...} ==> .=({...}, ID, E) - -Several styles of variable declaration can be used: - - - fun(x){ fun(y){x} } # K-combinator - - fun(x){ let f = fun(y){x} in f } # let-in style - - fun(x){ var f = fun(y){x}; f } # var-; style - - fun(x){ def f = fun(y){x} in f } # you can use any combination of (let|var|def)-(;|in) - - fun(x){ def f(y){x} in f } # syntax sugar for function declaration - - fun(x){ let f(y){x}; f } # this is also ok - - fun(x){ var f(y){x} } # omitting (;|in) returns the last declared object directly - - fun(x,y){x} #< this is not equal to the above ones. functions are no curried. - -NOTE: Theres no "let rec" syntax, but still recursive definition works - def f(x) { if(x==0){1}else{x*f(x-1)} } in f(10) #=> 3628800 - yet still the code below also works - def x=21 in def x=x+x in x #=> 42. - The internal scoping mechanism is a little tricky (this is for coping with - the "layer" feature explained below), but I hope that it works as everyone - expects in most cases, as long as you don't use the same-name-variables heavily :). - - - -<> - - Polemy is an untyped functional programming language that has - - integers: 0, 123, 456666666666666666666666666666666666666789, ... - - strings: "hello, world!\n", ... - - tables: {car: 1, cdr: {car: 2, cdr: {}}} - - functions: fun(x){x+1} - as primitive datatypes. Functions capture lexical closures. - It is almost 'pure' (except the primitve function "print" and some - trick inside scoping mechanisms). - - -<> - - to be written + - E.?ID ==> .?(E, ID) + - {} ==> {}() + - { ENTRIES } ==> {}{ ENTRIES } + - E {ID:E, ...} ==> (.=(E, ID, E)) { ... } + +Several styles of variable declaration can be used: + + - fun(x){ fun(y){x} } # K-combinator + - fun(x){ let f = fun(y){x} in f } # let-in style + - fun(x){ var f = fun(y){x}; f } # var-; style + - fun(x){ def f = fun(y){x} in f } # you can use any combination of (let|var|def)-(;|in) + - fun(x){ def f(y){x} in f } # syntax sugar for function declaration + - fun(x){ let f(y){x}; f } # this is also ok + - fun(x){ var f(y){x} } # omitting (;|in) returns the last declared object directly + - fun(x,y){x} #< this is not equal to the above ones. functions are no curried. + +NOTE: Theres no "let rec" syntax, but still recursive definition works + def f(x) { if(x==0){1}else{x*f(x-1)} } in f(10) #=> 3628800 + yet still the code below also works + def x=21 in def x=x+x in x #=> 42. + The internal scoping mechanism is a little tricky (this is for coping with + the "layer" feature explained below), but I hope that it works as everyone + expects in most cases, as long as you don't use the same-name-variables heavily :). + + + +<> + + Polemy is an untyped functional programming language that has + - integers: 0, 123, 456666666666666666666666666666666666666789, ... + - strings: "hello, world!\n", ... + - tables: {car: 1, cdr: {car: 2, cdr: {}}} + - functions: fun(x){x+1} + as primitive datatypes. Functions capture lexical closures. + It is almost 'pure' (except the primitve function "print" and some + trick inside scoping mechanisms). + + +<> + + Polemy's runtime environment has many "layer"s. + Usual execution run in the @value layer. + + >> 1 + 2 + 3 + >> @value( 1 + 2 ) + 3 + + Here you can see that @LayerName( Expression ) executes the inner Expression in + the @LayerName layer. Other than @value, one other predefined layer exists: @macro. + + >> @macro( 1+2 ) + {pos@value:{lineno@value:3, column@value:9, filename@value:}, + is@value:app, + arg@value:{car@value:{pos@value:{lineno@value:3, column@value:9, filename@value:}, + is@value:int, + data@value:1}, + cdr@value:{ + car@value:{pos@value:{lineno@value:3, column@value:11, filename@value:}, + is@value:int, + data@value:2}, + cdr@value:{}}}, + fun@value:{pos@value:{lineno@value:3, column@value:10, filename@value:}, + is@value:var, + name@value:+}} + + (Sorry, this pretty printing is not available on the actual interpreter...) + This evaluates the expression 1+2 in the @macro layer. In this layer, the meaning of + the program is its abstract syntax tree. + + You can interleave layers. + The root node of the abstract syntax tree is function "app"lication. + + >> @value(@macro( 1+2 ).is) + app + + + +<> + + To define a new layer, you should first tell how to "lift" existing values two the new layer. + Let us define the "@type" layer, where the meaning of programs is their static type. + + >> @@type = fun(x) { + >> if( _isint(x) ) { "int" } else { + >> if( _isfun(x) ) { x } else { "unknown" } } + >> } + (Note: polemy REPL may warn some exception here but please ignore) + + For simplicity, I here deal only with integers. + _isint is a primitive function of Polemy that checks the dynamic type of a value. + For function, leaving it untouched works well for almost all layers. + + >> @type( 1 ) + int + >> @type( 2 ) + int + >> @type( "foo" ) + unknown + + Fine! Let's try to type 1+2. + + >> @type( 1 + 2 ) + ...\value.d(119): [:6:8] only @value layer can call native function + + Note that the behavior of this program is + - run 1+2 in the @type layer + and NOT + - run 1+2 in @value and obtain 3 and run 3 in the @type. + The problem is, the variable "+" is defined only in the @value layer. + To carry out computation in the @type layer. We need to define it also + in the @type layer. + + To define some variable in a specific layer, use @LayerName in place of + (let|var|def)s. + + >> let x = 2 + >> @value x = 2 + >> @type x = "int" + >> @hoge x = "fuga" + + For "+", do it like this. + + >> @type "+" = fun(x,y) {@value( + >> if( @type(x)=="int" && @type(y)=="int" ) { "int" } else { "typeerror" } + >> )} + polemy.value.native!(IntValue,IntValue,IntValue).native.__anonclass24 + + It is just computing the return type from the input type. + Not here that the intended "meaning" of if-then-else is the runtime-branching, + and the meaning of "==" is the value-comparison. These are the @value layer + behavior. So we have defined the function body inside @value layer. + But when we refer the variables x and y, we need its @type layer meaning. + Hence we use @type() there. + + Now we get it. + + >> @type( 1 + 2 ) + int + + Well, but do we have to define the @type layer meaning for every variables??? + No. After you defined @type "+", you'll automatically get the following: + + >> def double(x) { x + x } + (function:17e4740:1789720) + + >> @type( double(123) ) + int + + Every user-defined functions are automatically "lift"ed to the appropriate layer. + Only primitive functions like "+" requires @yourNewLayer annotation. + + + +<> + + let|var|def is to define a variable in the "current" layer. + Not necessary to the @value layer. + + >> @value( let x = 1 in @value(x) ) + 1 + + >> @macro( let x = 1 in @value(x) ) + polemy.failure.RuntimeException: [:14:29] variable x not found + + >> @macro( let x = 1 in @macro(x) ) + {pos@value:{lineno@value:15, ... + + + +<> + + >> def foo(x @macro @value) { {fst: x, snd: @macro(x)} } + (function:1730360:1789720) + + If you annotate function parameters by @LayerNames, when you invoke the function... + + >> foo(1+2) + {snd@value: {pos@value:{lineno@value:17, column@value:5, filename@value:}, + is@value:app, arg@value:{... + /fst@value:3 + /} + + its corresponding arguments are evaluated in the layer and passed to it. + If you specify multiple layers, the argument expression is run multiple times. + If you do not specify any layer for a parameter, it works in the neutral layer. + + + +<<@macro layer>> + + When function is invoked, it first run in the @macro layer, and after that, + it run in the neutral layer. Here is an example. + + >> @macro twice(x) { x; x } + >> def f() { twice(print("Hello")); 999 } + (function:173b6a0:1789720) + >> f() + Hello + Hello + 999 + + When the interpreter evaluates f(), it first executes + "twice(print("Hello")); 999" + in the @macro layer. Basically what it does is to just construct its syntax tree. + But, since we have defined the "twice" function in the @macro layer, it is + execute as a function. Resulting syntax tree is + "print("Hello"); print("Hello"); 999" + and this is executed on the neutral (in this example, @value) layer. + This is the reason why you see two "Hello"s. + + + + [[limitations]] + + This @macro layer is a very primitive one, and not a perfect macro language. + Two major limitations are seen in the following "it" example. + + >> @macro LetItBe(x, y) { let it = x in y }; + + The variable name is not hygenic, and so without any effort, the syntax tree "y" + can access the outer variable "it". + + >> def foo() { LetItBe( 1+2+3, it*it ) } + >> foo() + 36 + + Of course, this is not just a limitation; it can sometimes allow us to write + many interesting macros. + + The other problem is that the macro expansion is only done at function startup. + So + + >> LetItBe( 1+2+3, it*it ) + ...\value.d(173): [:24:1] variable LetItBe is not set in layer @value + + you cannot directly use the macro in the same scope as the definition. + You need to wrap it up in a function (like the foo() in the above example). + + + + [[quote and unquote]] + + Here is more involved example of code genration. + From "x", it generates "x*x*x*x*x*x*x*x*x*x". + + @macro pow10(x) { + @value( + def pow(x, n) { + if( n == 1 ) { x } + else { + @macro( @value(x) * @value(pow(x,n-1)) ) + } + } + in + pow(@macro(x),10) + ) + }; + + Here, x is a syntax tree but n is an actual integer. If you read carefully, + you should get what is going on. Basically, @macro can be considered like + quasiquoting and @value to be an escape from it. Index: sample/macro.pmy ================================================================== --- sample/macro.pmy +++ sample/macro.pmy @@ -9,20 +9,18 @@ }; @macro maxBad(x,y) { if(x