1 //  Toy interpreter parser.
2 //
3 // Copyright (C) 2014-2015 Iain Buclaw.
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 
14 // You should have received a copy of the GNU General Public License
15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 
17 // Written by Iain  Buclaw <ibuclaw@gdcproject.org>
18 
19 module toy.parse;
20 
21 import toy.ast;
22 import toy.combinator;
23 import toy.lex;
24 import toy.diag;
25 
26 ///// Parser /////
27 
28 Statement parse(Token[] tokens)
29 {
30     Parser ast = new Phrase(stmtList());
31     Result result = ast(tokens, 0);
32 
33     if (result.values.length != 1 || (cast(Statement) result.values[0]) is null)
34         throw new ParseError("error occurred at " ~ tokens[result.pos].value);
35 
36     return cast(Statement) result.values[0];
37 }
38 
39 /// Statements
40 
41 Parser stmtList()
42 {
43     return (stmtTerm() * new Reserved(";"))
44         & (Statement l, Keyword sep, Statement r) => new CompoundStatement(l, r);
45 }
46 
47 Parser stmtTerm()
48 {
49     return stmtAssign() | stmtIf() | stmtWhile() | stmtPrint();
50 }
51 
52 Parser stmtAssign()
53 {
54     return (valueIdent() + new Reserved(":=") + arithExp())
55         ^ (Expression name, Keyword eq, Expression value) => new AssignStatement(name, value);
56 }
57 
58 Parser stmtIf()
59 {
60     return (new Reserved("if") + boolExp()
61             + new Reserved("then") + new Lazy(&stmtList)
62             + new Optional((new Reserved("else") + new Lazy(&stmtList))
63                            ^ (Keyword e, Statement elsebody) => elsebody)
64             + new Reserved("end"))
65         ^ (Keyword i, Expression condition, Keyword t, Statement ifbody, Statement elsebody, Keyword e)
66             => new IfStatement(condition, ifbody, elsebody);
67 }
68 
69 Parser stmtWhile()
70 {
71     return (new Reserved("while") + boolExp()
72             + new Reserved("do") + new Lazy(&stmtList)
73             + new Reserved("end"))
74         ^ (Keyword w, Expression condition, Keyword d, Statement whilebody, Keyword e)
75             => new WhileStatement(condition, whilebody);
76 }
77 
78 Parser stmtPrint()
79 {
80     return (new Reserved("print") + (boolExp() | arithExp()))
81         ^ (Keyword w, Expression e) => new PrintStatement(e);
82 }
83 
84 /// Boolean Expressions
85 
86 Parser boolExp()
87 {
88     return (boolTerm() * (new Reserved("and") | new Reserved("or")))
89         & (Expression l, Keyword op, Expression r) => (op.value == "and") ? new AndExp(l, r) : new OrExp(l, r);
90 }
91 
92 Parser boolTerm()
93 {
94     return boolNot() | boolCmp() | boolGroup();
95 }
96 
97 Parser boolNot()
98 {
99     return (new Reserved("not") + new Lazy(&boolTerm))
100         ^ (Keyword op, Expression e) => new NotExp(e);
101 }
102 
103 Parser boolCmp()
104 {
105     return (arithExp() + (new Reserved("<") | new Reserved("<=")
106                           | new Reserved(">") | new Reserved(">=")
107                           | new Reserved("=") | new Reserved("!=")) + arithExp())
108         ^ (Expression l, Keyword op, Expression r) => new CmpExp(op.value, l, r);
109 }
110 
111 Parser boolGroup()
112 {
113     return (new Reserved("(") + new Lazy(&boolExp) + new Reserved(")"))
114         ^ (Keyword b0, Expression e, Keyword b1) => e;
115 }
116 
117 /// Aritmetic Expressions
118 
119 Parser arithExp()
120 {
121     return (arithTerm() * (new Reserved("/") | new Reserved("*") | new Reserved("+") | new Reserved("-")))
122         & (Expression l, Keyword op, Expression r) => new BinExp(op.value, l, r);
123 }
124 
125 Parser arithTerm()
126 {
127     return arithValue() | arithGroup();
128 }
129 
130 Parser arithGroup()
131 {
132     return (new Reserved("(") + new Lazy(&arithExp) + new Reserved(")"))
133         ^ (Keyword b0, Expression e, Keyword b1) => e;
134 }
135 
136 Parser arithValue()
137 {
138     return valueInt() | valueIdent();
139 }
140 
141 /// Value types.
142 
143 Parser valueInt()
144 {
145     return new TokenTag(Tag.Integer)
146         ^ (TokenClass e)
147         {
148             static IntegerExp[string] icache;
149 
150             if (e.value !in icache)
151                 icache[e.value] = new IntegerExp(e.value);
152 
153             return icache[e.value];
154         };
155 }
156 
157 Parser valueIdent()
158 {
159     return new TokenTag(Tag.Identifier)
160         ^ (TokenClass e)
161         {
162             static VarExp[string] vcache;
163 
164             if (e.value !in vcache)
165                 vcache[e.value] = new VarExp(e.value);
166 
167             return vcache[e.value];
168         };
169 }