view python/c3/parser.py @ 149:74241ca312cc

Fixes on parser and semantics
author Windel Bouwman
date Fri, 01 Mar 2013 11:43:52 +0100
parents e5263f74b287
children 4ae0e02599de
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 }

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):
      if self.Peak in binopPrecs:
         return binopPrecs[self.Peak]
      return -1
   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):
      if self.Peak == 'function':
         self.parseFunctionDefinition()
      elif self.Peak == 'var':
         self.parseVarDef()
         self.Consume(';')
      else:
         self.Error('Expected function or variable')

   def parseDesignator(self):
      """ A designator designates an object """
      name = self.Consume('ID')
      return name.val

   # Type system
   def parseType(self):
      d = self.parseDesignator()
      return d

   # Variable declarations:
   def parseVarDef(self):
      self.Consume('var')
      t = self.parseType()
      def parseVar():
         name = self.Consume('ID')
         ival = None
         if self.hasConsumed('='):
            ival = self.parseExpression()
         self.sema.actOnVarDef(name.val, name.loc, t, ival)
      parseVar()
      while self.hasConsumed(','):
         parseVar()

   # Procedures
   def parseFunctionDefinition(self):
      self.Consume('function')
      returntype = self.parseType()
      pname = self.Consume('ID')
      self.sema.actOnFuncDef1(pname.val, pname.loc)
      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(')')
      body = self.parseCompoundStatement()
      self.sema.actOnFuncDef2(parameters, returntype, 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 parseIfStatement(self):
      self.Consume('if')
      self.Consume('(')
      condition = self.parseExpression()
      self.Consume(')')
      yes = self.parseCompoundStatement()
      if self.hasConsumed('else'):
         no = self.parseCompoundStatement()
         return astnodes.IfStatement(condition, yes, no)
      return astnodes.IfStatement(condition, yes)

   def parseWhileStatement(self):
      self.Consume('while')
      self.Consume('(')
      condition = self.parseExpression()
      self.Consume(')')
      statements = self.parseCompoundStatement()
      return astnodes.WhileStatement(condition, statements)

   def parseReturnStatement(self):
      self.Consume('return')
      expr = self.parseExpression()
      return astnodes.ReturnStatement(expr)

   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.parseVarDef()
      elif self.Peak == 'return':
         return self.parseReturnStatement()
      elif self.Peak == 'ID':
         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 self.sema.actOnNumber(val.val, val.loc)
      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 = self.sema.actOnBinop(lhs, op.typ, rhs, op.loc)
      return lhs