Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -65,17 +65,27 @@ goto asExpression; } } immutable LexPosition varpos = (lex.empty ? null : lex.front.pos); string var = eatId("after "~kwd,true); - eat("=", "after "~kwd); - kwd = (kwd[0]=='@' ? kwd : ""); // "let, var, def ==> neutral layer" - auto e = E(0); - if( tryEat(";") && !lex.empty && (lex.front.quoted || !["}",")","]"].canFind(lex.front.str)) ) - return new LetExpression(pos, var, kwd, e, Body()); - else - return new LetExpression(pos, var, kwd, e, new VarExpression(varpos, var)); + // [TODO] refactor. only auto e = ... differ + if(tryEat("(")) { + kwd = (kwd[0]=='@' ? kwd : ""); // "let, var, def ==> neutral layer" + auto e = parseLambdaAfterOpenParen(varpos); + if( tryEat(";") && !lex.empty && (lex.front.quoted || !["}",")","]"].canFind(lex.front.str)) ) + return new LetExpression(pos, var, kwd, e, Body()); + else + return new LetExpression(pos, var, kwd, e, new VarExpression(varpos, var)); + } else { + eat("=", "after "~kwd); + kwd = (kwd[0]=='@' ? kwd : ""); // "let, var, def ==> neutral layer" + auto e = E(0); + if( tryEat(";") && !lex.empty && (lex.front.quoted || !["}",")","]"].canFind(lex.front.str)) ) + return new LetExpression(pos, var, kwd, e, Body()); + else + return new LetExpression(pos, var, kwd, e, new VarExpression(varpos, var)); + } } else { asExpression: auto e = E(0); @@ -202,26 +212,31 @@ ); } if( tryEat("fun") || tryEat("\u03BB") ) { eat("(", "after fun"); - string[] params; - while( !tryEat(")") ) - { - params ~= eatId("for function parameter"); - if( !tryEat(",") ) { - eat(")", "after function parameters"); - break; - } - } - eat("{", "after function parameters"); - auto funbody = Body(); - eat("}", "after function body"); - return new FunLiteral(pos, params, funbody); + return parseLambdaAfterOpenParen(pos); } scope(exit) lex.popFront; return new VarExpression(pos, lex.front.str); + } + + AST parseLambdaAfterOpenParen(immutable LexPosition pos) + { + string[] params; + while( !tryEat(")") ) + { + params ~= eatId("for function parameter"); + if( !tryEat(",") ) { + eat(")", "after function parameters"); + break; + } + } + eat("{", "after function parameters"); + auto funbody = Body(); + eat("}", "after function body"); + return new FunLiteral(pos, params, funbody); } private: Lexer lex; this(Lexer lex) { this.lex = lex; } @@ -330,5 +345,15 @@ assert_throw!UnexpectedEOF(parseString(`var`)); assert_throw!ParseException(parseString(`@val x ==`)); assert_throw!ParseException(parseString(`if(){1}`)); assert_throw!UnexpectedEOF(parseString(`f(`)); } + +unittest +{ + mixin EasyAST; + assert_eq(parseString(`def foo(x) { x+1 }; foo`), + let("foo", "", + fun(["x"], call(var("+"), var("x"), intl(1))), + var("foo")) + ); +} ADDED sample/fib.pmy Index: sample/fib.pmy ================================================================== --- sample/fib.pmy +++ sample/fib.pmy @@ -0,0 +1,15 @@ +def fib(x) +{ + if( x < 2 ) { 1 } + else { fib(x-1) + fib(x-2) } +}; + +let upto = λ(n, f){ + if( n > 0 ){ upto(n-1,f) }; + f(n) +}; + +var compose = fun(f,g){ fun(x){f(g(x))} }; +var "<<" = compose; + +upto(16, print<