diff --git a/ablescript/src/ast.rs b/ablescript/src/ast.rs index 94dd46c..e1fe244 100644 --- a/ablescript/src/ast.rs +++ b/ablescript/src/ast.rs @@ -136,7 +136,10 @@ pub enum Stmt { expr: Spanned, args: Vec>, }, - Print(Spanned), + Print { + expr: Spanned, + newline: bool, + }, Read(Assignable), Melo(Spanned), Rlyeh, diff --git a/ablescript/src/interpret.rs b/ablescript/src/interpret.rs index ffd5bd3..9da76ec 100644 --- a/ablescript/src/interpret.rs +++ b/ablescript/src/interpret.rs @@ -214,8 +214,14 @@ impl ExecEnv { /// Perform the action indicated by a statement. fn eval_stmt(&mut self, stmt: &Spanned) -> Result { match &stmt.item { - Stmt::Print(expr) => { - println!("{}", self.eval_expr(expr)?); + Stmt::Print { expr, newline } => { + let value = self.eval_expr(expr)?; + if *newline { + println!("{value}"); + } else { + print!("{value}"); + stdout().lock().flush()?; + } } Stmt::Dim { ident, init } => { let init = match init { diff --git a/ablescript/src/parser.rs b/ablescript/src/parser.rs index b2fc828..4100119 100644 --- a/ablescript/src/parser.rs +++ b/ablescript/src/parser.rs @@ -363,10 +363,24 @@ impl<'source> Parser<'source> { match self.checked_next()? { // Print to stdout Token::Print => { - let stmt = Stmt::Print(buf.take().ok_or_else(|| { - Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span()) - })?); - break self.semicolon_terminated(stmt)?; + break Stmt::Print { + expr: buf.take().ok_or_else(|| { + Error::new(ErrorKind::UnexpectedToken(Token::Print), self.lexer.span()) + })?, + newline: match self.checked_next()? { + Token::Semicolon => true, + Token::Minus => { + self.require(Token::Semicolon)?; + false + } + token => { + return Err(Error::new( + ErrorKind::UnexpectedToken(token), + self.lexer.span(), + )); + } + }, + }; } // Functio call @@ -642,40 +656,43 @@ mod tests { fn simple_math() { let code = "1 * (num + 3) / 666 print;"; let expected = &[Spanned { - item: Stmt::Print(Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(1)), - span: 0..1, - }), - rhs: Box::new(Spanned { - item: Expr::BinOp { - lhs: Box::new(Spanned { - item: Expr::Variable("num".to_owned()), - span: 5..6, - }), - rhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(3)), - span: 9..10, - }), - kind: BinOpKind::Add, - }, - span: 5..10, - }), - kind: BinOpKind::Multiply, - }, - span: 0..11, - }), - rhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(666)), - span: 14..17, - }), - kind: BinOpKind::Divide, + item: Stmt::Print { + expr: Spanned { + item: Expr::BinOp { + lhs: Box::new(Spanned { + item: Expr::BinOp { + lhs: Box::new(Spanned { + item: Expr::Literal(Literal::Int(1)), + span: 0..1, + }), + rhs: Box::new(Spanned { + item: Expr::BinOp { + lhs: Box::new(Spanned { + item: Expr::Variable("num".to_owned()), + span: 5..6, + }), + rhs: Box::new(Spanned { + item: Expr::Literal(Literal::Int(3)), + span: 9..10, + }), + kind: BinOpKind::Add, + }, + span: 5..10, + }), + kind: BinOpKind::Multiply, + }, + span: 0..11, + }), + rhs: Box::new(Spanned { + item: Expr::Literal(Literal::Int(666)), + span: 14..17, + }), + kind: BinOpKind::Divide, + }, + span: 0..17, }, - span: 0..17, - }), + newline: true, + }, span: 0..24, }]; @@ -724,10 +741,13 @@ mod tests { span: 8..21, }, body: vec![Spanned { - item: Stmt::Print(Spanned { - item: Expr::Literal(Literal::Str("Buy Able products!".to_owned())), - span: 25..47, - }), + item: Stmt::Print { + expr: Spanned { + item: Expr::Literal(Literal::Str("Buy Able products!".to_owned())), + span: 25..47, + }, + newline: true, + }, span: 25..54, }], }, @@ -773,41 +793,44 @@ mod tests { fn cart_construction() { let code = "[/*able*/ <= 1, /*script*/ <= 3 - 1] print;"; let expected = &[Spanned { - item: Stmt::Print(Spanned { - item: Expr::Cart(vec![ - ( - Spanned { - item: Expr::Literal(Literal::Str("able".to_owned())), - span: 1..7, - }, - Spanned { - item: Expr::Literal(Literal::Int(1)), - span: 11..12, - }, - ), - ( - Spanned { - item: Expr::Literal(Literal::Str("script".to_owned())), - span: 14..22, - }, - Spanned { - item: Expr::BinOp { - kind: BinOpKind::Subtract, - lhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(3)), - span: 26..27, - }), - rhs: Box::new(Spanned { - item: Expr::Literal(Literal::Int(1)), - span: 30..31, - }), + item: Stmt::Print { + expr: Spanned { + item: Expr::Cart(vec![ + ( + Spanned { + item: Expr::Literal(Literal::Str("able".to_owned())), + span: 1..7, }, - span: 26..31, - }, - ), - ]), - span: 0..32, - }), + Spanned { + item: Expr::Literal(Literal::Int(1)), + span: 11..12, + }, + ), + ( + Spanned { + item: Expr::Literal(Literal::Str("script".to_owned())), + span: 14..22, + }, + Spanned { + item: Expr::BinOp { + kind: BinOpKind::Subtract, + lhs: Box::new(Spanned { + item: Expr::Literal(Literal::Int(3)), + span: 26..27, + }), + rhs: Box::new(Spanned { + item: Expr::Literal(Literal::Int(1)), + span: 30..31, + }), + }, + span: 26..31, + }, + ), + ]), + span: 0..32, + }, + newline: true, + }, span: 0..39, }]; @@ -819,28 +842,31 @@ mod tests { fn cart_index() { let code = "[/*able*/ <= /*ablecorp*/][/*ablecorp*/] print;"; let expected = &[Spanned { - item: Stmt::Print(Spanned { - item: Expr::Index { - expr: Box::new(Spanned { - item: Expr::Cart(vec![( - Spanned { - item: Expr::Literal(Literal::Str("able".to_owned())), - span: 1..7, - }, - Spanned { - item: Expr::Literal(Literal::Str("ablecorp".to_owned())), - span: 11..21, - }, - )]), - span: 0..22, - }), - index: Box::new(Spanned { - item: Expr::Literal(Literal::Str("ablecorp".to_owned())), - span: 23..33, - }), + item: Stmt::Print { + expr: Spanned { + item: Expr::Index { + expr: Box::new(Spanned { + item: Expr::Cart(vec![( + Spanned { + item: Expr::Literal(Literal::Str("able".to_owned())), + span: 1..7, + }, + Spanned { + item: Expr::Literal(Literal::Str("ablecorp".to_owned())), + span: 11..21, + }, + )]), + span: 0..22, + }), + index: Box::new(Spanned { + item: Expr::Literal(Literal::Str("ablecorp".to_owned())), + span: 23..33, + }), + }, + span: 0..34, }, - span: 0..34, - }), + newline: true, + }, span: 0..41, }];