interpreter: Implement ranges and for loops
This commit is contained in:
parent
55070bcc41
commit
aead97e357
@ -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)?,
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user