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