Support if statements AND expressions (pogchamp)
This commit is contained in:
parent
0604818b0e
commit
2beb493aad
144
src/parse.rs
144
src/parse.rs
|
@ -283,31 +283,49 @@ impl<'a> BranchBody<'a> {
|
||||||
pub fn build(lexer: &mut Lexer<'a>) -> Self {
|
pub fn build(lexer: &mut Lexer<'a>) -> Self {
|
||||||
let mut statements = Vec::new();
|
let mut statements = Vec::new();
|
||||||
|
|
||||||
let mut tail_expr = None;
|
let mut next = None;
|
||||||
loop {
|
let tail_expr = loop {
|
||||||
let tok = lexer.next().unwrap();
|
let tok = if let Some(tok) = next {
|
||||||
|
next = None;
|
||||||
|
tok
|
||||||
|
} else {
|
||||||
|
lexer.next().unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
match tok {
|
match tok {
|
||||||
Token::Let => {
|
Token::Let => {
|
||||||
let (statement, next) = Statement::build_let(lexer);
|
let (statement, next) = Statement::build_let(lexer);
|
||||||
statements.push(statement);
|
statements.push(statement);
|
||||||
match next {
|
match next {
|
||||||
Token::Semicolon => {}
|
Token::Semicolon => {}
|
||||||
Token::BraceClose => break,
|
Token::BraceClose => break None,
|
||||||
_ => lexer.panic_message("Unexpected token"),
|
_ => lexer.panic_message("Unexpected token"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Token::BraceClose => break,
|
Token::If => {
|
||||||
|
let (stmt, tail) = IfStmt::build(lexer);
|
||||||
|
if tail == Token::BraceClose {
|
||||||
|
if let Some(_) = stmt.else_body {
|
||||||
|
let expr = stmt.to_expr();
|
||||||
|
break Some(Expr::If(expr));
|
||||||
|
} else {
|
||||||
|
statements.push(Statement::If(stmt));
|
||||||
|
break None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
statements.push(Statement::If(stmt));
|
||||||
|
next = Some(tail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Token::BraceClose => break None,
|
||||||
_ => match Expr::build_start(tok, lexer) {
|
_ => match Expr::build_start(tok, lexer) {
|
||||||
(None, Token::Semicolon | Token::BraceClose) => {}
|
(None, Token::Semicolon | Token::BraceClose) => {}
|
||||||
(Some(expr), Token::Semicolon) => statements.push(Statement::Expr(expr)),
|
(Some(expr), Token::Semicolon) => statements.push(Statement::Expr(expr)),
|
||||||
(Some(expr), Token::BraceClose) => {
|
(Some(expr), Token::BraceClose) => break Some(expr),
|
||||||
tail_expr = Some(expr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => lexer.panic_message("Unexpected token"),
|
_ => lexer.panic_message("Unexpected token"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
statements,
|
statements,
|
||||||
|
@ -327,11 +345,7 @@ pub enum Expr<'a> {
|
||||||
SelfMember(&'a str),
|
SelfMember(&'a str),
|
||||||
Group(Box<Expr<'a>>),
|
Group(Box<Expr<'a>>),
|
||||||
FnCall(Box<Expr<'a>>, Vec<Expr<'a>>),
|
FnCall(Box<Expr<'a>>, Vec<Expr<'a>>),
|
||||||
If {
|
If(IfExpr<'a>),
|
||||||
test_expr: Box<Expr<'a>>,
|
|
||||||
then_body: BranchBody<'a>,
|
|
||||||
else_body: BranchBody<'a>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Expr<'a> {
|
impl<'a> Expr<'a> {
|
||||||
|
@ -346,24 +360,7 @@ impl<'a> Expr<'a> {
|
||||||
}
|
}
|
||||||
Self::Group(Box::new(inner.unwrap()))
|
Self::Group(Box::new(inner.unwrap()))
|
||||||
}
|
}
|
||||||
Token::If => {
|
Token::If => Self::If(IfExpr::build(lexer)),
|
||||||
let test_expr = match Self::build(lexer) {
|
|
||||||
(Some(test_expr), Token::BraceOpen) => Box::new(test_expr),
|
|
||||||
(None, Token::BraceOpen) => lexer.panic_message("Expected test expression"),
|
|
||||||
_ => lexer.panic_message("Expected opening brace"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let then_body = BranchBody::build(lexer);
|
|
||||||
lexer.eat_expect(Token::Else);
|
|
||||||
lexer.eat_expect(Token::BraceOpen);
|
|
||||||
let else_body = BranchBody::build(lexer);
|
|
||||||
|
|
||||||
Self::If {
|
|
||||||
test_expr,
|
|
||||||
then_body,
|
|
||||||
else_body,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Token::BinaryInteger => Self::Literal(Literal::BinaryInteger(lexer.slice())),
|
Token::BinaryInteger => Self::Literal(Literal::BinaryInteger(lexer.slice())),
|
||||||
Token::OctalInteger => Self::Literal(Literal::OctalInteger(lexer.slice())),
|
Token::OctalInteger => Self::Literal(Literal::OctalInteger(lexer.slice())),
|
||||||
Token::HexInteger => Self::Literal(Literal::HexInteger(lexer.slice())),
|
Token::HexInteger => Self::Literal(Literal::HexInteger(lexer.slice())),
|
||||||
|
@ -485,16 +482,12 @@ pub enum Literal<'a> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Statement<'a> {
|
pub enum Statement<'a> {
|
||||||
Expr(Expr<'a>),
|
Expr(Expr<'a>),
|
||||||
|
If(IfStmt<'a>),
|
||||||
Let {
|
Let {
|
||||||
var: &'a str,
|
var: &'a str,
|
||||||
mutable: bool,
|
mutable: bool,
|
||||||
expr: Expr<'a>,
|
expr: Expr<'a>,
|
||||||
},
|
},
|
||||||
If {
|
|
||||||
test_expr: Expr<'a>,
|
|
||||||
then_body: BranchBody<'a>,
|
|
||||||
else_body: Option<BranchBody<'a>>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Statement<'a> {
|
impl<'a> Statement<'a> {
|
||||||
|
@ -520,6 +513,81 @@ impl<'a> Statement<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IfExpr<'a> {
|
||||||
|
pub test_expr: Box<Expr<'a>>,
|
||||||
|
pub then_body: BranchBody<'a>,
|
||||||
|
pub else_body: BranchBody<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IfExpr<'a> {
|
||||||
|
pub fn build(lexer: &mut Lexer<'a>) -> Self {
|
||||||
|
let test_expr = Box::new(Self::eat_test_expr(lexer));
|
||||||
|
let then_body = BranchBody::build(lexer);
|
||||||
|
lexer.eat_expect(Token::Else);
|
||||||
|
lexer.eat_expect(Token::BraceOpen);
|
||||||
|
let else_body = BranchBody::build(lexer);
|
||||||
|
Self {
|
||||||
|
test_expr,
|
||||||
|
then_body,
|
||||||
|
else_body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eat_test_expr(lexer: &mut Lexer<'a>) -> Expr<'a> {
|
||||||
|
match Expr::build(lexer) {
|
||||||
|
(Some(test_expr), Token::BraceOpen) => test_expr,
|
||||||
|
(Some(_), _) => lexer.panic_message("Expected opening brace"),
|
||||||
|
_ => lexer.panic_message("Expected test expression"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IfStmt<'a> {
|
||||||
|
pub test_expr: Expr<'a>,
|
||||||
|
pub then_body: BranchBody<'a>,
|
||||||
|
pub else_body: Option<BranchBody<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IfStmt<'a> {
|
||||||
|
pub fn build(lexer: &mut Lexer<'a>) -> (Self, Token) {
|
||||||
|
let test_expr = IfExpr::eat_test_expr(lexer);
|
||||||
|
let then_body = BranchBody::build(lexer);
|
||||||
|
|
||||||
|
let (else_body, tail) = match lexer.next().unwrap() {
|
||||||
|
Token::Else => {
|
||||||
|
lexer.eat_expect(Token::BraceOpen);
|
||||||
|
(Some(BranchBody::build(lexer)), lexer.next().unwrap())
|
||||||
|
}
|
||||||
|
next => (None, next),
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
Self {
|
||||||
|
test_expr,
|
||||||
|
then_body,
|
||||||
|
else_body,
|
||||||
|
},
|
||||||
|
tail,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_expr(self) -> IfExpr<'a> {
|
||||||
|
let Self {
|
||||||
|
test_expr,
|
||||||
|
then_body,
|
||||||
|
else_body,
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
IfExpr {
|
||||||
|
test_expr: Box::new(test_expr),
|
||||||
|
then_body,
|
||||||
|
else_body: else_body.unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Reference in New Issue