Index: d2stacktrace/stacktrace.d ================================================================== --- d2stacktrace/stacktrace.d +++ d2stacktrace/stacktrace.d @@ -234,12 +234,17 @@ } } } static Throwable.TraceInfo TraceHandler(void* ptr){ - StackTrace trace = new StackTrace(); - return trace.GetCallstack(); + // modified by k.inaba to avoid a throw inside std.demangle.demangle + // not quite thread safe + Runtime.traceHandler(&core.runtime.defaultTraceHandler); + scope(exit) Runtime.traceHandler(&TraceHandler); + + StackTrace trace = new StackTrace(); + return trace.GetCallstack(); } public: static this(){ Runtime.traceHandler(&TraceHandler); @@ -339,11 +344,11 @@ symString = "_"; symString ~= symName; string demangeledName = demangle(symString); lineStr ~= demangeledName; - + DWORD zeichen = 0; if(Dbghelp.SymGetLineFromAddr64(hProcess,stackframe.AddrPC.Offset,&zeichen,&Line) == TRUE){ char[] fileName = new char[strlen(Line.FileName)]; fileName[] = Line.FileName[0..fileName.length]; lineStr = to!string(fileName ~ "::" ~ to!string(Line.LineNumber) ~ "(" ~ to!string(zeichen) ~ ") " ~ lineStr); @@ -361,6 +366,7 @@ } free(Symbol); return stack; } - }; + }; + Index: polemy/_common.d ================================================================== --- polemy/_common.d +++ polemy/_common.d @@ -8,6 +8,7 @@ public import std.array; public import std.range; public import std.algorithm; public import std.conv : to; public import std.bigint; +public import std.exception; public import polemy.tricks; Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -4,87 +4,90 @@ * * Evaluator for Polemy programming language. */ module polemy.eval; import polemy._common; +import polemy.lex : LexPosition; import polemy.ast; import polemy.parse; import polemy.runtime; +import std.typecons; Context createGlobalContext() { auto ctx = new Context; - ctx.add("+", new PrimitiveFunction(delegate Value(Value[] args){ + ctx.add("+", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 2 ) - throw new PolemyRuntimeException("+ takes two arguments!!"); // TODO improve this message + throw new PolemyRuntimeException("+ takes two arguments!! ["~to!string(pos)~"]"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data+y.data); - throw new PolemyRuntimeException("cannot add non-integers"); // TODO improve this message + throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); })); - ctx.add("-", new PrimitiveFunction(delegate Value(Value[] args){ + ctx.add("-", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 2 ) - throw new PolemyRuntimeException("- takes two arguments!!"); // TODO improve this message + throw new PolemyRuntimeException("- takes two arguments!! ["~to!string(pos)~"]"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data-y.data); - throw new PolemyRuntimeException("cannot add non-integers"); // TODO improve this message + throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); })); - ctx.add("*", new PrimitiveFunction(delegate Value(Value[] args){ + ctx.add("*", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 2 ) - throw new PolemyRuntimeException("* takes two arguments!!"); // TODO improve this message + throw new PolemyRuntimeException("* takes two arguments!! ["~to!string(pos)~"]"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data*y.data); - throw new PolemyRuntimeException("cannot add non-integers"); // TODO improve this message + throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); })); - ctx.add("/", new PrimitiveFunction(delegate Value(Value[] args){ + ctx.add("/", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ if( args.length != 2 ) - throw new PolemyRuntimeException("/ takes two arguments!!"); // TODO improve this message + throw new PolemyRuntimeException("/ takes two arguments!! ["~to!string(pos)~"]"); if( auto x = cast(IntValue)args[0] ) if( auto y = cast(IntValue)args[1] ) return new IntValue(x.data/y.data); - throw new PolemyRuntimeException("cannot add non-integers"); // TODO improve this message + throw new PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]"); })); return ctx; } -Context evalString(T...)(T params) +Tuple!(Value,"val",Context,"ctx") evalString(T...)(T params) { return eval( parserFromString(params).parseProgram() ); } -Context evalFile(T...)(T params) +Tuple!(Value,"val",Context,"ctx") evalFile(T...)(T params) { return eval( parserFromFile(params).parseProgram() ); } -Context eval(Program prog) +Tuple!(Value,"val",Context,"ctx") eval(Program prog) { - return eval(prog, createGlobalContext()); + Context ctx = createGlobalContext(); + return typeof(return)(eval(prog, ctx), ctx); } -Context eval(Program prog, Context ctx) +Value eval(Program prog, Context ctx) { + Value v = new UndefinedValue; foreach(s; prog) - ctx = eval(s, ctx); - return ctx; + v = eval(s, ctx); + return v; } -Context eval(Statement _s, Context ctx) +Value eval(Statement _s, Context ctx) { if( auto s = cast(DeclStatement)_s ) { auto v = eval(s.expr, ctx); ctx.add(s.var, v); - return ctx; + return v; } else if( auto s = cast(ExprStatement)_s ) { - eval(s.expr, ctx); - return ctx; + return eval(s.expr, ctx); } throw new PolemyRuntimeException(sprintf!"Unknown Kind of Statement %s at [%s]"(typeid(_s), _s.pos)); } Value eval(Expression _e, Context ctx) @@ -120,31 +123,51 @@ Value _f = eval(e.fun, ctx); if( auto f = cast(FunValue)_f ) { Value[] args; foreach(a; e.args) args ~= eval(a, ctx); - return f.call(args); + return f.call(e.pos, args); } else throw new PolemyRuntimeException(sprintf!"Non-funcion is applied at [%s]"(e.pos)); + } + else + if( auto e = cast(FunLiteralExpression)_e ) + { + return new FunValue(delegate Value(immutable LexPosition pos, Value[] args){ + if( e.params.length != args.length ) + throw new PolemyRuntimeException(sprintf!"Argument Number Mismatch (%d required but %d given) at [%s]" + (e.params.length, args.length, e.pos)); + Context ctxNeo = new Context(ctx); + foreach(i,p; e.params) + ctxNeo.add(p, args[i]); + return eval(e.funbody, ctxNeo); + }); } throw new PolemyRuntimeException(sprintf!"Unknown Kind of Expression %s at [%s]"(typeid(_e), _e.pos)); } - -version(unittest) import polemy.parse; -version(unittest) import std.stdio; -version(unittest) import std.exception; +import std.stdio; unittest { - auto ctx = evalString(`var x = 21; x = x + x*x;`); - assert( ctx["x"] == new IntValue(BigInt(21+21*21)) ); - assert( !collectException(ctx["x"]) ); - assert( collectException(ctx["y"]) ); + auto r = evalString(`var x = 21; x = x + x*x;`); + assert( r.val == new IntValue(BigInt(21+21*21)) ); + assert( r.ctx["x"] == new IntValue(BigInt(21+21*21)) ); + assert( !collectException(r.ctx["x"]) ); + assert( collectException(r.ctx["y"]) ); } unittest { assert( collectException(evalString(`var x = 21; x = x + x*y;`)) ); + assert( collectException(evalString(`x=1;`)) ); +} +unittest +{ + auto r = evalString(`var x = fun(a){1+a;}(2);`); + assert( r.ctx["x"] == new IntValue(BigInt(3)) ); + assert( r.val == new IntValue(BigInt(3)) ); } unittest { - assert( collectException(evalString(`var x = 21; y = x + x*x;`)) ); + auto r = evalString(`var x = 1; var f = fun(){x=x+1;}; f(); f(); f();`); + assert( r.ctx["x"] == new IntValue(BigInt(4)) ); + assert( r.val == new IntValue(BigInt(4)) ); } Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -190,11 +190,11 @@ if( tryEat("fun") ) { eat("(", "after fun"); string[] params; - for(;;) + while(!tryEat(")")) { if( lex.empty ) { auto e = ParserException.create(lex,"Unexpected EOF"); throw e; } @@ -307,6 +307,11 @@ new FuncallExpression(null, new FunLiteralExpression(null, ["abc"], [ ]), new IntLiteralExpression(null, BigInt(4)) )))); +} +unittest +{ + auto p = parserFromString(`var x = 1; var f = fun(){x=x+1;}; f(); f(); x;`); + Program prog = p.parseProgram(); } Index: polemy/runtime.d ================================================================== --- polemy/runtime.d +++ polemy/runtime.d @@ -4,19 +4,27 @@ * * Runtime data structures for Polemy programming language. */ module polemy.runtime; import polemy._common; +import polemy.lex : LexPosition; +import std.stdio; class PolemyRuntimeException : Exception { this(string msg) { super(msg); } } abstract class Value { } + +class UndefinedValue : Value +{ + mixin SimpleConstructor; + mixin SimpleCompare; +} class IntValue : Value { BigInt data; mixin SimpleConstructor; @@ -28,22 +36,17 @@ string data; mixin SimpleConstructor; mixin SimpleCompare; } -abstract class FunValue : Value +class FunValue : Value { - Value call(Value[] args); + Value delegate(immutable LexPosition pos, Value[]) data; + mixin SimpleConstructor; + Value call(immutable LexPosition pos, Value[] args) { return data(pos,args); } } - -class PrimitiveFunction : FunValue -{ - Value delegate(Value[]) data; - mixin SimpleConstructor; - override Value call(Value[] args) { return data(args); } -} - +import std.stdio; class Context { Context parent; Value[string] table; this(Context parent = null) { this.parent = parent; } Index: polemy/tricks.d ================================================================== --- polemy/tricks.d +++ polemy/tricks.d @@ -28,18 +28,20 @@ /*mixin*/ template SimpleConstructor() { static if( is(typeof(super) == Object) || super.tupleof.length==0 ) this( typeof(this.tupleof) params ) { - this.tupleof = params; + static if(this.tupleof.length>0) + this.tupleof = params; } else // this parameter list is not always desirable but should work for many cases this( typeof(super.tupleof) ps, typeof(this.tupleof) params ) { super(ps); - this.tupleof = params; + static if(this.tupleof.length>0) + this.tupleof = params; } } /// Mixing-in the (MOST-DERIVED) member-wise comparator for a class