diff --git a/src/lib/eval/mod.rs b/src/lib/eval/mod.rs new file mode 100644 index 0000000..5cd6c3d --- /dev/null +++ b/src/lib/eval/mod.rs @@ -0,0 +1,194 @@ +use std::{ + array, + borrow::{Borrow, BorrowMut}, + collections::{HashMap, VecDeque}, + ops::{Deref, DerefMut}, + rc::Rc, +}; + +use super::parse::ast; + +pub fn eval(program: &ast::Program) { + let mut realm = Realm::default(); + realm.eval_block(&program.body); + + for stat in &program.body.statements { + realm.eval_statement(stat); + } +} + +#[derive(Debug, Default)] +pub struct Realm { + scope: Scope<'static>, +} + +#[derive(Debug, Default)] +pub struct Scope<'p> { + parent: Option>>, + locals: HashMap, +} + +impl<'p> Scope<'p> { + fn child<'c: 'p>(&'c mut self) -> Scope<'c> { + Scope { + parent: Some(Box::new(self)), + ..Default::default() + } + } + + fn find_name(&mut self, name: &ast::Name) -> Option<&mut Value> { + let own = self.locals.get_mut(name); + own.or_else(|| self.parent.as_mut().map(|p| p.find_name(name)).flatten()) + } + + fn set(&mut self, name: ast::Name, value: Value) { + self.locals.insert(name, value); + } + + fn update(&mut self, name: &ast::Name, mut value: Value) -> bool { + let mut found = self.locals.get_mut(name); + if found.is_none() { + return false; + } + found.insert(&mut value); + true + } +} + +impl Realm { + fn eval_block(&mut self, block: &ast::Body) -> RealmResult> { + for stat in &block.statements { + self.eval_statement(stat)?; + } + let result = block.last_expr.as_ref().map(|e| self.eval_expr(&e.0)); + match result { + Some(Result::Err(error)) => Err(error), + Some(Result::Ok(result)) => Ok(Some(result)), + None => Ok(None), + } + } + + fn eval_statement(&mut self, stat: &ast::Statement) -> RealmResult<()> { + match stat { + ast::Statement::Definition(def) => self.eval_def(def), + ast::Statement::Assign(assign) => self.eval_assign(&assign), + ast::Statement::Expression(expr) => Ok(drop(self.eval_expr(&expr))), + } + } + + fn eval_def(&mut self, def: &ast::Definition) -> RealmResult<()> { + let value = self.eval_expr(&def.value)?; + + fn rec(scope: &mut Scope, pattern: &ast::Pattern, value: &Value) -> RealmResult<()> { + match pattern { + ast::Pattern::Name(name) => scope.set(name.clone(), value.clone()), + ast::Pattern::TuplDestr(tup) => { + let Value::Array(arr) = value else { + Err(RealmError::IllegalAccess)? + }; + if arr.len() != tup.fields.len() { + Err(RealmError::IllegalAccess)? + } + for (pattern, value) in tup.fields.iter().zip(arr.iter()) { + rec(scope, pattern, value)? + } + } + ast::Pattern::ArrDestr(arr_pat) => { + let Value::Array(arr) = value else { + Err(RealmError::IllegalAccess)? + }; + let expected_len = arr_pat.item_bef.len() + arr_pat.item_aft.len(); + let fold_len = arr.len() - expected_len; + if arr.len() < expected_len { + Err(RealmError::IllegalAccess)? + } + let mut values = arr.iter(); + for (pattern, value) in arr_pat.item_bef.iter().zip(&mut values) { + rec(scope, pattern, value)? + } + if let Some(fold) = &arr_pat.item_fold { + let value = (&mut values).take(fold_len).cloned().collect(); + let value = Value::new_array(value); + rec(scope, fold, &value)? + } + for (pattern, value) in arr_pat.item_aft.iter().zip(values) { + rec(scope, pattern, value)? + } + } + ast::Pattern::ObjDestr(obj_pat) => { + let Value::Obj(obj) = value else { + Err(RealmError::IllegalAccess)? + }; + for (name, pattern) in &obj_pat.fields { + let Some(value) = obj.get(&name.0) else { + Err(RealmError::IllegalAccess)? + }; + rec(scope, pattern, value)? + } + } + }; + + Ok(()) + } + + rec(&mut self.scope, &def.defined, &value); + Ok(()) + } + + fn eval_assign(&mut self, assign: &ast::Assign) -> RealmResult<()> { + let value = self.eval_expr(&assign.value); + todo!() + } + + fn eval_expr(&mut self, expr: &ast::Expression) -> RealmResult { + todo!() + } +} + +#[derive(Debug, Clone, Copy)] +pub enum RealmError { + IllegalAccess, +} +#[must_use] +pub type RealmResult = Result; + +pub type BoxVal = Box; +pub type Object = Rc>; +pub type Array = Rc>; + +#[derive(Debug, Clone)] +pub enum Value { + Bool(bool), + Int(i32), + Float(f32), + String(String), + Obj(Object), + Array(Array), +} + +pub fn error(message: impl Into) -> Value { + Value::Obj(Rc::new(HashMap::from([( + "message".into(), + Value::String(message.into()), + )]))) +} + +impl Value { + fn new_array(values: Vec) -> Self { + Self::Array(Rc::new(values)) + } + + fn index(&self, key: &Self) -> Result { + let result = match (self, key) { + (Self::Array(arr), Self::Int(num)) => (*num >= 0).then(|| arr.get(*num as usize)).flatten(), + (Self::Obj(obj), Self::String(str)) => obj.get(str), + _ => None, + }; + + let Some(result) = result else { + Err(error("Illegal access"))? + }; + + Ok(result.clone()) + } +} diff --git a/src/lib/mod.rs b/src/lib/mod.rs index b17c403..82b818d 100644 --- a/src/lib/mod.rs +++ b/src/lib/mod.rs @@ -1,3 +1,5 @@ +pub mod eval; pub mod parse; +pub mod utils; pub type Span = std::ops::Range; diff --git a/src/lib/parse/ast/mod.rs b/src/lib/parse/ast/mod.rs index 1c9e1ab..b8571bf 100644 --- a/src/lib/parse/ast/mod.rs +++ b/src/lib/parse/ast/mod.rs @@ -1,7 +1,11 @@ -use crate::lib::parse::{token::Tok, AstParser}; +use crate::lib::{ + parse::{token::Tok, AstParser}, + utils::Boxable, +}; use std::{ any::{type_name, type_name_of_val}, + collections::HashMap, fmt::Display, }; @@ -12,7 +16,7 @@ use super::token::Key; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Program { - body: Body, + pub body: Body, } impl Program { @@ -40,8 +44,8 @@ impl Statement { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Definition { - defined: Pattern, - value: Expression, + pub defined: Pattern, + pub value: Expression, } impl Definition { @@ -56,8 +60,8 @@ impl Definition { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Assign { - assigned: Pattern, - value: Expression, + pub assigned: Pattern, + pub value: Expression, } impl Assign { @@ -71,7 +75,7 @@ impl Assign { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Name(String); +pub struct Name(pub String); impl Name { fn parser() -> impl AstParser { @@ -79,15 +83,84 @@ impl Name { } } +pub fn name(name: impl ToString) -> Name { + Name(name.to_string()) +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Pattern { Name(Name), + TuplDestr(TuplDestr), + ArrDestr(ArrDestr), + ObjDestr(ObjDestr), + // todo : decide if Index(Expression), } impl Pattern { fn parser() -> impl AstParser { Name::parser().map(Self::Name) } + + pub fn as_accessors(&self) -> Vec<(Name, Vec)> { + todo!() + } +} + +#[test] +fn test_property_chain() { + let pattern = Pattern::ObjDestr(ObjDestr { + fields: vec![ + (name("a"), Pattern::Name(name("a"))), + (name("b"), Pattern::Name(name("c"))), + ( + name("d"), + Pattern::TuplDestr(TuplDestr { + fields: vec![Pattern::Name(name("e")), Pattern::Name(name("f"))], + }), + ), + (name("g"), Pattern::TuplDestr(TuplDestr { fields: vec![] })), + ( + name("h"), + Pattern::ArrDestr(ArrDestr { + item_bef: vec![Pattern::Name(name("i")), Pattern::Name(name("j"))], + item_fold: None, + item_aft: vec![], + }), + ), + ( + name("k"), + Pattern::ArrDestr(ArrDestr { + item_bef: vec![Pattern::Name(name("l"))], + item_fold: Some(Pattern::Name(name("m")).into_box()), + item_aft: vec![Pattern::Name(name("n")), Pattern::Name(name("o"))], + }), + ), + (name("p"), Pattern::ObjDestr(ObjDestr { fields: vec![] })), + ], + }); +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TuplDestr { + pub fields: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArrDestr { + pub item_bef: Vec, + pub item_fold: Option>, + pub item_aft: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ObjDestr { + pub fields: Vec<(Name, Pattern)>, +} + +impl ObjDestr { + fn parser() -> impl AstParser { + todo() + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -212,7 +285,7 @@ impl Access { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Invoke { - arguments: Vec, + pub arguments: Vec, } impl Invoke { @@ -225,7 +298,7 @@ impl Invoke { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Index { - index: BoxEx, + pub index: BoxEx, } impl Index { @@ -238,12 +311,12 @@ impl Index { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Property { - index: Name, + pub name: Name, } impl Property { fn parser() -> impl AstParser { - Name::parser().map(|index| Self { index }) + Name::parser().map(|index| Self { name: index }) } } @@ -288,8 +361,8 @@ impl Op { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Body { - statements: Vec, - last_expr: Option, + pub statements: Vec, + pub last_expr: Option, } impl Body { diff --git a/src/lib/utils.rs b/src/lib/utils.rs new file mode 100644 index 0000000..e064965 --- /dev/null +++ b/src/lib/utils.rs @@ -0,0 +1,9 @@ +pub trait Boxable { + fn into_box(self) -> Box; +} + +impl Boxable for T { + fn into_box(self) -> Box { + Box::new(self) + } +}