Index: main.d ================================================================== --- main.d +++ main.d @@ -28,11 +28,11 @@ { buf = ""; lineno = nextlineno; } buf ~= s; nextlineno ++; try - { lastVal = eval(parseString(buf, "", lineno), ctx); } + { lastVal = eval(parseString(buf, "", lineno), ctx, false, "@v"); } catch( UnexpectedEOF ) { return false; } // wait buf = ""; lineno = nextlineno; return true; Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -15,72 +15,72 @@ Table createGlobalContext() { auto ctx = new Table; // [TODO] autogenerate these typechecks - ctx.set("+", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("+", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){ if( args.length != 2 ) - throw new RuntimeException(pos, "+ takes two arguments!!"); + throw genex!RuntimeException(pos, "+ takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data+y.data); - throw new RuntimeException(pos, "cannot add non-integers"); + throw genex!RuntimeException(pos, "cannot add non-integers"); })); - ctx.set("-", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("-", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){ if( args.length != 2 ) - throw new RuntimeException(pos, "- takes two arguments!!"); + throw genex!RuntimeException(pos, "- takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data-y.data); - throw new RuntimeException(pos, "cannot subtract non-integers"); + throw genex!RuntimeException(pos, "cannot subtract non-integers"); })); - ctx.set("*", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("*", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){ if( args.length != 2 ) - throw new RuntimeException(pos, "* takes two arguments!!"); + throw genex!RuntimeException(pos, "* takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data*y.data); - throw new RuntimeException(pos, "cannot multiply non-integers"); + throw genex!RuntimeException(pos, "cannot multiply non-integers"); })); - ctx.set("/", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("/", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){ if( args.length != 2 ) - throw new RuntimeException(pos, "/ takes two arguments!!"); + throw genex!RuntimeException(pos, "/ takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data/y.data); - throw new RuntimeException(pos, "cannot divide non-integers"); + throw genex!RuntimeException(pos, "cannot divide non-integers"); })); - ctx.set("<", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("<", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){ if( args.length != 2 ) - throw new RuntimeException(pos, "< takes two arguments!!"); + throw genex!RuntimeException(pos, "< takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(BigInt(to!int(x.data < y.data))); - throw new RuntimeException(pos, "cannot compare non-integers"); + throw genex!RuntimeException(pos, "cannot compare non-integers"); })); - ctx.set(">", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set(">", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){ if( args.length != 2 ) - throw new RuntimeException(pos, "> takes two arguments!!"); + throw genex!RuntimeException(pos, "> takes two arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(BigInt(to!int(x.data>y.data))); - throw new RuntimeException(pos, "cannot compare non-integers"); + throw genex!RuntimeException(pos, "cannot compare non-integers"); })); - ctx.set("print", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("print", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){ foreach(a; args) write(a); writeln(""); return new IntValue(BigInt(178)); })); - ctx.set("if", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + ctx.set("if", "@v", new FunValue(delegate Value(immutable LexPosition pos, Layer lay, Value[] args){ if( args.length != 3 ) - throw new RuntimeException(pos, "if takes three arguments!!"); + throw genex!RuntimeException(pos, "if takes three arguments!!"); if( auto x = cast(IntValue)args[0] ) if( auto ft = cast(FunValue)args[1] ) if( auto fe = cast(FunValue)args[2] ) - return (x.data == 0 ? fe : ft).call(pos,[]); - throw new RuntimeException(pos, "type mismatch in if"); + return (x.data == 0 ? fe : ft).call(pos,lay,[]); + throw genex!RuntimeException(pos, "type mismatch in if"); })); return ctx; } /// Entry point of this module @@ -96,14 +96,14 @@ } Tuple!(Value,"val",Table,"ctx") eval(AST e) { Table ctx = createGlobalContext(); - return typeof(return)(eval(e, ctx), ctx); + return typeof(return)(eval(e, ctx, false, "@v"), ctx); } -Value eval(AST _e, Table ctx, bool splitCtx = false, Layer lay="@val") +Value eval(AST _e, Table ctx, bool splitCtx, Layer lay) { if( auto e = cast(StrLiteral)_e ) { return new StrValue(e.data); } @@ -124,60 +124,60 @@ } else if( auto e = cast(LetExpression)_e ) { // for letrec, we need this, but should avoid overwriting???? - // ctx.set(e.var, "@val", new UndefinedValue, e.pos); - Value v = eval(e.init, ctx, true); + // ctx.set(e.var, "@v", new UndefinedValue, e.pos); + Value v = eval(e.init, ctx, true, lay); if(splitCtx) ctx = new Table(ctx, Table.Kind.NotPropagateSet); ctx.set(e.var, (e.layer.length ? e.layer : lay), v, e.pos); - return eval(e.expr, ctx); + return eval(e.expr, ctx, false, lay); } else if( auto e = cast(FuncallExpression)_e ) { - Value _f = eval(e.fun, ctx); + Value _f = eval(e.fun, ctx, true, lay); if( auto f = cast(FunValue)_f ) { Value[] args; foreach(a; e.args) - args ~= eval(a, ctx); - return f.call(e.pos, args); + args ~= eval(a, ctx, true, lay); + return f.call(e.pos, lay, args); } else - throw new RuntimeException(e.pos, "Non-funcion is applied"); + throw genex!RuntimeException(e.pos, "Non-funcion is applied"); } else if( auto e = cast(FunLiteral)_e ) { - return new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + return new FunValue(delegate Value(immutable LexPosition pos, string lay, Value[] args){ if( e.params.length != args.length ) - throw new RuntimeException(e.pos, sprintf!"Argument Number Mismatch (%d required but %d given)" + throw genex!RuntimeException(e.pos, sprintf!"Argument Number Mismatch (%d required but %d given)" (e.params.length, args.length)); Table ctxNeo = new Table(ctx, Table.Kind.NotPropagateSet); foreach(i,p; e.params) - ctxNeo.set(p, "@val", args[i]); - return eval(e.funbody, ctxNeo); + ctxNeo.set(p, lay, args[i]); + return eval(e.funbody, ctxNeo, true, lay); }); } - throw new RuntimeException(_e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(_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)) ); - assert_eq( r.ctx.get("x","@val"), new IntValue(BigInt(21)) ); - assert_nothrow( r.ctx.get("x","@val") ); - assert_throw!RuntimeException( r.ctx.get("y","@val") ); + 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") ); } 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","@val"), new IntValue(BigInt(21+21*21)) ); - assert_nothrow( r.ctx.get("x","@val") ); - assert_throw!RuntimeException( r.ctx.get("y","@val") ); + 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") ); } 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)) ); @@ -205,5 +205,13 @@ else { fib(x-1) + fib(x-2); }; }; fib(10);`).val, new IntValue(BigInt(89))); } + +unittest +{ + assert_throw!Throwable( evalString(`@s "+"=fun(x,y){x-y};@s(1+2)`) ); + assert_eq( evalString(`@s "+"=fun(x,y){x-y};1+2`).val, new IntValue(BigInt(3)) ); + assert_eq( evalString(`@s "+"=fun(x,y){@v(@s(x)-@s(y))};1+2`).val, new IntValue(BigInt(3)) ); + assert_eq( evalString(`@s "+"=fun(x,y){@v(@s(x)-@s(y))};@s(1+2)`).val, new IntValue(BigInt(-1)) ); +} Index: polemy/lex.d ================================================================== --- polemy/lex.d +++ polemy/lex.d @@ -161,11 +161,11 @@ } public static { bool isSpace (dchar c) { return std.ctype.isspace(c)!=0; } bool isSymbol (dchar c) { return 0x21<=c && c<=0x7f && !std.ctype.isalnum(c) && c!='_' && c!='\''; } - bool isSSymbol (dchar c) { return !find("()[]{};", c).empty; } + bool isSSymbol (dchar c) { return "()[]{};@".canFind(c); } bool isMSymbol (dchar c) { return isSymbol(c) && !isSSymbol(c) && c!='"' && c!='#'; } bool isLetter (dchar c) { return !isSpace(c) && !isSymbol(c); } } string readQuoted(const LexPosition pos){char[] buf; return readQuoted(pos,buf);} @@ -271,14 +271,15 @@ assert_eq( ts[4].str, ":-" ); assert_eq( ts[5].pos.lineno, 2 ); assert_eq( ts[5].pos.column, 8 ); assert_eq( ts[5].str, "(" ); - assert_eq( ts[6].str, "@@" ); - assert_eq( ts[7].str, ";" ); // paren and simicolons are split + assert_eq( ts[6].str, "@" ); + assert_eq( ts[7].str, "@" ); + assert_eq( ts[8].str, ";" ); // paren and simicolons, atmarks are split - assert_eq( ts.length, 8 ); + assert_eq( ts.length, 9 ); } unittest { // !! be sure to run the unittest on the root of the source directory @@ -366,10 +367,11 @@ { auto lex = lexerFromString(`=""`); assert_eq(lex.front.str, "="); lex.popFront; assert_eq(lex.front.str, ""); lex.popFront; assert( lex.empty ); + assert_eq( lexerFromString(`-@`).front.str, "-" ); } /// Forward range for reader character by character, /// keeping track of position information and caring \r\n -> \n conversion. Index: polemy/value.d ================================================================== --- polemy/value.d +++ polemy/value.d @@ -10,14 +10,11 @@ /// Raised when something went wrong in runtime class RuntimeException : Exception { - 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; } + mixin ExceptionWithPosition; } /// Runtime values of Polemy abstract class Value @@ -26,27 +23,25 @@ class IntValue : Value { BigInt data; - mixin SimpleConstructor; - mixin SimpleCompare; + mixin SimpleClass; override string toString() const { return std.bigint.toDecimalString(cast(BigInt)data); } } class StrValue : Value { string data; - mixin SimpleConstructor; - mixin SimpleCompare; + mixin SimpleClass; override string toString() const { return data; } } class FunValue : Value { - Value delegate(immutable LexPosition pos, Value[]) data; + Value delegate(immutable LexPosition pos, string lay, Value[]) data; mixin SimpleConstructor; alias data call; override string toString() const { return sprintf!"(function:%s:%s)"(data.ptr,data.funcptr); } } @@ -105,36 +100,36 @@ 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", "@val", new IntValue(BigInt(12))) ); - assert_throw!RuntimeException( c013.get("x", "@val") ); - assert_nothrow( c013.set("x", "@val", new IntValue(BigInt(13))) ); - assert_eq( c013.get("x", "@val"), new IntValue(BigInt(13)) ); - assert_eq( c012.get("x", "@val"), new IntValue(BigInt(12)) ); - assert_throw!RuntimeException( c01.get("x", "@val") ); + 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( 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( c01.set("y", "@val", new IntValue(BigInt(1))) ); - assert_eq( c013.get("y", "@val"), new IntValue(BigInt(1)) ); - assert_eq( c012.get("y", "@val"), new IntValue(BigInt(1)) ); - assert_eq( c01.get("y", "@val"), new IntValue(BigInt(1)) ); + 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( c0.set("z", "@val", new IntValue(BigInt(0))) ); - assert_eq( c013.get("z", "@val"), new IntValue(BigInt(0)) ); - assert_eq( c012.get("z", "@val"), new IntValue(BigInt(0)) ); - assert_eq( c01.get("z", "@val"), new IntValue(BigInt(0)) ); - assert_eq( c0.get("z", "@val"), new IntValue(BigInt(0)) ); + 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("y", "@val", new IntValue(BigInt(444))) ); - assert_eq( c013.get("y", "@val"), new IntValue(BigInt(444)) ); - assert_eq( c012.get("y", "@val"), new IntValue(BigInt(444)) ); - assert_eq( c01.get("y", "@val"), new IntValue(BigInt(444)) ); - - assert_nothrow( c012.set("z", "@val", new IntValue(BigInt(555))) ); - assert_eq( c013.get("z", "@val"), new IntValue(BigInt(0)) ); - assert_eq( c012.get("z", "@val"), new IntValue(BigInt(555)) ); - assert_eq( c01.get("z", "@val"), new IntValue(BigInt(0)) ); - assert_eq( c0.get("z", "@val"), new IntValue(BigInt(0)) ); + 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)) ); // [TODO] define the semantics and test @layers } Index: tricks/test.d ================================================================== --- tricks/test.d +++ tricks/test.d @@ -8,18 +8,25 @@ import std.conv : to; import core.exception; /// Unittest helper that asserts an expression must throw something -void assert_throw(ExceptionType, T, string fn=__FILE__, size_t ln=__LINE__)(lazy T t, string msg="") -{ - try - { t(); } - catch(ExceptionType) - { return; } - catch(Throwable e) - { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception\n >> "~e.toString()); } +void assert_throw(ExceptionType=Throwable, + T, string fn=__FILE__, size_t ln=__LINE__)(lazy T t, string msg="") +{ + static if( is(ExceptionType == Throwable) ) + try + { t(); } + catch(ExceptionType) + { return; } + else + try + { t(); } + catch(ExceptionType) + { return; } + catch(Throwable e) + { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception\n >> "~e.toString()); } onAssertErrorMsg(fn, ln, msg.length ? msg : "not thrown"); } /// Unittest helper that asserts an expression must not throw anything