Index: .poseidon ================================================================== --- .poseidon +++ .poseidon @@ -31,15 +31,13 @@ d2stacktrace\dbghelp.d d2stacktrace\stacktrace.d main.d polemy\_common.d polemy\ast.d - polemy\eval.d polemy\lex.d polemy\parse.d polemy\tricks.d - polemy\value.d Index: main.d ================================================================== --- main.d +++ main.d @@ -4,11 +4,10 @@ * * Entry point for Polemy interpreter. */ import std.stdio; -import polemy.eval; version(unittest) static ~this() { writeln( "***********************" ); writeln( "* All Tests Passed <3 *" ); Index: polemy/ast.d ================================================================== --- polemy/ast.d +++ polemy/ast.d @@ -6,89 +6,71 @@ */ module polemy.ast; import polemy._common; import polemy.lex; -alias Statement[] Program; - -abstract class Statement +abstract class AST { immutable LexPosition pos; mixin SimpleConstructor; } -class DeclStatement : Statement -{ - string var; - Expression expr; - mixin SimpleClass; -} - -class ExprStatement : Statement -{ - Expression expr; - mixin SimpleClass; -} - -abstract class Expression -{ - immutable LexPosition pos; - mixin SimpleConstructor; -} - -class StrLiteralExpression : Expression +class StrLiteral : AST { string data; mixin SimpleClass; } -class IntLiteralExpression : Expression +class IntLiteral : AST { - BigInt data; + BigInt data; mixin SimpleClass; + this(immutable LexPosition pos, long n) {super(pos); data = n;} + this(immutable LexPosition pos, BigInt n) {super(pos); data = n;} + this(immutable LexPosition pos, string n) {super(pos); data = BigInt(n);} } -class VarExpression : Expression +class VarExpression : AST { string var; mixin SimpleClass; } -class AssignExpression : Expression -{ - Expression lhs; - Expression rhs; +class LetExpression : AST +{ + string var; + AST init; + AST expr; mixin SimpleClass; -} - -class FuncallExpression : Expression +} + +class FuncallExpression : AST { - Expression fun; - Expression[] args; - this(immutable LexPosition pos, Expression fun, Expression[] args...) + AST fun; + AST[] args; + this(immutable LexPosition pos, AST fun, AST[] args...) { super(pos); this.fun=fun; this.args=args.dup; } mixin SimpleClass; } - -class FunLiteralExpression : Expression -{ - string[] params; - Program funbody; - mixin SimpleClass; -} + +class FunLiteral : AST +{ + string[] params; + AST funbody; + mixin SimpleClass; +} /// Handy Generator for AST nodes. To use this, mixin EasyAst; /*mixin*/ -template EasyAst() +template EasyAST() { template genEast(T) { T genEast(P...)(P ps) { return new T(LexPosition.dummy, ps); } } - alias genEast!DeclStatement decl; - alias genEast!ExprStatement expr; + alias genEast!StrLiteral strl; + alias genEast!IntLiteral intl; + auto fun(string[] xs, AST ps) { return genEast!FunLiteral(xs,ps); } alias genEast!VarExpression var; - alias genEast!FuncallExpression funcall; - alias genEast!IntLiteralExpression intl; - alias genEast!StrLiteralExpression strl; - alias genEast!FunLiteralExpression fun; + alias genEast!LetExpression let; + alias genEast!FuncallExpression call; } Index: polemy/lex.d ================================================================== --- polemy/lex.d +++ polemy/lex.d @@ -11,22 +11,23 @@ /// Exception from this module class LexException : Exception { - this( const LexPosition pos, string msg ) - { super(sprintf!"%s [%s]"(msg, pos)); this.pos = pos; } const LexPosition pos; + + private this( const LexPosition pos, string msg ) + { super(sprintf!"%s [%s]"(msg, pos)); this.pos = pos; } }; /// Represents a position in a source code class LexPosition { immutable string filename; /// name of the source file - immutable int lineno; /// line number, 1, 2, ... - immutable int column; /// column, 1, 2, ... + immutable int lineno; /// 1-origin + immutable int column; /// 1-origin override string toString() const { return sprintf!"%s:%d:%d"(filename, lineno, column); } mixin SimpleConstructor; @@ -85,27 +86,25 @@ assert( !__traits(compiles, t.pos=p) ); assert( !__traits(compiles, t.str=789) ); assert( !__traits(compiles, t.quoted=true) ); } -/// Named Construtor for Lexer +/// Named Construtors for Lexer auto lexerFromFile(T...)( string filename, T rest ) { return lexerFromString( std.file.readText(filename), filename, rest ); } -/// Named Construtor for Lexer - auto lexerFromString(CharSeq)( CharSeq str, string filename="", int lineno=1, int column=1 ) { return new LexerT!(PositionedReader!CharSeq)( PositionedReader!CharSeq(str, filename, lineno, column) ); } -/// Standard Lexer Type (all users have to know is that this is a forward range of Tokens) +/// Standard Lexer Type (all you have to know is that this is a forward range of Tokens) alias LexerT!(PositionedReader!string) Lexer; /// Lexer Implementation Index: polemy/parse.d ================================================================== --- polemy/parse.d +++ polemy/parse.d @@ -6,361 +6,301 @@ */ module polemy.parse; import polemy._common; import polemy.lex; import polemy.ast; + +/// Exception from this module -/// Parsing Failure - -class ParserException : Exception +class ParseException : Exception { -private: - this(string msg) { super(msg); } - static ParserException create(Lexer)(Lexer lex, string msg) - { - return new ParserException(sprintf!"[%s] %s"( - lex.empty ? "EOF" : to!string(lex.front.pos), msg)); - } + const LexPosition pos; + + private this( const LexPosition pos, string msg ) + { super(sprintf!"%s [%s]"(msg, pos)); this.pos = pos; } } + +private auto createException(Lexer)(Lexer lex, string msg) + { return new ParseException(lex.empty?null:lex.front.pos, msg); } +/// Entry point of this module + +auto parseString(S, T...)(S str, T fn_ln_cn) + { return parserFromString(str, fn_ln_cn).parse(); } + +auto parseFile(S, T...)(S filename,T ln_cn) + { return parserFromString(filename, ln_cn).parse(); } + /// Named Constructor of Parser + +private auto parserFromLexer(Lexer)(Lexer lex) + { return new Parser!Lexer(lex); } -auto parserFromLexer(Lexer)(Lexer lex) -{ - return new Parser!Lexer(lex); -} +private auto parserFromString(T...)(T params) + { return parserFromLexer(polemy.lex.lexerFromString(params)); } -/// Named Constructor of Parser (just fwd to lexerFromString) - -auto parserFromString(T...)(T params) -{ - return parserFromLexer(polemy.lex.lexerFromString(params)); -} - -/// Named Constructor of Parser (just fwd to lexerFromFile) - -auto parserFromFile(T...)(T params) -{ - return parserFromLexer(polemy.lex.lexerFromFile(params)); -} - +private auto parserFromFile(T...)(T params) + { return parserFromLexer(polemy.lex.lexerFromFile(params)); } + /// Parser -class Parser(Lexer) -{ - this(Lexer lex) - { - this.lex = lex; - } - - Program parseProgram() - { - Program p = parseStatements(); - if( !lex.empty ) { - auto e = ParserException.create(lex, "cannot reach eof"); - throw e; - } - return p; - } - - Program parseStatements() - { - Program p; - while( !lex.empty && (lex.front.quoted || lex.front.str!="}") ) - p ~= parseStatement(); - return p; - } - - Statement parseStatement() - { - auto saved = lex.save; - scope(failure) lex = saved; - - if( lex.empty ) - throw new ParserException("EOF during parsing a statement"); - auto pos = lex.front.pos; +private class Parser(Lexer) + if( isForwardRange!(Lexer) && is(ElementType!(Lexer) == Token) ) +{ + AST parse() + { + auto e = Body(); + if( !lex.empty ) + throw createException(lex, "input is not ended but parser came to the end"); + return e; + } + + AST Body() + { + if( lex.empty || !lex.front.quoted && lex.front.str=="}" ) + return doNothingExpression(); + + auto pos = lex.front.pos; + if( tryEat("var") ) + { + immutable LexPosition varpos = (lex.empty ? null : lex.front.pos); + string var = eatId("after var"); + eat("=", "after var"); + auto e = E(0); + if( tryEat(";") && !lex.empty && (lex.front.quoted || (lex.front.str!="}" && lex.front.str!=")")) ) + return new LetExpression(pos, var, e, Body()); + else + return new LetExpression(pos, var, e, new VarExpression(varpos, var)); + } + else + { + 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; + } + } - if( !lex.front.quoted && lex.front.str=="var" ) - { - // "var" Var "=" Expression ";" - lex.popFront; - string var = lex.front.str; - lex.popFront; - eat("=", "for variable declaration"); - auto parsed = new DeclStatement(pos, var, parseExpression()); - eat(";", "after variable declaration"); - return parsed; - } - else - { - // Expression ";" - auto parsed = new ExprStatement(pos, parseExpression()); - eat(";", "after statement"); - return parsed; - } - } - - Expression parseExpression() - { - auto saved = lex.save; - scope(failure) lex = saved; - return parseE(0); - } - - // [TODO] multi-char operators are not supported by the lexer... + // [TODO] make customizable from program static immutable string[][] operator_perferences = [ - ["="], - ["or"], - ["and"], + ["||"], + ["&&"], ["!="], ["=="], ["<","<=",">",">="], ["|"], ["^"], ["&"], ["<<", ">>"], - ["+","-"], - ["*","/","%"] + ["+","-"], + ["~"], + ["*","/","%"], + ["^^"] ]; - Expression parseE(int level = 0) + AST E(int level) { if( operator_perferences.length <= level ) - return parseBaseExpression(); + return Funcall(); else { auto ops = operator_perferences[level]; - auto e = parseE(level+1); + auto e = E(level+1); seq: while( !lex.empty ) { auto pos = lex.front.pos; foreach(op; ops) if( tryEat(op) ) { - if( op == "=" ) // right assoc - return new AssignExpression(e.pos, e, parseE(level)); - else - e = new FuncallExpression(e.pos, new VarExpression(pos, op), e, parseE(level+1)); + e = new FuncallExpression(e.pos, new VarExpression(pos, op), e, E(level+1)); continue seq; } break; } return e; } } - Expression parseBaseExpression() + AST Funcall() { - if( lex.empty ) - throw new ParserException("EOF during parsing an expression"); - auto pos = lex.front.pos; - Expression e = parseBaseBaseExpression(); - while( tryEat("(") ) // funcall - { - Expression[] args; + auto e = BaseExpression(); + while( tryEat("(") ) + { + AST[] args; while( !tryEat(")") ) { - if( lex.empty ) { - auto ex = ParserException.create(lex,"Unexpected EOF"); - throw ex; - } - args ~= parseE(); + if( lex.empty ) + throw createException(lex,"Unexpected EOF"); + args ~= E(0); if( !tryEat(",") ) { eat(")", "after function parameters"); break; } } - e = new FuncallExpression(pos, e, args); + e = new FuncallExpression(e.pos, e, args); } return e; } - Expression parseBaseBaseExpression() + AST BaseExpression() { - if( lex.empty ) - throw new ParserException("EOF during parsing an expression"); + if( lex.empty ) + throw createException(lex, "Reached EOF when tried to parse an expression"); + auto pos = lex.front.pos; - if( lex.front.quoted ) { scope(exit) lex.popFront; - return new StrLiteralExpression(pos, lex.front.str); + return new StrLiteral(pos, lex.front.str); } - if( isNumber(lex.front.str) ) // is_number + if( isNumber(lex.front.str) ) { scope(exit) lex.popFront; - return new IntLiteralExpression(pos, BigInt(cast(string)lex.front.str)); + return new IntLiteral(pos, BigInt(cast(string)lex.front.str)); } if( tryEat("(") ) { - auto e = parseE(); + auto e = Body(); eat(")", "after parenthesized expression"); return e; } if( tryEat("if") ) { eat("(", "after if"); - auto cond = parseE(); + auto cond = E(0); eat(")", "after if condition"); auto thenPos = lex.front.pos; eat("{", "after if condition"); - Statement[] th = parseStatements(); + auto th = Body(); eat("}", "after if-then body"); - Statement[] el; - auto elsePos = lex.front.pos; + auto el = doNothingExpression(); + auto elsePos = (lex.empty ? LexPosition.dummy : lex.front.pos); if( tryEat("else") ) { eat("{", "after else"); - el = parseStatements(); + el = Body(); eat("}", "after else body"); } return new FuncallExpression(pos, new VarExpression(pos, "if"), cond, - new FunLiteralExpression(thenPos, [], th), - new FunLiteralExpression(elsePos, [], el) + new FunLiteral(thenPos, [], th), + new FunLiteral(elsePos, [], el) ); } - if( tryEat("fun") ) { eat("(", "after fun"); string[] params; - while(!tryEat(")")) + while( !tryEat(")") ) { - if( lex.empty ) { - auto e = ParserException.create(lex,"Unexpected EOF"); - throw e; - } - if( lex.front.quoted ) { - auto e = ParserException.create(lex,"Identifier Expected for parameters"); - throw e; - } - params ~= lex.front.str; - lex.popFront; + params ~= eatId("for function parameter"); 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); + eat("{", "after function parameters"); + auto funbody = Body(); + eat("}", "after function body"); + return new FunLiteral(pos, params, funbody); } scope(exit) lex.popFront; return new VarExpression(pos, lex.front.str); } private: - Lexer lex; + Lexer lex; + this(Lexer lex) { this.lex = lex; } void eat(string kwd, lazy string msg) { if( !tryEat(kwd) ) - { - auto e = ParserException.create(lex, "'"~kwd~"' is expected "~msg~" but '" + throw createException(lex, "'"~kwd~"' is expected "~msg~" but '" ~(lex.empty ? "EOF" : lex.front.str)~"' occured"); - throw e; - } } bool tryEat(string kwd) { if( lex.empty || lex.front.quoted || lex.front.str!=kwd ) return false; lex.popFront; return true; } + + string eatId(lazy string msg) + { + if( lex.empty || lex.front.quoted ) + throw createException(lex, "identifier is expected but not found "~msg); + string id = lex.front.str; + lex.popFront; + return id; + } bool isNumber(string s) { return find!(`a<'0'||'9'