423f308350 2010-11-07 kinaba: module polemy.parse; 423f308350 2010-11-07 kinaba: import polemy._common; 423f308350 2010-11-07 kinaba: /* 423f308350 2010-11-07 kinaba: * Author: k.inaba 423f308350 2010-11-07 kinaba: * License: NYSL 0.9982 (http://www.kmonos.net/nysl/ 423f308350 2010-11-07 kinaba: * Parser for the polemy programming language 423f308350 2010-11-07 kinaba: */ 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: import polemy.lex; 423f308350 2010-11-07 kinaba: import polemy.ast; 423f308350 2010-11-07 kinaba: import std.bigint; 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: /// Parsing Failure 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: class ParserException : Exception 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: private: 423f308350 2010-11-07 kinaba: this(string msg) { super(msg); } 423f308350 2010-11-07 kinaba: static ParserException create(Lexer)(Lexer lex, string msg) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: return new ParserException(sprintf!"[%s] %s"( 423f308350 2010-11-07 kinaba: lex.empty ? "EOF" : to!string(lex.front.pos), msg)); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: /// Named Constructor of Parser 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: auto parserFromLexer(Lexer)(Lexer lex) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: return new Parser!Lexer(lex); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: /// Named Constructor of Parser (just fwd to lexerFromString) 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: auto parserFromString(T...)(T params) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: return parserFromLexer(polemy.lex.lexerFromString(params)); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: /// Named Constructor of Parser (just fwd to lexerFromFile) 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: auto parserFromFile(T...)(T params) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: return parserFromLexer(polemy.lex.lexerFromFile(params)); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: /// Parser 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: class Parser(Lexer) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: this(Lexer lex) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: this.lex = lex; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: Program parseProgram() 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: Program p; 423f308350 2010-11-07 kinaba: while( !lex.empty ) 423f308350 2010-11-07 kinaba: p ~= parseStatement(); 423f308350 2010-11-07 kinaba: return p; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: Statement parseStatement() 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: auto saved = lex.save; 423f308350 2010-11-07 kinaba: scope(failure) lex = saved; 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: if( lex.empty ) 423f308350 2010-11-07 kinaba: throw new ParserException("EOF during parsing a statement"); 423f308350 2010-11-07 kinaba: auto pos = lex.front.pos; 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: if( lex.front.kind==Token.Kind.identifier && lex.front.str=="var" ) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: // "var" Var "=" Expression ";" 423f308350 2010-11-07 kinaba: lex.popFront; 423f308350 2010-11-07 kinaba: string var = lex.front.str; 423f308350 2010-11-07 kinaba: lex.popFront; 423f308350 2010-11-07 kinaba: eat("=", "for variable declaration"); 423f308350 2010-11-07 kinaba: auto parsed = new DeclStatement(pos, var, parseExpression()); 423f308350 2010-11-07 kinaba: eat(";", "after variable declaration"); 423f308350 2010-11-07 kinaba: return parsed; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: else 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: // Expression ";" 423f308350 2010-11-07 kinaba: auto parsed = new ExprStatement(pos, parseExpression()); 423f308350 2010-11-07 kinaba: eat(";", "after statement"); 423f308350 2010-11-07 kinaba: return parsed; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: Expression parseExpression() 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: auto saved = lex.save; 423f308350 2010-11-07 kinaba: scope(failure) lex = saved; 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: // Expr ::= E0 423f308350 2010-11-07 kinaba: // E0 ::= (E1 "=")* E1 423f308350 2010-11-07 kinaba: // E1 ::= (E2 "+"|"-")* E2 423f308350 2010-11-07 kinaba: // E2 ::= (E3 "*"|"/")* E3 423f308350 2010-11-07 kinaba: // E3 ::= int | str | id | "(" Expr ")" 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: return parseE0(); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: Expression parseE0() 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: auto lhs = parseE1(); 423f308350 2010-11-07 kinaba: if( tryEat("=") ) 423f308350 2010-11-07 kinaba: lhs = new BinOpExpression(lhs.pos, "=", lhs, parseE0()); 423f308350 2010-11-07 kinaba: return lhs; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: Expression parseE1() 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: for(auto lhs = parseE2();;) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: if( tryEat("+") ) 423f308350 2010-11-07 kinaba: lhs = new BinOpExpression(lhs.pos, "+", lhs, parseE2()); 423f308350 2010-11-07 kinaba: else if( tryEat("-") ) 423f308350 2010-11-07 kinaba: lhs = new BinOpExpression(lhs.pos, "-", lhs, parseE2()); 423f308350 2010-11-07 kinaba: else 423f308350 2010-11-07 kinaba: return lhs; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: Expression parseE2() 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: for(auto lhs = parseE3();;) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: if( tryEat("*") ) 423f308350 2010-11-07 kinaba: lhs = new BinOpExpression(lhs.pos, "*", lhs, parseE3()); 423f308350 2010-11-07 kinaba: else if( tryEat("/") ) 423f308350 2010-11-07 kinaba: lhs = new BinOpExpression(lhs.pos, "/", lhs, parseE3()); 423f308350 2010-11-07 kinaba: else 423f308350 2010-11-07 kinaba: return lhs; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: Expression parseE3() 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: if( lex.empty ) 423f308350 2010-11-07 kinaba: throw new ParserException("EOF during parsing an expression"); 423f308350 2010-11-07 kinaba: auto pos = lex.front.pos; 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: if( lex.front.kind == Token.Kind.number ) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: scope(exit) lex.popFront; 423f308350 2010-11-07 kinaba: return new IntLiteralExpression(pos, BigInt(cast(string)lex.front.str)); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: if( lex.front.kind == Token.Kind.stringLiteral ) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: scope(exit) lex.popFront; 423f308350 2010-11-07 kinaba: return new StrLiteralExpression(pos, lex.front.str); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: if( tryEat("(") ) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: auto e = parseE0(); 423f308350 2010-11-07 kinaba: eat(")", "after parenthesized expression"); 423f308350 2010-11-07 kinaba: return e; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: scope(exit) lex.popFront; 423f308350 2010-11-07 kinaba: return new VarExpression(pos, lex.front.str); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: private: 423f308350 2010-11-07 kinaba: Lexer lex; 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: void eat(string kwd, lazy string msg) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: if( !tryEat(kwd) ) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: auto e = ParserException.create(lex, "'"~kwd~"' is expected "~msg~" but '" 423f308350 2010-11-07 kinaba: ~(lex.empty ? "EOF" : lex.front.str)~"' occured"); 423f308350 2010-11-07 kinaba: throw e; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: bool tryEat(string kwd) 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: if( lex.empty || lex.front.kind!=Token.Kind.identifier || lex.front.str!=kwd ) 423f308350 2010-11-07 kinaba: return false; 423f308350 2010-11-07 kinaba: lex.popFront; 423f308350 2010-11-07 kinaba: return true; 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: unittest 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: auto p = parserFromString(` 423f308350 2010-11-07 kinaba: var x = 100; 423f308350 2010-11-07 kinaba: var y = 200; 423f308350 2010-11-07 kinaba: `); 423f308350 2010-11-07 kinaba: Program prog = p.parseProgram(); 423f308350 2010-11-07 kinaba: assert( prog.length == 2 ); 423f308350 2010-11-07 kinaba: auto p0 = cast(DeclStatement)prog[0]; 423f308350 2010-11-07 kinaba: auto p1 = cast(DeclStatement)prog[1]; 423f308350 2010-11-07 kinaba: assert( p0.var == "x" ); 423f308350 2010-11-07 kinaba: assert( p1.var == "y" ); 423f308350 2010-11-07 kinaba: assert( (cast(IntLiteralExpression)p0.expr).data == 100 ); 423f308350 2010-11-07 kinaba: assert( (cast(IntLiteralExpression)p1.expr).data == 200 ); 423f308350 2010-11-07 kinaba: } 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: unittest 423f308350 2010-11-07 kinaba: { 423f308350 2010-11-07 kinaba: auto p = parserFromString(` 423f308350 2010-11-07 kinaba: var zzz = 100; # comment 423f308350 2010-11-07 kinaba: zzz = zzz + zzz * "fo\no"; # comment 423f308350 2010-11-07 kinaba: 42; 423f308350 2010-11-07 kinaba: `); 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: auto s0 = new DeclStatement(null, "zzz", new IntLiteralExpression(null, BigInt(100))); 423f308350 2010-11-07 kinaba: auto s1 = new ExprStatement(null, new BinOpExpression(null, "=", 423f308350 2010-11-07 kinaba: new VarExpression(null, "zzz"), 423f308350 2010-11-07 kinaba: new BinOpExpression(null, "+", 423f308350 2010-11-07 kinaba: new VarExpression(null, "zzz"), 423f308350 2010-11-07 kinaba: new BinOpExpression(null, "*", 423f308350 2010-11-07 kinaba: new VarExpression(null, "zzz"), 423f308350 2010-11-07 kinaba: new StrLiteralExpression(null, "fo\\no") 423f308350 2010-11-07 kinaba: )))); 423f308350 2010-11-07 kinaba: auto s2 = new ExprStatement(null, new IntLiteralExpression(null, BigInt(42))); 423f308350 2010-11-07 kinaba: 423f308350 2010-11-07 kinaba: Program prog = p.parseProgram(); 423f308350 2010-11-07 kinaba: assert( prog.length == 3 ); 423f308350 2010-11-07 kinaba: assert( prog[0] == s0 ); 423f308350 2010-11-07 kinaba: assert( prog[1] == s1 ); 423f308350 2010-11-07 kinaba: assert( prog[2] == s2 ); 423f308350 2010-11-07 kinaba: }