view python/ppci/frontends/ks/parser.py @ 98:3f772feb12ef

movage
author windel
date Mon, 24 Dec 2012 13:57:00 +0100
parents python/ppci/frontends/ksparser.py@a350055d6119
children fe145e42259d
line wrap: on
line source

"""
  This module define a grammar for the 'K#' language.
"""

from .nodes import *
from .errors import CompilerException, Error
from .modules import loadModule
from .display import printNode
from .builtin import *
from . import assembler

class Grammar:
   # TODO: implement some base class?
   pass

class Parser:
   #TODO
   pass

class KsParser(Parser):
   def __init__(self):
      self.loadGrammar(KsGrammar)

# For now, try to parse an expression as test case:
class KsGrammer(Grammar):

   def __init__(self):
      pass

   # Parsing expressions:
   """
     grammar of expressions:
     expression       = term { addoperator term }
     addoperator      = '+' | '-'
     term             = factor { muloperator factor }
     muloperator      = '*' | '/'
     factor           = number | "(" expression ")"
   """

   @rule(Term)
   def Expression1(self, term):
      return Expression(term)

   @rule(Term, AddOperator, Term)
   def Expression2(self, term1, op, term2):
      return Expression(term1, op, term2)

   # Parsing arithmatic expressions:
   def parseTerm(self):
       a = self.parseFactor()
       while self.token.typ in ['*', '/', 'mod', 'div', 'and']:
           loc = self.getLocation()
           op = self.Consume()
           b = self.parseTerm()
           # Type determination and checking:
           if op in ['mod', 'div']:
              if not isType(a.typ, integer):
                 self.Error('First operand should be integer, not {0}'.format(a.typ))
              if not isType(b.typ, integer):
                 self.Error('Second operand should be integer, not {0}'.format(b.typ))
              typ = integer
           elif op == '*':
              if isType(a.typ, integer) and isType(b.typ, integer):
                 typ = integer
              elif isType(a.typ, real) or isType(b.typ, real):
                 if isType(a.typ, integer):
                    # Automatic type cast
                    a = Unop(a, 'INTTOREAL', real)
                 if isType(b.typ, integer):
                    b = Unop(b, 'INTTOREAL', real)
                 if not isType(a.typ, real):
                    self.Error('first operand must be a real!')
                 if not isType(b.typ, real):
                    self.Error('second operand must be a real!')
                 typ = real
              else:
                 self.Error('Unknown operands for multiply: {0}, {1}'.format(a, b))
           elif op == '/':
              # Division always yields a real result, for integer division use div
              if isType(a.typ, integer):
                 # Automatic type cast
                 a = Unop(a, 'INTTOREAL', real)
              if isType(b.typ, integer):
                 b = Unop(b, 'INTTOREAL', real)
              if not isType(a.typ, real):
                 self.Error('first operand must be a real!')
              if not isType(b.typ, real):
                 self.Error('second operand must be a real!')
              typ = real
           elif op == 'and':
              if not isType(a.typ, boolean):
                 self.Error('First operand of and must be boolean')
              if not isType(b.typ, boolean):
                 self.Error('Second operand of and must be boolean')
              typ = boolean
           else:
              self.Error('Unknown operand {0}'.format(op))

           a = self.setLocation(Binop(a, op, b, typ), loc)
       return a

   @rule(
   def parseFactor(self):
      if self.hasConsumed('('):
         e = self.parseExpression()
         self.Consume(')')
         return e
      elif self.token.typ == 'NUMBER':
         loc = self.getLocation() 
         val = self.Consume('NUMBER')
         return self.setLocation(Constant(val, integer), loc)
      elif self.token.typ == 'REAL':
         loc = self.getLocation()
         val = self.Consume('REAL')
         return self.setLocation(Constant(val, real), loc)
      elif self.token.typ == 'CHAR':
          val = self.Consume('CHAR')
          return Constant(val, char)
      elif self.token.typ in ['true', 'false']:
         val = self.Consume()
         val = True if val == 'true' else False
         return Constant(val, boolean)
      elif self.hasConsumed('nil'):
         return Constant(0, NilType())
      elif self.hasConsumed('not'):
         f = self.parseFactor()
         if not isType(f.typ, boolean):
            self.Error('argument of boolean negation must be boolean type')
         return Unop(f, 'not', boolean)
      elif self.token.typ == 'ID':
          designator = self.parseDesignator()
          # TODO: handle functions different here?
          if self.token.typ == '(' and type(designator.typ) is ProcedureType:
             return self.parseProcedureCall(designator)
          else:
             return designator
      else:
         self.Error('Expected NUMBER, ID or ( expr ), got'+str(self.token))

   def parseSimpleExpression(self):
      """ Arithmatic expression """
      if self.token.typ in ['+', '-']:
         # Handle the unary minus
         op = self.Consume()
         a = self.parseTerm()
         typ = a.typ
         if not isType(typ,real) and not isType(typ, integer):
            self.Error('Unary minus or plus can be only applied to real or integers')
         if op == '-':
            a = Unop(a, op, typ)
      else:
         a = self.parseTerm()
      while self.token.typ in ['+', '-', 'or']:
           loc = self.getLocation()
           op = self.Consume()
           b = self.parseTerm()
           if op in ['+', '-']:
              if isType(a.typ, real) or isType(b.typ, real):
                 typ = real
                 if isType(a.typ, integer):
                    # Automatic type cast
                    a = Unop(a, 'INTTOREAL', real)
                 if not isType(a.typ, real):
                    self.Error('first operand must be a real!')
                 if isType(b.typ, integer):
                    b = Unop(b, 'INTTOREAL', real)
                 if not isType(b.typ, real):
                    self.Error('second operand must be a real!')
              elif isType(a.typ, integer) and isType(b.typ, integer):
                 typ = integer
              else:
                 self.Error('Invalid types {0} and {1}'.format(a.typ, b.typ))
           elif op == 'or':
              if not isType(a.typ, boolean):
                 self.Error('first operand must be boolean for or operation')
              if not isType(b.typ, boolean):
                 self.Error('second operand must be boolean for or operation')
              typ = boolean
           else:
              self.Error('Unknown operand {0}'.format(op))
           a = self.setLocation(Binop(a, op, b, typ), loc)
      return a