File Annotation
Not logged in
820e7198cc 2010-11-07        kinaba: /**
4198578702 2010-11-07        kinaba:  * Authors: k.inaba
4198578702 2010-11-07        kinaba:  * License: NYSL 0.9982 http://www.kmonos.net/nysl/
4198578702 2010-11-07        kinaba:  *
4198578702 2010-11-07        kinaba:  * Parser for Polemy programming language
423f308350 2010-11-07        kinaba:  */
4198578702 2010-11-07        kinaba: module polemy.parse;
4198578702 2010-11-07        kinaba: import polemy._common;
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;
5d4cb856d8 2010-11-07        kinaba: 		return parseE(0);
423f308350 2010-11-07        kinaba: 	}
423f308350 2010-11-07        kinaba: 
5d4cb856d8 2010-11-07        kinaba: 	// [TODO] multi-char operators are not supported by the lexer...
5d4cb856d8 2010-11-07        kinaba: 	static immutable string[][] operator_perferences = [
3f5dc76a75 2010-11-07        kinaba: 		["="],
5d4cb856d8 2010-11-07        kinaba: 		["or"],
5d4cb856d8 2010-11-07        kinaba: 		["and"],
5d4cb856d8 2010-11-07        kinaba: 		["!="],
5d4cb856d8 2010-11-07        kinaba: 		["=="],
5d4cb856d8 2010-11-07        kinaba: 		["<","<=",">",">="],
5d4cb856d8 2010-11-07        kinaba: 		["|"],
5d4cb856d8 2010-11-07        kinaba: 		["^"],
5d4cb856d8 2010-11-07        kinaba: 		["&"],
5d4cb856d8 2010-11-07        kinaba: 		["<<", ">>"],
5d4cb856d8 2010-11-07        kinaba: 		["+","-"],
5d4cb856d8 2010-11-07        kinaba: 		["*","/","%"]
5d4cb856d8 2010-11-07        kinaba: 	];
423f308350 2010-11-07        kinaba: 
5d4cb856d8 2010-11-07        kinaba: 	Expression parseE(int level = 0)
423f308350 2010-11-07        kinaba: 	{
5d4cb856d8 2010-11-07        kinaba: 		if( operator_perferences.length <= level )
5d4cb856d8 2010-11-07        kinaba: 			return parseBaseExpression();
5d4cb856d8 2010-11-07        kinaba: 		else
423f308350 2010-11-07        kinaba: 		{
5d4cb856d8 2010-11-07        kinaba: 			auto ops = operator_perferences[level];
5d4cb856d8 2010-11-07        kinaba: 			auto e = parseE(level+1);
5d4cb856d8 2010-11-07        kinaba: 		seq:
5d4cb856d8 2010-11-07        kinaba: 			while( !lex.empty )
5d4cb856d8 2010-11-07        kinaba: 			{
5d4cb856d8 2010-11-07        kinaba: 				auto pos = lex.front.pos;
5d4cb856d8 2010-11-07        kinaba: 				foreach(op; ops)
5d4cb856d8 2010-11-07        kinaba: 					if( tryEat(op) )
5d4cb856d8 2010-11-07        kinaba: 					{
5d4cb856d8 2010-11-07        kinaba: 						if( op == "=" ) // right assoc
5d4cb856d8 2010-11-07        kinaba: 							return new AssignExpression(e.pos, e, parseE(level));
5d4cb856d8 2010-11-07        kinaba: 						else
5d4cb856d8 2010-11-07        kinaba: 							e = new FuncallExpression(e.pos, new VarExpression(pos, op), e, parseE(level+1));
5d4cb856d8 2010-11-07        kinaba: 						continue seq;
5d4cb856d8 2010-11-07        kinaba: 					}
5d4cb856d8 2010-11-07        kinaba: 				break;
5d4cb856d8 2010-11-07        kinaba: 			}
5d4cb856d8 2010-11-07        kinaba: 			return e;
423f308350 2010-11-07        kinaba: 		}
423f308350 2010-11-07        kinaba: 	}
423f308350 2010-11-07        kinaba: 
5d4cb856d8 2010-11-07        kinaba: 	Expression parseBaseExpression()
423f308350 2010-11-07        kinaba: 	{
5d4cb856d8 2010-11-07        kinaba: 		if( lex.empty )
5d4cb856d8 2010-11-07        kinaba: 			throw new ParserException("EOF during parsing an expression");
5d4cb856d8 2010-11-07        kinaba: 		auto pos = lex.front.pos;
3f5dc76a75 2010-11-07        kinaba: 		Expression e = parseBaseBaseExpression();
3f5dc76a75 2010-11-07        kinaba: 		while( tryEat("(") ) // funcall
3f5dc76a75 2010-11-07        kinaba: 		{
3f5dc76a75 2010-11-07        kinaba: 			Expression[] args;
3f5dc76a75 2010-11-07        kinaba: 			while( !tryEat(")") ) {
3f5dc76a75 2010-11-07        kinaba: 				if( lex.empty ) {
3f5dc76a75 2010-11-07        kinaba: 					auto ex = ParserException.create(lex,"Unexpected EOF");
3f5dc76a75 2010-11-07        kinaba: 					throw ex;
3f5dc76a75 2010-11-07        kinaba: 				}
3f5dc76a75 2010-11-07        kinaba: 				args ~= parseE();
3f5dc76a75 2010-11-07        kinaba: 				if( !tryEat(",") ) {
3f5dc76a75 2010-11-07        kinaba: 					eat(")", "after function parameters");
3f5dc76a75 2010-11-07        kinaba: 					break;
3f5dc76a75 2010-11-07        kinaba: 				}
3f5dc76a75 2010-11-07        kinaba: 			}
3f5dc76a75 2010-11-07        kinaba: 			e = new FuncallExpression(pos, e, args);
3f5dc76a75 2010-11-07        kinaba: 		}
3f5dc76a75 2010-11-07        kinaba: 		return e;
3f5dc76a75 2010-11-07        kinaba: 	}
3f5dc76a75 2010-11-07        kinaba: 
3f5dc76a75 2010-11-07        kinaba: 	Expression parseBaseBaseExpression()
3f5dc76a75 2010-11-07        kinaba: 	{
3f5dc76a75 2010-11-07        kinaba: 		if( lex.empty )
3f5dc76a75 2010-11-07        kinaba: 			throw new ParserException("EOF during parsing an expression");
3f5dc76a75 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: 		{
5d4cb856d8 2010-11-07        kinaba: 			auto e = parseE();
423f308350 2010-11-07        kinaba: 			eat(")", "after parenthesized expression");
423f308350 2010-11-07        kinaba: 			return e;
3f5dc76a75 2010-11-07        kinaba: 		}
3f5dc76a75 2010-11-07        kinaba: 
3f5dc76a75 2010-11-07        kinaba: 		if( tryEat("fun") )
3f5dc76a75 2010-11-07        kinaba: 		{
3f5dc76a75 2010-11-07        kinaba: 			eat("(", "after fun");
3f5dc76a75 2010-11-07        kinaba: 			string[] params;
0569f7b8c2 2010-11-07        kinaba: 			while(!tryEat(")"))
3f5dc76a75 2010-11-07        kinaba: 			{
3f5dc76a75 2010-11-07        kinaba: 				if( lex.empty ) {
3f5dc76a75 2010-11-07        kinaba: 					auto e = ParserException.create(lex,"Unexpected EOF");
3f5dc76a75 2010-11-07        kinaba: 					throw e;
3f5dc76a75 2010-11-07        kinaba: 				}
3f5dc76a75 2010-11-07        kinaba: 				if( lex.front.kind != Token.Kind.identifier ) {
3f5dc76a75 2010-11-07        kinaba: 					auto e = ParserException.create(lex,"Identifier Expected for parameters");
3f5dc76a75 2010-11-07        kinaba: 					throw e;
3f5dc76a75 2010-11-07        kinaba: 				}
3f5dc76a75 2010-11-07        kinaba: 				params ~= lex.front.str;
3f5dc76a75 2010-11-07        kinaba: 				lex.popFront;
3f5dc76a75 2010-11-07        kinaba: 				if( !tryEat(",") ) {
3f5dc76a75 2010-11-07        kinaba: 					eat(")", "after function parameters");
3f5dc76a75 2010-11-07        kinaba: 					break;
3f5dc76a75 2010-11-07        kinaba: 				}
3f5dc76a75 2010-11-07        kinaba: 			}
3f5dc76a75 2010-11-07        kinaba: 			eat("{", "after function parameters");
3f5dc76a75 2010-11-07        kinaba: 			Statement[] funbody;
3f5dc76a75 2010-11-07        kinaba: 			while(!tryEat("}")) {
3f5dc76a75 2010-11-07        kinaba: 				if( lex.empty ) {
3f5dc76a75 2010-11-07        kinaba: 					auto e = ParserException.create(lex,"Unexpected EOF");
3f5dc76a75 2010-11-07        kinaba: 					throw e;
3f5dc76a75 2010-11-07        kinaba: 				}
3f5dc76a75 2010-11-07        kinaba: 				funbody ~= parseStatement();
3f5dc76a75 2010-11-07        kinaba: 			}
3f5dc76a75 2010-11-07        kinaba: 			return new FunLiteralExpression(pos, params, funbody);
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)));
5d4cb856d8 2010-11-07        kinaba: 	auto s1 = new ExprStatement(null, new AssignExpression(null,
423f308350 2010-11-07        kinaba: 		new VarExpression(null, "zzz"),
5d4cb856d8 2010-11-07        kinaba: 		new FuncallExpression(null, new VarExpression(null,"+"),
423f308350 2010-11-07        kinaba: 			new VarExpression(null, "zzz"),
5d4cb856d8 2010-11-07        kinaba: 			new FuncallExpression(null, new VarExpression(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 );
3f5dc76a75 2010-11-07        kinaba: }
3f5dc76a75 2010-11-07        kinaba: 
3f5dc76a75 2010-11-07        kinaba: unittest
3f5dc76a75 2010-11-07        kinaba: {
3f5dc76a75 2010-11-07        kinaba: 	auto p = parserFromString(`
3f5dc76a75 2010-11-07        kinaba: 		var f = fun(x,y){x+y;};
3f5dc76a75 2010-11-07        kinaba: 		f(1,fun(abc){}(4));
3f5dc76a75 2010-11-07        kinaba: 	`);
3f5dc76a75 2010-11-07        kinaba: 	Program prog = p.parseProgram();
3f5dc76a75 2010-11-07        kinaba: 	assert( prog.length == 2 );
3f5dc76a75 2010-11-07        kinaba: 	assert( prog[0] == new DeclStatement(null, "f", new FunLiteralExpression(null,
3f5dc76a75 2010-11-07        kinaba: 		["x","y"], [new ExprStatement(null,
3f5dc76a75 2010-11-07        kinaba: 			new FuncallExpression(null, new VarExpression(null, "+"),
3f5dc76a75 2010-11-07        kinaba: 			new VarExpression(null, "x"), new VarExpression(null, "y")))]
3f5dc76a75 2010-11-07        kinaba: 			)));
3f5dc76a75 2010-11-07        kinaba: 	assert( prog[1] == new ExprStatement(null, new FuncallExpression(null,
3f5dc76a75 2010-11-07        kinaba: 		new VarExpression(null, "f"),
3f5dc76a75 2010-11-07        kinaba: 		new IntLiteralExpression(null, BigInt(1)),
3f5dc76a75 2010-11-07        kinaba: 		new FuncallExpression(null,
3f5dc76a75 2010-11-07        kinaba: 			new FunLiteralExpression(null, ["abc"], [
3f5dc76a75 2010-11-07        kinaba: 			]),
3f5dc76a75 2010-11-07        kinaba: 			new IntLiteralExpression(null, BigInt(4))
3f5dc76a75 2010-11-07        kinaba: 			))));
0569f7b8c2 2010-11-07        kinaba: }
0569f7b8c2 2010-11-07        kinaba: unittest
0569f7b8c2 2010-11-07        kinaba: {
0569f7b8c2 2010-11-07        kinaba: 	auto p = parserFromString(`var x = 1; var f = fun(){x=x+1;}; f(); f(); x;`);
0569f7b8c2 2010-11-07        kinaba: 	Program prog = p.parseProgram();
423f308350 2010-11-07        kinaba: }