mirror of
https://github.com/urinalcaketopper/fddl.git
synced 2025-06-07 05:34: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 crate::parser::ast::{Expression, Statement, Literal, Operator};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum FddlValue {
|
pub enum FddlValue {
|
||||||
@ -23,13 +24,56 @@ impl std::fmt::Display for FddlValue {
|
|||||||
pub enum RuntimeError {
|
pub enum RuntimeError {
|
||||||
TypeMismatch(String),
|
TypeMismatch(String),
|
||||||
UndefinedVariable(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 {
|
impl Evaluator {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Evaluator
|
Evaluator {
|
||||||
|
environment: Environment::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn evaluate_program(&mut self, statements: Vec<Statement>) -> Result<(), RuntimeError> {
|
pub fn evaluate_program(&mut self, statements: Vec<Statement>) -> Result<(), RuntimeError> {
|
||||||
@ -50,6 +94,27 @@ impl Evaluator {
|
|||||||
self.evaluate_expression(expr)?;
|
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);
|
println!("Interpreter: Skipping unimplemented statement: {:?}", statement);
|
||||||
}
|
}
|
||||||
@ -66,9 +131,13 @@ impl Evaluator {
|
|||||||
Literal::String(s) => Ok(FddlValue::String(s.clone())),
|
Literal::String(s) => Ok(FddlValue::String(s.clone())),
|
||||||
Literal::Nil => Ok(FddlValue::Nil),
|
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)?;
|
let right_val = self.evaluate_expression(right_expr)?;
|
||||||
match op {
|
match op {
|
||||||
Operator::Minus => {
|
Operator::Minus => {
|
||||||
@ -80,7 +149,7 @@ impl Evaluator {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operator::Not => {
|
Operator::Not => {
|
||||||
if let FddlValue::Boolean(b) = right_val {
|
if let FddlValue::Boolean(b) = right_val {
|
||||||
Ok(FddlValue::Boolean(!b))
|
Ok(FddlValue::Boolean(!b))
|
||||||
} else {
|
} else {
|
||||||
@ -89,7 +158,7 @@ impl Evaluator {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operator::Some => {
|
Operator::Some => {
|
||||||
Ok(right_val)
|
Ok(right_val)
|
||||||
}
|
}
|
||||||
Operator::Almost => {
|
Operator::Almost => {
|
||||||
@ -106,26 +175,23 @@ impl Evaluator {
|
|||||||
op
|
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 left_val = self.evaluate_expression(left_expr)?;
|
||||||
let right_val = self.evaluate_expression(right_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 {
|
match op {
|
||||||
Operator::Plus => {
|
Operator::Plus => {
|
||||||
// Example for addition (assuming numbers for now)
|
|
||||||
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||||
Ok(FddlValue::Number(l + r))
|
Ok(FddlValue::Number(l + r))
|
||||||
} else {
|
} else {
|
||||||
// Later, you might allow string concatenation here
|
|
||||||
Err(RuntimeError::TypeMismatch(
|
Err(RuntimeError::TypeMismatch(
|
||||||
format!("Operands for '+' must be numbers. Got {:?} and {:?}", left_val, right_val)
|
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) {
|
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||||
Ok(FddlValue::Number(l - r))
|
Ok(FddlValue::Number(l - r))
|
||||||
} else {
|
} else {
|
||||||
@ -146,7 +212,7 @@ impl Evaluator {
|
|||||||
Operator::Divide => {
|
Operator::Divide => {
|
||||||
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
if let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||||
if *r == 0.0 {
|
if *r == 0.0 {
|
||||||
Err(RuntimeError::TypeMismatch("Division by zero.".to_string())) // Or a specific DivisionByZero error
|
Err(RuntimeError::DivisionByZero)
|
||||||
} else {
|
} else {
|
||||||
Ok(FddlValue::Number(l / r))
|
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 let (FddlValue::Number(l), FddlValue::Number(r)) = (&left_val, &right_val) {
|
||||||
if *r == 0.0 {
|
if *r == 0.0 {
|
||||||
Err(RuntimeError::TypeMismatch("Modulus by zero.".to_string()))
|
Err(RuntimeError::TypeMismatch("Modulus by zero.".to_string())) // Or DivisionByZero
|
||||||
} else {
|
} else {
|
||||||
Ok(FddlValue::Number(l % r))
|
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!(
|
_ => Err(RuntimeError::TypeMismatch(format!(
|
||||||
"Unsupported binary operator {:?}.",
|
"Unsupported binary operator {:?}.",
|
||||||
op
|
op
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
Expression::Grouping(inner_expr) => {
|
Expression::Grouping(inner_expr) => {
|
||||||
self.evaluate_expression(inner_expr)
|
self.evaluate_expression(inner_expr)
|
||||||
}
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
println!("Interpreter: Unimplemented expression: {:?}", expression);
|
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::lexer::Lexer;
|
||||||
use fddl::parser::Parser;
|
use fddl::parser::Parser;
|
||||||
use fddl::interpreter::evaluator::Evaluator;
|
use fddl::interpreter::evaluator::Evaluator;
|
||||||
|
use fddl::parser::ast::{Statement, Expression};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
@ -21,29 +22,63 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// basic REPL
|
|
||||||
fn run_repl() {
|
fn run_repl() {
|
||||||
println!("FDDL REPL");
|
println!("fddl REPL");
|
||||||
println!("---------");
|
println!("---------");
|
||||||
loop {
|
let mut evaluator = Evaluator::new();
|
||||||
print!("fddl % ");
|
|
||||||
io::stdout().flush().unwrap();
|
|
||||||
|
|
||||||
let mut buffer = String::new();
|
loop {
|
||||||
io::stdin().read_line(&mut buffer).unwrap();
|
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() {
|
if buffer.trim().is_empty() {
|
||||||
continue;
|
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) {
|
fn run_file(path: &str) {
|
||||||
let source = fs::read_to_string(path).expect("Failed to read source file");
|
println!("Running file: {}", path);
|
||||||
run(source);
|
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) {
|
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