Index: d2stacktrace/stacktrace.d ================================================================== --- d2stacktrace/stacktrace.d +++ d2stacktrace/stacktrace.d @@ -355,17 +355,17 @@ DWORD zeichen = 0; if(Dbghelp.SymGetLineFromAddr64(hProcess,stackframe.AddrPC.Offset,&zeichen,&Line) == TRUE){ char[] fileName = new char[strlen(Line.FileName)]; fileName = std.path.basename( Line.FileName[0..fileName.length] ); - lineStr = to!string(fileName ~ "::" ~ to!string(Line.LineNumber) ~ "(" ~ to!string(zeichen) ~ ") " ~ lineStr); + lineStr = text(fileName ~ "::" ~ text(Line.LineNumber) ~ "(" ~ text(zeichen) ~ ") " ~ lineStr); } } else { - lineStr = to!string(cast(ulong)stackframe.AddrPC.Offset); + lineStr = text(cast(ulong)stackframe.AddrPC.Offset); } - lineStr = to!string(frameNum-2) ~ " " ~ lineStr; + lineStr = text(frameNum-2) ~ " " ~ lineStr; if(frameNum-2 < 10) lineStr = "0" ~ lineStr; if(frameNum >= 2) stack.append(lineStr); } Index: polemy/_common.d ================================================================== --- polemy/_common.d +++ polemy/_common.d @@ -1,16 +1,16 @@ /** * Authors: k.inaba * License: NYSL 0.9982 http://www.kmonos.net/nysl/ * - * "Always-opend" modules inside Polemy. + * These modules are globaly used inside Polemy. */ module polemy._common; -public import std.array; -public import std.range; +public import tricks.test; +public import tricks.tricks; public import std.algorithm; -public import std.conv : to; +public import std.array; public import std.bigint; +public import std.conv : text; public import std.exception; -public import tricks.tricks; -public import tricks.test; +public import std.range; public import std.stdio : writeln; // for debugging... Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -11,39 +11,10 @@ import polemy.parse; import polemy.value; import std.typecons; import std.stdio; -// [todo] move to value.d - -FunValue nativef(Value delegate(immutable LexPosition pos, Layer lay, Value[] args) dg) -{ - return new FunValue(dg); -} - -FunValue native(R,T...)(R delegate (T) dg) -{ - return nativef( delegate Value(immutable LexPosition pos, Layer lay, Value[] args) { - if( lay != "@v" ) - throw genex!RuntimeException(pos, "only @v layer can call native function"); - if( T.length != args.length ) - throw genex!RuntimeException(pos, "argument number mismatch!"); - T typed_args; - foreach(i, Ti; T) - { - typed_args[i] = cast(Ti) args[i]; - if( typed_args[i] is null ) - throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1)); - } - try { - return dg(typed_args); - } catch( RuntimeException e ) { - throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e; - } - }); -} - /// Table createGlobalContext() { auto ctx = new Table; ctx.set("+", "@v", native( (IntValue lhs, IntValue rhs){return new IntValue(lhs.data + rhs.data);} )); @@ -76,10 +47,25 @@ })); 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(".?", "@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){ + auto t2 = new Table(t, Table.Kind.NotPropagateSet); + t2.set(s.data, "@v", v); + return t2; + }) ); + ctx.set("{}", "@v", native( (){ + return new Table; + }) ); return ctx; } /// Entry point of this module @@ -259,6 +245,6 @@ } unittest { assert_eq( evalString(`@@t = fun(x){x+1}; @t(123)`).val, new IntValue(BigInt(124)) ); -} +} Index: polemy/lex.d ================================================================== --- polemy/lex.d +++ polemy/lex.d @@ -52,11 +52,11 @@ auto p = new LexPosition("hello.cpp", 123, 45); assert_eq( p.filename, "hello.cpp" ); assert_eq( p.lineno, 123 ); assert_eq( p.column, 45 ); - assert_eq( to!string(p), "hello.cpp:123:45" ); + assert_eq( text(p), "hello.cpp:123:45" ); assert( !__traits(compiles, new LexPosition) ); assert( !__traits(compiles, p.filename="foo") ); assert( !__traits(compiles, p.lineno =789) ); assert( !__traits(compiles, p.column =222) ); @@ -413,11 +413,11 @@ buffer.popFront; c = '\n'; } if( c=='\n' ) { - lineno ++; + lineno ++; column = 1; } else column ++; } Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -165,11 +165,12 @@ ["&"], ["<<", ">>"], ["+","-"], ["~"], ["*","/","%"], - ["^^","**"] + ["^^","**"], + [".",".?"] ]; AST E(size_t level) { /// Expression ::= (Binary left-associative operators over) Funcall @@ -180,10 +181,14 @@ return lhs; auto pos = currentPosition(); foreach(op; operator_perferences[level]) if( tryEat(op) ) + if( op[0]=='.' ) + return rec( + new FuncallExpression(lhs.pos, new VarExpression(pos, op), lhs, parseId())); + else return rec( new FuncallExpression(lhs.pos, new VarExpression(pos, op), lhs, E(level+1))); return lhs; } @@ -243,10 +248,30 @@ if( tryEat("(") ) { auto e = Body(); eat(")", "after parenthesized expression"); return e; + } + if( tryEat("{") ) + { + AST e = new FuncallExpression(pos, new VarExpression(pos,"{}")); + if( tryEat("}") ) + return e; + for(;;) + { + string key = eatId("for table key", AllowQuoted); + eat(":", "after table key"); + AST val = E(0); + e = new FuncallExpression(pos, new VarExpression(pos,".="), + e, new StrLiteral(pos,key), val); + if( !tryEat(",") ) + { + eat("}", "for the end of table literal"); + break; + } + } + return e; } if( tryEat("if") ) { eat("(", "after if"); auto cond = E(0); @@ -275,10 +300,16 @@ return parseLambdaAfterOpenParen(pos); } scope(exit) lex.popFront; return new VarExpression(pos, lex.front.str); } + + AST parseId() + { + scope(exit) lex.popFront; + return new StrLiteral(currentPosition(), lex.front.str); + } AST parseLambdaAfterOpenParen(immutable LexPosition pos) { Parameter[] params; while( !tryEat(")") ) @@ -436,6 +467,12 @@ var("foo")) ); assert_eq(parseString(`@@type ( x ) { x }`), let("@type", "(system)", 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"))); } Index: polemy/value.d ================================================================== --- polemy/value.d +++ polemy/value.d @@ -19,40 +19,75 @@ abstract class Value { } +/// class IntValue : Value { BigInt data; mixin SimpleClass; override string toString() const { return std.bigint.toDecimalString(cast(BigInt)data); } } +/// class StrValue : Value { string data; mixin SimpleClass; override string toString() const { return data; } } +/// class FunValue : Value { 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); } } +/// class UndValue : Value { mixin SimpleClass; override string toString() const { return ""; } } + +/// Named Constructor for FunValue + +FunValue nativef(Value delegate(immutable LexPosition pos, Layer lay, Value[] args) dg) +{ + return new FunValue(dg); +} + +/// Named Constructor for FunValue + +FunValue native(R,T...)(R delegate (T) dg) +{ + return nativef( delegate Value(immutable LexPosition pos, Layer lay, Value[] args) { + if( lay != "@v" ) + throw genex!RuntimeException(pos, "only @v layer can call native function"); + if( T.length != args.length ) + throw genex!RuntimeException(pos, "argument number mismatch!"); + T typed_args; + foreach(i, Ti; T) + { + typed_args[i] = cast(Ti) args[i]; + if( typed_args[i] is null ) + throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1)); + } + try { + return dg(typed_args); + } catch( RuntimeException e ) { + throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e; + } + }); +} /// Layer ID alias string Layer; @@ -72,13 +107,26 @@ if( setIfExist(i, lay, v) ) return; data[i][lay] = v; } + bool has(string i, Layer lay, in LexPosition pos=null) + { + if( i in data ) { + if( lay !in data[i] ) + return false; + return true; + } + if( prototype is null ) + return false; + return prototype.has(i, lay, pos); + } + Value get(string i, Layer lay, in LexPosition pos=null) { if( i in data ) { + // [TODO] consider forwarding to proto also in this case if( lay !in data[i] ) throw genex!RuntimeException(pos, sprintf!"variable %s is not set in layer %s"(i,lay)); return data[i][lay]; } if( prototype is null ) Index: readme.txt ================================================================== --- readme.txt +++ readme.txt @@ -1,18 +1,18 @@ ----------------------------------------------------------------------------- Polemy 0.1.0 by k.inaba (www.kmonos.net) - Nov 8, 2010 + Nov 20, 2010 ----------------------------------------------------------------------------- <> - Install DMD http://www.digitalmars.com/d/2.0/changelog.html - Version 2.050 is recommended. Older and/or newer version may not work. + Version 2.050 is recommended. Older or newer version may not work. - Build (for Windows) Run build.bat (for Unix) Run build.sh or use your favorite build tools upon main.d and polemy/*.d. @@ -42,35 +42,90 @@ <> > polemy starts REPL - > polemy foo.pmy - executes foo.pmy - + > polemy foo.pmy + executes foo.pmy + + > polemy -l foo.pmy + after executing foo.pmy, starts REPL + -<> - -syntax - - E ::= ("var"|"let"|"def"|LAYER) ID "=" E ; E - | "fun" "(" PARAMS ")" "{" E "}" - | E "(" ARGS ")" +<> + + Comment is "# ... \n" - | LAYER "(" E ")" - - | "(" E ")" - | E BINOP E - | "if" "(" E ")" "{" E "}" - | "if" "(" E ")" "{" E "}" "else "{" E "}" - | ("var"|"let"|"def"|LAYER) ID "(" PARAMS ")" "{" E "}" - -ARGS ::= ","-separated E's -PARAMS ::= ","-separated VAR's -LAYER ::= "@" ID - -if-then-else is a syntax sugar for a function call: if( E, fun(){E}, fun(){E} ) -binary ops (e.g., E + E) is a syntax sugar: +(E, E) - -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 "}" + // 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 ")" + +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) + - 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 Index: tricks/test.d ================================================================== --- tricks/test.d +++ tricks/test.d @@ -1,50 +1,59 @@ /** * Authors: k.inaba * License: NYSL 0.9982 http://www.kmonos.net/nysl/ * - * Unittest helpers. - * TODO: use stderr instead of stdout. the problem is that std.cstream is xxxx.... - * TODO: is there any way to clearnly implement "assert_compiles" and "assert_not_compile"? + * Hepler routines for unittesting. + * TODO: Is there any clean way to implement "assert_compiles" and "assert_not_compile"? */ module tricks.test; -import std.conv : to; +import std.conv : text; import core.exception; version(unittest) { - import std.stdio; + import std.cstream; import core.runtime; - // Install custom test runner static this() { + installCustomTestRunner(); + } + + private void installCustomTestRunner() + { Runtime.moduleUnitTester = function() { - Throwable ee = null; - size_t failed=0; - foreach(m; ModuleInfo) if(m) if(auto fp=m.unitTest) + Throwable firstError = null; + + void logError(Throwable e) + { + if(firstError is null) + firstError = e; + derr.writefln(" !! %s(%d): %s", e.file, e.line, e.msg); + } + + void testModule(ModuleInfo* m, void function() test) { - writeln("[TEST] ", m.name); - try { - fp(); - } catch( Throwable e ) { - if(ee is null) - ee = e; - failed++; - writeln(" !! ",e.file,"(",e.line,"): ",e.msg); - } + derr.writefln("[TEST] %s", m.name); + try { test(); } catch( Throwable e ) { logError(e); } } - if(ee !is null) + + bool report() { - writeln("[TEST] ",failed," modules failed. The first error was:"); - writeln(ee); - write("[TEST] press enter to exit."); - readln(); + if(firstError is null) + return true; + derr.writefln("[TEST] The first error was:\n%s", firstError); + derr.writeString("[TEST] press enter to exit."); + din.readLine(); return false; } - return true; + + foreach(m; ModuleInfo) + if(m && m.unitTest) + testModule(m, m.unitTest); + return report(); }; } } /// Unittest helper that asserts an expression must throw something @@ -99,11 +108,11 @@ { try { if( mixin("a"~op~"b") ) return; } catch(Throwable e) { onAssertErrorMsg(fn, ln, msg.length ? msg : "bad exception \n >> "~e.toString()); } - onAssertErrorMsg(fn, ln, msg.length ? msg : to!string(a)~" !"~op~" "~to!string(b)); + onAssertErrorMsg(fn, ln, msg.length ? msg : text(a, " !", op, " ", b)); } } alias assertOp!(`==`) assert_eq; /// asserts two operands are == alias assertOp!(`!=`) assert_ne; /// asserts two operands are != Index: tricks/tricks.d ================================================================== --- tricks/tricks.d +++ tricks/tricks.d @@ -4,13 +4,13 @@ * * Common tricks and utilities for programming in D. */ module tricks.tricks; import tricks.test; -import std.array : appender; -import std.format : formattedWrite; -import core.exception; +import core.exception; +import std.array : appender; +import std.format : formattedWrite; import std.traits; import std.typetuple; /// Simple Wrapper for std.format.doFormat @@ -108,11 +108,11 @@ override bool opEquals(Object rhs_) const /// member-by-member equality { if( auto rhs = cast(typeof(this))rhs_ ) { foreach(i,_; this.tupleof) - if( this.tupleof[i] != rhs.tupleof[i] ) + if( this.tupleof[i] != (cast(const)rhs).tupleof[i] ) return false; return true; } assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); } @@ -128,15 +128,16 @@ override int opCmp(Object rhs_) const /// member-by-member compare { if( auto rhs = cast(typeof(this))rhs_ ) { foreach(i,_; this.tupleof) - if( this.tupleof[i] != rhs.tupleof[i] ) { - auto a = this.tupleof[i]; + if( this.tupleof[i] != (cast(const)rhs).tupleof[i] ) { + auto a = (cast(SC_Unqual!(typeof(this)))this).tupleof[i]; auto b = rhs.tupleof[i]; - return cast(SC_Unqual!(typeof(a)))a < b ? -1 : +1; - } + return a < b ? -1 : +1; + } +// not work well for structures??????? // if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i])) // return c; return 0; } assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); @@ -321,11 +322,11 @@ class D2 : Base { string s; mixin SimpleConstructor; } class D3 : Base { - int[int]m; + int[int] m; mixin SimpleConstructor; } Base d1 = new D1(1, 2.3); Base d2 = new D2("foobar"); @@ -332,62 +333,62 @@ Base d3 = new D3(null); (cast(D3)d3).m[1]=10; // normal dispatch assert_eq( d1.match( (D1 x){return 1;}, - (D2 x){return 2;}, + (D2 x){return 2;} ), 1); assert_eq( d2.match( (D1 x){return 1;}, - (D2 x){return 2;}, + (D2 x){return 2;} ), 2); assert_throw!AssertError( d3.match( (D1 x){return 1;}, - (D2 x){return 2;}, + (D2 x){return 2;} )); assert_eq( d3.match( (D1 x){return 1;}, (D2 x){return 2;}, - (Base x){return 3;}, + (Base x){return 3;} ), 3); assert_eq( d2.match( (D1 x){return 1;}, (D2 x){return 2;}, - (Base x){return 3;}, + (Base x){return 3;} ), 2); assert_eq( d2.match( (D1 x){return 1;}, (Base x){return 3;}, - (D2 x){return 2;}, + (D2 x){return 2;} ), 3); // member decomposing match assert_eq( d1.match( when!(D1, (x, y){return x + cast(int)y;}), when!(D2, (x){return x.length;}), - when!(D3, (x){return x[1];}), + when!(D3, (x){return x[1];}) ), 3); assert_eq( d2.match( when!(D1, (x, y){return x + cast(int)y;}), when!(D2, (x){return x.length;}), - when!(D3, (x){return x[1];}), + when!(D3, (x){return x[1];}) ), 6); assert_eq( d3.match( when!(D1, (x, y){return x + cast(int)y;}), when!(D2, (x){return x.length;}), - when!(D3, (x){return x[1];}), + when!(D3, (x){return x[1];}) ), 10); assert_throw!AssertError( d3.match( when!(D1, (x, y){return x + cast(int)y;}), - when!(D2, (x){return x.length;}), + when!(D2, (x){return x.length;}) )); assert_eq( d2.match( when!(D1, (x, y){return x + cast(int)y;}), when!(D2, (x){return x.length;}), - otherwise!({return 999;}), + otherwise!({return 999;}) ), 6); assert_eq( d2.match( when!(D1, (x, y){return x + cast(int)y;}), otherwise!({return 999;}), - when!(D2, (x){return x.length;}), + when!(D2, (x){return x.length;}) ), 999); }