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