Index: .poseidon
==================================================================
--- .poseidon
+++ .poseidon
@@ -31,14 +31,16 @@
d2stacktrace\dbghelp.d
d2stacktrace\stacktrace.d
main.d
polemy\_common.d
polemy\ast.d
+ polemy\eval.d
polemy\lex.d
polemy\parse.d
- polemy\tricks.d
polemy\value.d
+ tricks\test.d
+ tricks\tricks.d
Index: build.bat
==================================================================
--- build.bat
+++ build.bat
@@ -1,6 +1,6 @@
@setlocal ENABLEDELAYEDEXPANSION
@set ARGS=
-@for %%I in (main.d polemy\*.d) do @set ARGS=!ARGS! %%I
+@for %%I in (main.d polemy\*.d tricks\*.d) do @set ARGS=!ARGS! %%I
@if not exist bin mkdir bin
@echo dmd -ofbin\polemy.exe -O -release -inline %ARGS%
@dmd -ofbin\polemy.exe -O -release -inline %ARGS%
Index: d2stacktrace/stacktrace.d
==================================================================
--- d2stacktrace/stacktrace.d
+++ d2stacktrace/stacktrace.d
@@ -238,11 +238,11 @@
static Throwable.TraceInfo TraceHandler(void* ptr){
// 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:
@@ -341,11 +341,11 @@
memcpy(symName.ptr,Symbol.Name.ptr,symName.length);
string symString = "";
if(symName[0] == 'D')
symString = "_";
symString ~= symName;
-
+
string demangeledName = demangle(symString);
lineStr ~= demangeledName;
DWORD zeichen = 0;
if(Dbghelp.SymGetLineFromAddr64(hProcess,stackframe.AddrPC.Offset,&zeichen,&Line) == TRUE){
Index: polemy/_common.d
==================================================================
--- polemy/_common.d
+++ polemy/_common.d
@@ -9,6 +9,7 @@
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;
+public import tricks.tricks;
+public import tricks.test;
Index: polemy/ast.d
==================================================================
--- polemy/ast.d
+++ polemy/ast.d
@@ -1,6 +1,6 @@
-/**
+/**
* Authors: k.inaba
* License: NYSL 0.9982 http://www.kmonos.net/nysl/
*
* Syntax tree for Polemy programming language.
*/
Index: polemy/eval.d
==================================================================
--- polemy/eval.d
+++ polemy/eval.d
@@ -1,6 +1,6 @@
-/**
+/**
* Authors: k.inaba
* License: NYSL 0.9982 http://www.kmonos.net/nysl/
*
* Evaluator for Polemy programming language.
*/
@@ -11,146 +11,123 @@
import polemy.parse;
import polemy.value;
import std.typecons;
import std.stdio;
-Context createGlobalContext()
-{
- auto ctx = new Context;
- ctx.add("+", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
+Table createGlobalContext()
+{
+ auto ctx = new Table;
+ // [TODO] autogenerate these typechecks
+ ctx.set("+", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
if( args.length != 2 )
- throw new PolemyRuntimeException("+ takes two arguments!! ["~to!string(pos)~"]");
+ throw new 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 PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
+ throw new RuntimeException(pos, "cannot add non-integers");
}));
- ctx.add("-", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
+ ctx.set("-", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
if( args.length != 2 )
- throw new PolemyRuntimeException("- takes two arguments!! ["~to!string(pos)~"]");
+ throw new 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 PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
+ throw new RuntimeException(pos, "cannot subtract non-integers");
}));
- ctx.add("*", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
+ ctx.set("*", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
if( args.length != 2 )
- throw new PolemyRuntimeException("* takes two arguments!! ["~to!string(pos)~"]");
+ throw new 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 PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
+ throw new RuntimeException(pos, "cannot multiply non-integers");
}));
- ctx.add("/", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
+ ctx.set("/", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
if( args.length != 2 )
- throw new PolemyRuntimeException("/ takes two arguments!! ["~to!string(pos)~"]");
+ throw new 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 PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
+ throw new RuntimeException(pos, "cannot divide non-integers");
}));
- ctx.add("<", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
+ ctx.set("<", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
if( args.length != 2 )
- throw new PolemyRuntimeException("< takes two arguments!! ["~to!string(pos)~"]");
+ throw new 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 PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
+ throw new RuntimeException(pos, "cannot compare non-integers");
}));
- ctx.add(">", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
+ ctx.set(">", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
if( args.length != 2 )
- throw new PolemyRuntimeException("> takes two arguments!! ["~to!string(pos)~"]");
+ throw new 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 PolemyRuntimeException("cannot add non-integers ["~to!string(pos)~"]");
+ throw new RuntimeException(pos, "cannot compare non-integers");
}));
- ctx.add("print", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
+ ctx.set("print", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
foreach(a; args)
write(a);
writeln("");
- return new UndefinedValue;
+ return new IntValue(BigInt(178));
}));
- ctx.add("if", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
+ ctx.set("if", "@val", new FunValue(delegate Value(immutable LexPosition pos, Value[] args){
if( args.length != 3 )
- throw new PolemyRuntimeException("if takes three arguments!! ["~to!string(pos)~"]");
+ throw new 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 PolemyRuntimeException("type mismatch in if ["~to!string(pos)~"]");
+ throw new RuntimeException(pos, "type mismatch in if");
}));
return ctx;
}
-Tuple!(Value,"val",Context,"ctx") evalString(T...)(T params)
+/// Entry point of this module
+
+Tuple!(Value,"val",Table,"ctx") evalString(S,T...)(S str, T fn_ln_cn)
{
- return eval( parserFromString(params).parseProgram() );
+ return eval( polemy.parse.parseString(str, fn_ln_cn) );
}
-Tuple!(Value,"val",Context,"ctx") evalFile(T...)(T params)
+Tuple!(Value,"val",Table,"ctx") evalFile(S, T...)(S filenae, T ln_cn)
{
- return eval( parserFromFile(params).parseProgram() );
+ return eval( polemy.parse.parseFile(filename, ln_cn) );
}
-Tuple!(Value,"val",Context,"ctx") eval(Program prog)
+Tuple!(Value,"val",Table,"ctx") eval(AST e)
{
- Context ctx = createGlobalContext();
- return typeof(return)(eval(prog, ctx), ctx);
+ Table ctx = createGlobalContext();
+ return typeof(return)(eval(e, ctx), ctx);
}
-Value eval(Program prog, Context ctx)
-{
- Value v = new UndefinedValue;
- foreach(s; prog)
- v = eval(s, ctx);
- return v;
-}
-
-Value eval(Statement _s, Context ctx)
+Value eval(AST _e, Table ctx, bool splitCtx = true)
{
- if( auto s = cast(DeclStatement)_s )
- {
- auto v = eval(s.expr, ctx);
- ctx.add(s.var, v);
- return v;
- }
- else
- if( auto s = cast(ExprStatement)_s )
- {
- 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)
-{
- if( auto e = cast(StrLiteralExpression)_e )
+ if( auto e = cast(StrLiteral)_e )
{
return new StrValue(e.data);
}
else
- if( auto e = cast(IntLiteralExpression)_e )
+ if( auto e = cast(IntLiteral)_e )
{
return new IntValue(e.data);
}
- else
- if( auto e = cast(VarExpression)_e )
- {
- return ctx[e.var];
- }
- else
- if( auto e = cast(AssignExpression)_e )
- {
- if( auto ev = cast(VarExpression)e.lhs )
- {
- Value r = eval(e.rhs, ctx);
- ctx[ev.var] = r;
- return r;
- }
- throw new PolemyRuntimeException(sprintf!"Lhs of assignment must be a variable: %s"(e.pos));
- }
+ else
+ if( auto e = cast(VarExpression)_e )
+ {
+ return ctx.get(e.var, "@val", e.pos);
+ }
+ 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, "@val", v, e.pos);
+ return eval(e.expr, ctx);
+ }
else
if( auto e = cast(FuncallExpression)_e )
{
Value _f = eval(e.fun, ctx);
if( auto f = cast(FunValue)_f ) {
@@ -157,74 +134,65 @@
Value[] args;
foreach(a; e.args)
args ~= eval(a, ctx);
return f.call(e.pos, args);
} else
- throw new PolemyRuntimeException(sprintf!"Non-funcion is applied at [%s]"(e.pos));
+ throw new RuntimeException(e.pos, "Non-funcion is applied");
}
else
- if( auto e = cast(FunLiteralExpression)_e )
+ if( auto e = cast(FunLiteral)_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);
+ throw new 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.add(p, args[i]);
+ ctxNeo.set(p, "@val", args[i]);
return eval(e.funbody, ctxNeo);
});
}
- throw new PolemyRuntimeException(sprintf!"Unknown Kind of Expression %s at [%s]"(typeid(_e), _e.pos));
+ throw new RuntimeException(_e.pos, sprintf!"Unknown Kind of Expression %s"(typeid(_e)));
}
-
+
unittest
-{
- 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"]) );
+{
+ 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") );
}
+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") );
+}
unittest
{
- assert( collectException(evalString(`var x = 21; x = x + x*y;`)) );
- assert( collectException(evalString(`x=1;`)) );
+ assert_nothrow( evalString(`print("Hello, world!");`) );
+ assert_nothrow( evalString(`print(fun(){});`) );
}
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
-{
- 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)) );
-}
-unittest
-{
- evalString(`print("Hello, world!");`);
- evalString(`print(fun(){});`);
-}
-unittest
-{
- evalString(`var fac = fun(x){
+ assert_nothrow( evalString(`var fac = fun(x){
1;
};
- print(fac(3));`);
- evalString(`var fac = fun(x){
+ print(fac(3));`));
+ assert_nothrow( evalString(`var fac = fun(x){
if(x)
{ x*fac(x-1); }
else
{ 1; };
};
- print(fac(10));`);
- evalString(`var fib = fun(x){
+ print(fac(10));`));
+ assert_nothrow( evalString(`var fib = fun(x){
if(x<2)
{ 1; }
else
{ fib(x-1) + fib(x-2); };
};
- print(fib(10));`);
+ print(fib(10));`));
}
Index: polemy/lex.d
==================================================================
--- polemy/lex.d
+++ polemy/lex.d
@@ -13,27 +13,25 @@
class LexException : Exception
{
const LexPosition pos;
- private this( const LexPosition pos, string msg )
- { super(sprintf!"%s [%s]"(msg, pos)); this.pos = pos; }
-};
-
+ this( const LexPosition pos, string msg, string file="", int line=0 )
+ { super(sprintf!"[%s] %s"(pos, msg), file, line); this.pos = pos; }
+};
+
/// Represents a position in a source code
class LexPosition
{
immutable string filename; /// name of the source file
immutable int lineno; /// 1-origin
immutable int column; /// 1-origin
-
+
+ mixin SimpleClass;
override string toString() const
{ return sprintf!"%s:%d:%d"(filename, lineno, column); }
-
- mixin SimpleConstructor;
- mixin SimpleCompare;
static immutable LexPosition dummy;
static this(){ dummy = new immutable(LexPosition)("",0,0); }
}
@@ -61,13 +59,11 @@
{
immutable LexPosition pos; /// Position where the token occurred in the source
immutable string str; /// The token string itself
immutable bool quoted; /// Was it a "quoted" token or unquoted?
- mixin SimpleConstructor;
- mixin SimpleCompare;
- mixin SimpleToString;
+ mixin SimpleClass;
}
unittest
{
auto p = new immutable(LexPosition)("hello.cpp", 123, 45);
Index: polemy/parse.d
==================================================================
--- polemy/parse.d
+++ polemy/parse.d
@@ -20,11 +20,11 @@
}
private auto createException(Lexer)(Lexer lex, string msg)
{ return new ParseException(lex.empty?null:lex.front.pos, msg); }
-/// Entry point of this module
+/// Entry points of this module
auto parseString(S, T...)(S str, T fn_ln_cn)
{ return parserFromString(str, fn_ln_cn).parse(); }
auto parseFile(S, T...)(S filename,T ln_cn)
DELETED polemy/tricks.d
Index: polemy/tricks.d
==================================================================
--- polemy/tricks.d
+++ polemy/tricks.d
@@ -1,283 +0,0 @@
-/**
- * Authors: k.inaba
- * License: NYSL 0.9982 http://www.kmonos.net/nysl/
- *
- * Common tricks and utilities for programming in D.
- */
-module polemy.tricks;
-import std.array : appender;
-import std.format : formattedWrite;
-import core.exception : onAssertErrorMsg, AssertError;
-
-/// Simple Wrapper for std.format.doFormat
-
-string sprintf(string fmt, T...)(T params)
-{
- auto writer = appender!string();
- formattedWrite(writer, fmt, params);
- return writer.data;
-}
-
-unittest
-{
- assert( sprintf!"%s == %d"("1+2", 3) == "1+2 == 3" );
- assert( sprintf!"%s == %04d"("1+2", 3) == "1+2 == 0003" );
-}
-
-/// Unittest helper that asserts an expression must throw something
-
-void assert_throw(ExceptionType, T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="")
-{
- try {
- t();
- } catch(ExceptionType) {
- return;
- } catch(Throwable e) {
- onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e));
- }
- onAssertErrorMsg(fn, ln, msg.length ? msg : "no execption");
-}
-
-/// Unittest helper that asserts an expression must not throw anything
-
-void assert_nothrow(T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="")
-{
- try {
- t();
- } catch(Throwable e) {
- onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e));
- }
-}
-
-unittest
-{
- auto error = {throw new Error("hello");};
- auto nothing = (){};
- auto assertError = {assert(0);};
-
- assert_nothrow ( assert_nothrow(nothing()) );
- assert_throw!AssertError( assert_nothrow(error()) );
- assert_throw!AssertError( assert_nothrow(assertError()) );
-
- assert_nothrow ( assert_throw!Error(error()) );
- assert_throw!AssertError( assert_throw!Error(nothing()) );
- assert_nothrow ( assert_throw!Error(assertError()) );
- assert_throw!AssertError( assert_throw!AssertError(error()) );
-}
-
-/// Unittest helpers asserting two values are in some relation ==, !=, <, <=, >, >=
-
-template assertOp(string op)
-{
- void assertOp(A, B, string fn=__FILE__, int ln=__LINE__)(A a, B b, string msg="")
- {
- try {
- if( mixin("a"~op~"b") ) return;
- } catch(Throwable e) {
- onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e));
- }
- onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"%s !%s %s"(a,op,b));
- }
-}
-
-alias assertOp!(`==`) assert_eq;
-alias assertOp!(`!=`) assert_ne;
-alias assertOp!(`<`) assert_lt;
-alias assertOp!(`<=`) assert_le;
-alias assertOp!(`>`) assert_gt;
-alias assertOp!(`>=`) assert_ge;
-
-unittest
-{
- assert_nothrow( assert_eq(1, 1) );
- assert_nothrow( assert_ne(1, 0) );
- assert_nothrow( assert_lt(0, 1) );
- assert_nothrow( assert_le(0, 1) );
- assert_nothrow( assert_le(0, 0) );
- assert_nothrow( assert_gt(1, 0) );
- assert_nothrow( assert_ge(1, 0) );
- assert_nothrow( assert_ge(0, 0) );
-
- assert_throw!AssertError( assert_eq(1, 0) );
- assert_throw!AssertError( assert_ne(1, 1) );
- assert_throw!AssertError( assert_lt(1, 1) );
- assert_throw!AssertError( assert_lt(1, 0) );
- assert_throw!AssertError( assert_le(1, 0) );
- assert_throw!AssertError( assert_gt(0, 0) );
- assert_throw!AssertError( assert_gt(0, 1) );
- assert_throw!AssertError( assert_ge(0, 1) );
-
- class Temp { bool opEquals(int x){return x/x==x;} }
- assert_throw!AssertError( assert_eq(new Temp, 0) );
- assert_nothrow ( assert_eq(new Temp, 1) );
- assert_throw!AssertError( assert_eq(new Temp, 2) );
-}
-
-/* [Todo] is there any way to clearnly implement "assert_compiles" and "assert_not_compile"? */
-
-/// Mixing-in the bean constructor for a class
-
-/*mixin*/
-template SimpleConstructor()
-{
- static if( is(typeof(super) == Object) || super.tupleof.length==0 )
- this( typeof(this.tupleof) params )
- {
- static if(this.tupleof.length>0)
- this.tupleof = params;
- }
- else
- this( typeof(super.tupleof) ps, typeof(this.tupleof) params )
- {
- // including (only) the direct super class members
- // may not always be a desirable choice, but should work for many cases
- super(ps);
- static if(this.tupleof.length>0)
- this.tupleof = params;
- }
-}
-
-unittest
-{
- class Temp
- {
- int x;
- string y;
- mixin SimpleConstructor;
- }
- assert_eq( (new Temp(1,"foo")).x, 1 );
- assert_eq( (new Temp(1,"foo")).y, "foo" );
- assert( !__traits(compiles, new Temp) );
- assert( !__traits(compiles, new Temp(1)) );
- assert( !__traits(compiles, new Temp("foo",1)) );
-
- class Tomp : Temp
- {
- real z;
- mixin SimpleConstructor;
- }
- assert_eq( (new Tomp(1,"foo",2.5)).x, 1 );
- assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" );
- assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 );
- assert( !__traits(compiles, new Tomp(3.14)) );
-
- // shiyo- desu. Don't use in this way.
- // Tamp tries to call new Tomp(real) (because it only sees Tomp's members),
- // but it fails because Tomp takes (int,string,real).
- assert( !__traits(compiles, {
- class Tamp : Tomp
- {
- mixin SimpleConstructor;
- }
- }) );
-}
-
-/// Mixing-in the MOST-DERIVED-member-wise comparator for a class
-
-/*mixin*/
-template SimpleCompare()
-{
- override bool opEquals(Object rhs_) const
- {
- if( auto rhs = cast(typeof(this))rhs_ )
- {
- foreach(i,_; this.tupleof)
- if( this.tupleof[i] != rhs.tupleof[i] )
- return false;
- return true;
- }
- assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
- }
-
- override hash_t toHash() const
- {
- hash_t h = 0;
- foreach(mem; this.tupleof)
- h += typeid(mem).getHash(&mem);
- return h;
- }
-
- override int opCmp(Object rhs_) const
- {
- if( auto rhs = cast(typeof(this))rhs_ )
- {
- foreach(i,_; this.tupleof)
- 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_)));
- }
-}
-
-unittest
-{
- class Temp
- {
- int x;
- string y;
- mixin SimpleConstructor;
- mixin SimpleCompare;
- }
- assert_eq( new Temp(1,"foo"), new Temp(1,"foo") );
- assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash );
- assert_ne( new Temp(1,"foo"), new Temp(2,"foo") );
- assert_ne( new Temp(1,"foo"), new Temp(1,"bar") );
- assert_gt( new Temp(1,"foo"), new Temp(1,"bar") );
- assert_lt( new Temp(1,"foo"), new Temp(2,"bar") );
- assert_ge( new Temp(1,"foo"), new Temp(1,"foo") );
-
- class TempDummy
- {
- int x;
- string y;
- mixin SimpleConstructor;
- mixin SimpleCompare;
- }
- assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") );
- assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") );
-}
-
-/// Mixing-in a simple toString method
-
-/*mixin*/
-template SimpleToString()
-{
- override string toString()
- {
- string str = sprintf!"%s("(typeof(this).stringof);
- foreach(i,mem; this.tupleof)
- {
- if(i) str ~= ",";
- static if( is(typeof(mem) == std.bigint.BigInt) )
- str ~= std.bigint.toDecimalString(mem);
- else
- str ~= sprintf!"%s"(mem);
- }
- return str ~ ")";
- }
-}
-
-version(unittest) import std.bigint;
-unittest
-{
- class Temp
- {
- int x;
- string y;
- BigInt z;
- mixin SimpleConstructor;
- mixin SimpleToString;
- }
- assert_eq( (new Temp(1,"foo",BigInt(42))).toString(), "Temp(1,foo,42)" );
-}
-
-/// Everything is in
-
-/*mixin*/
-template SimpleClass()
-{
- mixin SimpleConstructor;
- mixin SimpleCompare;
- mixin SimpleToString;
-}
Index: polemy/value.d
==================================================================
--- polemy/value.d
+++ polemy/value.d
@@ -1,6 +1,6 @@
-/**
+/**
* Authors: k.inaba
* License: NYSL 0.9982 http://www.kmonos.net/nysl/
*
* Runtime data structures for Polemy programming language.
*/
@@ -13,12 +13,11 @@
class RuntimeException : Exception
{
const LexPosition pos;
this( const LexPosition pos, string msg )
- { super(sprintf!"%s [%s]"(msg, pos)); this.pos = pos; }
- this( string msg ) { super(msg); this.pos = null; }
+ { super(sprintf!"%s at [%s]"(msg, pos)); this.pos = pos; }
}
/// Runtime values of Polemy
abstract class Value
@@ -65,22 +64,23 @@
enum Kind {PropagateSet, NotPropagateSet};
this( Table proto=null, Kind k = Kind.PropagateSet )
{ this.prototype = proto; this.kind = k; }
- void set(string i, Layer lay, Value v)
+ void set(string i, Layer lay, Value v, in LexPosition pos=null)
{
- if( !setIfExist(i, lay, v) )
- data[i][lay] = v;
+ if( setIfExist(i, lay, v) )
+ return;
+ data[i][lay] = v;
}
- Value get(string i, Layer lay)
+ Value get(string i, Layer lay, in LexPosition pos=null)
{
if( i in data )
return data[i][lay];
if( prototype is null )
- throw new RuntimeException(sprintf!"variable %s not found"(i));
+ throw new RuntimeException(pos, sprintf!"variable %s not found"(i));
return prototype.get(i, lay);
}
private:
Table prototype;
ADDED tricks/test.d
Index: tricks/test.d
==================================================================
--- tricks/test.d
+++ tricks/test.d
@@ -0,0 +1,98 @@
+/**
+ * Authors: k.inaba
+ * License: NYSL 0.9982 http://www.kmonos.net/nysl/
+ *
+ * Unittest helpers.
+ */
+module tricks.test;
+import std.conv : to;
+import core.exception;
+
+/// Unittest helper that asserts an expression must throw something
+
+void assert_throw(ExceptionType, T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="")
+{
+ try
+ { t(); }
+ catch(ExceptionType)
+ { return; }
+ catch(Throwable e)
+ { onAssertErrorMsg(fn, ln, msg.length ? msg : "exception ["~e.toString()~"]"); }
+ onAssertErrorMsg(fn, ln, msg.length ? msg : "no execption");
+}
+
+/// Unittest helper that asserts an expression must not throw anything
+
+auto assert_nothrow(T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="")
+{
+ try
+ { return t(); }
+ catch(Throwable e)
+ { onAssertErrorMsg(fn, ln, msg.length ? msg : "exception ["~e.toString()~"]"); }
+ assert(false);
+}
+
+unittest
+{
+ auto error = {throw new Error("hello");};
+ auto nothing = (){};
+ auto assertError = {assert(0);};
+
+ assert_nothrow ( assert_nothrow(nothing()) );
+ assert_throw!AssertError( assert_nothrow(error()) );
+ assert_throw!AssertError( assert_nothrow(assertError()) );
+
+ assert_nothrow ( assert_throw!Error(error()) );
+ assert_throw!AssertError( assert_throw!Error(nothing()) );
+ assert_nothrow ( assert_throw!Error(assertError()) );
+ assert_throw!AssertError( assert_throw!AssertError(error()) );
+}
+
+/// Unittest helpers asserting two values are in some relation ==, !=, <, <=, >, >=
+
+template assertOp(string op)
+{
+ void assertOp(A, B, string fn=__FILE__, int ln=__LINE__)(A a, B b, string msg="")
+ {
+ try
+ { if( mixin("a"~op~"b") ) return; }
+ catch(Throwable e)
+ { onAssertErrorMsg(fn, ln, msg.length ? msg : "exception ["~e.toString()~"]"); }
+ onAssertErrorMsg(fn, ln, msg.length ? msg : to!string(a)~" !"~op~to!string(b));
+ }
+}
+
+alias assertOp!(`==`) assert_eq;
+alias assertOp!(`!=`) assert_ne;
+alias assertOp!(`<`) assert_lt;
+alias assertOp!(`<=`) assert_le;
+alias assertOp!(`>`) assert_gt;
+alias assertOp!(`>=`) assert_ge;
+
+unittest
+{
+ assert_nothrow( assert_eq(1, 1) );
+ assert_nothrow( assert_ne(1, 0) );
+ assert_nothrow( assert_lt(0, 1) );
+ assert_nothrow( assert_le(0, 1) );
+ assert_nothrow( assert_le(0, 0) );
+ assert_nothrow( assert_gt(1, 0) );
+ assert_nothrow( assert_ge(1, 0) );
+ assert_nothrow( assert_ge(0, 0) );
+
+ assert_throw!AssertError( assert_eq(1, 0) );
+ assert_throw!AssertError( assert_ne(1, 1) );
+ assert_throw!AssertError( assert_lt(1, 1) );
+ assert_throw!AssertError( assert_lt(1, 0) );
+ assert_throw!AssertError( assert_le(1, 0) );
+ assert_throw!AssertError( assert_gt(0, 0) );
+ assert_throw!AssertError( assert_gt(0, 1) );
+ assert_throw!AssertError( assert_ge(0, 1) );
+
+ class Temp { bool opEquals(int x){return x/x==x;} }
+ assert_throw!AssertError( assert_eq(new Temp, 0) );
+ assert_nothrow ( assert_eq(new Temp, 1) );
+ assert_throw!AssertError( assert_eq(new Temp, 2) );
+}
+
+/* [Todo] is there any way to clearnly implement "assert_compiles" and "assert_not_compile"? */
ADDED tricks/tricks.d
Index: tricks/tricks.d
==================================================================
--- tricks/tricks.d
+++ tricks/tricks.d
@@ -0,0 +1,210 @@
+/**
+ * Authors: k.inaba
+ * License: NYSL 0.9982 http://www.kmonos.net/nysl/
+ *
+ * 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 : AssertError;
+
+/// Simple Wrapper for std.format.doFormat
+
+string sprintf(string fmt, T...)(T params)
+{
+ auto writer = appender!string();
+ formattedWrite(writer, fmt, params);
+ return writer.data;
+}
+
+unittest
+{
+ assert( sprintf!"%s == %d"("1+2", 3) == "1+2 == 3" );
+ assert( sprintf!"%s == %04d"("1+2", 3) == "1+2 == 0003" );
+}
+
+/// Create an exception with automatically completed filename and lineno information
+
+auto genex(ExceptionType, string fn=__FILE__, int ln=__LINE__, T...)(T params)
+{
+ static if( T.length > 0 && is(T[$-1] : Throwable) )
+ return new ExceptionType(params[0..$-1], fn, ln, params[$-1]);
+ else
+ return new ExceptionType(params, fn, ln);
+}
+
+unittest
+{
+ assert_ne( genex!Exception("msg").file, "" );
+ assert_ne( genex!Exception("msg").line, 0 );
+ assert_ne( genex!Exception("msg",new Exception("bar")).next, Exception.init );
+}
+
+/// Mixing-in the bean constructor for a class
+
+/*mixin*/
+template SimpleConstructor()
+{
+ static if( is(typeof(super) == Object) || super.tupleof.length==0 )
+ this( typeof(this.tupleof) params )
+ {
+ static if(this.tupleof.length>0)
+ this.tupleof = params;
+ }
+ else
+ this( typeof(super.tupleof) ps, typeof(this.tupleof) params )
+ {
+ // including (only) the direct super class members
+ // may not always be a desirable choice, but should work for many cases
+ super(ps);
+ static if(this.tupleof.length>0)
+ this.tupleof = params;
+ }
+}
+
+unittest
+{
+ class Temp
+ {
+ int x;
+ string y;
+ mixin SimpleConstructor;
+ }
+ assert_eq( (new Temp(1,"foo")).x, 1 );
+ assert_eq( (new Temp(1,"foo")).y, "foo" );
+ assert( !__traits(compiles, new Temp) );
+ assert( !__traits(compiles, new Temp(1)) );
+ assert( !__traits(compiles, new Temp("foo",1)) );
+
+ class Tomp : Temp
+ {
+ real z;
+ mixin SimpleConstructor;
+ }
+ assert_eq( (new Tomp(1,"foo",2.5)).x, 1 );
+ assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" );
+ assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 );
+ assert( !__traits(compiles, new Tomp(3.14)) );
+
+ // shiyo- desu. Don't use in this way.
+ // Tamp tries to call new Tomp(real) (because it only sees Tomp's members),
+ // but it fails because Tomp takes (int,string,real).
+ assert( !__traits(compiles, {
+ class Tamp : Tomp
+ {
+ mixin SimpleConstructor;
+ }
+ }) );
+}
+
+/// Mixing-in the MOST-DERIVED-member-wise comparator for a class
+
+/*mixin*/
+template SimpleCompare()
+{
+ override bool opEquals(Object rhs_) const
+ {
+ if( auto rhs = cast(typeof(this))rhs_ )
+ {
+ foreach(i,_; this.tupleof)
+ if( this.tupleof[i] != rhs.tupleof[i] )
+ return false;
+ return true;
+ }
+ assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
+ }
+
+ override hash_t toHash() const
+ {
+ hash_t h = 0;
+ foreach(mem; this.tupleof)
+ h += typeid(mem).getHash(&mem);
+ return h;
+ }
+
+ override int opCmp(Object rhs_) const
+ {
+ if( auto rhs = cast(typeof(this))rhs_ )
+ {
+ foreach(i,_; this.tupleof)
+ 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_)));
+ }
+}
+
+unittest
+{
+ class Temp
+ {
+ int x;
+ string y;
+ mixin SimpleConstructor;
+ mixin SimpleCompare;
+ }
+ assert_eq( new Temp(1,"foo"), new Temp(1,"foo") );
+ assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash );
+ assert_ne( new Temp(1,"foo"), new Temp(2,"foo") );
+ assert_ne( new Temp(1,"foo"), new Temp(1,"bar") );
+ assert_gt( new Temp(1,"foo"), new Temp(1,"bar") );
+ assert_lt( new Temp(1,"foo"), new Temp(2,"bar") );
+ assert_ge( new Temp(1,"foo"), new Temp(1,"foo") );
+
+ class TempDummy
+ {
+ int x;
+ string y;
+ mixin SimpleConstructor;
+ mixin SimpleCompare;
+ }
+ assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") );
+ assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") );
+}
+
+/// Mixing-in a simple toString method
+
+/*mixin*/
+template SimpleToString()
+{
+ override string toString()
+ {
+ string str = sprintf!"%s("(typeof(this).stringof);
+ foreach(i,mem; this.tupleof)
+ {
+ if(i) str ~= ",";
+ static if( is(typeof(mem) == std.bigint.BigInt) )
+ str ~= std.bigint.toDecimalString(mem);
+ else
+ str ~= sprintf!"%s"(mem);
+ }
+ return str ~ ")";
+ }
+}
+
+version(unittest) import std.bigint;
+unittest
+{
+ class Temp
+ {
+ int x;
+ string y;
+ BigInt z;
+ mixin SimpleConstructor;
+ mixin SimpleToString;
+ }
+ assert_eq( (new Temp(1,"foo",BigInt(42))).toString(), "Temp(1,foo,42)" );
+}
+
+/// Everything is in
+
+/*mixin*/
+template SimpleClass()
+{
+ mixin SimpleConstructor;
+ mixin SimpleCompare;
+ mixin SimpleToString;
+}