148
|
1 from . import astnodes, lexer, semantics
|
152
|
2 from ppci import CompilerError
|
148
|
3
|
|
4 # binop precedence for expressions:
|
|
5 binopPrecs = {'or': 5, 'and': 10, \
|
|
6 '<': 20, '>': 20, '==': 20, '<=': 20, '>=': 20, '!=': 20, \
|
|
7 '+': 30, '-': 30, '*': 40, '/': 40 }
|
|
8
|
|
9 class Parser:
|
|
10 """ Parses sourcecode into an abstract syntax tree (AST) """
|
166
|
11 def __init__(self, diag):
|
|
12 self.sema = semantics.Semantics(diag)
|
148
|
13 self.diag = diag
|
|
14 def parseSource(self, source):
|
|
15 self.initLex(source)
|
|
16 try:
|
|
17 self.parsePackage()
|
152
|
18 except CompilerError as e:
|
|
19 self.diag.addDiag(e)
|
166
|
20 return self.sema.mod
|
148
|
21 def Error(self, msg):
|
152
|
22 raise CompilerError(msg, self.token.loc)
|
148
|
23 # Lexer helpers:
|
|
24 def Consume(self, typ):
|
|
25 if self.Peak == typ:
|
|
26 return self.NextToken()
|
|
27 else:
|
|
28 self.Error('Excected: "{0}", got "{1}"'.format(typ, self.Peak))
|
|
29 @property
|
|
30 def Peak(self):
|
|
31 return self.token.typ
|
|
32 @property
|
|
33 def PeakPrec(self):
|
149
|
34 if self.Peak in binopPrecs:
|
|
35 return binopPrecs[self.Peak]
|
|
36 return -1
|
148
|
37 def hasConsumed(self, typ):
|
|
38 if self.Peak == typ:
|
|
39 self.Consume(typ)
|
|
40 return True
|
|
41 return False
|
|
42 def NextToken(self):
|
|
43 t = self.token
|
166
|
44 if t.typ != 'END':
|
|
45 self.token = self.tokens.__next__()
|
148
|
46 return t
|
|
47 def initLex(self, source):
|
|
48 self.tokens = lexer.tokenize(source) # Lexical stage
|
|
49 self.token = self.tokens.__next__()
|
166
|
50 def skipToSemi(self, tt):
|
|
51 while self.Peak != tt and self.Peak != 'END':
|
|
52 self.NextToken()
|
|
53 if self.Peak == tt:
|
|
54 self.Consume(tt)
|
148
|
55
|
|
56 def parsePackage(self):
|
|
57 self.Consume('package')
|
|
58 name = self.Consume('ID')
|
|
59 self.Consume(';')
|
|
60 self.sema.handlePackage(name.val, name.loc)
|
|
61 # TODO: parse uses
|
|
62 while self.Peak != 'END':
|
|
63 self.parseTopLevel()
|
|
64 self.Consume('END')
|
|
65
|
|
66 def parseTopLevel(self):
|
|
67 if self.Peak == 'function':
|
149
|
68 self.parseFunctionDefinition()
|
148
|
69 elif self.Peak == 'var':
|
149
|
70 self.parseVarDef()
|
163
|
71 elif self.Peak == 'const':
|
|
72 self.parseConstDef()
|
149
|
73 else:
|
|
74 self.Error('Expected function or variable')
|
148
|
75
|
|
76 def parseDesignator(self):
|
|
77 """ A designator designates an object """
|
|
78 name = self.Consume('ID')
|
150
|
79 return self.sema.actOnDesignator(name.val, name.loc)
|
148
|
80
|
|
81 # Type system
|
|
82 def parseType(self):
|
|
83 d = self.parseDesignator()
|
|
84 return d
|
|
85
|
|
86 # Variable declarations:
|
149
|
87 def parseVarDef(self):
|
148
|
88 self.Consume('var')
|
149
|
89 t = self.parseType()
|
|
90 def parseVar():
|
|
91 name = self.Consume('ID')
|
|
92 ival = None
|
|
93 if self.hasConsumed('='):
|
|
94 ival = self.parseExpression()
|
|
95 self.sema.actOnVarDef(name.val, name.loc, t, ival)
|
|
96 parseVar()
|
|
97 while self.hasConsumed(','):
|
|
98 parseVar()
|
157
|
99 self.Consume(';')
|
148
|
100
|
163
|
101 def parseConstDef(self):
|
|
102 self.Consume('const')
|
|
103 t = self.parseType()
|
|
104 def parseConst():
|
|
105 name = self.Consume('ID')
|
|
106 self.Consume('=')
|
|
107 val = self.parseExpression()
|
|
108 self.sema.actOnConstDef(name.val, name.loc, t, val)
|
|
109 parseConst()
|
|
110 while self.hasConsumed(','):
|
|
111 parseConst()
|
|
112 self.Consume(';')
|
|
113
|
148
|
114 # Procedures
|
149
|
115 def parseFunctionDefinition(self):
|
148
|
116 self.Consume('function')
|
|
117 returntype = self.parseType()
|
149
|
118 pname = self.Consume('ID')
|
|
119 self.sema.actOnFuncDef1(pname.val, pname.loc)
|
148
|
120 self.Consume('(')
|
|
121 parameters = []
|
|
122 if not self.hasConsumed(')'):
|
150
|
123 def parseParameter():
|
148
|
124 typ = self.parseType()
|
|
125 name = self.Consume('ID')
|
150
|
126 parameters.append(self.sema.actOnParameter(name.val, name.loc, typ))
|
|
127 parseParameter()
|
|
128 while self.hasConsumed(','):
|
|
129 parseParameter()
|
148
|
130 self.Consume(')')
|
|
131 body = self.parseCompoundStatement()
|
149
|
132 self.sema.actOnFuncDef2(parameters, returntype, body)
|
148
|
133
|
|
134 # Statements:
|
|
135 def parseAssignment(self, lval):
|
165
|
136 lval = self.sema.actOnVariableUse(lval, lval.loc)
|
164
|
137 loc = self.Consume('=').loc
|
148
|
138 rval = self.parseExpression()
|
157
|
139 self.Consume(';')
|
164
|
140 return self.sema.actOnAssignment(lval, rval, loc)
|
148
|
141
|
|
142 def parseProcedureCall(self, procedure):
|
|
143 self.Consume('(')
|
|
144 args = []
|
|
145 if not self.hasConsumed(')'):
|
|
146 args.append(self.parseExpression())
|
|
147 while self.hasConsumed(','):
|
|
148 args.append(self.parseExpression())
|
|
149 self.Consume(')')
|
155
|
150 return astnodes.ProcedureCall(procedure, args)
|
148
|
151
|
|
152 def parseIfStatement(self):
|
165
|
153 loc = self.Consume('if').loc
|
148
|
154 self.Consume('(')
|
|
155 condition = self.parseExpression()
|
|
156 self.Consume(')')
|
149
|
157 yes = self.parseCompoundStatement()
|
148
|
158 if self.hasConsumed('else'):
|
149
|
159 no = self.parseCompoundStatement()
|
165
|
160 else:
|
|
161 no = astnodes.EmptyStatement()
|
|
162 return self.sema.actOnIfStatement(condition, yes, no, loc)
|
148
|
163
|
|
164 def parseWhileStatement(self):
|
|
165 self.Consume('while')
|
|
166 self.Consume('(')
|
|
167 condition = self.parseExpression()
|
|
168 self.Consume(')')
|
149
|
169 statements = self.parseCompoundStatement()
|
|
170 return astnodes.WhileStatement(condition, statements)
|
|
171
|
148
|
172 def parseReturnStatement(self):
|
|
173 self.Consume('return')
|
|
174 expr = self.parseExpression()
|
157
|
175 self.Consume(';')
|
149
|
176 return astnodes.ReturnStatement(expr)
|
148
|
177
|
|
178 def parseCompoundStatement(self):
|
149
|
179 self.Consume('{')
|
157
|
180 statements = []
|
|
181 while not self.hasConsumed('}'):
|
166
|
182 s = self.parseStatement()
|
|
183 if not type(s) is astnodes.EmptyStatement:
|
|
184 statements.append(s)
|
149
|
185 return astnodes.CompoundStatement(statements)
|
148
|
186
|
|
187 def parseStatement(self):
|
|
188 # Determine statement type based on the pending token:
|
|
189 if self.Peak == 'if':
|
|
190 return self.parseIfStatement()
|
|
191 elif self.Peak == 'while':
|
|
192 return self.parseWhileStatement()
|
|
193 elif self.Peak == '{':
|
|
194 return self.parseCompoundStatement()
|
158
|
195 elif self.hasConsumed(';'):
|
155
|
196 return astnodes.EmptyStatement()
|
148
|
197 elif self.Peak == 'var':
|
158
|
198 self.parseVarDef()
|
|
199 return astnodes.EmptyStatement()
|
148
|
200 elif self.Peak == 'return':
|
|
201 return self.parseReturnStatement()
|
|
202 elif self.Peak == 'ID':
|
|
203 designator = self.parseDesignator()
|
|
204 if self.Peak == '(':
|
|
205 return self.parseProcedureCall(designator)
|
|
206 elif self.Peak == '=':
|
|
207 return self.parseAssignment(designator)
|
|
208 self.Error('Unable to determine statement')
|
|
209
|
|
210 # Parsing expressions:
|
|
211 def parseExpression(self):
|
|
212 return self.parseBinopRhs(self.parsePrimary(), 0)
|
166
|
213 # TODO: use this error handling:
|
|
214 try:
|
|
215 return self.parseBinopRhs(self.parsePrimary(), 0)
|
|
216 except CompilerError as e:
|
|
217 self.diag.addDiag(e)
|
|
218 self.skipToSemi(';')
|
|
219 return astnodes.Literal(0)
|
148
|
220 def parsePrimary(self):
|
|
221 if self.hasConsumed('('):
|
|
222 e = self.parseExpression()
|
|
223 self.Consume(')')
|
|
224 return e
|
|
225 elif self.Peak == 'NUMBER':
|
|
226 val = self.Consume('NUMBER')
|
149
|
227 return self.sema.actOnNumber(val.val, val.loc)
|
163
|
228 elif self.Peak == 'REAL':
|
|
229 val = self.Consume('REAL')
|
|
230 return self.sema.actOnNumber(val.val, val.loc)
|
166
|
231 elif self.Peak == 'true':
|
|
232 val = self.Consume('true')
|
|
233 return self.sema.actOnNumber(True, val.loc)
|
|
234 elif self.Peak == 'false':
|
|
235 val = self.Consume('false')
|
|
236 return self.sema.actOnNumber(False, val.loc)
|
148
|
237 elif self.Peak == 'ID':
|
|
238 d = self.parseDesignator()
|
155
|
239 if self.Peak == '(':
|
|
240 return self.parseProcedureCall(d)
|
|
241 else:
|
165
|
242 return self.sema.actOnVariableUse(d, d.loc)
|
148
|
243 self.Error('Expected NUM, ID or (expr), got {0}'.format(self.Peak))
|
|
244
|
|
245 def parseBinopRhs(self, lhs, min_prec):
|
|
246 while self.PeakPrec >= min_prec:
|
|
247 op_prec = self.PeakPrec
|
|
248 op = self.Consume(self.Peak)
|
|
249 rhs = self.parsePrimary()
|
|
250 while self.PeakPrec > op_prec:
|
|
251 rhs = self.parseBinopRhs(rhs, self.PeakPrec)
|
149
|
252 lhs = self.sema.actOnBinop(lhs, op.typ, rhs, op.loc)
|
148
|
253 return lhs
|
|
254
|