diff options
author | Vincent Ambo <mail@tazj.in> | 2021-01-14T02·02+0300 |
---|---|---|
committer | tazjin <mail@tazj.in> | 2021-01-14T02·05+0000 |
commit | 1ed34443d832fcfd7b683ecdcb58b0c445443def (patch) | |
tree | eb69960be2b3c8f3b8236b2dc10878bf048e87e0 /users/tazjin/rlox/src/parser.rs | |
parent | 8bcbb041606b0fd15459415a3e5531a753c8bfd8 (diff) |
feat(tazjin/rlox): Parse function declarations r/2103
Change-Id: I1db4316563827976e5233dc7a626968f80b992ef Reviewed-on: https://cl.tvl.fyi/c/depot/+/2390 Reviewed-by: tazjin <mail@tazj.in> Tested-by: BuildkiteCI
Diffstat (limited to 'users/tazjin/rlox/src/parser.rs')
-rw-r--r-- | users/tazjin/rlox/src/parser.rs | 70 |
1 files changed, 66 insertions, 4 deletions
diff --git a/users/tazjin/rlox/src/parser.rs b/users/tazjin/rlox/src/parser.rs index 74c2f448ede9..626eda641c03 100644 --- a/users/tazjin/rlox/src/parser.rs +++ b/users/tazjin/rlox/src/parser.rs @@ -94,6 +94,13 @@ pub struct While<'a> { pub type Block<'a> = Vec<Statement<'a>>; #[derive(Debug)] +pub struct Function<'a> { + pub name: Token<'a>, + pub params: Vec<Token<'a>>, + pub body: Block<'a>, +} + +#[derive(Debug)] pub enum Statement<'a> { Expr(Expr<'a>), Print(Expr<'a>), @@ -101,6 +108,7 @@ pub enum Statement<'a> { Block(Block<'a>), If(If<'a>), While(While<'a>), + Function(Function<'a>), } // Parser @@ -108,9 +116,15 @@ pub enum Statement<'a> { /* program → declaration* EOF ; -declaration → varDecl +declaration → funDecl + | varDecl | statement ; +funDecl → "fun" function ; +function → IDENTIFIER "(" parameters? ")" block ; +parameters → IDENTIFIER ( "," IDENTIFIER )* ; + + statement → exprStmt | forStmt | ifStmt @@ -159,6 +173,10 @@ impl<'a> Parser<'a> { // recursive-descent parser functions fn declaration(&mut self) -> StmtResult<'a> { + if self.match_token(&TokenKind::Fun) { + return self.function(); + } + if self.match_token(&TokenKind::Var) { return self.var_declaration(); } @@ -166,6 +184,50 @@ impl<'a> Parser<'a> { self.statement() } + fn function(&mut self) -> StmtResult<'a> { + let name = self.identifier("Expected function name.")?; + + self.consume( + &TokenKind::LeftParen, + ErrorKind::ExpectedToken("Expect '(' after function name."), + )?; + + let mut params = vec![]; + + if !self.check_token(&TokenKind::RightParen) { + loop { + if params.len() >= 255 { + return Err(Error { + line: self.peek().line, + kind: ErrorKind::InternalError("255 parameter limit exceeded.".into()), + }); + } + + params.push(self.identifier("Expected parameter name.")?); + + if !self.match_token(&TokenKind::Comma) { + break; + } + } + } + + self.consume( + &TokenKind::RightParen, + ErrorKind::ExpectedToken("Expect ')' after parameters."), + )?; + + self.consume( + &TokenKind::LeftBrace, + ErrorKind::ExpectedToken("Expect '{' before function body."), + )?; + + Ok(Statement::Function(Function { + name, + params, + body: self.block_statement()?, + })) + } + fn var_declaration(&mut self) -> StmtResult<'a> { // Since `TokenKind::Identifier` carries data, we can't use // `consume`. @@ -186,7 +248,7 @@ impl<'a> Parser<'a> { if self.match_token(&TokenKind::Print) { self.print_statement() } else if self.match_token(&TokenKind::LeftBrace) { - self.block_statement() + Ok(Statement::Block(self.block_statement()?)) } else if self.match_token(&TokenKind::If) { self.if_statement() } else if self.match_token(&TokenKind::While) { @@ -204,7 +266,7 @@ impl<'a> Parser<'a> { Ok(Statement::Print(expr)) } - fn block_statement(&mut self) -> StmtResult<'a> { + fn block_statement(&mut self) -> Result<Block<'a>, Error> { let mut block: Block<'a> = vec![]; while !self.check_token(&TokenKind::RightBrace) && !self.is_at_end() { @@ -213,7 +275,7 @@ impl<'a> Parser<'a> { self.consume(&TokenKind::RightBrace, ErrorKind::ExpectedClosingBrace)?; - Ok(Statement::Block(block)) + Ok(block) } fn if_statement(&mut self) -> StmtResult<'a> { |