1 module calcool.parser; 2 3 import std.stdio; 4 import std.array : front, popFront; 5 6 import calcool.lexer; 7 import calcool.token; 8 import calcool.expression; 9 import calcool.parselets; 10 import calcool.exceptions; 11 12 public class Parser { 13 private: 14 Lexer lexer; 15 Token[] input; 16 static const syntaxError = new ParseException("Syntax error"); 17 18 static immutable { 19 PrefixParselet[TokenType] prefixParselets; 20 InfixParselet[TokenType] infixParselets; 21 } 22 23 shared static this() { 24 prefixParselets[TokenType.NUMBER] = new NumberParselet(); 25 prefixParselets[TokenType.OP_MINUS] = new NegateParselet(); 26 prefixParselets[TokenType.FUNC] = new FuncParselet(); 27 prefixParselets[TokenType.PR_OPEN] = new GroupParselet(); 28 prefixParselets[TokenType.EOL] = new EolParselet(); 29 30 infixParselets[TokenType.OP_ADD] = infixParselets[TokenType.OP_MINUS] = new AddMinusParselet(); 31 infixParselets[TokenType.OP_MULT] = infixParselets[TokenType.OP_DIV] = new MultDivParselet(); 32 infixParselets[TokenType.OP_POW] = new PowerParselet(); 33 } 34 35 public: 36 this() { 37 lexer = new Lexer(); 38 } 39 40 this(File f) { 41 lexer = new Lexer(f); 42 } 43 44 void setInput(File f) { 45 lexer = new Lexer(f); 46 input.length = 0; 47 } 48 49 private Token consume() { 50 if (input.length == 0) { 51 input = lexer.nextLine(); 52 if (input.length == 0) { 53 throw new EofException(); 54 } 55 } 56 const f = input.front(); 57 input.popFront(); 58 return f; 59 } 60 61 Expression parseGroupExpression() { 62 auto inside = parseExpression(Precedence.START); 63 expect(TokenType.PR_CLOSE); 64 return inside; 65 } 66 67 Expression parseExpression() { 68 return parseExpression(Precedence.START, true); 69 } 70 71 Expression parseExpression(const Precedence precedence = Precedence.START, bool start = false) { 72 auto token = consume(); 73 while (start && token.type == TokenType.EOL) { 74 token = consume(); 75 } 76 if (auto parselet = token.type in prefixParselets) { 77 auto left = parselet.parse(this, token); 78 79 while (precedence < getPrecedence()) { 80 token = consume(); 81 immutable infix = infixParselets[token.type]; 82 left = infix.parse(this, left, token); 83 } 84 85 if (start && input.length > 0 && consume().type != TokenType.EOL) { 86 error(); 87 } 88 return left; 89 90 } else { 91 error(); 92 } 93 assert(false); 94 } 95 96 private Precedence getPrecedence() { 97 if (input.length > 0) { 98 const t = input.front().type; 99 if (t == TokenType.PR_CLOSE || t == TokenType.EOL) { 100 return Precedence.START; 101 } 102 103 if (auto parselet = t in infixParselets) { 104 return parselet.getPrecedence(); 105 } else { 106 error(); 107 } 108 } 109 110 return Precedence.START; 111 } 112 113 private void error() { 114 input.length = 0; 115 throw syntaxError; 116 } 117 118 void expect(const TokenType t) { 119 if ((input.length > 0 && input.front().type != t) || input.length == 0) { 120 import std.format : format; 121 122 input.length = 0; 123 throw new ParseException(format("Expected token of type %s", t)); 124 } 125 input.popFront(); 126 } 127 128 string evaluateFromString(in string exp) { 129 import std.conv : to; 130 131 input = lexer.nextLine(exp); 132 return parseExpression(Precedence.START, true).evaluate().to!string; 133 } 134 135 }