Index: polemy/ast.d ================================================================== --- polemy/ast.d +++ polemy/ast.d @@ -57,13 +57,20 @@ string var; mixin SimpleConstructor; mixin SimpleCompare; // do not take "pos" into account } -class BinOpExpression : Expression +class AssignExpression : Expression { - string op; Expression lhs; Expression rhs; mixin SimpleConstructor; mixin SimpleCompare; // do not take "pos" into account } +class FuncallExpression : Expression +{ + Expression fun; + Expression[] args; + this(immutable LexPosition pos, Expression fun, Expression[] args...) + { super(pos); this.fun=fun; this.args=args.dup; } + mixin SimpleCompare; // do not take "pos" into account +} Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -6,14 +6,52 @@ */ module polemy.eval; import polemy._common; import polemy.ast; import polemy.runtime; + +Context createGlobalContext() +{ + auto ctx = new Context; + ctx.add("+", new PrimitiveFunction(delegate Value(Value[] args){ + if( args.length != 2 ) + throw new PolemyRuntimeException("+ takes two arguments!!"); // TODO improve this message + 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 + })); + ctx.add("-", new PrimitiveFunction(delegate Value(Value[] args){ + if( args.length != 2 ) + throw new PolemyRuntimeException("- takes two arguments!!"); // TODO improve this message + 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 + })); + ctx.add("*", new PrimitiveFunction(delegate Value(Value[] args){ + if( args.length != 2 ) + throw new PolemyRuntimeException("* takes two arguments!!"); // TODO improve this message + 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 + })); + ctx.add("/", new PrimitiveFunction(delegate Value(Value[] args){ + if( args.length != 2 ) + throw new PolemyRuntimeException("/ takes two arguments!!"); // TODO improve this message + 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 + })); + return ctx; +} Context eval(Program prog) { - return eval(prog, new Context); + return eval(prog, createGlobalContext()); } Context eval(Program prog, Context ctx) { foreach(s; prog) @@ -53,38 +91,31 @@ if( auto e = cast(VarExpression)_e ) { return ctx[e.var]; } else - if( auto e = cast(BinOpExpression)_e ) + if( auto e = cast(AssignExpression)_e ) { - if( e.op == "=" ) + if( auto ev = cast(VarExpression)e.lhs ) { - 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)); + Value r = eval(e.rhs, ctx); + ctx[ev.var] = r; + return r; } - - Value l = eval(e.lhs, ctx); - Value r = eval(e.rhs, ctx); - if( auto lv = cast(IntValue)l ) - if( auto rv = cast(IntValue)r ) - final switch(e.op) - { - case "+": return new IntValue(lv.data+rv.data); - case "-": return new IntValue(lv.data-rv.data); - case "*": return new IntValue(lv.data*rv.data); - case "/": return new IntValue(lv.data/rv.data); - } - else - throw new PolemyRuntimeException(sprintf!"rhs of %s must be an integer but was %s at [%s]"(e.op, typeid(r), e.rhs.pos)); - else - throw new PolemyRuntimeException(sprintf!"lhs of %s must be an integer but was %s at [%s]"(e.op, typeid(l), e.lhs.pos)); + throw new PolemyRuntimeException(sprintf!"Lhs of assignment must be a variable: %s"(e.pos)); + } + else + if( auto e = cast(FuncallExpression)_e ) + { + 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); + } else + throw new PolemyRuntimeException(sprintf!"Non-funcion is applied at [%s]"(e.pos)); } throw new PolemyRuntimeException(sprintf!"Unknown Kind of Expression %s at [%s]"(typeid(_e), _e.pos)); } Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -92,55 +92,58 @@ Expression parseExpression() { auto saved = lex.save; scope(failure) lex = saved; - - // Expr ::= E0 - // E0 ::= (E1 "=")* E1 - // E1 ::= (E2 "+"|"-")* E2 - // E2 ::= (E3 "*"|"/")* E3 - // E3 ::= int | str | id | "(" Expr ")" - - return parseE0(); + return parseE(0); } - Expression parseE0() - { - auto lhs = parseE1(); - if( tryEat("=") ) - lhs = new BinOpExpression(lhs.pos, "=", lhs, parseE0()); - return lhs; - } + // [TODO] multi-char operators are not supported by the lexer... + static immutable string[][] operator_perferences = [ + [","], + ["="], // caution! left associative + ["or"], + ["and"], + ["!="], + ["=="], + ["<","<=",">",">="], + ["|"], + ["^"], + ["&"], + ["<<", ">>"], + ["+","-"], + ["*","/","%"] + ]; - Expression parseE1() + Expression parseE(int level = 0) { - for(auto lhs = parseE2();;) + if( operator_perferences.length <= level ) + return parseBaseExpression(); + else { - if( tryEat("+") ) - lhs = new BinOpExpression(lhs.pos, "+", lhs, parseE2()); - else if( tryEat("-") ) - lhs = new BinOpExpression(lhs.pos, "-", lhs, parseE2()); - else - return lhs; + auto ops = operator_perferences[level]; + auto e = parseE(level+1); + seq: + while( !lex.empty ) + { + auto pos = lex.front.pos; + foreach(op; ops) + if( tryEat(op) ) + { + if( op == "=" ) // right assoc + return new AssignExpression(e.pos, e, parseE(level)); + else + e = new FuncallExpression(e.pos, new VarExpression(pos, op), e, parseE(level+1)); + continue seq; + } + break; + } + return e; } } - Expression parseE2() - { - for(auto lhs = parseE3();;) - { - if( tryEat("*") ) - lhs = new BinOpExpression(lhs.pos, "*", lhs, parseE3()); - else if( tryEat("/") ) - lhs = new BinOpExpression(lhs.pos, "/", lhs, parseE3()); - else - return lhs; - } - } - - Expression parseE3() + Expression parseBaseExpression() { if( lex.empty ) throw new ParserException("EOF during parsing an expression"); auto pos = lex.front.pos; @@ -154,11 +157,11 @@ scope(exit) lex.popFront; return new StrLiteralExpression(pos, lex.front.str); } if( tryEat("(") ) { - auto e = parseE0(); + auto e = parseE(); eat(")", "after parenthesized expression"); return e; } scope(exit) lex.popFront; return new VarExpression(pos, lex.front.str); @@ -209,15 +212,15 @@ zzz = zzz + zzz * "fo\no"; # comment 42; `); auto s0 = new DeclStatement(null, "zzz", new IntLiteralExpression(null, BigInt(100))); - auto s1 = new ExprStatement(null, new BinOpExpression(null, "=", + auto s1 = new ExprStatement(null, new AssignExpression(null, new VarExpression(null, "zzz"), - new BinOpExpression(null, "+", + new FuncallExpression(null, new VarExpression(null,"+"), new VarExpression(null, "zzz"), - new BinOpExpression(null, "*", + new FuncallExpression(null, new VarExpression(null,"*"), new VarExpression(null, "zzz"), new StrLiteralExpression(null, "fo\\no") )))); auto s2 = new ExprStatement(null, new IntLiteralExpression(null, BigInt(42))); Index: polemy/runtime.d ================================================================== --- polemy/runtime.d +++ polemy/runtime.d @@ -27,10 +27,22 @@ { string data; mixin SimpleConstructor; mixin SimpleCompare; } + +abstract class FunValue : Value +{ + Value call(Value[] args); +} + +class PrimitiveFunction : FunValue +{ + Value delegate(Value[]) data; + mixin SimpleConstructor; + override Value call(Value[] args) { return data(args); } +} class Context { Context parent; Value[string] table;