From e29b8e16203df86efd396f1959a22197a178c344 Mon Sep 17 00:00:00 2001 From: me Date: Sat, 20 Dec 2025 15:34:30 +0200 Subject: [PATCH] if then else --- readme.md | 8 +- src/parser/parser.rs | 119 +++++++++++------- src/parser/scanner.rs | 8 +- .../ayin__parser__parser__tests__if_then.snap | 43 +++++++ ...__parser__parser__tests__if_then_else.snap | 45 +++++++ src/parser/types.rs | 13 +- 6 files changed, 184 insertions(+), 52 deletions(-) create mode 100644 src/parser/snapshots/ayin__parser__parser__tests__if_then.snap create mode 100644 src/parser/snapshots/ayin__parser__parser__tests__if_then_else.snap diff --git a/readme.md b/readme.md index c630f22..026a077 100644 --- a/readme.md +++ b/readme.md @@ -17,21 +17,21 @@ let setup = fn() { return { .player: { .position: { .x: 10, .y: 20 }, }, }; -}; +} let update = fn(state, events) { let new = 100; state.player.position.x = new; return state; -}; +} let draw = fn(frame, state) { frame.clear(0,0,0); -}; +} let migrate = fn(state) { return { .player: { .pos: state.player.position } }; -}; +} ``` - dynamic software updating could occur every frame diff --git a/src/parser/parser.rs b/src/parser/parser.rs index e51c17f..66d9bfb 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -77,18 +77,11 @@ fn parse_definition(tokens: &mut Tokens) -> ParseResult { }; if let Some(name) = parse_identifier_name(tokens)? { if let Some(expr) = parse_set(tokens)? { - if let Some(_) = tokens.next_if(&Token::Semicolon) { - Ok(Some(ast::Definition { - mutable, - name, - expr, - })) - } else { - Err(Error::UnexpectedToken { - expected: Token::Semicolon, - got: tokens.next(), - }) - } + Ok(Some(ast::Definition { + mutable, + name, + expr, + })) } else { Err(Error::UnexpectedToken { expected: Token::Equals, @@ -180,6 +173,45 @@ fn parse_simple_expr(tokens: &mut Tokens) -> ParseResult { parse_record, parse_vector, |t| t.between(&Token::OpenParen, &Token::CloseParen, parse_expr), + |t| { + if let Some(_) = t.next_if(&Token::If) { + if let Some(condition) = parse_expr(t)? { + if let Some(then) = t + .between(&Token::OpenCurly, &Token::CloseCurly, parse_block)? + .map(ast::Expr::Block) + { + if let Some(r#else) = if let Some(_) = t.next_if(&Token::Else) { + Ok( + t.between(&Token::OpenCurly, &Token::CloseCurly, parse_block)? + .map(ast::Expr::Block), + ) + } else { + Ok(Some(ast::UNIT)) + }? { + Ok(Some(ast::Expr::If { + condition: Box::new(condition), + then: Box::new(then), + r#else: Box::new(r#else), + })) + } else { + Err(Error::UnexpectedTokenForParser( + "if then block".into(), + t.next(), + )) + } + } else { + Err(Error::UnexpectedTokenForParser( + "if condition".into(), + t.next(), + )) + } + } else { + Err(Error::UnexpectedTokenForParser("if".into(), t.next())) + } + } else { + Ok(None) + } + }, ]) } @@ -211,38 +243,29 @@ fn parse_fn(tokens: &mut Tokens) -> ParseResult { } fn parse_block(tokens: &mut Tokens) -> ParseResult> { - tokens.many(parse_statement) + if let Some(mut stmts) = tokens.many_sep_by(&Token::Semicolon, parse_statement)? { + println!("{stmts:#?} {} {:#?}", stmts.len(), tokens.peek()); + if stmts.len() > 0 && tokens.next_if(&Token::Semicolon).is_some() { + stmts.push(ast::Statement::Expr(ast::UNIT)); + println!("stmts: {stmts:#?}"); + Ok(Some(stmts)) + } else { + println!("stmts or not: {stmts:#?} {tokens:#?}"); + Ok(Some(stmts)) + } + } else { + Ok(None) + } } fn parse_statement(tokens: &mut Tokens) -> ParseResult { tokens.one_of(vec![ - |tokens| { - if let Some(expr) = parse_expr(tokens)?.map(ast::Statement::Expr) { - if let Some(_) = tokens.next_if(&Token::Semicolon) { - Ok(Some(expr)) - } else { - println!("parse statement expr"); - Err(Error::UnexpectedToken { - expected: Token::Semicolon, - got: tokens.next(), - }) - } - } else { - Ok(None) - } - }, + |tokens| Ok(parse_expr(tokens)?.map(ast::Statement::Expr)), |tokens| Ok(parse_definition(tokens)?.map(ast::Statement::Let)), |tokens| { if let Some(_) = tokens.next_if(&Token::Return) { let expr = parse_expr(tokens)?; - if let Some(_) = tokens.next_if(&Token::Semicolon) { - Ok(Some(ast::Statement::Return(expr))) - } else { - Err(Error::UnexpectedToken { - expected: (Token::Return), - got: tokens.next(), - }) - } + Ok(Some(ast::Statement::Return(expr))) } else { Ok(None) } @@ -411,6 +434,18 @@ mod tests { insta::assert_debug_snapshot!(result); } #[test] + fn if_then_else() { + let program = "if 1 == 1 { \"Hello\" } else { \"World\" }".to_string(); + let result = parse(&mut scan(program), parse_expr); + insta::assert_debug_snapshot!(result); + } + #[test] + fn if_then() { + let program = "if !(3 < 2) { \"Hello\" }".to_string(); + let result = parse(&mut scan(program), parse_expr); + insta::assert_debug_snapshot!(result); + } + #[test] fn vector_empty() { let program = "[]".to_string(); let result = parse(&mut scan(program), parse_expr); @@ -454,13 +489,13 @@ mod tests { } #[test] fn let_number() { - let program = "let x = 108;".to_string(); + let program = "let x = 108".to_string(); let result = parse(&mut scan(program), parse_definition); insta::assert_debug_snapshot!(result); } #[test] fn let_mut_number() { - let program = "let mut x = 108;".to_string(); + let program = "let mut x = 108".to_string(); let result = parse(&mut scan(program), parse_definition); insta::assert_debug_snapshot!(result); } @@ -471,21 +506,21 @@ let setup = fn() { return { .player: { .position: { .x: 10, .y: 20 }, }, }; -}; +} let update = fn(state, events) { let new = 100; state.player.position.x = new; return state; -}; +} let draw = fn(frame, state) { frame.clear(0,0,0); -}; +} let migrate = fn(state) { return { .player: { .pos: state.player.position } }; -}; +} " .to_string(); let result = parse(&mut scan(program), parse_definitions); diff --git a/src/parser/scanner.rs b/src/parser/scanner.rs index cac6638..5c10a6f 100644 --- a/src/parser/scanner.rs +++ b/src/parser/scanner.rs @@ -30,9 +30,9 @@ impl Scanner { self.cursor.column += 1; } } - //if let Some(chr) = ch { - // println!("'{}' {}", chr, self.cursor); - //} + // if let Some(chr) = ch { + // println!("'{}' {}", chr, self.cursor); + // } ch } pub fn cursor(&self) -> Pos { @@ -303,6 +303,8 @@ pub fn scan(source: String) -> Tokens { "fn" => Token::Fn, "let" => Token::Let, "mut" => Token::Mut, + "if" => Token::If, + "else" => Token::Else, "return" => Token::Return, "true" => Token::True, "false" => Token::False, diff --git a/src/parser/snapshots/ayin__parser__parser__tests__if_then.snap b/src/parser/snapshots/ayin__parser__parser__tests__if_then.snap new file mode 100644 index 0000000..5e3477d --- /dev/null +++ b/src/parser/snapshots/ayin__parser__parser__tests__if_then.snap @@ -0,0 +1,43 @@ +--- +source: src/parser/parser.rs +expression: result +--- +Ok( + If { + condition: Not( + Op { + lhs: Value( + Int( + 3, + ), + ), + op: Compare( + Lt, + ), + rhs: Value( + Int( + 2, + ), + ), + }, + ), + then: Block( + [ + Expr( + Value( + String( + "Hello", + ), + ), + ), + ], + ), + else: Value( + Record( + Record( + {}, + ), + ), + ), + }, +) diff --git a/src/parser/snapshots/ayin__parser__parser__tests__if_then_else.snap b/src/parser/snapshots/ayin__parser__parser__tests__if_then_else.snap new file mode 100644 index 0000000..41588d2 --- /dev/null +++ b/src/parser/snapshots/ayin__parser__parser__tests__if_then_else.snap @@ -0,0 +1,45 @@ +--- +source: src/parser/parser.rs +expression: result +--- +Ok( + If { + condition: Op { + lhs: Value( + Int( + 1, + ), + ), + op: Compare( + Eq, + ), + rhs: Value( + Int( + 1, + ), + ), + }, + then: Block( + [ + Expr( + Value( + String( + "Hello", + ), + ), + ), + ], + ), + else: Block( + [ + Expr( + Value( + String( + "World", + ), + ), + ), + ], + ), + }, +) diff --git a/src/parser/types.rs b/src/parser/types.rs index dfc6823..390ae65 100644 --- a/src/parser/types.rs +++ b/src/parser/types.rs @@ -21,6 +21,8 @@ pub enum Token { Let, Fn, Mut, + If, + Else, Return, OpenParen, CloseParen, @@ -77,10 +79,13 @@ impl Tokens { self.0.last() } pub fn next(&mut self) -> Option { - self.0.pop() + let t = self.0.pop(); + + // println!("'{:?}'", t); + t } pub fn next_if(&mut self, token: &Token) -> Option { - self.0.pop_if(|t| match (t.token.clone(), token) { + let t = self.0.pop_if(|t| match (t.token.clone(), token) { (a, b) if a == *b => true, (Token::Identifier(_), Token::Identifier(_)) => true, (Token::Number(_), Token::Number(_)) => true, @@ -88,7 +93,9 @@ impl Tokens { (Token::Label(_), Token::Label(_)) => true, (Token::Op(_), Token::Op(_)) => true, _ => false, - }) + }); + // println!("'{:?}'", t); + t } pub fn between(&mut self, start: &Token, end: &Token, parser: Parser) -> ParseResult { if let Some(_start) = self.next_if(start) {