mirror of
https://github.com/urinalcaketopper/fddl.git
synced 2025-06-05 21:14:47 +00:00
variables are being stored
This commit is contained in:
parent
7e3d423814
commit
1742cf647d
@ -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);
|
||||
|
57
src/main.rs
57
src/main.rs
@ -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) {
|
||||
|
@ -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.");
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user