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 }