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
+