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

                       




                            
 
             



                              




                             
                                      

                         
                             
 
                          

                                                 





                                                                            




                                



                                                                          
                                                                                               

                  
                                        
                   
                             

                                                                                                   
                  
 
                                                                                     
                       
                               

                                                                                                        
                  
 
                                               
                         

                                                                                
                                                                                               
                  
 
                                                 
                   

                                                                                     
                                                                                                
                  


                                             

                                                                                           


                                                                                    
                     





                                        
                                                                    

                                      
                                               

                                       
                                               
                                       




                                                                                 


                                                               
                            
                                              


                                                                            
                                                        


                                       




                                               
 
                                                
                              








                                            
                         




                                               


                                                                                 

                                                                               
                       
                              
                                             

                                                                       
                                          

                                   
                                     
                                        
                                                
 
                                                                                                                                
                        
                                                  

                                                     
                                                      
                                                     
                                                   
                                   

                               
 
                                                                                       
                           
                              



                                         
                        

                                      
                                   
                                             
                           
                                                                 
 
                                                                
                              
                              



                                    
                           
                                                    
 
                                     
                               
                              

                              
                                         
 
                                     
                         
                              


                                    
                                   
                                               
 





                                        


                                            
# -*- coding: utf-8 -*-
from ast import *
from token import Tag
from scope import Scope

class ParseError(Exception):
    pass

class Parser:
    def __init__(self, lexer):
        self.lexer = lexer
        self.scope = Scope()
        self.move()

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

    def move(self):
        self.token = self.lexer.scan()

    def error(self, msg):
        raise ParseError(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

    def match(self, tag, *tags):
        for t in (tag,) + tags:
            res = self._match(t)
        return res

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

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

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

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

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

    # unary = "!" unary | "-" unary | factor.
    def unary(self):
        if self.token.tag == Tag.OPERATOR and self.token.value in ["!", "-"]:
            return UnaryExpression(self.match(Tag.OPERATOR), self.unary(), self.lexer.line)
        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), self.lexer.line)
        if self.token.tag == Tag.TRUE:
            self.move()
            return Constant(1, self.lexer.line)
        if self.token.tag == Tag.FALSE:
            self.move()
            return Constant(0, self.lexer.line)
        if self.token.tag == Tag.IDENT:
            line = self.lexer.line
            name = self.match(Tag.IDENT)
            if not self.scope.contains(name):
                raise SyntaxError("%s is not declared in line %d" % (name, line))
            return Variable(name, line)
        return self.function_call()

    # function_call = "call" ident "[" [ expression_list ] "]".
    def function_call(self):
        name = self.match(Tag.CALL, 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, self.lexer.line)

    # 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):
        if self.token.tag in [Tag.END, Tag.ELSE]:
            return None
        return Sequence(self.statement(), self.statement_list(), self.lexer.line)

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

    # statement = ( if_statement | while_statement | assign_statement | return_statement | function_call | print_statement ) nl.
    def statement(self):
        res =  {Tag.IF:         self.if_statement,
                Tag.WHILE:      self.while_statement,
                Tag.IDENT:      self.assignment,
                Tag.RETURN:     self.return_statement,
                Tag.PRINT:      self.print_statement,
                Tag.CALL:       self.function_call,
                }[self.token.tag]()
        self.match(Tag.NEWLINE)
        return res

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

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

    # return_statement = "@" boolean.
    def return_statement(self):
        line = self.lexer.line
        self.match(Tag.RETURN)
        exp = self.boolean()
        return ReturnStatement(exp, line)

    # assignment = ident "=" boolean.
    def assignment(self):
        line = self.lexer.line
        name = self.match(Tag.IDENT)
        self.match(Tag.ASSIGNMENT)
        exp = self.boolean()
        self.scope.add_locals(name)
        return AssignStatement(name, exp, line)

    def print_statement(self):
        line = self.lexer.line
        self.match(Tag.PRINT)
        exp = self.boolean()
        return PrintStatement(exp, line)

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