diff python/c3/parser.py @ 220:3f6c30a5d234

Major change in expression parsing to enable pointers and structs
author Windel Bouwman
date Sat, 06 Jul 2013 21:32:20 +0200
parents 1fa3e0050b49
children 848c4b15fd0b
line wrap: on
line diff
--- a/python/c3/parser.py	Sat Jul 06 12:38:09 2013 +0200
+++ b/python/c3/parser.py	Sat Jul 06 21:32:20 2013 +0200
@@ -1,11 +1,6 @@
 from . import astnodes, lexer
 from ppci import CompilerError
 
-# 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, diag):
@@ -18,27 +13,26 @@
          return self.mod
       except CompilerError as e:
          self.diag.addDiag(e)
+
     def Error(self, msg):
-      raise CompilerError(msg, self.token.loc)
+        raise CompilerError(msg, self.token.loc)
+
     # Lexer helpers:
     def Consume(self, typ):
-      if self.Peak == typ:
-         return self.NextToken()
-      else:
-         self.Error('Excected: "{0}", got "{1}"'.format(typ, self.Peak))
+        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
+        return self.token.typ
+
     def hasConsumed(self, typ):
-      if self.Peak == typ:
-         self.Consume(typ)
-         return True
-      return False
+        if self.Peak == typ:
+            self.Consume(typ)
+            return True
+        return False
 
     def NextToken(self):
         t = self.token
@@ -49,23 +43,24 @@
     def initLex(self, source):
         self.tokens = lexer.tokenize(source) # Lexical stage
         self.token = self.tokens.__next__()
+
     def addDeclaration(self, decl):
         self.currentPart.declarations.append(decl)
     
     def parseUses(self):
+        # TODO: parse uses
         pass
 
     def parsePackage(self):
-      self.Consume('package')
-      name = self.Consume('ID')
-      self.Consume(';')
-      self.mod = astnodes.Package(name.val, name.loc)
-      self.currentPart = self.mod
-      self.parseUses()
-      # TODO: parse uses
-      while self.Peak != 'END':
-         self.parseTopLevel()
-      self.Consume('END')
+        self.Consume('package')
+        name = self.Consume('ID')
+        self.Consume(';')
+        self.mod = astnodes.Package(name.val, name.loc)
+        self.currentPart = self.mod
+        self.parseUses()
+        while self.Peak != 'END':
+            self.parseTopLevel()
+        self.Consume('END')
 
     def parseTopLevel(self):
       if self.Peak == 'function':
@@ -127,7 +122,7 @@
          v = astnodes.Variable(name.val, t)
          v.loc = name.loc
          if self.hasConsumed('='):
-            v.ival = self.parseExpression()
+            v.ival = self.Expression()
          self.addDeclaration(v)
       parseVar()
       while self.hasConsumed(','):
@@ -140,7 +135,7 @@
       def parseConst():
          name = self.Consume('ID')
          self.Consume('=')
-         val = self.parseExpression()
+         val = self.Expression()
          c = astnodes.Constant(name.val, t, val)
          c.loc = name.loc
       parseConst()
@@ -177,27 +172,11 @@
       self.currentPart = savePart
 
     # Statements:
-    def parseAssignment(self, lval):
-      lval = astnodes.VariableUse(lval, lval.loc)
-      loc = self.Consume('=').loc
-      rval = self.parseExpression()
-      self.Consume(';')
-      return astnodes.Assignment(lval, rval, loc)
-
-    def parseCall(self, func):
-      self.Consume('(')
-      args = [] 
-      if not self.hasConsumed(')'):
-         args.append(self.parseExpression())
-         while self.hasConsumed(','):
-            args.append(self.parseExpression())
-         self.Consume(')')
-      return astnodes.FunctionCall(func, args, func.loc)
 
     def parseIfStatement(self):
       loc = self.Consume('if').loc
       self.Consume('(')
-      condition = self.parseExpression()
+      condition = self.Expression()
       self.Consume(')')
       yes = self.parseCompoundStatement()
       if self.hasConsumed('else'):
@@ -207,88 +186,170 @@
       return astnodes.IfStatement(condition, yes, no, loc)
 
     def parseWhileStatement(self):
-      loc = self.Consume('while').loc
-      self.Consume('(')
-      condition = self.parseExpression()
-      self.Consume(')')
-      statements = self.parseCompoundStatement()
-      return astnodes.WhileStatement(condition, statements, loc)
+        loc = self.Consume('while').loc
+        self.Consume('(')
+        condition = self.Expression()
+        self.Consume(')')
+        statements = self.parseCompoundStatement()
+        return astnodes.WhileStatement(condition, statements, loc)
 
     def parseReturnStatement(self):
-      self.Consume('return')
-      expr = self.parseExpression()
-      self.Consume(';')
-      return astnodes.ReturnStatement(expr)
+        loc = self.Consume('return').loc
+        expr = self.Expression()
+        self.Consume(';')
+        return astnodes.ReturnStatement(expr, loc)
 
     def parseCompoundStatement(self):
-      self.Consume('{')
-      statements = []
-      while not self.hasConsumed('}'):
-         s = self.parseStatement()
-         if not type(s) is astnodes.EmptyStatement:
+        self.Consume('{')
+        statements = []
+        while not self.hasConsumed('}'):
+            s = self.Statement()
+            if type(s) is astnodes.EmptyStatement:
+                continue
             statements.append(s)
