Mercurial > lcfOS
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/c3/parser.py Fri Mar 01 10:24:01 2013 +0100 @@ -0,0 +1,217 @@ +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 +