summaryrefslogblamecommitdiffstats
path: root/src/front/parser.py
blob: 29e300d8aa49e9a439db09b918e45f50058d30da (plain) (tree)
1
2
3
4
5
6
7
8
9
10



                       
 




                               





                             


                                           


                         


                            


                                                 














                                                                            
                   




                                                                          
 
                                                       
                       




                                                                                  
 
                                                                       
                       




                                                                                            
 
                                               
                         




                                                                                
 
                                                 
                   




                                                                                     


                                             








                                                                                    
                     













































                                                                                    


                                       




                                               
 
                                                
                              








                                            
                         










                                                                               
                       











                                                                                           
                        




                                                     
 
                                                                                              
                           













                                                                   
                              













                                              
 
                                        
                         




                                         
 


                                            
 
# -*- coding: utf-8 -*-
from ast import *
from lexer import *
from token import *

class Parser:
    def __init__(self, lex):
        self.lexer = lex
        self.token = lex.scan()
        self.count = 1 #debug
        return

    def parse(self):
        return self.program()

    def move(self):
        print self.count, self.token #debug
        self.count += 1 #debug
        self.token = self.lexer.scan()
        return

    def error(self, msg):
        raise Exception(msg)

    def match(self, tag):
        if self.token == None:
            self.error("Unexpected end of file.")

        if self.token.tag != tag:
            self.error("match: expected %s got %s\n" %(tag, self.token.tag))
        val = self.token.value
        self.move()
        return val

    # boolean = join { "||" join }.
    def boolean(self):
        res = self.join()
        while self.token.tag == Tag.OPERATOR and self.token.value == "||":
            self.move()
            res = BinaryExpression(res, "||", self.join())
        return res

    # join = equality { "&&" equality }.
    def join(self):
        res = self.equality()
        while self.token.tag == Tag.OPERATOR and self.token.value == "&&":
            self.move()
            res = BinaryExpression(res, "&&", self.relation())
        return res

    # equality = relation { ( "==" | "!=" ) relation }.
    def equality(self):
        res = self.relation()
        while self.token.tag == Tag.OPERATOR and self.token.value in ["==", "!="]:
            op = self.match(Tag.OPERATOR)
            res = BinaryExpression(res, op, self.relation())
        return res

    # relation = expression { ( "<" | "<=" | ">=" | ">" ) expression }.
    def relation(self):
        res = self.expression()
        while self.token.tag == Tag.OPERATOR and self.token.value in ["<", "<=", ">=", ">"]:
            op = self.match(Tag.OPERATOR)
            res = BinaryExpression(res, op, self.expression())
        return res

    # expression = term { ( "+" | "-" ) term }.
    def expression(self):
        res = self.term()
        while self.token.tag == Tag.OPERATOR and self.token.value in ["+", "-"]:
            op = self.match(Tag.OPERATOR)
            res = BinaryExpression(res, op, self.term())
        return res

    # term = unary { ( "*" | "/" | "%" ) unary }.
    def term(self):
        res = self.unary()
        while self.token.tag == Tag.OPERATOR and self.token.value in ["*", "/", "%"]:
            op = self.match(Tag.OPERATOR)
            res = BinaryExpression(res, op, self.unary())
        return res

    # unary = "!" unary | "-" unary | factor.
    def unary(self):
        if self.token.tag == Tag.OPERATOR and self.token.value == "!":
            self.move()
            return UnaryExpression("!", self.unary())
        if self.token.tag == Tag.OPERATOR and self.token.value == "-":
            self.move()
            return UnaryExpression("-", self.unary())
        return self.factor()

    # factor = "(" boolean ")" | integer | "true" | "false" | ident | function_call.
    def factor(self):
        if self.token.tag == Tag.LPAREN:
            self.move()
            res = self.boolean()
            self.match(Tag.RPAREN)
            return res
        if self.token.tag == Tag.NUMBER:
            return Constant(self.match(Tag.NUMBER))
        if self.token.tag == Tag.TRUE:
            self.move()
            return Constant(1)
        if self.token.tag == Tag.FALSE:
            self.move()
            return Constant(0)
        ''' Wegen der Änderung in function_call() kann die Grammatik nicht
            mehr mit einem Lookahead von 1 geparst werden. Die Lösung hier
            ist, den Aufruf von function_call() zu "inlinen". Muss irgendwann
            aber geändert werden. Eine Lösung wäre Funktionen mit call
            aufzurufen oder eben auf LL(2) oder LR umzusteigen.
        '''
        if self.token.tag == Tag.IDENT:
            name = self.match(Tag.IDENT)
            if self.token.tag == Tag.LBRAK: # function_call
                self.match(Tag.LBRAK)
                args = [] if self.token.tag == Tag.RBRAK else self.expression_list()
                self.match(Tag.RBRAK)
                return FunctionCall(name, args)
            return Variable(name)# variable

        error("unknown factor begins with " + self.token.tag)

    # function_call = ident "[" [ expression_list ] "]".
    ''' function_call muss eine expression sein sonst geht alles folgende nicht:
            foo = bar[]
            @foo[bar]
            spam[5] + eggs[1]
        andererseits ist function_call dann kein statement mehr
            exit[]
        wäre also falsch. Der Aufruf muss in einem assignment verkapselt werden:
            dummy = exit[]
    '''
    def function_call(self):
        name = self.match(Tag.IDENT)
        self.match(Tag.LBRAK)
        args = [] if self.token.tag == Tag.RBRAK else self.expression_list()
        self.match(Tag.RBRAK)
        return FunctionCall(name, args)

    # ident_list = ident { "," ident }.
    def ident_list(self):
        ident = [self.match(Tag.IDENT)]
        while self.token.tag == Tag.COMMA:
            self.move()
            ident.append(self.match(Tag.IDENT))
        return ident

    # expression_list = boolean { "," boolean }.
    def expression_list(self):
        exp = [self.boolean()]
        while self.token.tag == Tag.COMMA:
            self.move()
            exp.append(self.boolean())
        return exp

    # function_list = function { function }.
    def function_list(self):
        funcs = [self.function()]
        while self.token:
            funcs.append(self.function())
        return funcs

    # statement_list = statement { statement }.
    def statement_list(self):
        stat = [self.statement()]
        while self.token.tag not in [Tag.END, Tag.ELSE]:
            stat.append(self.statement())
        return stat

    # function = "fun" ident "[" [ ident_list ] "]" nl statement_list "end" nl.
    def function(self):
        self.match(Tag.FUN)
        name = self.match(Tag.IDENT)
        self.match(Tag.LBRAK)
        args = [] if self.token.tag == Tag.RBRAK else self.ident_list()
        self.match(Tag.RBRAK)
        self.match(Tag.NEWLINE)
        block = self.statement_list()
        self.match(Tag.END)
        self.match(Tag.NEWLINE)
        return Function(name, args, block)

    # statement = [ if_statement | while_statement | assign_statement | return_statement ].
    def statement(self):
        return {Tag.IF:         self.if_statement,
                Tag.WHILE:      self.while_statement,
                Tag.IDENT:      self.assignment,
                Tag.RETURN:     self.return_statement
                }[self.token.tag]()

    # if_statement = "if" boolean nl statement_list [ "else" [ nl ] statement_list ] "end" nl.
    def if_statement(self):
        self.match(Tag.IF)
        condition = self.boolean()
        self.match(Tag.NEWLINE)
        true_case = self.statement_list()
        else_case = []
        if self.token.tag == Tag.ELSE:
            self.move()
            if self.token.tag == Tag.NEWLINE: self.move()
            else_case = self.statement_list()
        self.match(Tag.END)
        self.match(Tag.NEWLINE)
        return IfStatement(condition, true_case, else_case)

    # while_statement = "while" boolean nl statement_list "end" nl.
    def while_statement(self):
        self.match(Tag.WHILE)
        condition = self.boolean()
        self.match(Tag.NEWLINE)
        body = self.statement_list()
        self.match(Tag.END)
        self.match(Tag.NEWLINE)
        return WhileStatement(condition, body)

    # return_statement = "@" boolean nl.
    def return_statement(self):
        self.match(Tag.RETURN)
        exp = self.boolean()
        self.match(Tag.NEWLINE)
        return ReturnStatement(exp)

    # assignment = ident "=" boolean nl.
    def assignment(self):
        name = self.match(Tag.IDENT)
        self.match(Tag.ASSIGNMENT)
        exp = self.boolean()
        self.match(Tag.NEWLINE)
        return AssignStatement(name, exp)

    # program = function_list.
    def program(self):
        return Program(self.function_list())