interpreter: Implement ranges and for loops

This commit is contained in:
John 2023-10-26 21:51:18 -05:00
parent 55070bcc41
commit aead97e357
2 changed files with 57 additions and 9 deletions

View File

@ -31,12 +31,13 @@ impl Config {
fn take_stdin() -> Result<(), Box<dyn Error>> { fn take_stdin() -> Result<(), Box<dyn Error>> {
const PROMPT: &str = "> "; const PROMPT: &str = "> ";
if stdin().is_terminal() { if stdin().is_terminal() {
let mut interpreter = Interpreter::new();
print!("{PROMPT}"); print!("{PROMPT}");
stdout().flush()?; stdout().flush()?;
for line in stdin().lines() { for line in stdin().lines() {
let line = line?; let line = line?;
if !line.is_empty() { if !line.is_empty() {
let _ = run(&line).map_err(|e| eprintln!("{e}")); let _ = run(&line, &mut interpreter).map_err(|e| eprintln!("{e}"));
println!(); println!();
} }
print!("{PROMPT}"); print!("{PROMPT}");
@ -58,8 +59,7 @@ fn parse(file: &str, path: Option<&Path>) -> Result<(), Box<dyn Error>> {
Ok(()) Ok(())
} }
fn run(file: &str) -> Result<(), Box<dyn Error>> { fn run(file: &str, interpreter: &mut Interpreter) -> Result<(), Box<dyn Error>> {
let mut interpreter = Interpreter::new();
// If it parses successfully as a program, run the program // If it parses successfully as a program, run the program
match Parser::from(Lexer::new(file)).parse() { match Parser::from(Lexer::new(file)).parse() {
Ok(ast) => interpreter.interpret(&ast)?, Ok(ast) => interpreter.interpret(&ast)?,

View File

@ -24,6 +24,10 @@ pub mod temp_type_impl {
Char(char), Char(char),
/// A string /// A string
String(String), String(String),
/// An exclusive range
RangeExc(i128, i128),
/// An inclusive range
RangeInc(i128, i128),
} }
impl ConValue { impl ConValue {
/// Gets whether the current value is true or false /// Gets whether the current value is true or false
@ -33,6 +37,18 @@ pub mod temp_type_impl {
_ => Err(Error::with_reason(Reason::TypeError))?, _ => Err(Error::with_reason(Reason::TypeError))?,
} }
} }
pub fn range_exc(self, other: Self) -> IResult<Self> {
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<Self> {
let (Self::Int(a), Self::Int(b)) = (self, other) else {
Err(Error::with_reason(Reason::TypeError))?
};
Ok(Self::RangeInc(a, b))
}
cmp! { cmp! {
lt: false, <; lt: false, <;
lt_eq: true, <=; lt_eq: true, <=;
@ -171,6 +187,8 @@ pub mod temp_type_impl {
ConValue::Bool(v) => v.fmt(f), ConValue::Bool(v) => v.fmt(f),
ConValue::Char(v) => write!(f, "'{v}'"), ConValue::Char(v) => write!(f, "'{v}'"),
ConValue::String(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<()> { pub fn interpret(&mut self, start: &Start) -> IResult<()> {
self.visit(start) self.visit(start)
} }
/// Evaluates a single [Expression](expression::Expr) /// Evaluates a single [Expression](expression::Expr) and returns the value stack.
pub fn eval(mut self, expr: &expression::Expr) -> IResult<Vec<ConValue>> { pub fn eval(&mut self, expr: &expression::Expr) -> IResult<Vec<ConValue>> {
self.visit_expr(expr)?; self.visit_expr(expr)?;
Ok(self.stack) Ok(std::mem::take(&mut self.stack))
} }
fn push(&mut self, value: impl Into<ConValue>) { fn push(&mut self, value: impl Into<ConValue>) {
self.stack.push(value.into()) self.stack.push(value.into())
@ -299,8 +317,8 @@ impl Visitor<IResult<()>> for Interpreter {
Binary::LogAnd | Binary::LogOr | Binary::LogXor => { Binary::LogAnd | Binary::LogOr | Binary::LogXor => {
unimplemented!("Implemented in visit_operation") unimplemented!("Implemented in visit_operation")
} }
Binary::RangeExc => todo!("Range expressions"), Binary::RangeExc => first.range_exc(second),
Binary::RangeInc => todo!("Range expressions"), Binary::RangeInc => first.range_inc(second),
Binary::Less => first.lt(&second), Binary::Less => first.lt(&second),
Binary::LessEq => first.lt_eq(&second), Binary::LessEq => first.lt_eq(&second),
Binary::Equal => first.eq(&second), Binary::Equal => first.eq(&second),
@ -357,6 +375,9 @@ impl Visitor<IResult<()>> for Interpreter {
self.pop()?.truthy()? self.pop()?.truthy()?
} { } {
let Err(out) = self.visit_block(&expr.body) else { 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; continue;
}; };
match out.reason() { match out.reason() {
@ -376,7 +397,31 @@ impl Visitor<IResult<()>> for Interpreter {
} }
fn visit_for(&mut self, expr: &control::For) -> IResult<()> { 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<()> { fn visit_else(&mut self, else_: &control::Else) -> IResult<()> {
@ -482,6 +527,8 @@ pub mod error {
/// Type incompatibility /// Type incompatibility
// TODO: store the type information in this error // TODO: store the type information in this error
TypeError, TypeError,
/// In clause of For loop didn't yield a Range
NotIterable,
} }
impl std::error::Error for Error {} impl std::error::Error for Error {}
@ -498,6 +545,7 @@ pub mod error {
Reason::Continue => "continue".fmt(f), Reason::Continue => "continue".fmt(f),
Reason::StackUnderflow => "Stack underflow".fmt(f), Reason::StackUnderflow => "Stack underflow".fmt(f),
Reason::TypeError => "Type error".fmt(f), Reason::TypeError => "Type error".fmt(f),
Reason::NotIterable => "`in` clause of `for` loop did not yield an iterable".fmt(f),
} }
} }
} }