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