Index: main.d ================================================================== --- main.d +++ main.d @@ -12,10 +12,11 @@ import polemy.failure; import polemy.layer; import polemy.parse; import polemy.ast; import polemy.eval; +import polemy.runtime; enum VersionNoMajor = 0; enum VersionNoMinor = 1; enum VersionNoRev = 0; @@ -26,10 +27,11 @@ Evaluator ev; /// Load the prelude environment this() { ev = new Evaluator; + enrollRuntimeLibrary(ev); } /// Print the version number etc. void greet() { Index: polemy/ast.d ================================================================== --- polemy/ast.d +++ polemy/ast.d @@ -11,18 +11,19 @@ /// abstract class AST { LexPosition pos; + mixin SimpleConstructor; - mixin SimplePatternMatch; } /// class Int : AST { BigInt data; + mixin SimpleClass; this(LexPosition pos, int n) {super(pos); data = n;} this(LexPosition pos, long n) {super(pos); data = n;} this(LexPosition pos, BigInt n) {super(pos); data = n;} this(LexPosition pos, string n) {super(pos); data = BigInt(n);} @@ -30,25 +31,28 @@ /// class Str : AST { string data; + mixin SimpleClass; } /// class Var : AST { string name; + mixin SimpleClass; } /// class Lay : AST { Layer layer; AST expr; + mixin SimpleClass; } /// class Let : AST @@ -55,36 +59,39 @@ { string name; Layer layer; AST init; AST expr; + mixin SimpleClass; } /// class App : AST { AST fun; - AST[] args; - this(LexPosition pos, AST fun, AST[] args...) - { super(pos); this.fun=fun; this.args=args.dup; } + AST[] args; + mixin SimpleClass; + this(LexPosition pos, AST fun, AST[] args...) { super(pos); this.fun=fun; this.args=args.dup; } } /// class Parameter { string name; Layer[] layers; + mixin SimpleClass; } /// class Fun : AST { Parameter[] params; AST funbody; + mixin SimpleClass; } /// Handy Generator for AST nodes. To use this, mixin EasyAst; Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -10,41 +10,45 @@ import polemy.ast; import polemy.parse; import polemy.value; import polemy.layer; +/// Objects for maitaining global environment and evaluation of expression on it class Evaluator { public: + /// Initialize evaluator with empty context this() { theContext = new Table; } + /// Evaluate the AST Value evalAST(AST e) { return eval(e, ValueLayer, theContext, OverwriteCtx); } + /// Evaluate the string Value evalString(S,T...)(S str, T fn_ln_cn) { return evalAST(parseString(str,fn_ln_cn)); } + /// Evaluate the file Value evalFile(S,T...)(S filename, T ln_cn) { return evalAST(parseFile(filename,ln_cn)); } + /// Get the global context Table globalContext() { return theContext; } private: Table theContext; -private: enum : bool { CascadeCtx=false, OverwriteCtx=true }; - Value eval( AST e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { // dynamic-overload-resolution-pattern: modify here enum funName = "eval"; alias TypeTuple!(e,lay,ctx,overwriteCtx) params; @@ -59,148 +63,78 @@ // dynamic-overload-resolution-pattern: default behavior assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined")); } -private: Value eval( Str e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - Value v = new StrValue(e.data); - if( lay==RawMacroLayer || lay==MacroLayer ) - { - auto ast = new Table; - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("str")); - ast.set("data", ValueLayer, v); - return ast; - } + if( isMacroishLayer(lay) ) + return ast2table(e, (AST e){return eval(e,lay,ctx);}); if( lay==ValueLayer ) - return v; - return lift(v, lay, ctx, e.pos); + return new StrValue(e.data); + return lift(new StrValue(e.data), lay, ctx, e.pos); } Value eval( Int e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - Value v = new IntValue(e.data); - if( lay==RawMacroLayer || lay==MacroLayer ) - { - auto ast = new Table; - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("int")); - ast.set("data", ValueLayer, v); - return ast; - } + if( isMacroishLayer(lay) ) + return ast2table(e, (AST e){return eval(e,lay,ctx);}); if( lay==ValueLayer ) - return v; - return lift(v, lay, ctx, e.pos); + return new IntValue(e.data); + return lift(new IntValue(e.data), lay, ctx, e.pos); } Value eval( Var e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - if( lay==RawMacroLayer || lay==MacroLayer ) - { + if( isMacroishLayer(lay) ) if( ctx.has(e.name,MacroLayer) ) return ctx.get(e.name, MacroLayer, e.pos); - auto ast = new Table; - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("var")); - ast.set("name", ValueLayer, new StrValue(e.name)); - return ast; - } + else + return ast2table(e, (AST e){return eval(e,lay,ctx);}); if( lay==ValueLayer || ctx.has(e.name, lay) ) return ctx.get(e.name, lay, e.pos); return lift(ctx.get(e.name, ValueLayer, e.pos), lay, ctx, e.pos); } Value eval( App e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { Value f = eval( e.fun, lay, ctx ); - if( lay==RawMacroLayer || lay==MacroLayer ) - { + if( isMacroishLayer(lay) ) if( auto ff = cast(FunValue)f ) return invokeFunction(ff, e.args, MacroLayer, ctx, e.pos); - Table ast = new Table; - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("app")); - ast.set("fun", ValueLayer, f); - Table args = new Table; - foreach_reverse(a; e.args) - args = makeCons(eval(a, lay, ctx), args); - ast.set("args", ValueLayer, args); - return ast; - } - else - { - return invokeFunction(f, e.args, lay, ctx, e.pos); - } + else + return ast2table(e, (AST e){return eval(e,lay,ctx);}); + return invokeFunction(f, e.args, lay, ctx, e.pos); } Value eval( Fun e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - if( lay==RawMacroLayer || lay==MacroLayer ) - { - Table t = new Table; - t.set("pos", ValueLayer, fromPos(e.pos)); - t.set("is", ValueLayer, new StrValue("fun")); - t.set("funbody", ValueLayer, eval(e.funbody,lay,ctx)); - Table params = new Table; - foreach_reverse(p; e.params) - { - Table lays = new Table; - foreach_reverse(l; p.layers) - lays = makeCons(new StrValue(l), lays); - Table kv = new Table; - kv.set("name", ValueLayer, new StrValue(p.name)); - kv.set("layers", ValueLayer, lays); - Table cons = new Table; - params = makeCons(kv, params); - } - t.set("params", ValueLayer, params); - return t; - } + if( isMacroishLayer(lay) ) + return ast2table(e, (AST e){return eval(e,lay,ctx);}); else - { return createNewFunction(e, ctx); - } } Value eval( Lay e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { - if( lay == RawMacroLayer ) - { - Value r = eval(e.expr, lay, ctx); - auto ast = new Table; // todo: pos - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("lay")); - ast.set("layer", ValueLayer, new StrValue(e.layer)); - ast.set("expr", ValueLayer, r); - return ast; - } + if( isNoLayerChangeLayer(lay) ) + return ast2table(e, (AST e){return eval(e,lay,ctx);}); else return eval(e.expr, e.layer, ctx); } Value eval( Let e, Layer lay, Table ctx, bool overwriteCtx=CascadeCtx ) { // todo @macro let - if( lay==RawMacroLayer || lay==MacroLayer ) - { - auto ast = new Table; // todo: pos - ast.set("pos", ValueLayer, fromPos(e.pos)); - ast.set("is", ValueLayer, new StrValue("let")); - ast.set("name", ValueLayer, new StrValue(e.name)); - ast.set("layer", ValueLayer, new StrValue(e.layer)); - ast.set("init", ValueLayer, eval(e.init, lay, ctx)); - ast.set("expr", ValueLayer, eval(e.expr, lay, ctx)); - return ast; - } + if( isMacroishLayer(lay) ) + return ast2table(e, (AST e){return eval(e,lay,ctx);}); else { if( !overwriteCtx ) ctx = new Table(ctx, Table.Kind.NotPropagateSet); Value ri = eval(e.init, lay, ctx); - string theLayer = e.layer.empty ? (lay==RawMacroLayer ? MacroLayer : lay) : e.layer; + string theLayer = e.layer.empty ? lay : e.layer; // neutral layer ctx.set(e.name, theLayer, ri); return eval(e.expr, lay, ctx, OverwriteCtx); } } @@ -214,17 +148,19 @@ if( p.layers.empty ) newCtx.set(p.name, (lay==RawMacroLayer ? MacroLayer : lay), eval(args[i], lay, ctx)); else foreach(argLay; p.layers) newCtx.set(p.name, argLay, eval(args[i], argLay, ctx)); - return f.invoke(pos, lay, newCtx); + return f.invoke(lay==RawMacroLayer ? MacroLayer : lay, newCtx, pos); } throw genex!RuntimeException(pos, text("tried to call non-function: ",_f)); } Value lift(Value v, Layer lay, Table ctx, LexPosition pos=null) { + assert( !isMacroishLayer(lay), "lift to the @macro layer should not happen" ); + // functions are automatically lifterd if( cast(FunValue) v ) return v; // similar to invoke Function, but with only one argument bound to ValueLayer @@ -235,11 +171,11 @@ if( ps.length != 1 ) 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 ) { newCtx.set(ps[0].name, ValueLayer, v); - return f.invoke(pos, ValueLayer, newCtx); + return f.invoke(ValueLayer, newCtx, pos); } else throw genex!RuntimeException(pos, "lift function must take exactly one argument at "~ValueLayer~" layer"); } throw genex!RuntimeException(pos, "tried to call non-function"); @@ -275,54 +211,69 @@ return this.defCtx.opCmp(rhs.defCtx); } assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); } - override Value invoke(LexPosition pos, Layer lay, Table ctx) + override Value invoke(Layer lay, Table ctx, LexPosition pos) { if( lay == MacroLayer ) return eval(ast.funbody, lay, ctx); - auto macroed = tableToAST(ValueLayer, eval(e.funbody, RawMacroLayer, ctx)); - return eval(macroed, lay, ctx); + if( afterMacroAST is null ) + afterMacroAST = tableToAST(ValueLayer, eval(e.funbody, RawMacroLayer, ctx)); + return eval(afterMacroAST, lay, ctx); } + + AST afterMacroAST; } return new UserDefinedFunValue(e,ctx); } public: - /// TODO: move up - /// TDOO: to other layers? - void addPrimitive(R,T...)(string name, Layer lay, R delegate (T) dg) + /// Add primitive function to the global context + void addPrimitive(R,T...)(string name, Layer defLay, R delegate (T) dg) { class NativeFunValue : FunValue { - Parameter[] params_data; - override string toString() { return sprintf!"(native:%x)"(dg.funcptr); } override const(Parameter[]) params() { return params_data; } - override Table definitionContext() { return new Table; } // todo: cache - this(){ + override Table definitionContext() { return theContext; } + + override string toString() { return sprintf!"(native:%x)"(dg.funcptr); } + override int opCmp(Object rhs) { + if(auto r = cast(NativeFunValue)rhs) return typeid(typeof(dg)).compare(&dg,&r.dg); + if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)); + throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other"); + } + mixin SimpleToHash; + + R delegate(T) dg; + Parameter[] params_data; + + this(R delegate(T) dg) + { + this.dg = dg; foreach(i, Ti; T) params_data ~= new Parameter(text(i), []); } - override Value invoke(LexPosition pos, Layer lay, Table ctx) + + override Value invoke(Layer lay, Table ctx, LexPosition pos) { - if( lay != ValueLayer ) - throw genex!RuntimeException(pos, "only "~ValueLayer~" layer can call native function"); + if( lay != defLay ) + throw genex!RuntimeException(pos, text("only ", defLay, " layer can call native function: ", name)); T typed_args; foreach(i, Ti; T) { - typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer); + typed_args[i] = cast(Ti) ctx.get(text(i), ValueLayer, pos); if( typed_args[i] is null ) - throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d"(i+1)); + throw genex!RuntimeException(pos, sprintf!"type mismatch on the argument %d of native function: %s"(i+1,name)); } try { return dg(typed_args); } catch( RuntimeException e ) { throw e.pos is null ? new RuntimeException(pos, e.msg, e.file, e.line) : e; } } } - theContext.set(name, lay, new NativeFunValue); + theContext.set(name, defLay, new NativeFunValue(dg)); } } version(unittest) import polemy.runtime; unittest Index: polemy/layer.d ================================================================== --- polemy/layer.d +++ polemy/layer.d @@ -14,8 +14,18 @@ enum : Layer { SystemLayer = "(system)", /// Predefined layer for internal data ValueLayer = "@value", /// Predefined layer for normal run - MacroLayer = "@macro", /// Predefined layer for macro run - RawMacroLayer = "(rawmacro)", /// Predefined layer for raw-macro run + MacroLayer = "@macro", /// Predefined layer for macro run (@lay() changes layer) + RawMacroLayer = "(rawmacro)", /// Predefined layer for macro run (@lay() becomes AST) +} + +bool isMacroishLayer( Layer lay ) +{ + return lay==MacroLayer || lay==RawMacroLayer; +} + +bool isNoLayerChangeLayer( Layer lay ) +{ + return lay==RawMacroLayer; } Index: polemy/value.d ================================================================== --- polemy/value.d +++ polemy/value.d @@ -7,49 +7,71 @@ module polemy.value; import polemy._common; import polemy.failure; import polemy.ast; import polemy.layer; +import std.string; /// Runtime values of Polemy abstract class Value { + override bool opEquals(Object rhs) { return 0==opCmp(rhs); } } /// class IntValue : Value { BigInt data; - mixin SimpleClass; - override string toString() const { return std.bigint.toDecimalString(cast(BigInt)data); } + this(int n) { this.data = n; } + this(long n) { this.data = n; } + this(BigInt n) { this.data = n; } + this(string n) { this.data = BigInt(n); } + override string toString() const { return toDecimalString(cast(BigInt)data); } + override int opCmp(Object rhs) { + if(auto r = cast(IntValue)rhs) return data.opCmp(r.data); + if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)); + throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other"); + } + mixin SimpleToHash; } /// class StrValue : Value { string data; - mixin SimpleClass; + mixin SimpleConstructor; override string toString() const { return data; } + override int opCmp(Object rhs) { + if(auto r = cast(StrValue)rhs) return typeid(string).compare(&data, &r.data); + if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)); + throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other"); + } + mixin SimpleToHash; } /// -class UndValue : Value +class UndefinedValue : Value { - mixin SimpleClass; + mixin SimpleConstructor; override string toString() const { return ""; } + override int opCmp(Object rhs) { + if(auto r = cast(StrValue)rhs) return 0; + if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)); + throw genex!RuntimeException(LexPosition.dummy, "comparison with value and somithing other"); + } + mixin SimpleToHash; } - /// abstract class FunValue : Value { const(Parameter[]) params(); Table definitionContext(); - Value invoke(LexPosition pos, Layer lay, Table ctx); + Value invoke(Layer lay, Table ctx, LexPosition pos); } /// Context (variable environment) /// Simlar to prototype chain of ECMAScript etc. /// But extended with the notion of "Layer" @@ -336,14 +358,65 @@ Table fromPos(LexPosition pos) { Table t = new Table; if( pos !is null ) { t.set("filename", ValueLayer, new StrValue(pos.filename)); - t.set("lineno", ValueLayer, new IntValue(BigInt(pos.lineno))); - t.set("column", ValueLayer, new IntValue(BigInt(pos.column))); + t.set("lineno", ValueLayer, new IntValue(pos.lineno)); + t.set("column", ValueLayer, new IntValue(pos.column)); } else { t.set("filename", ValueLayer, new StrValue("nullpos")); - t.set("lineno", ValueLayer, new IntValue(BigInt(0))); - t.set("column", ValueLayer, new IntValue(BigInt(0))); + t.set("lineno", ValueLayer, new IntValue(0)); + t.set("column", ValueLayer, new IntValue(0)); } return t; } + +/// Convert AST to Table so that it can be used in Polemy +/// TODO: generalize to DValue2PolemyValue + +Value ast2table(T)(T e, Value delegate(AST) rec) +{ + assert( typeid(e) == typeid(T) ); + + static if(is(T==BigInt) || is(T==long) || is(T==int)) + return new IntValue(e); + else + static if(is(T==string)) + return new StrValue(e); + else + static if(is(T S : S[])) + { + Table lst = new Table; + foreach_reverse(a; e) + static if(is(S : AST)) + lst = makeCons(rec(a), lst); + else + lst = makeCons(ast2table(a,rec), lst); + return lst; + } + else + static if(is(T : AST)) + { + auto t = new Table; + t.set("pos", ValueLayer, fromPos(e.pos)); + t.set("is" , ValueLayer, new StrValue(typeid(e).name.split(".")[$-1].tolower())); + foreach(i,m; e.tupleof) + static if(is(typeof(m) : AST)) + t.set(e.tupleof[i].stringof[2..$], ValueLayer, rec(m)); + else + t.set(e.tupleof[i].stringof[2..$], ValueLayer, ast2table(m,rec)); + return t; + } + else + static if(is(T == class)) + { + auto t = new Table; + foreach(i,m; e.tupleof) + static if(is(typeof(m) : AST)) + t.set(e.tupleof[i].stringof[2..$], ValueLayer, rec(m)); + else + t.set(e.tupleof[i].stringof[2..$], ValueLayer, ast2table(m,rec)); + return t; + } + else + static assert(false, "unknown type <"~T.stringof~"> during AST encoding"); +} Index: tricks/tricks.d ================================================================== --- tricks/tricks.d +++ tricks/tricks.d @@ -98,10 +98,23 @@ assert( !__traits(compiles, { class Tamp : Tomp { mixin SimpleConstructor; } }) ); } +/// Mixing-in the MOST-DERIVED-member-wise comparator for a class + +template SimpleToHash() +{ + override hash_t toHash() const /// member-by-member hash + { + hash_t h = 0; + foreach(mem; this.tupleof) + h += typeid(mem).getHash(&mem); + return h; + } +} + /// Mixing-in the MOST-DERIVED-member-wise comparator for a class /*mixin*/ template SimpleCompare() { @@ -114,18 +127,12 @@ return false; return true; } assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); } - - override hash_t toHash() const /// member-by-member hash - { - hash_t h = 0; - foreach(mem; this.tupleof) - h += typeid(mem).getHash(&mem); - return h; - } + + mixin SimpleToHash; override int opCmp(Object rhs_) const /// member-by-member compare { if( auto rhs = cast(typeof(this))rhs_ ) { @@ -217,185 +224,11 @@ mixin SimpleConstructor; mixin SimpleCompare; mixin SimpleToString; } -/// Simple PatternMatcher - -/*mixin*/ -template SimplePatternMatch() -{ - SPM_Return!(PP) match(string fn=__FILE__, size_t ln=__LINE__, PP...)(PP pts) - { - foreach(i,_; pts) - { - alias pts[i] pt; // bug? pts[i]-->pt do not work - static if(__traits(compiles, SPM_isMatchTag(pt))) - { - if( auto v = cast(pt.dynamicType)this ) - return pt(v.tupleof); - } - else - static if(__traits(compiles, SPM_isMatchAny(pt))) - { - return pt(); - } - else - { - if( auto v = cast(SPM_PTT!(pt)[0])this ) - return pt(v); - } - } - SPM_throwAssertError(fn, ln, "pattern matching failure"); - assert(false); - } -} - -/// Pattern case clause - -SPM_MatchTag!(T, fn) when(T, alias fn)() -{ - SPM_MatchTag!(T, fn) m; - return m; -} - -/// Pattern case clause - -SPM_MatchAny!(fn) otherwise(alias fn)() -{ - SPM_MatchAny!(fn) m; - return m; -} - -// implementation detail of SimplePatternMatch - -void SPM_throwAssertError(T...)(T t) { core.exception.onAssertErrorMsg(t); } - -struct SPM_MatchTag(T, alias fn) -{ - alias T dynamicType; - auto opCall(typeof(T.tupleof) s) { return fn(s); } -} - -struct SPM_MatchAny(alias fn) -{ - auto opCall() { return fn(); } -} - -template SPM_PTT(alias p) -{ - alias ParameterTypeTuple!(p) SPM_PTT; -} - -template SPM_Each(P) -{ - static if(__traits(compiles, SPM_isMatchTag(P.init))) - alias typeof(P(P.dynamicType.tupleof)) SPM_Each; - else - static if(__traits(compiles, SPM_isMatchAny(P.init))) - alias typeof(P()) SPM_Each; - else - alias ReturnType!(P) SPM_Each; -} - -template SPM_aVoid(T:void, TS...) { alias SPM_aVoid!(TS) SPM_aVoid; } -template SPM_aVoid(T, TS...) { alias TypeTuple!(T,SPM_aVoid!(TS)) SPM_aVoid; } -template SPM_aVoid() { alias TypeTuple!() SPM_aVoid; } - -template SPM_Return(PP...) -{ - alias CommonType!(SPM_aVoid!(staticMap!(SPM_Each, PP))) SPM_Return; -} - -void SPM_isMatchTag(T,alias fn)(SPM_MatchTag!(T,fn)){} -void SPM_isMatchAny(alias fn)(SPM_MatchAny!(fn)){} - -unittest -{ - static abstract class Base { - mixin SimplePatternMatch; - } - class D1 : Base { - int x; - real y; - mixin SimpleConstructor; - } - class D2 : Base { - string s; - mixin SimpleConstructor; - } - class D3 : Base { - int[int] m; - mixin SimpleConstructor; - } - - Base d1 = new D1(1, 2.3); - Base d2 = new D2("foobar"); - 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;} - ), 1); - assert_eq( d2.match( - (D1 x){return 1;}, - (D2 x){return 2;} - ), 2); - assert_throw!AssertError( d3.match( - (D1 x){return 1;}, - (D2 x){return 2;} - )); - assert_eq( d3.match( - (D1 x){return 1;}, - (D2 x){return 2;}, - (Base x){return 3;} - ), 3); - assert_eq( d2.match( - (D1 x){return 1;}, - (D2 x){return 2;}, - (Base x){return 3;} - ), 2); - assert_eq( d2.match( - (D1 x){return 1;}, - (Base x){return 3;}, - (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];}) - ), 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];}) - ), 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];}) - ), 10); - assert_throw!AssertError( d3.match( - when!(D1, (x, y){return x + cast(int)y;}), - 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;}) - ), 6); - assert_eq( d2.match( - when!(D1, (x, y){return x + cast(int)y;}), - otherwise!({return 999;}), - when!(D2, (x){return x.length;}) - ), 999); -} - /// Will be used for dynamic overload resolution pattern template firstParam(T) { alias ParameterTypeTuple!(T)[0] firstParam; }