Mercurial > lcfOS
comparison python/libs/compiler/parsergen.py @ 69:60cc36ef5a50
Rework in compiler
author | windel |
---|---|
date | Sat, 27 Oct 2012 14:31:58 +0200 |
parents | python/libs/compiler/parser.py@32078200cdd6 |
children |
comparison
equal
deleted
inserted
replaced
68:654c5ac4f2c5 | 69:60cc36ef5a50 |
---|---|
1 """ | |
2 This module parses source code into an abstract syntax tree (AST) | |
3 """ | |
4 | |
5 from .symboltable import SymbolTable | |
6 from .nodes import * | |
7 from .errors import CompilerException, Error | |
8 from .modules import loadModule | |
9 from .display import printNode | |
10 from .builtin import * | |
11 from . import assembler | |
12 | |
13 class Parser: | |
14 def __init__(self, tokens): | |
15 """ provide the parser with the tokens iterator from the lexer. """ | |
16 self.tokens = tokens | |
17 self.NextToken() | |
18 self.errorlist = [] | |
19 | |
20 def Error(self, msg): | |
21 raise CompilerException(msg, self.token.row, self.token.col) | |
22 | |
23 # Lexer helpers: | |
24 def Consume(self, typ=''): | |
25 if self.token.typ == typ or typ == '': | |
26 v = self.token.val | |
27 self.NextToken() | |
28 return v | |
29 else: | |
30 self.Error('Excected: "{0}", got "{1}"'.format(typ, self.token.val)) | |
31 | |
32 def hasConsumed(self, typ): | |
33 if self.token.typ == typ: | |
34 self.Consume(typ) | |
35 return True | |
36 return False | |
37 | |
38 def NextToken(self): | |
39 self.token = self.tokens.__next__() | |
40 # TODO: store filename in location? | |
41 self.location = (self.token.row, self.token.col) | |
42 | |
43 # Helpers to find location of the error in the code: | |
44 def setLocation(self, obj, location): | |
45 obj.location = location | |
46 return obj | |
47 def getLocation(self): | |
48 return self.location | |
49 | |
50 """ | |
51 Recursive descent parser functions: | |
52 A set of mutual recursive functions. | |
53 Starting symbol is the Module. | |
54 """ | |
55 def parseModule(self): | |
56 self.imports = [] | |
57 loc = self.getLocation() | |
58 self.Consume('module') | |
59 modname = self.Consume('ID') | |
60 self.Consume(';') | |
61 mod = Module(modname) | |
62 | |
63 # Construct a symbol table for this program | |
64 mod.symtable = SymbolTable() | |
65 # Add built in types and functions: | |
66 for x in [real, integer, boolean, char, chr_func]: | |
67 mod.symtable.addSymbol(x) | |
68 | |
69 self.cst = mod.symtable | |
70 self.parseImportList() | |
71 | |
72 self.parseDeclarationSequence() | |
73 # Procedures only allowed in this scope | |
74 self.parseProcedureDeclarations() | |
75 | |
76 if self.hasConsumed('begin'): | |
77 mod.initcode = self.parseStatementSequence() | |
78 else: | |
79 mod.initcode = EmptyStatement() | |
80 | |
81 self.Consume('end') | |
82 endname = self.Consume('ID') | |
83 if endname != modname: | |
84 self.Error('end denoter must be module name') | |
85 self.Consume('.') | |
86 | |
87 mod.imports = self.imports | |
88 return self.setLocation(mod, loc) | |
89 | |
90 # Import part | |
91 def parseImportList(self): | |
92 if self.hasConsumed('import'): | |
93 self.parseImport() | |
94 while self.hasConsumed(','): | |
95 self.parseImport() | |
96 self.Consume(';') | |
97 | |
98 def parseImport(self): | |
99 loc = self.getLocation() | |
100 modname = self.Consume('ID') | |
101 mod = loadModule(modname) | |
102 self.setLocation(mod, loc) | |
103 self.cst.addSymbol(mod) | |
104 | |
105 # Helper to parse an identifier defenitions | |
106 def parseIdentDef(self): | |
107 loc = self.getLocation() | |
108 name = self.Consume('ID') | |
109 ispublic = self.hasConsumed('*') | |
110 # Make a node of this thing: | |
111 i = Id(name) | |
112 i.ispublic = ispublic | |
113 return self.setLocation(i, loc) | |
114 | |
115 def parseIdentList(self): | |
116 ids = [ self.parseIdentDef() ] | |
117 while self.hasConsumed(','): | |
118 ids.append( self.parseIdentDef() ) | |
119 return ids | |
120 | |
121 def parseQualIdent(self): | |
122 """ Parse a qualified identifier """ | |
123 name = self.Consume('ID') | |
124 if self.cst.has(Module, name): | |
125 modname = name | |
126 mod = self.cst.get(Module, modname) | |
127 self.Consume('.') | |
128 name = self.Consume('ID') | |
129 # Try to find existing imported symbol: | |
130 for imp in self.imports: | |
131 if imp.modname == modname and imp.name == name: | |
132 return imp | |
133 # Try to find the symbol in the modules exports: | |
134 for sym in mod.exports: | |
135 if sym.name == name: | |
136 impsym = ImportedSymbol(modname, name) | |
137 impsym.typ = sym.typ | |
138 impsym.signature = mod.signature | |
139 self.imports.append(impsym) | |
140 return impsym | |
141 self.Error("Cannot find symbol {0}".format(name)) | |
142 else: | |
143 return self.cst.getSymbol(name) | |
144 | |
145 # Helper to parse a designator | |
146 def parseDesignator(self): | |
147 """ A designator designates an object. | |
148 The base location in memory is denoted by the qualified identifier | |
149 The actual address depends on the selector. | |
150 """ | |
151 loc = self.getLocation() | |
152 obj = self.parseQualIdent() | |
153 typ = obj.typ | |
154 selectors = [] | |
155 while self.token.typ in ['.', '[', '^']: | |
156 if self.hasConsumed('.'): | |
157 field = self.Consume('ID') | |
158 if typ is PointerType: | |
159 selectors.append(Deref()) | |
160 typ = typ.pointedType | |
161 if not type(typ) is RecordType: | |
162 self.Error("field reference, type not record but {0}".format(typ)) | |
163 typ = typ.fields[field] | |
164 selectors.append(Field(field)) | |
165 elif self.hasConsumed('['): | |
166 indexes = self.parseExpressionList() | |
167 self.Consume(']') | |
168 for idx in indexes: | |
169 if not type(typ) is ArrayType: | |
170 self.Error('Cannot index non array type') | |
171 if not isType(idx.typ, integer): | |
172 self.Error('Only integer expressions can be used as an index') | |
173 selectors.append(Index(idx, typ)) | |
174 typ = typ.elementType | |
175 elif self.hasConsumed('^'): | |
176 selectors.append(Deref()) | |
177 typ = typ.pointedType | |
178 return self.setLocation(Designator(obj, selectors, typ), loc) | |
179 | |
180 # Declaration sequence | |
181 def parseDeclarationSequence(self): | |
182 """ 1. constants, 2. types, 3. variables """ | |
183 self.parseConstantDeclarations() | |
184 self.parseTypeDeclarations() | |
185 self.parseVariableDeclarations() | |
186 | |
187 # Constants | |
188 def evalExpression(self, expr): | |
189 if type(expr) is Binop: | |
190 a = self.evalExpression(expr.a) | |
191 b = self.evalExpression(expr.b) | |
192 if expr.op == '+': | |
193 return a + b | |
194 elif expr.op == '-': | |
195 return a - b | |
196 elif expr.op == '*': | |
197 return a * b | |
198 elif expr.op == '/': | |
199 return float(a) / float(b) | |
200 elif expr.op == 'mod': | |
201 return int(a % b) | |
202 elif expr.op == 'div': | |
203 return int(a / b) | |
204 elif expr.op == 'or': | |
205 return a or b | |
206 elif expr.op == 'and': | |
207 return a and b | |
208 else: | |
209 self.Error('Cannot evaluate expression with {0}'.format(expr.op)) | |
210 elif type(expr) is Constant: | |
211 return expr.value | |
212 elif type(expr) is Designator: | |
213 if type(expr.obj) is Constant: | |
214 return self.evalExpression(expr.obj) | |
215 else: | |
216 self.Error('Cannot evaluate designated object {0}'.format(expr.obj)) | |
217 elif type(expr) is Unop: | |
218 a = self.evalExpression(expr.a) | |
219 if expr.op == 'not': | |
220 return not a | |
221 elif expr.op == '-': | |
222 return -a | |
223 else: | |
224 self.Error('Unimplemented unary operation {0}'.format(expr.op)) | |
225 else: | |
226 self.Error('Cannot evaluate expression {0}'.format(expr)) | |
227 | |
228 def parseConstExpression(self): | |
229 e = self.parseExpression() | |
230 return self.evalExpression(e), e.typ | |
231 | |
232 def parseConstantDeclarations(self): | |
233 """ Parse const part of a module """ | |
234 if self.hasConsumed('const'): | |
235 while self.token.typ == 'ID': | |
236 i = self.parseIdentDef() | |
237 self.Consume('=') | |
238 constvalue, typ = self.parseConstExpression() | |
239 self.Consume(';') | |
240 c = Constant(constvalue, typ, name=i.name, public=i.ispublic) | |
241 self.setLocation(c, i.location) | |
242 self.cst.addSymbol(c) | |
243 | |
244 # Type system | |
245 def parseTypeDeclarations(self): | |
246 if self.hasConsumed('type'): | |
247 while self.token.typ == 'ID': | |
248 typename, export = self.parseIdentDef() | |
249 self.Consume('=') | |
250 typ = self.parseStructuredType() | |
251 self.Consume(';') | |
252 t = DefinedType(typename, typ) | |
253 self.cst.addSymbol(t) | |
254 | |
255 def parseType(self): | |
256 if self.token.typ == 'ID': | |
257 typename = self.Consume('ID') | |
258 if self.cst.has(Type, typename): | |
259 typ = self.cst.get(Type, typename) | |
260 while type(typ) is DefinedType: | |
261 typ = typ.typ | |
262 return typ | |
263 else: | |
264 self.Error('Cannot find type {0}'.format(typename)) | |
265 else: | |
266 return self.parseStructuredType() | |
267 | |
268 def parseStructuredType(self): | |
269 if self.hasConsumed('array'): | |
270 dimensions = [] | |
271 dimensions.append( self.parseConstExpression() ) | |
272 while self.hasConsumed(','): | |
273 dimensions.append( self.parseConstExpression() ) | |
274 self.Consume('of') | |
275 arr = self.parseType() | |
276 for dimension, consttyp in reversed(dimensions): | |
277 if not isType(consttyp, integer): | |
278 self.Error('array dimension must be an integer type (not {0})'.format(consttyp)) | |
279 if dimension < 2: | |
280 self.Error('array dimension must be bigger than 1 (not {0})'.format(dimension)) | |
281 arr = ArrayType(dimension, arr) | |
282 return arr | |
283 elif self.hasConsumed('record'): | |
284 fields = {} | |
285 while self.token.typ == 'ID': | |
286 # parse a fieldlist: | |
287 identifiers = self.parseIdentList() | |
288 self.Consume(':') | |
289 typ = self.parseType() | |
290 self.Consume(';') | |
291 for i in identifiers: | |
292 if i.name in fields.keys(): | |
293 self.Error('record field "{0}" multiple defined.'.format(i.name)) | |
294 fields[i.name] = typ | |
295 # TODO store this in another way, symbol table? | |
296 self.Consume('end') | |
297 return RecordType(fields) | |
298 elif self.hasConsumed('pointer'): | |
299 self.Consume('to') | |
300 typ = self.parseType() | |
301 return PointerType(typ) | |
302 elif self.hasConsumed('procedure'): | |
303 parameters, returntype = self.parseFormalParameters() | |
304 return ProcedureType(parameters, returntype) | |
305 else: | |
306 self.Error('Unknown structured type "{0}"'.format(self.token.val)) | |
307 | |
308 # Variable declarations: | |
309 def parseVariableDeclarations(self): | |
310 if self.hasConsumed('var'): | |
311 if self.token.typ == 'ID': | |
312 while self.token.typ == 'ID': | |
313 ids = self.parseIdentList() | |
314 self.Consume(':') | |
315 typename = self.parseType() | |
316 self.Consume(';') | |
317 for i in ids: | |
318 v = Variable(i.name, typename, public=i.ispublic) | |
319 self.setLocation(v, i.location) | |
320 self.cst.addSymbol(v) | |
321 else: | |
322 self.Error('Expected ID, got'+str(self.token)) | |
323 | |
324 # Procedures | |
325 def parseFPsection(self): | |
326 if self.hasConsumed('const'): | |
327 kind = 'const' | |
328 elif self.hasConsumed('var'): | |
329 kind = 'var' | |
330 else: | |
331 kind = 'value' | |
332 names = [ self.Consume('ID') ] | |
333 while self.hasConsumed(','): | |
334 names.append( self.Consume('ID') ) | |
335 self.Consume(':') | |
336 typ = self.parseType() | |
337 parameters = [Parameter(kind, name, typ) | |
338 for name in names] | |
339 return parameters | |
340 | |
341 def parseFormalParameters(self): | |
342 parameters = [] | |
343 self.Consume('(') | |
344 if not self.hasConsumed(')'): | |
345 parameters += self.parseFPsection() | |
346 while self.hasConsumed(';'): | |
347 parameters += self.parseFPsection() | |
348 self.Consume(')') | |
349 if self.hasConsumed(':'): | |
350 returntype = self.parseQualIdent() | |
351 else: | |
352 returntype = void | |
353 return ProcedureType(parameters, returntype) | |
354 | |
355 def parseProcedureDeclarations(self): | |
356 procedures = [] | |
357 while self.token.typ == 'procedure': | |
358 p = self.parseProcedureDeclaration() | |
359 procedures.append(p) | |
360 self.Consume(';') | |
361 return procedures | |
362 | |
363 def parseProcedureDeclaration(self): | |
364 loc = self.getLocation() | |
365 self.Consume('procedure') | |
366 i = self.parseIdentDef() | |
367 procname = i.name | |
368 proctyp = self.parseFormalParameters() | |
369 procsymtable = SymbolTable(parent = self.cst) | |
370 self.cst = procsymtable # Switch symbol table: | |
371 # Add parameters as variables to symbol table: | |
372 for parameter in proctyp.parameters: | |
373 vname = parameter.name | |
374 vtyp = parameter.typ | |
375 if parameter.kind == 'var': | |
376 vtyp = PointerType(vtyp) | |
377 variable = Variable(vname, vtyp, False) | |
378 if parameter.kind == 'const': | |
379 variable.isReadOnly = True | |
380 variable.isParameter = True | |
381 self.cst.addSymbol(variable) | |
382 self.Consume(';') | |
383 self.parseDeclarationSequence() | |
384 # Mark all variables as local: | |
385 for variable in self.cst.getAllLocal(Variable): | |
386 variable.isLocal = True | |
387 | |
388 if self.hasConsumed('begin'): | |
389 block = self.parseStatementSequence() | |
390 if self.hasConsumed('return'): | |
391 returnexpression = self.parseExpression() | |
392 else: | |
393 returnexpression = None | |
394 | |
395 if proctyp.returntype.isType(void): | |
396 if not returnexpression is None: | |
397 self.Error('Void procedure cannot return a value') | |
398 else: | |
399 if returnexpression is None: | |
400 self.Error('Procedure must return a value') | |
401 if not isType(returnexpression.typ, proctyp.returntype): | |
402 self.Error('Returned type {0} does not match function return type {1}'.format(returnexpression.typ, proctyp.returntype)) | |
403 | |
404 self.Consume('end') | |
405 endname = self.Consume('ID') | |
406 if endname != procname: | |
407 self.Error('endname should match {0}'.format(name)) | |
408 self.cst = procsymtable.parent # Switch back to parent symbol table | |
409 proc = Procedure(procname, proctyp, block, procsymtable, returnexpression) | |
410 self.setLocation(proc, loc) | |
411 self.cst.addSymbol(proc) | |
412 proc.public = i.ispublic | |
413 return proc | |
414 | |
415 # Statements: | |
416 def parseAssignment(self, lval): | |
417 loc = self.getLocation() | |
418 self.Consume(':=') | |
419 rval = self.parseExpression() | |
420 if isType(lval.typ, real) and isType(rval.typ, integer): | |
421 rval = Unop(rval, 'INTTOREAL', real) | |
422 if type(rval.typ) is NilType: | |
423 if not type(lval.typ) is ProcedureType and not type(lval.typ) is PointerType: | |
424 self.Error('Can assign nil only to pointers or procedure types, not {0}'.format(lval)) | |
425 elif not isType(lval.typ, rval.typ): | |
426 self.Error('Type mismatch {0} != {1}'.format(lval.typ, rval.typ)) | |
427 return self.setLocation(Assignment(lval, rval), loc) | |
428 | |
429 def parseExpressionList(self): | |
430 expressions = [ self.parseExpression() ] | |
431 while self.hasConsumed(','): | |
432 expressions.append( self.parseExpression() ) | |
433 return expressions | |
434 | |
435 def parseProcedureCall(self, procedure): | |
436 self.Consume('(') | |
437 if self.token.typ != ')': | |
438 args = self.parseExpressionList() | |
439 else: | |
440 args = [] | |
441 self.Consume(')') | |
442 parameters = procedure.typ.parameters | |
443 if len(args) != len(parameters): | |
444 self.Error("Procedure requires {0} arguments, {1} given".format(len(parameters), len(args))) | |
445 for arg, param in zip(args, parameters): | |
446 if not arg.typ.isType(param.typ): | |
447 print(arg.typ, param.typ) | |
448 self.Error('Mismatch in parameter') | |
449 return ProcedureCall(procedure, args) | |
450 | |
451 def parseIfStatement(self): | |
452 loc = self.getLocation() | |
453 self.Consume('if') | |
454 ifs = [] | |
455 condition = self.parseExpression() | |
456 if not isType(condition.typ, boolean): | |
457 self.Error('condition of if statement must be boolean') | |
458 self.Consume('then') | |
459 truestatement = self.parseStatementSequence() | |
460 ifs.append( (condition, truestatement) ) | |
461 while self.hasConsumed('elsif'): | |
462 condition = self.parseExpression() | |
463 if not isType(condition.typ, boolean): | |
464 self.Error('condition of if statement must be boolean') | |
465 self.Consume('then') | |
466 truestatement = self.parseStatementSequence() | |
467 ifs.append( (condition, truestatement) ) | |
468 if self.hasConsumed('else'): | |
469 statement = self.parseStatementSequence() | |
470 else: | |
471 statement = None | |
472 self.Consume('end') | |
473 for condition, truestatement in reversed(ifs): | |
474 statement = IfStatement(condition, truestatement, statement) | |
475 return self.setLocation(statement, loc) | |
476 | |
477 def parseCase(self): | |
478 # TODO | |
479 pass | |
480 | |
481 def parseCaseStatement(self): | |
482 self.Consume('case') | |
483 expr = self.parseExpression() | |
484 self.Consume('of') | |
485 self.parseCase() | |
486 while self.hasConsumed('|'): | |
487 self.parseCase() | |
488 self.Consume('end') | |
489 | |
490 def parseWhileStatement(self): | |
491 loc = self.getLocation() | |
492 self.Consume('while') | |
493 condition = self.parseExpression() | |
494 self.Consume('do') | |
495 statements = self.parseStatementSequence() | |
496 if self.hasConsumed('elsif'): | |
497 self.Error('elsif in while not yet implemented') | |
498 self.Consume('end') | |
499 return self.setLocation(WhileStatement(condition, statements), loc) | |
500 | |
501 def parseRepeatStatement(self): | |
502 self.Consume('repeat') | |
503 stmt = self.parseStatementSequence() | |
504 self.Consume('until') | |
505 cond = self.parseBoolExpression() | |
506 | |
507 def parseForStatement(self): | |
508 loc = self.getLocation() | |
509 self.Consume('for') | |
510 variable = self.parseDesignator() | |
511 if not variable.typ.isType(integer): | |
512 self.Error('loop variable of for statement must have integer type') | |
513 assert(variable.typ.isType(integer)) | |
514 self.Consume(':=') | |
515 begin = self.parseExpression() | |
516 if not begin.typ.isType(integer): | |
517 self.Error('begin expression of a for statement must have integer type') | |
518 self.Consume('to') | |
519 end = self.parseExpression() | |
520 if not end.typ.isType(integer): | |
521 self.Error('end expression of a for statement must have integer type') | |
522 if self.hasConsumed('by'): | |
523 increment, typ = self.parseConstExpression() | |
524 if not typ.isType(integer): | |
525 self.Error('Increment must be integer') | |
526 else: | |
527 increment = 1 | |
528 assert(type(increment) is int) | |
529 self.Consume('do') | |
530 statements = self.parseStatementSequence() | |
531 self.Consume('end') | |
532 return self.setLocation(ForStatement(variable, begin, end, increment, statements), loc) | |
533 | |
534 def parseAsmcode(self): | |
535 # TODO: move this to seperate file | |
536 def parseOpcode(): | |
537 return self.Consume('ID') | |
538 def parseOperand(): | |
539 if self.hasConsumed('['): | |
540 memref = [] | |
541 memref.append(parseOperand()) | |
542 self.Consume(']') | |
543 return memref | |
544 else: | |
545 if self.token.typ == 'NUMBER': | |
546 return self.Consume('NUMBER') | |
547 else: | |
548 ID = self.Consume('ID') | |
549 if self.cst.has(Variable, ID): | |
550 return self.cst.get(Variable, ID) | |
551 else: | |
552 return ID | |
553 | |
554 def parseOperands(n): | |
555 operands = [] | |
556 if n > 0: | |
557 operands.append( parseOperand() ) | |
558 n = n - 1 | |
559 while n > 0: | |
560 self.Consume(',') | |
561 operands.append(parseOperand()) | |
562 n = n - 1 | |
563 return operands | |
564 self.Consume('asm') | |
565 asmcode = [] | |
566 while self.token.typ != 'end': | |
567 opcode = parseOpcode() | |
568 func, numargs = assembler.opcodes[opcode] | |
569 operands = parseOperands(numargs) | |
570 asmcode.append( (opcode, operands) ) | |
571 #print('opcode', opcode, operands) | |
572 self.Consume('end') | |
573 return AsmCode(asmcode) | |
574 | |
575 def parseStatement(self): | |
576 try: | |
577 # Determine statement type based on the pending token: | |
578 if self.token.typ == 'if': | |
579 return self.parseIfStatement() | |
580 elif self.token.typ == 'case': | |
581 return self.parseCaseStatement() | |
582 elif self.token.typ == 'while': | |
583 return self.parseWhileStatement() | |
584 elif self.token.typ == 'repeat': | |
585 return self.parseRepeatStatement() | |
586 elif self.token.typ == 'for': | |
587 return self.parseForStatement() | |
588 elif self.token.typ == 'asm': | |
589 return self.parseAsmcode() | |
590 elif self.token.typ == 'ID': | |
591 # Assignment or procedure call | |
592 designator = self.parseDesignator() | |
593 if self.token.typ == '(' and type(designator.typ) is ProcedureType: | |
594 return self.parseProcedureCall(designator) | |
595 elif self.token.typ == ':=': | |
596 return self.parseAssignment(designator) | |
597 else: | |
598 self.Error('Unknown statement following designator: {0}'.format(self.token)) | |
599 else: | |
600 # TODO: return empty statement??: | |
601 return EmptyStatement() | |
602 self.Error('Unknown statement {0}'.format(self.token)) | |
603 except CompilerException as e: | |
604 print(e) | |
605 self.errorlist.append( (e.row, e.col, e.msg)) | |
606 # Do error recovery by skipping all tokens until next ; or end | |
607 while not (self.token.typ == ';' or self.token.typ == 'end'): | |
608 self.Consume(self.token.typ) | |
609 return EmptyStatement() | |
610 | |
611 def parseStatementSequence(self): | |
612 """ Sequence of statements seperated by ';' """ | |
613 statements = [ self.parseStatement() ] | |
614 while self.hasConsumed(';'): | |
615 statements.append( self.parseStatement() ) | |
616 return StatementSequence( statements ) | |
617 | |
618 # Parsing expressions: | |
619 """ | |
620 grammar of expressions: | |
621 expression = SimpleExpression [ reloperator SimpleExpression ] | |
622 reloperator = '=' | '<=' | '>=' | '<>' | |
623 Simpleexpression = [ '+' | '-' ] term { addoperator term } | |
624 addoperator = '+' | '-' | 'or' | |
625 term = factor { muloperator factor } | |
626 muloperator = '*' | '/' | 'div' | 'mod' | 'and' | |
627 factor = number | nil | true | false | "(" expression ")" | | |
628 designator [ actualparameters ] | 'not' factor | |
629 """ | |
630 def parseExpression(self): | |
631 """ The connector between the boolean and expression domain """ | |
632 expr = self.parseSimpleExpression() | |
633 if self.token.typ in ['>=','<=','<','>','<>','=']: | |
634 relop = self.Consume() | |
635 expr2 = self.parseSimpleExpression() | |
636 # Automatic type convert to reals: | |
637 if isType(expr.typ, real) and isType(expr2.typ, integer): | |
638 expr2 = Unop(expr2, 'INTTOREAL', real) | |
639 if isType(expr2.typ, real) and isType(expr.typ, integer): | |
640 expr = Unop(expr, 'INTTOREAL', real) | |
641 # Type check: | |
642 if not isType(expr.typ, expr2.typ): | |
643 self.Error('Type mismatch in relop') | |
644 if isType(expr.typ, real) and relop in ['<>', '=']: | |
645 self.Error('Cannot check real values for equality') | |
646 | |
647 expr = Relop(expr, relop, expr2, boolean) | |
648 return expr | |
649 | |
650 # Parsing arithmatic expressions: | |
651 def parseTerm(self): | |
652 a = self.parseFactor() | |
653 while self.token.typ in ['*', '/', 'mod', 'div', 'and']: | |
654 loc = self.getLocation() | |
655 op = self.Consume() | |
656 b = self.parseTerm() | |
657 # Type determination and checking: | |
658 if op in ['mod', 'div']: | |
659 if not isType(a.typ, integer): | |
660 self.Error('First operand should be integer, not {0}'.format(a.typ)) | |
661 if not isType(b.typ, integer): | |
662 self.Error('Second operand should be integer, not {0}'.format(b.typ)) | |
663 typ = integer | |
664 elif op == '*': | |
665 if isType(a.typ, integer) and isType(b.typ, integer): | |
666 typ = integer | |
667 elif isType(a.typ, real) or isType(b.typ, real): | |
668 if isType(a.typ, integer): | |
669 # Automatic type cast | |
670 a = Unop(a, 'INTTOREAL', real) | |
671 if isType(b.typ, integer): | |
672 b = Unop(b, 'INTTOREAL', real) | |
673 if not isType(a.typ, real): | |
674 self.Error('first operand must be a real!') | |
675 if not isType(b.typ, real): | |
676 self.Error('second operand must be a real!') | |
677 typ = real | |
678 else: | |
679 self.Error('Unknown operands for multiply: {0}, {1}'.format(a, b)) | |
680 elif op == '/': | |
681 # Division always yields a real result, for integer division use div | |
682 if isType(a.typ, integer): | |
683 # Automatic type cast | |
684 a = Unop(a, 'INTTOREAL', real) | |
685 if isType(b.typ, integer): | |
686 b = Unop(b, 'INTTOREAL', real) | |
687 if not isType(a.typ, real): | |
688 self.Error('first operand must be a real!') | |
689 if not isType(b.typ, real): | |
690 self.Error('second operand must be a real!') | |
691 typ = real | |
692 elif op == 'and': | |
693 if not isType(a.typ, boolean): | |
694 self.Error('First operand of and must be boolean') | |
695 if not isType(b.typ, boolean): | |
696 self.Error('Second operand of and must be boolean') | |
697 typ = boolean | |
698 else: | |
699 self.Error('Unknown operand {0}'.format(op)) | |
700 | |
701 a = self.setLocation(Binop(a, op, b, typ), loc) | |
702 return a | |
703 | |
704 def parseFactor(self): | |
705 if self.hasConsumed('('): | |
706 e = self.parseExpression() | |
707 self.Consume(')') | |
708 return e | |
709 elif self.token.typ == 'NUMBER': | |
710 loc = self.getLocation() | |
711 val = self.Consume('NUMBER') | |
712 return self.setLocation(Constant(val, integer), loc) | |
713 elif self.token.typ == 'REAL': | |
714 loc = self.getLocation() | |
715 val = self.Consume('REAL') | |
716 return self.setLocation(Constant(val, real), loc) | |
717 elif self.token.typ == 'CHAR': | |
718 val = self.Consume('CHAR') | |
719 return Constant(val, char) | |
720 elif self.token.typ == 'STRING': | |
721 txt = self.Consume('STRING') | |
722 return StringConstant(txt) | |
723 elif self.token.typ in ['true', 'false']: | |
724 val = self.Consume() | |
725 val = True if val == 'true' else False | |
726 return Constant(val, boolean) | |
727 elif self.hasConsumed('nil'): | |
728 return Constant(0, NilType()) | |
729 elif self.hasConsumed('not'): | |
730 f = self.parseFactor() | |
731 if not isType(f.typ, boolean): | |
732 self.Error('argument of boolean negation must be boolean type') | |
733 return Unop(f, 'not', boolean) | |
734 elif self.token.typ == 'ID': | |
735 designator = self.parseDesignator() | |
736 # TODO: handle functions different here? | |
737 if self.token.typ == '(' and type(designator.typ) is ProcedureType: | |
738 return self.parseProcedureCall(designator) | |
739 else: | |
740 return designator | |
741 else: | |
742 self.Error('Expected NUMBER, ID or ( expr ), got'+str(self.token)) | |
743 | |
744 def parseSimpleExpression(self): | |
745 """ Arithmatic expression """ | |
746 if self.token.typ in ['+', '-']: | |
747 # Handle the unary minus | |
748 op = self.Consume() | |
749 a = self.parseTerm() | |
750 typ = a.typ | |
751 if not isType(typ,real) and not isType(typ, integer): | |
752 self.Error('Unary minus or plus can be only applied to real or integers') | |
753 if op == '-': | |
754 a = Unop(a, op, typ) | |
755 else: | |
756 a = self.parseTerm() | |
757 while self.token.typ in ['+', '-', 'or']: | |
758 loc = self.getLocation() | |
759 op = self.Consume() | |
760 b = self.parseTerm() | |
761 if op in ['+', '-']: | |
762 if isType(a.typ, real) or isType(b.typ, real): | |
763 typ = real | |
764 if isType(a.typ, integer): | |
765 # Automatic type cast | |
766 a = Unop(a, 'INTTOREAL', real) | |
767 if not isType(a.typ, real): | |
768 self.Error('first operand must be a real!') | |
769 if isType(b.typ, integer): | |
770 b = Unop(b, 'INTTOREAL', real) | |
771 if not isType(b.typ, real): | |
772 self.Error('second operand must be a real!') | |
773 elif isType(a.typ, integer) and isType(b.typ, integer): | |
774 typ = integer | |
775 else: | |
776 self.Error('Invalid types {0} and {1}'.format(a.typ, b.typ)) | |
777 elif op == 'or': | |
778 if not isType(a.typ, boolean): | |
779 self.Error('first operand must be boolean for or operation') | |
780 if not isType(b.typ, boolean): | |
781 self.Error('second operand must be boolean for or operation') | |
782 typ = boolean | |
783 else: | |
784 self.Error('Unknown operand {0}'.format(op)) | |
785 a = self.setLocation(Binop(a, op, b, typ), loc) | |
786 return a | |
787 |