From aead97e3574db4b90ea001104b51a8263ea6711f Mon Sep 17 00:00:00 2001 From: John Date: Thu, 26 Oct 2023 21:51:18 -0500 Subject: [PATCH] interpreter: Implement ranges and for loops --- libconlang/examples/interpret.rs | 6 ++-- libconlang/src/interpreter.rs | 60 ++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/libconlang/examples/interpret.rs b/libconlang/examples/interpret.rs index d53ed10..0114f7d 100644 --- a/libconlang/examples/interpret.rs +++ b/libconlang/examples/interpret.rs @@ -31,12 +31,13 @@ impl Config { fn take_stdin() -> Result<(), Box> { const PROMPT: &str = "> "; if stdin().is_terminal() { + let mut interpreter = Interpreter::new(); print!("{PROMPT}"); stdout().flush()?; for line in stdin().lines() { let line = line?; if !line.is_empty() { - let _ = run(&line).map_err(|e| eprintln!("{e}")); + let _ = run(&line, &mut interpreter).map_err(|e| eprintln!("{e}")); println!(); } print!("{PROMPT}"); @@ -58,8 +59,7 @@ fn parse(file: &str, path: Option<&Path>) -> Result<(), Box> { Ok(()) } -fn run(file: &str) -> Result<(), Box> { - let mut interpreter = Interpreter::new(); +fn run(file: &str, interpreter: &mut Interpreter) -> Result<(), Box> { // If it parses successfully as a program, run the program match Parser::from(Lexer::new(file)).parse() { Ok(ast) => interpreter.interpret(&ast)?, diff --git a/libconlang/src/interpreter.rs b/libconlang/src/interpreter.rs index 2d2958b..40d4f8b 100644 --- a/libconlang/src/interpreter.rs +++ b/libconlang/src/interpreter.rs @@ -24,6 +24,10 @@ pub mod temp_type_impl { Char(char), /// A string String(String), + /// An exclusive range + RangeExc(i128, i128), + /// An inclusive range + RangeInc(i128, i128), } impl ConValue { /// Gets whether the current value is true or false @@ -33,6 +37,18 @@ pub mod temp_type_impl { _ => Err(Error::with_reason(Reason::TypeError))?, } } + pub fn range_exc(self, other: Self) -> IResult { + let (Self::Int(a), Self::Int(b)) = (self, other) else { + Err(Error::with_reason(Reason::TypeError))? + }; + Ok(Self::RangeExc(a, b.saturating_sub(1))) + } + pub fn range_inc(self, other: Self) -> IResult { + let (Self::Int(a), Self::Int(b)) = (self, other) else { + Err(Error::with_reason(Reason::TypeError))? + }; + Ok(Self::RangeInc(a, b)) + } cmp! { lt: false, <; lt_eq: true, <=; @@ -171,6 +187,8 @@ pub mod temp_type_impl { ConValue::Bool(v) => v.fmt(f), ConValue::Char(v) => write!(f, "'{v}'"), ConValue::String(v) => write!(f, "\"{v}\""), + ConValue::RangeExc(a, b) => write!(f, "{a}..{}", b + 1), + ConValue::RangeInc(a, b) => write!(f, "{a}..={b}"), } } } @@ -191,10 +209,10 @@ impl Interpreter { pub fn interpret(&mut self, start: &Start) -> IResult<()> { self.visit(start) } - /// Evaluates a single [Expression](expression::Expr) - pub fn eval(mut self, expr: &expression::Expr) -> IResult> { + /// Evaluates a single [Expression](expression::Expr) and returns the value stack. + pub fn eval(&mut self, expr: &expression::Expr) -> IResult> { self.visit_expr(expr)?; - Ok(self.stack) + Ok(std::mem::take(&mut self.stack)) } fn push(&mut self, value: impl Into) { self.stack.push(value.into()) @@ -299,8 +317,8 @@ impl Visitor> for Interpreter { Binary::LogAnd | Binary::LogOr | Binary::LogXor => { unimplemented!("Implemented in visit_operation") } - Binary::RangeExc => todo!("Range expressions"), - Binary::RangeInc => todo!("Range expressions"), + Binary::RangeExc => first.range_exc(second), + Binary::RangeInc => first.range_inc(second), Binary::Less => first.lt(&second), Binary::LessEq => first.lt_eq(&second), Binary::Equal => first.eq(&second), @@ -357,6 +375,9 @@ impl Visitor> for Interpreter { self.pop()?.truthy()? } { let Err(out) = self.visit_block(&expr.body) else { + // Every expression returns a value. If allowed to pile up, they'll overflow the + // stack. + self.pop()?; continue; }; match out.reason() { @@ -376,7 +397,31 @@ impl Visitor> for Interpreter { } fn visit_for(&mut self, expr: &control::For) -> IResult<()> { - todo!("Visit for: {expr:?}") + self.visit_expr(&expr.iter)?; + let mut broke = false; + let bounds = match self.pop()? { + ConValue::RangeExc(a, b) | ConValue::RangeInc(a, b) => (a, b), + _ => Err(Error::with_reason(Reason::NotIterable))?, + }; + for _ in bounds.0..=bounds.1 { + let Err(out) = self.visit_block(&expr.body) else { + self.pop()?; + continue; + }; + match out.reason() { + Reason::Continue => continue, + Reason::Break(value) => { + self.push(value); + broke = true; + break; + } + r => Err(Error::with_reason(r))?, + } + } + if let (Some(r#else), false) = (&expr.else_, broke) { + self.visit_else(r#else)?; + } + Ok(()) } fn visit_else(&mut self, else_: &control::Else) -> IResult<()> { @@ -482,6 +527,8 @@ pub mod error { /// Type incompatibility // TODO: store the type information in this error TypeError, + /// In clause of For loop didn't yield a Range + NotIterable, } impl std::error::Error for Error {} @@ -498,6 +545,7 @@ pub mod error { Reason::Continue => "continue".fmt(f), Reason::StackUnderflow => "Stack underflow".fmt(f), Reason::TypeError => "Type error".fmt(f), + Reason::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f), } } }