Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
parser.rs 8.44 KiB
use crate::{C1Lexer, C1Token, ParseResult};

pub struct C1Parser<'a> {
    lx: C1Lexer<'a>,
}

impl C1Parser<'_> {
    pub fn parse(text: &'_ str) -> ParseResult {
        let mut parser: C1Parser = C1Parser { lx: C1Lexer::new(text) };
        return Ok(parser.program());
        
    }




    fn program(&mut self) {
        while self.lx.current_token().is_some() {
            let _ = self.functiondefinition();
        }
    }


    fn functiondefinition(&mut self) -> ParseResult {
        self.statement_type()?;
        self.check_and_eat_token(Some(C1Token::Identifier))?;
        self.check_and_eat_token(Some(C1Token::LeftParenthesis))?;
        self.check_and_eat_token(Some(C1Token::RightParenthesis))?;
        self.check_and_eat_token(Some(C1Token::LeftBrace))?;
        self.statementlist()?;
        self.check_and_eat_token(Some(C1Token::RightBrace))
    }

    fn statement_type(&mut self) -> ParseResult {
        return match self.lx.current_token() {
            Some(C1Token::KwBoolean) | Some(C1Token::KwFloat) | Some(C1Token::KwInt) | Some(C1Token::KwVoid) => {
                self.eat();
                Result::Ok(())
            },
            _ => Result::Err(self.error_format()),
        };
    }

    fn statementlist(&mut self) -> ParseResult {
        print!("statementlist->");
        if self.lx.current_token() == Some(C1Token::RightBrace) {
            return Result::Ok(());
        }
        self.block()?;
        self.statementlist()
    }

    fn block(&mut self) -> ParseResult {
        print!("block->");
        if self.current_matches(C1Token::LeftBrace) {
            self.eat();
            self.statementlist()?;
            return self.check_and_eat_token(Some(C1Token::RightBrace));
        }
        self.statement()        
    }

    fn statement(&mut self) -> ParseResult {
        print!("statement->");
        return match self.lx.current_token() {
            Some(C1Token::KwIf) => self.ifstatement(),
            Some(C1Token::KwReturn) => {
                self.returnstatement()?;
                self.check_and_eat_token(Some(C1Token::Semicolon))
            },
            Some(C1Token::KwPrintf) => {
                self.printfstatement()?;
                self.check_and_eat_token(Some(C1Token::Semicolon))
            },
            Some(C1Token::Identifier) => {
                if self.next_matches(C1Token::LeftParenthesis) {
                    self.functioncall()?;
                    return self.check_and_eat_token(Some(C1Token::Semicolon));
                } else if self.next_matches(C1Token::Assign) {
                    self.statassignment()?;
                    return self.check_and_eat_token(Some(C1Token::Semicolon));
                }
                Result::Err(self.error_format())
            },
            _ => Result::Err(self.error_format()),
        };
    }

    fn ifstatement(&mut self) -> ParseResult {
        self.check_and_eat_token(Some(C1Token::KwIf))?;
        self.check_and_eat_token(Some(C1Token::LeftParenthesis))?;
        self.assignment()?;
        self.check_and_eat_token(Some(C1Token::RightParenthesis))?;
        self.block()
    }

    fn returnstatement(&mut self) -> ParseResult {
        self.check_and_eat_token(Some(C1Token::KwReturn))?;
        self.returnstatement_alt()
    }

    fn returnstatement_alt(&mut self) -> ParseResult {
        if self.current_matches(C1Token::Semicolon) {
            return Result::Ok(());
        };
        self.assignment()
    }

    fn printfstatement(&mut self) -> ParseResult {
        self.check_and_eat_token(Some(C1Token::KwPrintf))?;
        self.check_and_eat_token(Some(C1Token::LeftParenthesis))?;
        self.assignment()?;
        self.check_and_eat_token(Some(C1Token::RightParenthesis))
    }

    fn statassignment(&mut self) -> ParseResult {
        self.check_and_eat_token(Some(C1Token::Identifier))?;
        self.check_and_eat_token(Some(C1Token::Assign))?;
        self.assignment()
    }

    fn functioncall(&mut self) -> ParseResult {
        self.check_and_eat_token(Some(C1Token::Identifier))?;
        self.check_and_eat_token(Some(C1Token::LeftParenthesis))?;
        self.check_and_eat_token(Some(C1Token::RightParenthesis))
    }

    fn assignment(&mut self) -> ParseResult {
        if self.current_matches(C1Token::Identifier) {
            if self.next_matches(C1Token::Assign) {
                return self.assignment();
            }
        }
        self.expr()
    }

    fn expr(&mut self) -> ParseResult {
        print!("simplexpr->");
        self.simpexpr()?;
        self.expr_2()
    }

    fn expr_2(&mut self) -> ParseResult {
        if self.current_matches(C1Token::Semicolon) || self.current_matches(C1Token::RightParenthesis) {
            return Result::Ok(());
        }
        self.compare()?;
        self.simpexpr()
    }

    fn compare(&mut self) -> ParseResult {
        return match self.lx.current_token() {
            Some(C1Token::Equal) | Some(C1Token::NotEqual) | Some(C1Token::Less) | Some(C1Token::LessEqual) | Some(C1Token::GreaterEqual) | Some(C1Token::Greater) => {
                self.eat();
                Result::Ok(())
            },
            _ => Result::Err(self.error_format()),
        }
    }

    fn simpexpr(&mut self) -> ParseResult {
        self.minus()?;
        self.term()?;
        self.simpexpr_2()
    }

    fn simpexpr_2(&mut self) -> ParseResult {
        match self.lx.current_token() {
            Some(C1Token::Plus) | Some(C1Token::Minus) | Some(C1Token::Or) => {
                self.lowop()?;
                self.term()?;
                self.simpexpr_2()
            },
            _ => Result::Ok(()),
        }
    }

    fn minus(&mut self) -> ParseResult {
        if self.current_matches(C1Token::Minus) {
            self.eat();
        };
        return Result::Ok(());
    }

    fn lowop(&mut self) -> ParseResult {
        return match self.lx.current_token() {
            Some(C1Token::Plus) | Some(C1Token::Minus) | Some(C1Token::Or) => {
                self.eat();
                Result::Ok(())
            },
            _ => Result::Err(self.error_format()),
        };
    }

    fn term(&mut self) -> ParseResult {
        self.factor()?;
        self.term_2()
    }

    fn term_2(&mut self) -> ParseResult {
        return match self.lx.current_token() {
            Some(C1Token::Asterisk) | Some(C1Token::Slash) | Some(C1Token::And) => {
                self.highop()?;
                self.factor()?;
                self.term_2()
            },
            _ => Result::Ok(()),
        }
    }
    fn highop(&mut self) -> ParseResult {
        return match self.lx.current_token() {
            Some(C1Token::Asterisk) | Some(C1Token::Slash) | Some(C1Token::And) => {
                self.eat();
                Result::Ok(())
            },
            _ => Result::Err(self.error_format()),
        };
    }

    fn factor(&mut self) -> ParseResult {
        print!("factor->");
        return match self.lx.current_token() {
            Some(C1Token::ConstFloat) | Some(C1Token::ConstInt) | Some(C1Token::ConstBoolean) => {
                self.eat();
                Result::Ok(())
            },
            Some(C1Token::Identifier) => {
                if self.next_matches(C1Token::LeftParenthesis) {
                    return self.functioncall();
                }
                self.eat();
                Result::Ok(())
            },
            Some(C1Token::LeftParenthesis) => {
                self.eat();
                self.assignment()?;
                self.check_and_eat_token(Some(C1Token::RightParenthesis))
            },
            _ => Result::Err(self.error_format()),
        }
    }

    //konsumiert aktuelles Token
    fn eat(&mut self) {
        self.lx.eat();
    }

    //Fehler wird zurückgegeben
    fn error_format(&self) -> String {
        return format!("Error on line: {}", self.lx.current_line_number().unwrap());
    }

    //überprüft, ob das ihr übergebene Token gleich dem aktuellen
    fn check_and_eat_token(&mut self, token: Option<C1Token>) -> ParseResult {
        if self.lx.current_token() == token {
            self.eat();
            return Result::Ok(());
        }
        return Result::Err(self.error_format());
    }

    //wie check_and_eat_token, gibt Vergleich  zurück
    fn current_matches(&mut self, token: C1Token) -> bool {
        self.lx.current_token() == Some(token)
    }

    //wie check_and_eat_token für nächstes Token, gibt Vergleich zurück
    fn next_matches(&mut self, token: C1Token) -> bool {
        self.lx.peek_token() == Some(token)
    }
}