added a couple macros, removed null type, added environment
This commit is contained in:
@@ -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 {}
|
||||||
|
|||||||
@@ -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,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
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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,2 +1,3 @@
|
|||||||
|
pub mod environment;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
pub mod values;
|
pub mod values;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user