Index: polemy/ast.d ================================================================== --- polemy/ast.d +++ polemy/ast.d @@ -64,13 +64,22 @@ 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 } + +class FunLiteralExpression : Expression +{ + string[] params; + Program funbody; + mixin SimpleConstructor; + mixin SimpleCompare; // do not take "pos" into account +} Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -5,10 +5,11 @@ * Evaluator for Polemy programming language. */ module polemy.eval; import polemy._common; import polemy.ast; +import polemy.parse; import polemy.runtime; Context createGlobalContext() { auto ctx = new Context; @@ -44,10 +45,20 @@ return new IntValue(x.data/y.data); throw new PolemyRuntimeException("cannot add non-integers"); // TODO improve this message })); return ctx; } + +Context evalString(T...)(T params) +{ + return eval( parserFromString(params).parseProgram() ); +} + +Context evalFile(T...)(T params) +{ + return eval( parserFromFile(params).parseProgram() ); +} Context eval(Program prog) { return eval(prog, createGlobalContext()); } @@ -122,24 +133,18 @@ version(unittest) import polemy.parse; version(unittest) import std.stdio; version(unittest) import std.exception; unittest { - auto parser = parserFromString(`var x = 21; x = x + x*x;`); - auto prog = parser.parseProgram(); - auto ctx = eval(prog); + 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"]) ); } unittest { - auto parser = parserFromString(`var x = 21; x = x + x*y;`); - auto prog = parser.parseProgram(); - assert( collectException(eval(prog)) ); + assert( collectException(evalString(`var x = 21; x = x + x*y;`)) ); } unittest { - auto parser = parserFromString(`var x = 21; y = x + x*x;`); - auto prog = parser.parseProgram(); - assert( collectException(eval(prog)) ); + assert( collectException(evalString(`var x = 21; y = x + x*x;`)) ); } Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -97,12 +97,11 @@ return parseE(0); } // [TODO] multi-char operators are not supported by the lexer... static immutable string[][] operator_perferences = [ - [","], - ["="], // caution! left associative + ["="], ["or"], ["and"], ["!="], ["=="], ["<","<=",">",">="], @@ -144,10 +143,35 @@ Expression parseBaseExpression() { if( lex.empty ) throw new ParserException("EOF during parsing an expression"); auto pos = lex.front.pos; + Expression e = parseBaseBaseExpression(); + while( tryEat("(") ) // funcall + { + Expression[] args; + while( !tryEat(")") ) { + if( lex.empty ) { + auto ex = ParserException.create(lex,"Unexpected EOF"); + throw ex; + } + args ~= parseE(); + if( !tryEat(",") ) { + eat(")", "after function parameters"); + break; + } + } + e = new FuncallExpression(pos, e, args); + } + return e; + } + + Expression parseBaseBaseExpression() + { + if( lex.empty ) + throw new ParserException("EOF during parsing an expression"); + auto pos = lex.front.pos; if( lex.front.kind == Token.Kind.number ) { scope(exit) lex.popFront; return new IntLiteralExpression(pos, BigInt(cast(string)lex.front.str)); @@ -161,10 +185,43 @@ { auto e = parseE(); eat(")", "after parenthesized expression"); return e; } + + if( tryEat("fun") ) + { + eat("(", "after fun"); + string[] params; + for(;;) + { + if( lex.empty ) { + auto e = ParserException.create(lex,"Unexpected EOF"); + throw e; + } + if( lex.front.kind != Token.Kind.identifier ) { + auto e = ParserException.create(lex,"Identifier Expected for parameters"); + throw e; + } + params ~= lex.front.str; + lex.popFront; + if( !tryEat(",") ) { + eat(")", "after function parameters"); + break; + } + } + eat("{", "after function parameters"); + Statement[] funbody; + while(!tryEat("}")) { + if( lex.empty ) { + auto e = ParserException.create(lex,"Unexpected EOF"); + throw e; + } + funbody ~= parseStatement(); + } + return new FunLiteralExpression(pos, params, funbody); + } scope(exit) lex.popFront; return new VarExpression(pos, lex.front.str); } private: @@ -228,5 +285,28 @@ assert( prog.length == 3 ); assert( prog[0] == s0 ); assert( prog[1] == s1 ); assert( prog[2] == s2 ); } + +unittest +{ + auto p = parserFromString(` + var f = fun(x,y){x+y;}; + f(1,fun(abc){}(4)); + `); + Program prog = p.parseProgram(); + assert( prog.length == 2 ); + assert( prog[0] == new DeclStatement(null, "f", new FunLiteralExpression(null, + ["x","y"], [new ExprStatement(null, + new FuncallExpression(null, new VarExpression(null, "+"), + new VarExpression(null, "x"), new VarExpression(null, "y")))] + ))); + assert( prog[1] == new ExprStatement(null, new FuncallExpression(null, + new VarExpression(null, "f"), + new IntLiteralExpression(null, BigInt(1)), + new FuncallExpression(null, + new FunLiteralExpression(null, ["abc"], [ + ]), + new IntLiteralExpression(null, BigInt(4)) + )))); +}