added a couple macros, removed null type, added environment

This commit is contained in:
2026-07-10 15:45:01 -05:00
parent 68955312bb
commit ff2c78f5e0
8 changed files with 165 additions and 24 deletions
-4
View File
@@ -5,7 +5,6 @@ pub trait Expr: Stmt {}
pub enum NodeType { pub enum NodeType {
Program(Program), Program(Program),
NumericLiteral(NumericLiteral), NumericLiteral(NumericLiteral),
NullLiteral(NullLiteral),
Identifier(Identifier), Identifier(Identifier),
BinaryExpr(BinaryExpr), BinaryExpr(BinaryExpr),
EOF, EOF,
@@ -33,9 +32,6 @@ pub struct NumericLiteral {
pub value: f64, pub value: f64,
} }
#[derive(Debug)]
pub struct NullLiteral {}
impl Stmt for Program {} impl Stmt for Program {}
impl Stmt for NumericLiteral {} impl Stmt for NumericLiteral {}
-3
View File
@@ -27,14 +27,12 @@ pub mod interpreter {
CloseParen, CloseParen,
OpenParen, OpenParen,
Equals, Equals,
Null,
Let, Let,
EOF, EOF,
} }
static KEYWORDS: phf::Map<&'static str, Token> = phf::phf_map! { static KEYWORDS: phf::Map<&'static str, Token> = phf::phf_map! {
"let" => Token::Let, "let" => Token::Let,
"null" => Token::Null,
}; };
impl Token { impl Token {
@@ -81,7 +79,6 @@ pub mod interpreter {
Token::OpenParen => write!(f, "Token::OpenParen"), Token::OpenParen => write!(f, "Token::OpenParen"),
Token::CloseParen => write!(f, "Token::CloseParen"), Token::CloseParen => write!(f, "Token::CloseParen"),
Token::EOF => write!(f, "Token::EOF"), Token::EOF => write!(f, "Token::EOF"),
Token::Null => write!(f, "Token::Null"),
} }
} }
} }
+1 -2
View File
@@ -1,6 +1,6 @@
use std::process; use std::process;
use crate::ast::{BinaryExpr, Identifier, NodeType, NullLiteral, NumericLiteral, Program}; use crate::ast::{BinaryExpr, Identifier, NodeType, NumericLiteral, Program};
use crate::lexer::interpreter; use crate::lexer::interpreter;
pub struct Parser { pub struct Parser {
@@ -50,7 +50,6 @@ impl Parser {
Token::Number(n) => NodeType::NumericLiteral(NumericLiteral { Token::Number(n) => NodeType::NumericLiteral(NumericLiteral {
value: n.parse().expect(&format!("Failed to parse number: {}", n)), value: n.parse().expect(&format!("Failed to parse number: {}", n)),
}), }),
Token::Null => NodeType::NullLiteral(NullLiteral {}),
Token::EOF => NodeType::EOF, Token::EOF => NodeType::EOF,
Token::OpenParen => { Token::OpenParen => {
+24 -2
View File
@@ -1,4 +1,5 @@
use clap::Parser; use clap::Parser;
use std::io::Write;
use std::process; use std::process;
mod frontend; mod frontend;
@@ -7,11 +8,32 @@ use frontend::lexer;
use frontend::parser; use frontend::parser;
mod runtime; mod runtime;
use runtime::environment::Environment;
use runtime::interpreter::evaluate; use runtime::interpreter::evaluate;
macro_rules! declare_var {
($env:ident, $name:expr, $value:expr) => {
$env.declare_var(String::from($name), $value)
.expect(&format!(
"Failed to declare variable '{}' in the REPL environment.",
$name
));
};
}
pub fn repl() { pub fn repl() {
// Read-Eval-Print Loop // Read-Eval-Print Loop
use std::io::{self, Write}; use std::{cell::RefCell, io, rc::Rc};
let mut env = Environment::new();
declare_var!(env, "x", make_num!(10.0));
declare_var!(env, "true", make_bool!(true));
declare_var!(env, "false", make_bool!(false));
declare_var!(env, "null", make_null!());
let env = Rc::new(RefCell::new(env));
println!("Welcome to the Riv Lang REPL! Type 'exit' to quit."); println!("Welcome to the Riv Lang REPL! Type 'exit' to quit.");
@@ -30,7 +52,7 @@ pub fn repl() {
let program = parser::Parser::new(input).produce_ast(); let program = parser::Parser::new(input).produce_ast();
let result = evaluate(frontend::ast::NodeType::Program(program)); let result = evaluate(frontend::ast::NodeType::Program(program), &env);
println!("{:#?}", result); println!("{:#?}", result);
} }
} }
+87
View File
@@ -0,0 +1,87 @@
use crate::runtime::values::ValueType;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Debug, Clone)]
pub struct Environment {
parent: Option<Rc<RefCell<Environment>>>,
variables: HashMap<String, ValueType>,
}
impl Environment {
/// Creates a new root environment with no parent.
pub fn new() -> Self {
Self {
parent: None,
variables: HashMap::new(),
}
}
/// Creates a new child environment wrapped in Rc<RefCell<...>>
/// so it can be passed down safely to deeper scopes.
pub fn new_child(parent: Rc<RefCell<Environment>>) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
parent: Some(parent),
variables: HashMap::new(),
}))
}
pub fn declare_var(&mut self, varname: String, value: ValueType) -> Result<ValueType, String> {
if self.variables.contains_key(&varname) {
return Err(format!(
"Cannot declare variable '{}'. As it already is defined.",
varname
));
}
self.variables.insert(varname, value.clone());
Ok(value)
}
pub fn assign_var(
env_ref: &Rc<RefCell<Self>>,
varname: String,
value: ValueType,
) -> Result<ValueType, String> {
let resolved_env = Self::resolve(env_ref, &varname)?;
resolved_env
.borrow_mut()
.variables
.insert(varname, value.clone());
Ok(value)
}
pub fn lookup_var(env_ref: &Rc<RefCell<Self>>, varname: &str) -> Result<ValueType, String> {
let resolved_env = Self::resolve(env_ref, varname)?;
// We clone the value out to avoid holding a reference borrow
let val = resolved_env
.borrow()
.variables
.get(varname)
.cloned()
.unwrap();
Ok(val)
}
pub fn resolve(
env_ref: &Rc<RefCell<Self>>,
varname: &str,
) -> Result<Rc<RefCell<Self>>, String> {
if env_ref.borrow().variables.contains_key(varname) {
return Ok(Rc::clone(env_ref));
}
// Access the parent if it exists
let parent_opt = env_ref.borrow().parent.as_ref().map(Rc::clone);
if let Some(parent_env) = parent_opt {
Self::resolve(&parent_env, varname)
} else {
Err(format!(
"Cannot resolve '{}' as it does not exist.",
varname
))
}
}
}
+30 -13
View File
@@ -1,27 +1,31 @@
use std::cell::RefCell;
use std::process; use std::process;
use std::rc::Rc;
use crate::frontend::ast::{BinaryExpr, NodeType, Program}; use crate::frontend::ast::{BinaryExpr, NodeType, Program};
use crate::runtime::environment::Environment;
use crate::runtime::values::ValueType; use crate::runtime::values::ValueType;
use crate::{make_null, make_num};
fn eval_program(program: Program) -> ValueType { fn eval_program(program: Program, env: &Rc<RefCell<Environment>>) -> ValueType {
// Initialize with your equivalent of NullVal // Initialize with your equivalent of NullVal
let mut last_evaluated = ValueType::Null; let mut last_evaluated = make_null!();
// Iterate through each statement in the program body // Iterate through each statement in the program body
for statement in program.body { for statement in program.body {
last_evaluated = evaluate(statement); last_evaluated = evaluate(statement, env);
} }
last_evaluated last_evaluated
} }
fn eval_binop(binop: BinaryExpr) -> ValueType { fn eval_binop(binop: BinaryExpr, env: &Rc<RefCell<Environment>>) -> ValueType {
let lhs = evaluate(*binop.left); let lhs = evaluate(*binop.left, env);
let rhs = evaluate(*binop.right); let rhs = evaluate(*binop.right, env);
match (&lhs, &rhs) { match (&lhs, &rhs) {
(ValueType::Number(l), ValueType::Number(r)) => eval_num_binop(*l, *r, binop.operator), (ValueType::Number(l), ValueType::Number(r)) => eval_num_binop(*l, *r, binop.operator),
_ => ValueType::Null, _ => make_null!(),
} }
} }
@@ -41,15 +45,28 @@ fn eval_num_binop(lhs: f64, rhs: f64, operator: String) -> ValueType {
} }
} }
ValueType::Number(result) make_num!(result)
} }
pub fn evaluate(node: NodeType) -> ValueType { // Change `env: &Rc<RefCell<Environment>>` or `env: &Environment`
// to `env: &Rc<RefCell<Environment>>`
fn eval_ident(ident: String, env: &Rc<RefCell<Environment>>) -> ValueType {
// Now &env perfectly matches the expected `&Rc<RefCell<Environment>>`
match Environment::lookup_var(env, &ident) {
Ok(value) => value,
Err(err) => {
eprintln!("Error looking up identifier '{}': {}", ident, err);
std::process::exit(1);
}
}
}
pub fn evaluate(node: NodeType, env: &Rc<RefCell<Environment>>) -> ValueType {
match node { match node {
NodeType::NumericLiteral(n) => ValueType::Number(n.value), NodeType::NumericLiteral(n) => make_num!(n.value),
NodeType::NullLiteral(_) => ValueType::Null, NodeType::BinaryExpr(e) => eval_binop(e, env),
NodeType::BinaryExpr(e) => eval_binop(e), NodeType::Program(p) => eval_program(p, env),
NodeType::Program(p) => eval_program(p), NodeType::Identifier(i) => eval_ident(i.symbol, env),
_ => { _ => {
eprintln!( eprintln!(
"Evaluation for node type {:#?} is not implemented yet.", "Evaluation for node type {:#?} is not implemented yet.",
+1
View File
@@ -1,2 +1,3 @@
pub mod environment;
pub mod interpreter; pub mod interpreter;
pub mod values; pub mod values;
+22
View File
@@ -2,4 +2,26 @@
pub enum ValueType { pub enum ValueType {
Null, Null,
Number(f64), Number(f64),
Boolean(bool),
}
#[macro_export]
macro_rules! make_num {
($n: expr) => {
crate::runtime::values::ValueType::Number($n)
};
}
#[macro_export]
macro_rules! make_null {
() => {
crate::runtime::values::ValueType::Null
};
}
#[macro_export]
macro_rules! make_bool {
($b: expr) => {
crate::runtime::values::ValueType::Boolean($b)
};
} }