Index: polemy/ast.d ================================================================== --- polemy/ast.d +++ polemy/ast.d @@ -99,6 +99,7 @@ auto funp(Parameter[] xs, AST ps) { return genEast!FunLiteral(xs,ps); } /// alias genEast!VarExpression var; /// alias genEast!LayeredExpression lay; /// alias genEast!LetExpression let; /// alias genEast!FuncallExpression call; /// + auto param(string name, string[] lay...) { return new Parameter(name, lay); } /// } Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -7,36 +7,43 @@ module polemy.parse; import polemy._common; import polemy.lex; import polemy.ast; -/// +/// Thrown when encountered a syntax error + class ParseException : Exception { mixin ExceptionWithPosition; } -/// Entry points of this module +/// Parse a string and return its AST +/// Throws: ParseException, LexException, UnexpectedEOF AST parseString(S, T...)(S str, T fn_ln_cn) - { return parserFromString(str, fn_ln_cn).parse(); } +{ + return parserFromString(str, fn_ln_cn).parse(); +} -/// Entry points of this module +/// Parse the content of a file and return its AST +/// Throws: ParseException, LexException, UnexpectedEOF AST parseFile(S, T...)(S filename, T ln_cn) - { return parserFromFile(filename, ln_cn).parse(); } +{ + return parserFromFile(filename, ln_cn).parse(); +} // Named Constructors of Parser private auto parserFromLexer(Lexer)(Lexer lex) { return new Parser!Lexer(lex); } private auto parserFromString(T...)(T params) - { return parserFromLexer(polemy.lex.lexerFromString(params)); } + { return parserFromLexer(lexerFromString(params)); } private auto parserFromFile(T...)(T params) - { return parserFromLexer(polemy.lex.lexerFromFile(params)); } + { return parserFromLexer(lexerFromFile(params)); } // Parser private class Parser(Lexer) if( isForwardRange!(Lexer) && is(ElementType!(Lexer) == Token) ) @@ -49,58 +56,80 @@ return e; } AST Body() { - if( lex.empty || !lex.front.quoted && ["}",")","]"].canFind(lex.front.str) ) + /// Body ::= Declaration + /// | TopLevelExpression + + if( closingBracket() ) return doNothingExpression(); auto saved = lex.save; - auto pos = lex.front.pos; - string kwd = lex.front.str; - if( tryEat("let") || tryEat("var") || tryEat("def") || tryEat("@") ) + if( auto e = Declaration() ) + return e; + lex = saved; + return TopLevelExpression(); + } + + AST Declaration() // returns null if it is not a declaration + { + /// Declaration ::= + /// ["@" Layer|"let"|"var"|"def"] Var "=" Expression ([";"|"in"] Body?)? + /// | ["@" Layer|"let"|"var"|"def"] Var "(" Param%"," ")" "{" Body "}" ([";"|"in"] Body?)? + + auto pos = currentPosition(); + string layer = ""; + + if( tryEat("@") ) { - if( kwd == "@" ) { - kwd ~= eatId("after @",true); - if( tryEat("(") ) { - lex = saved; - goto asExpression; - } - } - immutable LexPosition varpos = (lex.empty ? null : lex.front.pos); - string var = eatId("after "~kwd,true); - // [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)); - } + layer = "@" ~ eatId("after @", AllowQuoted); + if( tryEat("(") ) + return null; // @lay(...) expression, not a declaration } + + string kwd = layer; + if( layer.empty && !tryEat(kwd="let") && !tryEat(kwd="var") && !tryEat(kwd="def") ) + return null; // none of {@lay, let, var, def} occurred, it's not a declaration + + auto varpos = currentPosition(); + string var = eatId("after "~kwd, AllowQuoted); // name of the declared variable + + auto e = tryEat("(") + ? parseLambdaAfterOpenParen(varpos) // let var ( ... + : (eat("=", "after "~kwd), E(0)); // let var = ... + + if( moreDeclarationExists() ) + return new LetExpression(pos, var, layer, e, Body()); + else + return new LetExpression(pos, var, layer, e, new VarExpression(varpos, var)); + } + + AST TopLevelExpression() + { + /// TopLevelExpression ::= Expression ([";"|"in"] Body?)? + + auto pos = currentPosition(); + auto e = E(0); + if( moreDeclarationExists() ) + return new LetExpression(pos, "_", "", e, Body()); else - { - asExpression: - auto e = E(0); - if( tryEat(";") && !lex.empty && (lex.front.quoted || (lex.front.str!="}" && lex.front.str!=")")) ) - return new LetExpression(pos, "_", "", e, Body()); - else - return e; - } + return e; + } + + private bool moreDeclarationExists() + { + return (tryEat(";") || tryEat("in")) && !closingBracket(); + } + + private bool closingBracket() + { + return lex.empty || !lex.front.quoted && ["}",")","]"].canFind(lex.front.str); } - // [TODO] make customizable from program - static immutable string[][] operator_perferences = [ + // [TODO] make this customizable from program + private static string[][] operator_perferences = [ ["||"], ["&&"], ["!="], ["=="], ["<","<=",">",">="], @@ -109,47 +138,48 @@ ["&"], ["<<", ">>"], ["+","-"], ["~"], ["*","/","%"], - ["^^"] + ["^^","**"] ]; - AST E(int level) + AST E(size_t level) { + /// Expression ::= (Binary left-associative operators over) Funcall + + AST rec(AST lhs) + { + if( closingBracket() ) + return lhs; + + auto pos = currentPosition(); + foreach(op; operator_perferences[level]) + if( tryEat(op) ) + return rec( + new FuncallExpression(lhs.pos, new VarExpression(pos, op), lhs, E(level+1))); + return lhs; + } + if( operator_perferences.length <= level ) return Funcall(); else - { - auto ops = operator_perferences[level]; - auto e = E(level+1); - seq: - while( !lex.empty ) - { - auto pos = lex.front.pos; - foreach(op; ops) - if( tryEat(op) ) - { - e = new FuncallExpression(e.pos, new VarExpression(pos, op), e, E(level+1)); - continue seq; - } - break; - } - return e; - } + return rec(E(level+1)); } AST Funcall() { + /// Funcall ::= BaseExpression ["(" Expression%"," ")"]* + auto e = BaseExpression(); while( tryEat("(") ) { auto pos = currentPosition(); AST[] args; while( !tryEat(")") ) { if( lex.empty ) - throw genex!UnexpectedEOF(pos,"Closing ')' for arguments not found"); + throw genex!UnexpectedEOF(pos, "closing ')' for arguments not found"); args ~= E(0); if( !tryEat(",") ) { eat(")", "after function parameters"); break; } @@ -210,11 +240,11 @@ cond, new FunLiteral(thenPos, [], th), new FunLiteral(elsePos, [], el) ); } - if( tryEat("fun") || tryEat("\u03BB") ) + if( tryEat("fun") || tryEat("\u03BB") ) // lambda!! { eat("(", "after fun"); return parseLambdaAfterOpenParen(pos); } scope(exit) lex.popFront; @@ -224,11 +254,11 @@ AST parseLambdaAfterOpenParen(immutable LexPosition pos) { Parameter[] params; while( !tryEat(")") ) { - params ~= new Parameter(eatId("for function parameter"), []); + params ~= parseParam(); if( !tryEat(",") ) { eat(")", "after function parameters"); break; } } @@ -235,15 +265,38 @@ eat("{", "after function parameters"); auto funbody = Body(); eat("}", "after function body"); return new FunLiteral(pos, params, funbody); } + + Parameter parseParam() + { + string var; + string[] lay; + while( !closingBracket() && !lex.empty && lex.front.str!="," ) + { + auto pos = currentPosition(); + string p = eatId("for function parameter", AllowQuoted); + if( p == "@" ) + lay ~= "@" ~ eatId("after @", AllowQuoted); + else if( var.empty ) + var = p; + else + throw genex!ParseException(pos, "one parameter has two names"); + } + return new Parameter(var, lay); + } private: Lexer lex; this(Lexer lex) { this.lex = lex; } + bool isNumber(string s) + { + return find!(`a<'0' || '9'