-      return astnodes.CompoundStatement(statements)
+        return astnodes.CompoundStatement(statements)
+
+    def Statement(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.hasConsumed(';'):
+            return astnodes.EmptyStatement()
+        elif self.Peak == 'var':
+            self.parseVarDef()
+            return astnodes.EmptyStatement()
+        elif self.Peak == 'return':
+            return self.parseReturnStatement()
+        else:
+            return self.AssignmentOrCall()
+
+    def AssignmentOrCall(self):
+        x = self.UnaryExpression()
+        if self.Peak == '=':
+            # We enter assignment mode here.
+            loc = self.Consume('=').loc
+            rhs = self.Expression()
+            return astnodes.Assignment(x, rhs, loc)
+        else:
+            return x
 
-    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.hasConsumed(';'):
-         return astnodes.EmptyStatement()
-      elif self.Peak == 'var':
-         self.parseVarDef()
-         return astnodes.EmptyStatement()
-      elif self.Peak == 'return':
-         return self.parseReturnStatement()
-      else:
-         designator = self.parseDesignator()
-         if self.Peak == '(':
-            return self.parseCall(designator)
-         elif self.Peak == '=':
-            return self.parseAssignment(designator)
-         else:
-              self.Error('Unable to determine statement')
+    # Expression section:
+    # We not implement these C constructs:
+    # a(2), f = 2
+    # and this:
+    # a = 2 < x : 4 ? 1;
+
+    def Expression(self):
+        exp = self.LogicalAndExpression()
+        while self.Peak == 'or':
+            loc = self.Consume('or').loc
+            e2 = self.LogicalAndExpression()
+            exp = astnodes.Binop(exp, 'or', e2, loc)
+        return exp
 
-    # Parsing expressions:
-    def parseExpression(self):
-      return self.parseBinopRhs(self.parsePrimary(), 0)
+    def LogicalAndExpression(self):
+        o = self.EqualityExpression()
+        while self.Peak == 'and':
+            loc = self.Consume('and').loc
+            o2 = self.EqualityExpression()
+            o = astnodes.Binop(o, 'and', o2, loc)
+        return o
+
+    def EqualityExpression(self):
+        ee = self.SimpleExpression()
+        while self.Peak in ['<', '==', '>']:
+            op = self.Consume(self.Peak)
+            ee2 = self.SimpleExpression()
+            ee = astnodes.Binop(ee, op.typ, ee2, op.loc)
+        return ee
+
+    def SimpleExpression(self):
+        e = self.Term()
+        while self.Peak in ['+', '-']:
+            op = self.Consume(self.Peak)
+            e2 = self.Term()
+            e = astnodes.Binop(e, op.typ, e2, op.loc)
+        return e
 
-    def parsePrimary(self):
-      if self.hasConsumed('('):
-         e = self.parseExpression()
-         self.Consume(')')
-         return e
-      elif self.Peak == 'NUMBER':
-         val = self.Consume('NUMBER')
-         return astnodes.Literal(val.val, val.loc)
-      elif self.Peak == 'REAL':
-         val = self.Consume('REAL')
-         return astnodes.Literal(val.val, val.loc)
-      elif self.Peak == 'true':
-         val = self.Consume('true')
-         return astnodes.Literal(True, val.loc)
-      elif self.Peak == 'false':
-         val = self.Consume('false')
-         return astnodes.Literal(False, val.loc)
-      elif self.Peak == 'ID':
-         d = self.parseDesignator()
-         if self.Peak == '(':
-            return self.parseCall(d)
-         else:
+    def Term(self):
+        t = self.Factor()
+        while self.Peak in ['*', '/']:
+            op = self.Consume(self.Peak)
+            t2 = self.Factor()
+            t = astnodes.Binop(t, op.typ, t2, op.loc)
+        return t
+        
+    def Factor(self):
+        # TODO: eliminate this step?
+        return self.CastExpression()
+
+    # Domain of unary expressions:
+
+    def CastExpression(self):
+        # TODO: cast conflicts with '(' expr ')'
+        if self.Peak == '(ii':
+            self.Consume('(')
+            print('TODO: implement type cast')
+            #rrrrr
+            self.parseTypeSpec()
+
+            # Type
+            self.Consume(')')
+            ce = self.CastExpression()
+            return ce 
+        else:
+            return self.UnaryExpression()
+        
+    def UnaryExpression(self):
+        if self.Peak in ['&', '*']:
+            op = self.Consume(self.Peak)
+            ce = self.CastExpression()
+            return astnodes.Unop(op.typ, ce, op.loc)
+        else:
+            return self.PostFixExpression()
+
+    def PostFixExpression(self):
+        pfe = self.PrimaryExpression()
+        while self.Peak in ['[', '(', '.', '->']:
+            if self.hasConsumed('['):
+                pass
+            elif self.hasConsumed('('):
+                # Function call
+                args = []
+                if not self.hasConsumed(')'):
+                    args.append(self.Expression())
+                    while self.hasConsumed(','):
+                        args.append(self.Expression())
+                    self.Consume(')')
+                pfe = astnodes.FunctionCall(pfe, args, pfe.loc)
+            else:
+                rrrr
+        return pfe
+
+    def PrimaryExpression(self):
+        if self.hasConsumed('('):
+            e = self.Expression()
+            self.Consume(')')
+            return e
+        elif self.Peak == 'NUMBER':
+            val = self.Consume('NUMBER')
+            return astnodes.Literal(val.val, val.loc)
+        elif self.Peak == 'REAL':
+            val = self.Consume('REAL')
+            return astnodes.Literal(val.val, val.loc)
+        elif self.Peak == 'true':
+            val = self.Consume('true')
+            return astnodes.Literal(True, val.loc)
+        elif self.Peak == 'false':
+            val = self.Consume('false')
+            return astnodes.Literal(False, val.loc)
+        elif self.Peak == 'ID':
+            d = self.parseDesignator()
             return astnodes.VariableUse(d, d.loc)
-      self.Error('Expected NUM, ID or (expr), got {0}'.format(self.Peak))
+        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.typ, rhs, op.loc)
-      return lhs