variables are being stored

This commit is contained in:
Tristan 2025-05-29 22:34:57 -04:00
parent 7e3d423814
commit 1742cf647d
3 changed files with 279 additions and 31 deletions

View File

@ -1,4 +1,5 @@
use crate::parser::ast::{Expression, Statement, Literal, Operator};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub enum FddlValue {
@ -23,13 +24,56 @@ impl std::fmt::Display for FddlValue {
pub enum RuntimeError {
TypeMismatch(String),
UndefinedVariable(String),
DivisionByZero,
}
pub struct Evaluator;
pub struct Environment {
values: HashMap<String, FddlValue>,
}
impl Environment {
pub fn new() -> Self {
Environment {
values: HashMap::new(),
}
}
pub fn define(&mut self, name: String, value: FddlValue) {
self.values.insert(name, value);
}
pub fn get(&self, name: &str) -> Result<FddlValue, RuntimeError> {
match self.values.get(name) {
Some(value) => Ok(value.clone()), // Clone to return an owned value
None => Err(RuntimeError::UndefinedVariable(format!(
"Undefined variable '{}'.",
name
))),
}
}
pub fn assign(&mut self, name: &str, value: FddlValue) -> Result<(), RuntimeError> {
if self.values.contains_key(name) {
self.values.insert(name.to_string(), value);
Ok(())
} else {
Err(RuntimeError::UndefinedVariable(format!(
"Cannot assign to undefined variable '{}'.",
name
)))
}
}
}
pub struct Evaluator {
environment: Environment,
}
impl Evaluator {
pub fn new() -> Self {
Evaluator
Evaluator {
environment: Environment::new(),
}
}
pub fn evaluate_program(&mut self, statements: Vec<Statement>) -> Result<(), RuntimeError> {
@ -50,6 +94,27 @@ impl Evaluator {
self.evaluate_expression(expr)?;
}
Statement::VariableDeclaration(name, initializer) => {
let value = match initializer {
Some(init_expr) => self.evaluate_expression(init_expr)?,
None => FddlValue::Nil,
};
self.environment.define(name.clone(), value);
}
Statement::Assignment { target_name, value } => {
let val_to_assign = self.evaluate_expression(value)?;
self.environment.assign(target_name, val_to_assign)?;
}
Statement::Block(statements) => {
for stmt_in_block in statements {
self.evaluate_statement(stmt_in_block)?;
}
}
// TODO: IfStatement, WhileStatement, ForStatement, FunctionDeclaration, ReturnStatement
_ => {
println!("Interpreter: Skipping unimplemented statement: {:?}", statement);
}
@ -66,9 +131,13 @@ impl Evaluator {
Literal::String(s) => Ok(FddlValue::String(s.clone())),
Literal::Nil => Ok(FddlValue::Nil),
}
}
},
Expression::Unary(op, right_expr) => {
Expression::Variable(name) => {
self.environment.get(name)
},
Expression::Unary(op, right_expr) => {
let right_val = self.evaluate_expression(right_expr)?;
match op {
Operator::Minus => {
@ -80,7 +149,7 @@ impl Evaluator {
))
}
}
Operator::Not => {
Operator::Not => {
if let FddlValue::Boolean(b) = right_val {
Ok(FddlValue::Boolean(!b))
} else {
@ -89,7 +158,7 @@ impl Evaluator {
))
}
}
Operator::Some => {
Operator::Some => {
Ok(right_val)
}
Operator::Almost => {
@ -106,26 +175,23 @@ impl Evaluator {
op
))),
}
}
},
Expression::Binary(left_expr, op, right_expr) => { // Add this new arm
Expression::Binary(left_expr, op, right_expr) => {
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
Operator::Minus => {
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
Ok(FddlValue::Number(l - r))
} else {
@ -146,7 +212,7 @@ impl Evaluator {
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
Err(RuntimeError::DivisionByZero)
} else {
Ok(FddlValue::Number(l / r))
}
@ -156,10 +222,10 @@ impl Evaluator {
))
}
}
Operator::Modulus => { // Assuming you have Operator::Modulus from earlier
Operator::Modulus => {
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
if *r == 0.0 {
Err(RuntimeError::TypeMismatch("Modulus by zero.".to_string()))
Err(RuntimeError::TypeMismatch("Modulus by zero.".to_string())) // Or DivisionByZero
} else {
Ok(FddlValue::Number(l % r))
}
@ -169,18 +235,16 @@ impl Evaluator {
))
}
}
// 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) => {
Expression::Grouping(inner_expr) => {
self.evaluate_expression(inner_expr)
}
},
_ => {
println!("Interpreter: Unimplemented expression: {:?}", expression);

View File

@ -9,6 +9,7 @@ use std::io::{self, Write};
use fddl::lexer::Lexer;
use fddl::parser::Parser;
use fddl::interpreter::evaluator::Evaluator;
use fddl::parser::ast::{Statement, Expression};
fn main() {
let args: Vec<String> = env::args().collect();
@ -21,29 +22,63 @@ fn main() {
}
}
// basic REPL
fn run_repl() {
println!("FDDL REPL");
println!("fddl REPL");
println!("---------");
loop {
print!("fddl % ");
io::stdout().flush().unwrap();
let mut evaluator = Evaluator::new();
let mut buffer = String::new();
io::stdin().read_line(&mut buffer).unwrap();
loop {
print!("fddl % ");
std::io::stdout().flush().unwrap();
let mut buffer = String::new();
if std::io::stdin().read_line(&mut buffer).is_err() || buffer.trim() == "exit" {
break;
}
if buffer.trim().is_empty() {
continue;
}
run(buffer.clone());
run_line(buffer, &mut evaluator);
}
}
// runs file
fn run_line(source: String, evaluator: &mut Evaluator) {
println!("Source: {}", source.trim());
let mut lexer = Lexer::new(source);
let tokens = lexer.scan_tokens();
let mut parser = Parser::new(tokens);
let program_ast: Vec<Statement> = parser.parse_program();
if !program_ast.is_empty() {
println!("Output:");
match evaluator.evaluate_program(program_ast) {
Ok(()) => { /* Statement executed successfully */ }
Err(e) => {
eprintln!("Runtime Error: {:?}", e);
}
}
} else {
println!("No AST generated or parsing failed for this line.");
}
println!("---");
}
fn run_file(path: &str) {
let source = fs::read_to_string(path).expect("Failed to read source file");
run(source);
println!("Running file: {}", path);
match std::fs::read_to_string(path) {
Ok(source) => {
let mut file_evaluator = Evaluator::new();
run_line(source, &mut file_evaluator);
}
Err(e) => {
eprintln!("Error reading file '{}': {}", path, e);
}
}
}
fn run(source: String) {

View File

@ -0,0 +1,149 @@
use fddl::lexer::Lexer;
use fddl::parser::Parser;
use fddl::parser::ast::{Statement, Expression, Literal, Operator};
#[test]
fn test_simple_print_statement_number() {
let source = String::from("print 123;");
let mut lexer = Lexer::new(source);
let tokens = lexer.scan_tokens();
let mut parser = Parser::new(tokens);
let program_ast = parser.parse_program();
let expected_ast = vec![
Statement::PrintStatement(
Expression::Literal(Literal::Number(123.0))
)
];
assert_eq!(program_ast, expected_ast, "AST for 'print 123;' did not match.");
}
#[test]
fn test_variable_declaration_with_initializer() {
let source = String::from("let x = 10;");
let mut lexer = Lexer::new(source);
let tokens = lexer.scan_tokens();
let mut parser = Parser::new(tokens);
let program_ast = parser.parse_program();
let expected_ast = vec![
Statement::VariableDeclaration(
"x".to_string(),
Some(Expression::Literal(Literal::Number(10.0)))
)
];
assert_eq!(program_ast, expected_ast, "AST for 'let x = 10;' did not match.");
}
#[test]
fn test_unary_not_expression() {
let source = String::from("print not true;");
let mut lexer = Lexer::new(source);
let tokens = lexer.scan_tokens();
let mut parser = Parser::new(tokens);
let program_ast = parser.parse_program();
let expected_ast = vec![
Statement::PrintStatement(
Expression::Unary(
Operator::Not, // Assuming Operator::Not exists from previous steps
Box::new(Expression::Literal(Literal::Boolean(true)))
)
)
];
assert_eq!(program_ast, expected_ast, "AST for 'print not true;' did not match.");
}
#[test]
fn test_binary_precedence() {
let source = String::from("print 1 + 2 * 3;");
let mut lexer = Lexer::new(source);
let tokens = lexer.scan_tokens();
let mut parser = Parser::new(tokens);
let program_ast = parser.parse_program();
let expected_ast = vec![
Statement::PrintStatement(
Expression::Binary(
Box::new(Expression::Literal(Literal::Number(1.0))),
Operator::Plus,
Box::new(Expression::Binary(
Box::new(Expression::Literal(Literal::Number(2.0))),
Operator::Multiply,
Box::new(Expression::Literal(Literal::Number(3.0)))
))
)
)
];
assert_eq!(program_ast, expected_ast, "AST for 'print 1 + 2 * 3;' did not match.");
}
#[test]
fn test_simple_function_call_statement() {
let source = String::from("my_func();"); // As an expression statement
let mut lexer = Lexer::new(source);
let tokens = lexer.scan_tokens();
let mut parser = Parser::new(tokens);
let program_ast = parser.parse_program();
let expected_ast = vec![
Statement::ExpressionStatement(
Expression::FunctionCall(
Box::new(Expression::Variable("my_func".to_string())),
Vec::new() // No arguments
)
)
];
assert_eq!(program_ast, expected_ast, "AST for 'my_func();' did not match.");
}
#[test]
fn test_assignment_statement() {
let source = String::from("count = count + 1;");
let mut lexer = Lexer::new(source);
let tokens = lexer.scan_tokens();
let mut parser = Parser::new(tokens);
let program_ast = parser.parse_program();
let expected_ast = vec![
Statement::Assignment { // Assuming you added this variant to Statement
target_name: "count".to_string(),
value: Expression::Binary(
Box::new(Expression::Variable("count".to_string())),
Operator::Plus,
Box::new(Expression::Literal(Literal::Number(1.0)))
)
}
];
assert_eq!(program_ast, expected_ast, "AST for 'count = count + 1;' did not match.");
}
#[test]
fn test_if_else_statement_with_blocks() {
let source = String::from("if (x < 10) { print \"small\"; } else { print \"large\"; }");
let mut lexer = Lexer::new(source);
let tokens = lexer.scan_tokens();
let mut parser = Parser::new(tokens);
let program_ast = parser.parse_program();
let expected_ast = vec![
Statement::IfStatement(
Expression::Binary( // Condition: x < 10
Box::new(Expression::Variable("x".to_string())),
Operator::Less,
Box::new(Expression::Literal(Literal::Number(10.0)))
),
Box::new(Statement::Block(vec![ // Then branch
Statement::PrintStatement(Expression::Literal(Literal::String("small".to_string())))
])),
Some(Box::new(Statement::Block(vec![ // Else branch
Statement::PrintStatement(Expression::Literal(Literal::String("large".to_string())))
])))
)
];
assert_eq!(program_ast, expected_ast, "AST for if-else statement did not match.");
}