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 
14 private:
15     real[string] variables;
16     Lexer lexer;
17     Token[] input;
18     static const syntaxError = new ParseException("Syntax error");
19 
20     static immutable {
21         PrefixParselet[TokenType] prefixParselets;
22         InfixParselet[TokenType] infixParselets;
23     }
24 
25     shared static this() {
26         prefixParselets[TokenType.NUMBER] = new NumberParselet();
27         prefixParselets[TokenType.IDENTIFIER] = new IdentifierParselet();
28         prefixParselets[TokenType.OP_MINUS] = new NegateParselet();
29         prefixParselets[TokenType.FUNC] = new FuncParselet();
30         prefixParselets[TokenType.PR_OPEN] = new GroupParselet();
31         prefixParselets[TokenType.EOL] = new EolParselet();
32 
33         infixParselets[TokenType.OP_ADD] = infixParselets[TokenType.OP_MINUS] = new AddMinusParselet();
34         infixParselets[TokenType.OP_MULT] = infixParselets[TokenType.OP_DIV] = new MultDivParselet();
35         infixParselets[TokenType.OP_POW] = new PowerParselet();
36     }
37 
38 public:
39     this() {
40         lexer = new Lexer();
41     }
42 
43     this(File f) {
44         lexer = new Lexer(f);
45     }
46 
47     void setInput(File f) {
48         lexer = new Lexer(f);
49         input.length = 0;
50     }
51 
52     const auto ref getVariables() {
53         return variables;
54     }
55 
56     void setVariable(string name, real value) {
57         import std.algorithm : canFind;
58 
59         if (FuncExpression.funcNames().canFind(name)) {
60             error("Cannot use reserved function name '%s' as a variable", name);
61         }
62         variables[name] = value;
63     }
64 
65     private Token consume() {
66         if (input.length == 0) {
67             input = lexer.nextLine();
68             if (input.length == 0) {
69                 throw new EofException();
70             }
71         }
72         const f = input.front();
73         input.popFront();
74         return f;
75     }
76 
77     Expression parseGroupExpression() {
78         auto inside = parseExpression(Precedence.START);
79         expect(TokenType.PR_CLOSE);
80         return inside;
81     }
82 
83     Expression parseExpression() {
84         return parseExpression(Precedence.START, true);
85     }
86 
87     Expression parseExpression(const Precedence precedence = Precedence.START, bool start = false) {
88         auto token = consume();
89         while (start && token.type == TokenType.EOL) {
90             token = consume();
91         }
92 
93         if (start && token.type == TokenType.SET_VAR) {
94             const variable = expect(TokenType.IDENTIFIER);
95             expect(TokenType.EQUALS);
96             if (input.front().type == TokenType.EOL) {
97                 error();
98             }
99             auto expr = parseExpression(Precedence.START, true);
100             setVariable(variable.value, expr.evaluate());
101             return expr;
102         }
103 
104         if (auto parselet = token.type in prefixParselets) {
105             auto left = parselet.parse(this, token);
106 
107             while (precedence < getPrecedence()) {
108                 token = consume();
109                 immutable infix = infixParselets[token.type];
110                 left = infix.parse(this, left, token);
111             }
112 
113             if (start && input.length > 0 && consume().type != TokenType.EOL) {
114                 error();
115             }
116             return left;
117 
118         } else {
119             error();
120         }
121         assert(false);
122     }
123 
124     private Precedence getPrecedence() {
125         if (input.length > 0) {
126             const t = input.front().type;
127             if (t == TokenType.PR_CLOSE || t == TokenType.EOL) {
128                 return Precedence.START;
129             }
130 
131             if (auto parselet = t in infixParselets) {
132                 return parselet.getPrecedence();
133             } else {
134                 error();
135             }
136         }
137 
138         return Precedence.START;
139     }
140 
141     private noreturn error(const ParseException exception = syntaxError) {
142         input.length = 0;
143         throw exception;
144     }
145 
146     private noreturn error(Args...)(const string msg, Args args) {
147         import std.format : format;
148 
149         return error(new ParseException(format(msg, args)));
150     }
151 
152     auto expect(const TokenType t) {
153         if ((input.length > 0 && input.front().type != t) || input.length == 0) {
154             error("Expected token of type %s", t);
155         }
156         const res = input.front();
157         input.popFront();
158         return res;
159     }
160 
161     string evaluateFromString(in string exp) {
162         import std.conv : to;
163 
164         input = lexer.nextLine(exp);
165         return parseExpression(Precedence.START, true).evaluate().to!string;
166     }
167 
168 }