From d9f53abeb2e95a537335193d5d77bff18721fe17 Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Mon, 1 Nov 2021 12:35:25 -0700 Subject: [PATCH] While loops --- TODO.md | 5 ++ schala-lang/language/src/reduced_ir/mod.rs | 11 +++- schala-lang/language/src/reduced_ir/types.rs | 1 + .../language/src/tree_walk_eval/evaluator.rs | 55 +++++++++++++++++-- .../language/src/tree_walk_eval/test.rs | 16 +++++- 5 files changed, 82 insertions(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index ab03791..f03e5f1 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,10 @@ # Immediate TODOs / General Code Cleanup + +## Evaluator + +* Make the evaluator take ReducedIR items by reference + ## Testing * Make an automatic (macro-based?) system for numbering compiler errors, this should be every type of error diff --git a/schala-lang/language/src/reduced_ir/mod.rs b/schala-lang/language/src/reduced_ir/mod.rs index 9f74e0d..b22424f 100644 --- a/schala-lang/language/src/reduced_ir/mod.rs +++ b/schala-lang/language/src/reduced_ir/mod.rs @@ -181,7 +181,15 @@ impl<'a, 'b> Reducer<'a, 'b> { Expression::Call { f: Box::new(constructor), args: ordered_args } } Index { .. } => Expression::ReductionError("Index expr not implemented".to_string()), - WhileExpression { .. } => Expression::ReductionError("While expr not implemented".to_string()), + WhileExpression { condition, body } => { + let cond = Box::new(if let Some(condition) = condition { + self.expression(condition) + } else { + Expression::Literal(Literal::Bool(true)) + }); + let statements = self.function_internal_block(body); + Expression::Loop { cond, statements } + } ForExpression { .. } => Expression::ReductionError("For expr not implemented".to_string()), ListLiteral { .. } => Expression::ReductionError("ListLiteral expr not implemented".to_string()), Access { name, expr } => @@ -271,6 +279,7 @@ impl<'a, 'b> Reducer<'a, 'b> { } fn prefix(&mut self, prefix: &ast::PrefixOp, arg: &ast::Expression) -> Expression { + println!("PREFIX: {:?} and ARG: {:?}", prefix, arg); let builtin: Option = TryFrom::try_from(prefix).ok(); match builtin { Some(op) => Expression::Call { diff --git a/schala-lang/language/src/reduced_ir/types.rs b/schala-lang/language/src/reduced_ir/types.rs index 2b79be4..ca6a1f2 100644 --- a/schala-lang/language/src/reduced_ir/types.rs +++ b/schala-lang/language/src/reduced_ir/types.rs @@ -58,6 +58,7 @@ pub enum Expression { Call { f: Box, args: Vec }, Conditional { cond: Box, then_clause: Vec, else_clause: Vec }, CaseMatch { cond: Box, alternatives: Vec }, + Loop { cond: Box, statements: Vec }, ReductionError(String), } diff --git a/schala-lang/language/src/tree_walk_eval/evaluator.rs b/schala-lang/language/src/tree_walk_eval/evaluator.rs index 18ad344..04c18f0 100644 --- a/schala-lang/language/src/tree_walk_eval/evaluator.rs +++ b/schala-lang/language/src/tree_walk_eval/evaluator.rs @@ -16,15 +16,22 @@ enum StatementOutput { Nothing, } +#[derive(Debug, Clone, Copy)] +enum LoopControlFlow { + Break, + Continue, +} + pub struct Evaluator<'a, 'b> { type_context: &'b TypeContext, state: &'b mut State<'a>, early_returning: bool, + loop_control: Option, } impl<'a, 'b> Evaluator<'a, 'b> { pub(crate) fn new(state: &'b mut State<'a>, type_context: &'b TypeContext) -> Self { - Self { state, type_context, early_returning: false } + Self { state, type_context, early_returning: false, loop_control: None } } pub fn evaluate(&mut self, reduced: ReducedIR, repl: bool) -> Vec> { @@ -61,12 +68,14 @@ impl<'a, 'b> Evaluator<'a, 'b> { if self.early_returning { break; } + if let Some(_) = self.loop_control { + break; + } } Ok(if let Some(ret) = retval { ret } else { self.expression(Expression::unit())? }) } fn statement(&mut self, stmt: Statement) -> EvalResult { - println!("Statement: {:?}", stmt); match stmt { Statement::Binding { ref id, expr, constant: _ } => { let evaluated = self.expression(expr)?; @@ -82,8 +91,14 @@ impl<'a, 'b> Evaluator<'a, 'b> { self.early_returning = true; Ok(StatementOutput::Primitive(evaluated)) } - Statement::Break => unimplemented!(), - Statement::Continue => unimplemented!(), + Statement::Break => { + self.loop_control = Some(LoopControlFlow::Break); + Ok(StatementOutput::Nothing) + } + Statement::Continue => { + self.loop_control = Some(LoopControlFlow::Continue); + Ok(StatementOutput::Nothing) + } } } @@ -150,6 +165,7 @@ impl<'a, 'b> Evaluator<'a, 'b> { } Expression::CaseMatch { box cond, alternatives } => self.case_match_expression(cond, alternatives)?, + Expression::Loop { box cond, statements } => self.loop_expression(cond, statements)?, Expression::ReductionError(e) => return Err(e.into()), Expression::Access { name, box expr } => { let expr = self.expression(expr)?; @@ -176,6 +192,37 @@ impl<'a, 'b> Evaluator<'a, 'b> { }) } + fn loop_expression(&mut self, cond: Expression, statements: Vec) -> EvalResult { + let existing = self.loop_control; + let output = self.loop_expression_inner(cond, statements); + self.loop_control = existing; + output + } + + fn loop_expression_inner( + &mut self, + cond: Expression, + statements: Vec, + ) -> EvalResult { + loop { + let cond = self.expression(cond.clone())?; + match cond { + Primitive::Literal(Literal::Bool(true)) => (), + Primitive::Literal(Literal::Bool(false)) => break, + e => return Err(format!("Loop condition evaluates to non-boolean: {:?}", e).into()), + }; + //TODO eventually loops shoudl be able to return something + let _output = self.block(statements.clone())?; + match self.loop_control { + None | Some(LoopControlFlow::Continue) => (), + Some(LoopControlFlow::Break) => { + break; + } + } + } + Ok(Primitive::unit()) + } + fn case_match_expression( &mut self, cond: Expression, diff --git a/schala-lang/language/src/tree_walk_eval/test.rs b/schala-lang/language/src/tree_walk_eval/test.rs index 0b351ea..1c6ec77 100644 --- a/schala-lang/language/src/tree_walk_eval/test.rs +++ b/schala-lang/language/src/tree_walk_eval/test.rs @@ -412,5 +412,19 @@ let z = marbuk(5, 6) (x, y, z) "#; eval_assert(source, "((100, 100), (5, 2), (50, 50))"); - +} + +#[test] +fn loops() { + let source = r#" +let mut a = 0 +let mut count = 0 +while !(a == 5) { + a = a + 1 + count = count + 100 +} + +count + "#; + eval_assert(source, "500"); }