Mercurial > lcfOS
view python/c3/parser.py @ 148:e5263f74b287
Added c3 language frontend initial parser
author | Windel Bouwman |
---|---|
date | Fri, 01 Mar 2013 10:24:01 +0100 |
parents | |
children | 74241ca312cc |
line wrap: on
line source
from . import astnodes, lexer, semantics from ppci.errors import CompilerException, SourceLocation # binop precedence for expressions: binopPrecs = {'or': 5, 'and': 10, \ '<': 20, '>': 20, '==': 20, '<=': 20, '>=': 20, '!=': 20, \ '+': 30, '-': 30, '*': 40, '/': 40 } def getTokenPrecedence(typ): if typ in binopPrecs: return binopPrecs[typ] return -1 class Parser: """ Parses sourcecode into an abstract syntax tree (AST) """ def __init__(self, sema, diag): self.sema = sema self.diag = diag def parseSource(self, source): self.initLex(source) try: self.parsePackage() except CompilerException as e: self.diag.diag(e) def Error(self, msg): raise CompilerException(msg, self.token.loc) def skipToSemiCol(self): while not (self.Peak == ';' or self.Peak == 'END'): self.NextToken() # Lexer helpers: def Consume(self, typ): if self.Peak == typ: return self.NextToken() else: self.Error('Excected: "{0}", got "{1}"'.format(typ, self.Peak)) @property def Peak(self): return self.token.typ @property def PeakPrec(self): return getTokenPrecedence(self.Peak) def hasConsumed(self, typ): if self.Peak == typ: self.Consume(typ) return True return False def NextToken(self): t = self.token if t.typ != 'END': self.token = self.tokens.__next__() return t def initLex(self, source): self.tokens = lexer.tokenize(source) # Lexical stage self.token = self.tokens.__next__() def parsePackage(self): self.Consume('package') name = self.Consume('ID') self.Consume(';') self.sema.handlePackage(name.val, name.loc) # TODO: parse uses while self.Peak != 'END': self.parseTopLevel() self.Consume('END') def parseTopLevel(self): is_public = self.hasConsumed('public') if self.Peak == 'function': self.parseFunctionDefinition(is_public) elif self.Peak == 'var': self.parseVarDef(is_public) def parseDesignator(self): """ A designator designates an object """ name = self.Consume('ID') return name # Type system def parseType(self): d = self.parseDesignator() return d # Variable declarations: def parseVarDef(self, is_public): self.Consume('var') typ = self.parseType() ID = self.Consume('ID') self.Consume(';') v = Variable(i.name, typename, public=is_public) self.curScope.add(v) # Procedures def parseFunctionDefinition(self, is_pub): self.Consume('function') returntype = self.parseType() procname = self.Consume('ID') self.Consume('(') parameters = [] if not self.hasConsumed(')'): typ = self.parseType() name = self.Consume('ID') parameters.append(astnodes.Parameter(name, typ)) while self.hasConsumed(','): typ = self.parseType() name = self.Consume('ID') parameters.append(astnodes.Parameter(name, typ)) self.Consume(')') proctyp = astnodes.FunctionType(parameters, returntype) body = self.parseCompoundStatement() return astnodes.Procedure(procname, proctyp, body) # Statements: def parseAssignment(self, lval): self.Consume('=') rval = self.parseExpression() return astnodes.Assignment(lval, rval) def parseProcedureCall(self, procedure): self.Consume('(') args = [] if not self.hasConsumed(')'): args.append(self.parseExpression()) while self.hasConsumed(','): args.append(self.parseExpression()) self.Consume(')') return ProcedureCall(procedure, args) def parseLocal(self, t): name = self.Consume('ID') if self.hasConsumed('='): ival = self.parseExpression() else: ival = None self.sema.actOnLocal(t, name, ival) def parseLocalDeclaration(self): self.Consume('var') t = self.parseType() self.parseLocal(t) while self.hasConsumed(','): self.parseLocal(t) def parseIfStatement(self): self.Consume('if') self.Consume('(') condition = self.parseExpression() self.Consume(')') truestatement = self.parseStatement() if self.hasConsumed('else'): els = self.parseStatement() return astnodes.IfStatement(condition, truestatement, els) return astnodes.IfStatement(condition, truestatement) def parseWhileStatement(self): self.Consume('while') self.Consume('(') condition = self.parseExpression() self.Consume(')') statements = self.parseStatement() def parseReturnStatement(self): self.Consume('return') expr = self.parseExpression() def parseCompoundStatement(self): self.Consume('{') statements = [self.parseStatement()] while self.hasConsumed(';'): statements.append(self.parseStatement()) self.Consume('}') return astnodes.CompoundStatement(statements) def parseStatement(self): # Determine statement type based on the pending token: if self.Peak == 'if': return self.parseIfStatement() elif self.Peak == 'while': return self.parseWhileStatement() elif self.Peak == '{': return self.parseCompoundStatement() elif self.Peak == 'var': return self.parseLocalDeclaration() elif self.Peak == 'return': return self.parseReturnStatement() elif self.Peak == 'ID': # Assignment or procedure call designator = self.parseDesignator() if self.Peak == '(': return self.parseProcedureCall(designator) elif self.Peak == '=': return self.parseAssignment(designator) self.Error('Unable to determine statement') # Parsing expressions: def parseExpression(self): return self.parseBinopRhs(self.parsePrimary(), 0) def parsePrimary(self): if self.hasConsumed('('): e = self.parseExpression() self.Consume(')') return e elif self.Peak == 'NUMBER': val = self.Consume('NUMBER') return astnodes.Constant(val, val) elif self.Peak == 'ID': d = self.parseDesignator() return d self.Error('Expected NUM, ID or (expr), got {0}'.format(self.Peak)) def parseBinopRhs(self, lhs, min_prec): while self.PeakPrec >= min_prec: op_prec = self.PeakPrec op = self.Consume(self.Peak) rhs = self.parsePrimary() while self.PeakPrec > op_prec: rhs = self.parseBinopRhs(rhs, self.PeakPrec) lhs = astnodes.Binop(lhs, op, rhs) return lhs