Add scope and basic stdlib for evaluation
Introduces a `Scope` struct for dynamic scope management and a `Context` struct to hold the current scope. Implements a basic standard library with arithmetic operations and a greater-than comparison. Modifies `Node::eval` to handle identifiers by resolving them within the current scope and `Call` nodes for function execution. Updates the parser to use `Context::new()` for evaluating vector elements, ensuring a fresh scope. Enhances the main loop to handle potential runtime errors during evaluation.
This commit is contained in:
+127
-12
@@ -1,4 +1,5 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use crate::ast::types::{Identity, Value};
|
use crate::ast::types::{Identity, Value};
|
||||||
|
|
||||||
@@ -11,15 +12,111 @@ pub struct Node<K> {
|
|||||||
|
|
||||||
/// The base for custom node types (extensions)
|
/// The base for custom node types (extensions)
|
||||||
pub trait CustomNode: Debug + Send + Sync {
|
pub trait CustomNode: Debug + Send + Sync {
|
||||||
fn eval(&self, node: &Node<UntypedKind>, ctx: &mut Context) -> Value;
|
fn eval(&self, node: &Node<UntypedKind>, ctx: &mut Context) -> Result<Value, String>;
|
||||||
fn display_name(&self) -> &'static str;
|
fn display_name(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dynamic Scope for the interpreter (Temporary solution)
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Scope {
|
||||||
|
values: HashMap<String, Value>,
|
||||||
|
parent: Option<Arc<Mutex<Scope>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scope {
|
||||||
|
pub fn new(parent: Option<Arc<Mutex<Scope>>>) -> Self {
|
||||||
|
Self {
|
||||||
|
values: HashMap::new(),
|
||||||
|
parent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define(&mut self, name: &str, value: Value) {
|
||||||
|
self.values.insert(name.to_string(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve(&self, name: &str) -> Option<Value> {
|
||||||
|
if let Some(val) = self.values.get(name) {
|
||||||
|
return Some(val.clone());
|
||||||
|
}
|
||||||
|
if let Some(parent) = &self.parent {
|
||||||
|
return parent.lock().unwrap().resolve(name);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Evaluation Context (Scope management)
|
/// Evaluation Context (Scope management)
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
// Add variables, scope management here later
|
pub scope: Arc<Mutex<Scope>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut root_scope = Scope::new(None);
|
||||||
|
|
||||||
|
// Register standard library (built-ins)
|
||||||
|
register_stdlib(&mut root_scope);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
scope: Arc::new(Mutex::new(root_scope)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_stdlib(scope: &mut Scope) {
|
||||||
|
// Helper macro to reduce boilerplate
|
||||||
|
macro_rules! bin_op {
|
||||||
|
($name:expr, $op:tt) => {
|
||||||
|
scope.define($name, Value::Function(Arc::new(|args| {
|
||||||
|
if args.len() < 2 { return Value::Void; } // Error handling later
|
||||||
|
|
||||||
|
// Fold allow multi-arg: (+ 1 2 3) -> 6
|
||||||
|
let mut acc = match &args[0] {
|
||||||
|
Value::Int(i) => *i as f64,
|
||||||
|
Value::Float(f) => *f,
|
||||||
|
_ => return Value::Void,
|
||||||
|
};
|
||||||
|
|
||||||
|
for arg in &args[1..] {
|
||||||
|
let val = match arg {
|
||||||
|
Value::Int(i) => *i as f64,
|
||||||
|
Value::Float(f) => *f,
|
||||||
|
_ => return Value::Void,
|
||||||
|
};
|
||||||
|
acc = acc $op val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return Int if result is integer-like (simplified)
|
||||||
|
if acc.fract() == 0.0 {
|
||||||
|
Value::Int(acc as i64)
|
||||||
|
} else {
|
||||||
|
Value::Float(acc)
|
||||||
|
}
|
||||||
|
})));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bin_op!("+", +);
|
||||||
|
bin_op!("-", -);
|
||||||
|
bin_op!("*", *);
|
||||||
|
bin_op!("/", /);
|
||||||
|
|
||||||
|
// Logic
|
||||||
|
scope.define(">", Value::Function(Arc::new(|args| {
|
||||||
|
if args.len() != 2 { return Value::Void; }
|
||||||
|
let (v1, v2) = (&args[0], &args[1]);
|
||||||
|
match (v1, v2) {
|
||||||
|
(Value::Int(a), Value::Int(b)) => Value::Bool(a > b),
|
||||||
|
(Value::Float(a), Value::Float(b)) => Value::Bool(a > b),
|
||||||
|
(Value::Int(a), Value::Float(b)) => Value::Bool((*a as f64) > *b),
|
||||||
|
(Value::Float(a), Value::Int(b)) => Value::Bool(*a > (*b as f64)),
|
||||||
|
_ => Value::Bool(false),
|
||||||
|
}
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// The core AST variant enum for performance and structure
|
/// The core AST variant enum for performance and structure
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum UntypedKind {
|
pub enum UntypedKind {
|
||||||
@@ -41,25 +138,43 @@ pub enum UntypedKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Node<UntypedKind> {
|
impl Node<UntypedKind> {
|
||||||
pub fn eval(&self, ctx: &mut Context) -> Value {
|
pub fn eval(&self, ctx: &mut Context) -> Result<Value, String> {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
UntypedKind::Nop => Value::Void,
|
UntypedKind::Nop => Ok(Value::Void),
|
||||||
UntypedKind::Constant(v) => v.clone(),
|
UntypedKind::Constant(v) => Ok(v.clone()),
|
||||||
UntypedKind::Identifier(_) => todo!("Lookup in Context"),
|
|
||||||
|
UntypedKind::Identifier(name) => {
|
||||||
|
let scope = ctx.scope.lock().unwrap();
|
||||||
|
scope.resolve(name)
|
||||||
|
.ok_or_else(|| format!("Undefined variable: '{}'", name))
|
||||||
|
},
|
||||||
|
|
||||||
UntypedKind::If { cond, then_br, else_br } => {
|
UntypedKind::If { cond, then_br, else_br } => {
|
||||||
if cond.eval(ctx).is_truthy() {
|
let cond_val = cond.eval(ctx)?;
|
||||||
|
if cond_val.is_truthy() {
|
||||||
then_br.eval(ctx)
|
then_br.eval(ctx)
|
||||||
} else if let Some(eb) = else_br {
|
} else if let Some(eb) = else_br {
|
||||||
eb.eval(ctx)
|
eb.eval(ctx)
|
||||||
} else {
|
} else {
|
||||||
Value::Void
|
Ok(Value::Void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UntypedKind::Call { callee, args } => {
|
UntypedKind::Call { callee, args } => {
|
||||||
let _func = callee.eval(ctx);
|
let func_val = callee.eval(ctx)?;
|
||||||
let _eval_args: Vec<Value> = args.iter().map(|a| a.eval(ctx)).collect();
|
|
||||||
todo!("Execute func with args")
|
// Evaluate all arguments first (strict evaluation)
|
||||||
|
let mut eval_args = Vec::with_capacity(args.len());
|
||||||
|
for arg in args {
|
||||||
|
eval_args.push(arg.eval(ctx)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
match func_val {
|
||||||
|
Value::Function(f) => Ok(f(eval_args)),
|
||||||
|
_ => Err(format!("Attempt to call a non-function value: {}", func_val)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UntypedKind::Extension(ext) => ext.eval(self, ctx),
|
UntypedKind::Extension(ext) => ext.eval(self, ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -122,7 +122,11 @@ impl<'a> Parser<'a> {
|
|||||||
while *self.peek() != TokenKind::RightBracket && *self.peek() != TokenKind::EOF {
|
while *self.peek() != TokenKind::RightBracket && *self.peek() != TokenKind::EOF {
|
||||||
// Vector elements in Myc are usually just constants in a list
|
// Vector elements in Myc are usually just constants in a list
|
||||||
let expr = self.parse_expression()?;
|
let expr = self.parse_expression()?;
|
||||||
elements.push(expr.eval(&mut crate::ast::nodes::Context {})); // Simple direct eval for literals
|
// Simple direct eval for literals (Context::new creates fresh scope)
|
||||||
|
match expr.eval(&mut crate::ast::nodes::Context::new()) {
|
||||||
|
Ok(val) => elements.push(val),
|
||||||
|
Err(e) => return Err(format!("Error evaluating vector element: {}", e)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.expect(TokenKind::RightBracket)?;
|
self.expect(TokenKind::RightBracket)?;
|
||||||
|
|||||||
+13
-7
@@ -53,13 +53,19 @@ impl eframe::App for CompilerApp {
|
|||||||
|
|
||||||
match parser.parse_expression() {
|
match parser.parse_expression() {
|
||||||
Ok(ast) => {
|
Ok(ast) => {
|
||||||
let mut context = Context {};
|
let mut context = Context::new(); // Use new() which registers stdlib
|
||||||
let result = ast.eval(&mut context);
|
match ast.eval(&mut context) {
|
||||||
self.output_log = format!(
|
Ok(result) => {
|
||||||
"AST Parsed Successfully.\nResult: {}\n\nFinished at {:?}",
|
self.output_log = format!(
|
||||||
result,
|
"AST Parsed & Evaluated Successfully.\nResult: {}\n\nFinished at {:?}",
|
||||||
std::time::SystemTime::now(),
|
result,
|
||||||
);
|
std::time::SystemTime::now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.output_log = format!("Runtime Error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.output_log = format!("Parser Error: {}", e);
|
self.output_log = format!("Parser Error: {}", e);
|
||||||
|
|||||||
Reference in New Issue
Block a user