mirror of
https://github.com/urinalcaketopper/fddl.git
synced 2025-06-07 05:34:47 +00:00
feat: Implement interpreter for core expressions and print statements
This commit is contained in:
parent
558cbbc0d2
commit
3ce945ca0f
@ -1,5 +0,0 @@
|
||||
[Buildset]
|
||||
BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x08\x00f\x00d\x00d\x00l)
|
||||
|
||||
[Project]
|
||||
VersionControlSupport=kdevgit
|
195
src/interpreter/evaluator.rs
Normal file
195
src/interpreter/evaluator.rs
Normal file
@ -0,0 +1,195 @@
|
||||
use crate::parser::ast::{Expression, Statement, Literal, Operator};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FddlValue {
|
||||
Number(f64),
|
||||
Boolean(bool),
|
||||
String(String),
|
||||
Nil,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FddlValue {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
FddlValue::Number(n) => write!(f, "{}", n),
|
||||
FddlValue::Boolean(b) => write!(f, "{}", b),
|
||||
FddlValue::String(s) => write!(f, "{}", s),
|
||||
FddlValue::Nil => write!(f, "nil"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RuntimeError {
|
||||
TypeMismatch(String),
|
||||
UndefinedVariable(String),
|
||||
}
|
||||
|
||||
pub struct Evaluator;
|
||||
|
||||
impl Evaluator {
|
||||
pub fn new() -> Self {
|
||||
Evaluator
|
||||
}
|
||||
|
||||
pub fn evaluate_program(&mut self, statements: Vec<Statement>) -> Result<(), RuntimeError> {
|
||||
for statement in statements {
|
||||
self.evaluate_statement(&statement)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn evaluate_statement(&mut self, statement: &Statement) -> Result<(), RuntimeError> {
|
||||
match statement {
|
||||
Statement::PrintStatement(expr) => {
|
||||
let value = self.evaluate_expression(expr)?;
|
||||
println!("{}", value);
|
||||
}
|
||||
|
||||
Statement::ExpressionStatement(expr) => {
|
||||
self.evaluate_expression(expr)?;
|
||||
}
|
||||
|
||||
_ => {
|
||||
println!("Interpreter: Skipping unimplemented statement: {:?}", statement);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn evaluate_expression(&mut self, expression: &Expression) -> Result<FddlValue, RuntimeError> {
|
||||
match expression {
|
||||
Expression::Literal(literal) => {
|
||||
match literal {
|
||||
Literal::Number(n) => Ok(FddlValue::Number(*n)),
|
||||
Literal::Boolean(b) => Ok(FddlValue::Boolean(*b)),
|
||||
Literal::String(s) => Ok(FddlValue::String(s.clone())),
|
||||
Literal::Nil => Ok(FddlValue::Nil),
|
||||
}
|
||||
}
|
||||
|
||||
Expression::Unary(op, right_expr) => {
|
||||
let right_val = self.evaluate_expression(right_expr)?;
|
||||
match op {
|
||||
Operator::Minus => {
|
||||
if let FddlValue::Number(n) = right_val {
|
||||
Ok(FddlValue::Number(-n))
|
||||
} else {
|
||||
Err(RuntimeError::TypeMismatch(
|
||||
"Operand for unary '-' must be a number.".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Operator::Not => {
|
||||
if let FddlValue::Boolean(b) = right_val {
|
||||
Ok(FddlValue::Boolean(!b))
|
||||
} else {
|
||||
Err(RuntimeError::TypeMismatch(
|
||||
"Operand for 'not' must be a boolean.".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Operator::Some => {
|
||||
Ok(right_val)
|
||||
}
|
||||
Operator::Almost => {
|
||||
if let FddlValue::Number(n) = right_val {
|
||||
Ok(FddlValue::Number(n.round()))
|
||||
} else {
|
||||
Err(RuntimeError::TypeMismatch(
|
||||
"Operand for unary '~' (Almost) must be a number for this example.".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(RuntimeError::TypeMismatch(format!(
|
||||
"Unsupported unary operator {:?}.",
|
||||
op
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
Expression::Binary(left_expr, op, right_expr) => { // Add this new arm
|
||||
let left_val = self.evaluate_expression(left_expr)?;
|
||||
let right_val = self.evaluate_expression(right_expr)?;
|
||||
|
||||
// Now, perform the operation based on 'op' and the types of left_val and right_val
|
||||
match op {
|
||||
Operator::Plus => {
|
||||
// Example for addition (assuming numbers for now)
|
||||
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||
Ok(FddlValue::Number(l + r))
|
||||
} else {
|
||||
// Later, you might allow string concatenation here
|
||||
Err(RuntimeError::TypeMismatch(
|
||||
format!("Operands for '+' must be numbers. Got {:?} and {:?}", left_val, right_val)
|
||||
))
|
||||
}
|
||||
}
|
||||
Operator::Minus => { // Binary Minus
|
||||
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||
Ok(FddlValue::Number(l - r))
|
||||
} else {
|
||||
Err(RuntimeError::TypeMismatch(
|
||||
format!("Operands for '-' must be numbers. Got {:?} and {:?}", left_val, right_val)
|
||||
))
|
||||
}
|
||||
}
|
||||
Operator::Multiply => {
|
||||
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||
Ok(FddlValue::Number(l * r))
|
||||
} else {
|
||||
Err(RuntimeError::TypeMismatch(
|
||||
format!("Operands for '*' must be numbers. Got {:?} and {:?}", left_val, right_val)
|
||||
))
|
||||
}
|
||||
}
|
||||
Operator::Divide => {
|
||||
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||
if *r == 0.0 {
|
||||
Err(RuntimeError::TypeMismatch("Division by zero.".to_string())) // Or a specific DivisionByZero error
|
||||
} else {
|
||||
Ok(FddlValue::Number(l / r))
|
||||
}
|
||||
} else {
|
||||
Err(RuntimeError::TypeMismatch(
|
||||
format!("Operands for '/' must be numbers. Got {:?} and {:?}", left_val, right_val)
|
||||
))
|
||||
}
|
||||
}
|
||||
Operator::Modulus => { // Assuming you have Operator::Modulus from earlier
|
||||
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||
if *r == 0.0 {
|
||||
Err(RuntimeError::TypeMismatch("Modulus by zero.".to_string()))
|
||||
} else {
|
||||
Ok(FddlValue::Number(l % r))
|
||||
}
|
||||
} else {
|
||||
Err(RuntimeError::TypeMismatch(
|
||||
format!("Operands for '%' must be numbers. Got {:?} and {:?}", left_val, right_val)
|
||||
))
|
||||
}
|
||||
}
|
||||
// TODO: Add cases for Operator::Greater, Less, EqualEqual, NotEqual, And, Or etc.
|
||||
// These will typically operate on numbers or booleans and produce FddlValue::Boolean.
|
||||
_ => Err(RuntimeError::TypeMismatch(format!(
|
||||
"Unsupported binary operator {:?}.",
|
||||
op
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
Expression::Grouping(inner_expr) => {
|
||||
self.evaluate_expression(inner_expr)
|
||||
}
|
||||
|
||||
_ => {
|
||||
println!("Interpreter: Unimplemented expression: {:?}", expression);
|
||||
Err(RuntimeError::TypeMismatch(format!(
|
||||
"Unimplemented expression type: {:?}",
|
||||
expression
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
1
src/interpreter/mod.rs
Normal file
1
src/interpreter/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod evaluator;
|
@ -269,6 +269,7 @@ impl Lexer {
|
||||
"else" => Token::Else,
|
||||
"true" => Token::True,
|
||||
"false" => Token::False,
|
||||
"nil" => Token::Nil,
|
||||
"let" => Token::Let,
|
||||
"const" => Token::Const,
|
||||
"func" => Token::Func,
|
||||
|
@ -35,6 +35,7 @@ pub enum Token {
|
||||
Else,
|
||||
True,
|
||||
False,
|
||||
Nil,
|
||||
Let,
|
||||
Const,
|
||||
Func,
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod compiler;
|
||||
pub mod interpreter;
|
54
src/main.rs
54
src/main.rs
@ -6,9 +6,9 @@ use std::env;
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
|
||||
// use lexer::Lexer;
|
||||
use fddl::lexer::Lexer;
|
||||
use fddl::parser::Parser;
|
||||
use fddl::interpreter::evaluator::Evaluator;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
@ -23,7 +23,8 @@ fn main() {
|
||||
|
||||
// basic REPL
|
||||
fn run_repl() {
|
||||
println!("fddl repl");
|
||||
println!("FDDL REPL");
|
||||
println!("---------");
|
||||
loop {
|
||||
print!("fddl % ");
|
||||
io::stdout().flush().unwrap();
|
||||
@ -45,45 +46,40 @@ fn run_file(path: &str) {
|
||||
run(source);
|
||||
}
|
||||
|
||||
// runs source code
|
||||
fn run(source: String) {
|
||||
println!("Source: {}", source.trim()); // prints source for debugging
|
||||
println!("Source: {}", source.trim());
|
||||
|
||||
let mut lexer = Lexer::new(source);
|
||||
let tokens = lexer.scan_tokens();
|
||||
|
||||
// old code begins
|
||||
// println!("Tokens:");
|
||||
// for token in &tokens { // Iterate by reference if you use tokens later
|
||||
// println!("{:?}", token);
|
||||
// }
|
||||
// println!("---");
|
||||
// old code ends - delete if not needed
|
||||
println!("Tokens: {:?}", tokens);
|
||||
|
||||
let mut parser = Parser::new(tokens); // Create a new parser instance
|
||||
let ast_statements = parser.parse_program(); // NEW!
|
||||
let mut parser = Parser::new(tokens);
|
||||
|
||||
println!("Parsed Statements (AST):");
|
||||
let program_ast = parser.parse_program();
|
||||
|
||||
for stmt in ast_statements {
|
||||
println!("{:?}", stmt);
|
||||
if !program_ast.is_empty() {
|
||||
println!("Parsed Statements (AST):");
|
||||
for stmt in &program_ast {
|
||||
println!("{:?}", stmt);
|
||||
}
|
||||
} else {
|
||||
println!("No AST generated or parsing failed.");
|
||||
}
|
||||
|
||||
loop {
|
||||
|
||||
if parser.is_at_end() {
|
||||
break;
|
||||
}
|
||||
|
||||
match parser.parse_statement() {
|
||||
Some(statement) => {
|
||||
println!("{:?}", statement);
|
||||
}
|
||||
None => {
|
||||
println!("Parser returned None, might be an error or unhandled EOF by parse_statement.");
|
||||
break;
|
||||
if !program_ast.is_empty() {
|
||||
println!("Output:");
|
||||
let mut evaluator = Evaluator::new();
|
||||
match evaluator.evaluate_program(program_ast) {
|
||||
Ok(()) => { /* Program executed successfully */ }
|
||||
Err(e) => {
|
||||
eprintln!("Runtime Error: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
println!("Skipping execution.");
|
||||
}
|
||||
|
||||
println!("---");
|
||||
}
|
@ -67,4 +67,8 @@ pub enum Statement {
|
||||
body: Vec<Statement>,
|
||||
},
|
||||
ReturnStatement(Option<Expression>),
|
||||
Assignment {
|
||||
target_name: String,
|
||||
value: Expression,
|
||||
},
|
||||
}
|
@ -17,57 +17,46 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
|
||||
/* fn parse_for_statement(&mut self) -> Option<Statement> {
|
||||
if !self.match_token(Token::LeftParen) {
|
||||
eprintln!("Error: Expected '(' after 'for'.");
|
||||
return None;
|
||||
}
|
||||
// fn parse_expression_statement(&mut self) -> Option<Statement> {
|
||||
// let expr = self.parse_expression()?;
|
||||
// if self.match_token(Token::Semicolon) {
|
||||
// Some(Statement::ExpressionStatement(expr))
|
||||
// } else {
|
||||
// eprintln!("Error: Expected ';' after expression statement.");
|
||||
// None
|
||||
// }
|
||||
// }
|
||||
|
||||
let initializer = if self.match_token(Token::Semicolon) {
|
||||
None
|
||||
} else if self.match_token(Token::Let) {
|
||||
Some(Box::new(self.parse_variable_declaration()?))
|
||||
} else {
|
||||
let expr_stmt = self.parse_expression_statement()?;
|
||||
Some(Box::new(expr_stmt))
|
||||
};
|
||||
let condition = if self.check(&Token::Semicolon) {
|
||||
eprintln!("Error: For loop condition is required (for now).");
|
||||
return None;
|
||||
} else {
|
||||
self.parse_expression()?
|
||||
};
|
||||
|
||||
if !self.match_token(Token::Semicolon) {
|
||||
eprintln!("Error: Expected ';' after for loop condition.");
|
||||
return None;
|
||||
}
|
||||
|
||||
let increment = if self.check(&Token::RightParen) {
|
||||
None
|
||||
} else {
|
||||
Some(self.parse_expression()?)
|
||||
};
|
||||
|
||||
if !self.match_token(Token::RightParen) {
|
||||
eprintln!("Error: Expected ')' after for loop clauses.");
|
||||
return None;
|
||||
}
|
||||
|
||||
let body = Box::new(self.parse_statement()?);
|
||||
|
||||
eprintln!("Warning: For statement AST structure might need review.");
|
||||
None
|
||||
|
||||
} */
|
||||
|
||||
fn parse_expression_statement(&mut self) -> Option<Statement> {
|
||||
fn parse_assignment_or_expression_statement(&mut self) -> Option<Statement> {
|
||||
let expr = self.parse_expression()?;
|
||||
if self.match_token(Token::Semicolon) {
|
||||
|
||||
self.skip_comments(); // Skip comments before semicolon
|
||||
|
||||
if self.check(&Token::Equal) {
|
||||
self.advance();
|
||||
|
||||
match expr {
|
||||
Expression::Variable(target_name) => {
|
||||
self.skip_comments();
|
||||
let value_expr = self.parse_expression()?;
|
||||
|
||||
self.skip_comments();
|
||||
if !self.match_token(Token::Semicolon) {
|
||||
eprintln!("Error: Expected ';' after assignment.");
|
||||
return None;
|
||||
}
|
||||
Some(Statement::Assignment { target_name, value: value_expr })
|
||||
}
|
||||
_ => {
|
||||
eprintln!("Error: Invalid assignment target. Must be an identifier.");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
} else if self.match_token(Token::Semicolon) {
|
||||
Some(Statement::ExpressionStatement(expr))
|
||||
} else {
|
||||
eprintln!("Error: Expected ';' after expression statement.");
|
||||
None
|
||||
eprintln!("Error: Expected '=' for assignment or ';' after expression.");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,6 +91,10 @@ impl Parser {
|
||||
self.advance();
|
||||
Some(Expression::Literal(Literal::Boolean(false)))
|
||||
}
|
||||
Token::Nil => {
|
||||
self.advance();
|
||||
Some(Expression::Literal(Literal::Nil))
|
||||
}
|
||||
Token::Identifier(name) => {
|
||||
self.advance();
|
||||
Some(Expression::Variable(name))
|
||||
@ -312,7 +305,9 @@ impl Parser {
|
||||
self.skip_comments();
|
||||
if self.is_at_end() { return None; }
|
||||
|
||||
if self.check(&Token::For) {
|
||||
if self.check(&Token::Return) {
|
||||
self.parse_return_statement()
|
||||
} else if self.check(&Token::For) {
|
||||
self.parse_for_statement()
|
||||
} else if self.check(&Token::Func) {
|
||||
self.parse_function_declaration()
|
||||
@ -327,7 +322,7 @@ impl Parser {
|
||||
} else if self.check(&Token::While) {
|
||||
self.parse_while_statement()
|
||||
} else {
|
||||
self.parse_expression_statement()
|
||||
self.parse_assignment_or_expression_statement()
|
||||
}
|
||||
}
|
||||
|
||||
@ -734,5 +729,40 @@ impl Parser {
|
||||
matches!(self.tokens[self.current], Token::EOF)
|
||||
}
|
||||
|
||||
//5-28/25
|
||||
fn parse_return_statement(&mut self) -> Option<Statement> {
|
||||
if !self.match_token(Token::Return) {
|
||||
eprintln!("Internal parser error: Expected 'return' token.");
|
||||
return None;
|
||||
}
|
||||
|
||||
self.skip_comments(); // Skip comments before the expression
|
||||
|
||||
if self.check(&Token::Semicolon) {
|
||||
self.advance();
|
||||
return Some(Statement::ReturnStatement(None));
|
||||
}
|
||||
|
||||
if self.is_at_end() {
|
||||
eprintln!("Error: Expected expression or ';' after 'return'.");
|
||||
return None;
|
||||
}
|
||||
|
||||
match self.parse_expression() {
|
||||
Some(expr) => {
|
||||
self.skip_comments(); // Skip comments after the expression
|
||||
if !self.match_token(Token::Semicolon) {
|
||||
eprintln!("Error: Expected ';' after return expression.");
|
||||
return None;
|
||||
}
|
||||
Some(Statement::ReturnStatement(Some(expr)))
|
||||
}
|
||||
None => {
|
||||
eprintln!("Error: Invalid expression after 'return'.");
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user