From 09c9abf23b935c93c25d1a9b3239a7dd98a5128a Mon Sep 17 00:00:00 2001 From: todesking Date: Mon, 15 Apr 2024 00:59:02 +0900 Subject: [PATCH] for --- src/ast.rs | 6 +++ src/eval_error.rs | 28 ++++++++++ src/evaluator.rs | 102 +++++++++++++++++++++++++++++++---- src/lib.rs | 6 +++ src/value.rs | 10 ++++ tree-sitter-borlang/rules.js | 8 +++ 6 files changed, 150 insertions(+), 10 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index fe05d1c..c76360b 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -91,6 +91,12 @@ pub enum Expr { th: Box, el: Option>, }, + #[serde(rename = "expr_for")] + For { + name: Ident, + target: Box, + body: Box, + }, #[serde(rename = "expr_fun")] Fun { params: Vec, expr: Box }, #[serde(rename = "expr_paren")] diff --git a/src/eval_error.rs b/src/eval_error.rs index be41a98..77f1f98 100644 --- a/src/eval_error.rs +++ b/src/eval_error.rs @@ -16,6 +16,8 @@ pub enum EvalError { IndexOutOfBound { len: usize, index: usize }, AssignNotSupported, CastError(String, Value), + TraitProtocol(String), + NumericRange, } impl EvalError { pub fn name_not_found>(name: S) -> Self { @@ -27,6 +29,32 @@ impl EvalError { pub fn cast_error, V: Into>(name: S, value: V) -> Self { Self::CastError(name.into(), value.into()) } + + pub fn trait_protocol>(msg: S) -> EvalError { + Self::TraitProtocol(msg.into()) + } + + pub fn argument_length(expected: usize, actual: usize) -> EvalError { + Self::ArgumentLength { expected, actual } + } + + pub fn property_not_found>(name: S) -> Self { + EvalError::PropertyNotFound(name.into().into()) + } + + pub fn check_argument_len(expected: usize, actual: usize) -> Result<(), EvalError> { + if expected != actual { + return Err(EvalError::argument_length(expected, actual)); + } + Ok(()) + } + + pub fn check_array_index(len: usize, index: usize) -> Result<(), EvalError> { + if len <= index { + return Err(Self::IndexOutOfBound { len, index }); + } + Ok(()) + } } impl Display for EvalError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/src/evaluator.rs b/src/evaluator.rs index 341beef..864f9cf 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -17,11 +17,29 @@ use std::collections::HashMap; pub type EvalResult = Result; pub type IntrinsicFn = dyn Fn(&[Value]) -> EvalResult; +pub struct Names { + trait_iterator: Ident, + next: Ident, + data: Ident, + cur: Ident, +} +impl Default for Names { + fn default() -> Self { + Self { + trait_iterator: Ident::new("#Iterator"), + next: Ident::new("next"), + data: Ident::new("data"), + cur: Ident::new("cur"), + } + } +} + pub struct Env { vars: HashMap, intrinsics: HashMap>, allow_rebind_global: bool, array_proto: ObjectValue, + names: Names, } impl Default for Env { fn default() -> Self { @@ -56,6 +74,7 @@ impl Env { intrinsics: Default::default(), allow_rebind_global: false, array_proto: ObjectValue::new(), + names: Default::default(), }; env.register_instrinsic("#array_len", |args| match args { [this] => this.use_array(|arr| Ok((arr.len() as i32).into())), @@ -76,8 +95,53 @@ impl Env { actual: args.len(), }), }); + env.array_proto .insert("push".into(), Value::intrinsic("#array_push")); + env.array_proto.insert( + env.names.trait_iterator.clone(), + Value::intrinsic("#array#iterator#new"), + ); + + env.register_instrinsic("#array#iterator#new", { + let next = env.names.next.clone(); + let data = env.names.data.clone(); + let cur = env.names.cur.clone(); + move |args| { + EvalError::check_argument_len(1, args.len())?; + let this = &args[0]; + let mut iter = ObjectValue::new(); + iter.insert(next.clone(), Value::intrinsic("#array#iterator#next")); + iter.insert(data.clone(), this.clone()); + iter.insert(cur.clone(), Value::int(0)); + Ok(iter.into()) + } + }); + env.register_instrinsic("#array#iterator#next", { + let data = env.names.data.clone(); + let cur = env.names.cur.clone(); + move |args| { + EvalError::check_argument_len(1, args.len())?; + args[0].use_object_mut(|iter| { + let Some(arr) = iter.get(&data) else { + return Err(EvalError::trait_protocol("Array iterator value(data)")); + }; + let Some(index) = iter.get(&cur) else { + return Err(EvalError::trait_protocol("Array iterator value(cur)")); + }; + let index = index.try_into()?; + let next_value = arr.use_array(|arr| { + if arr.len() <= index { + return Ok(Value::array(vec![])); + } + Ok(Value::array(vec![arr[index].clone()])) + })?; + iter.insert(cur.clone(), Value::try_int(index + 1)?); + Ok(next_value) + }) + } + }); + env.register_instrinsic("+", binop(|lhs: i32, rhs: i32| Ok(Value::int(lhs + rhs)))); env.register_instrinsic("-", binop(|lhs: i32, rhs: i32| Ok(Value::int(lhs - rhs)))); env.register_instrinsic("*", binop(|lhs: i32, rhs: i32| Ok(Value::int(lhs * rhs)))); @@ -259,6 +323,30 @@ impl Env { }; Ok(value) } + Expr::For { name, target, body } => { + let target = self.eval_expr(target, local_env)?; + let iter_new = self.get_prop(&target, &self.names.trait_iterator)?; + let iter = self.eval_app(&iter_new, &[target])?; + let args = [iter]; + loop { + let next = self.get_prop(&args[0], &self.names.next)?; + let next_value = self.eval_app(&next, &args)?; + let next_value = next_value.use_array(|a| match a { + [] => Ok(None), + [v] => Ok(Some(v.clone())), + _ => Err(EvalError::trait_protocol( + "Iterator.next() should return [] or [next_value]", + )), + })?; + let Some(next_value) = next_value else { + break; + }; + let env = LocalEnv::extend(local_env.clone()); + LocalEnv::bind(&env, name.clone(), next_value.clone())?; + self.eval_expr(body, &Some(env))?; + } + Ok(Value::null()) + } Expr::Fun { params, expr } => { Ok(Value::fun(params.clone(), expr.clone(), local_env.clone())) } @@ -293,14 +381,8 @@ impl Env { fn get_index(&self, value: &Value, index: &Value) -> EvalResult { let index: usize = index.try_into()?; value.use_array(|values| { - if index < values.len() { - Ok(values[index].clone()) - } else { - Err(EvalError::IndexOutOfBound { - len: values.len(), - index, - }) - } + EvalError::check_array_index(values.len(), index)?; + Ok(values[index].clone()) }) } @@ -320,14 +402,14 @@ impl Env { f: F, ) -> EvalResult { match value { - Value::Atom(_) => todo!(), + Value::Atom(_) => f(&ObjectValue::new()), Value::Ref(ref_value) => match &**ref_value { RefValue::Object(obj) => { let obj = obj.borrow(); f(&obj) } RefValue::Array(_) => f(&self.array_proto), - _ => todo!(), + _ => f(&ObjectValue::new()), }, } } diff --git a/src/lib.rs b/src/lib.rs index 74e0048..435d5c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -263,6 +263,12 @@ mod test { assert_eval_ok!(["let a = 1", "if false { a = 1 } else { a = 2 }", "a"], 2); } + #[test] + fn test_for() { + assert_eval_ok!(["let a = 0", "for x in [1, 2, 3] { a = a + x }", "a"], 6); + assert_eval_err!("for x in 0 {}", EvalError::property_not_found("#Iterator")); + } + #[test] fn obj() { assert_eval_ok!("{}", object_value! {}); diff --git a/src/value.rs b/src/value.rs index 3fbeee0..b4791c1 100644 --- a/src/value.rs +++ b/src/value.rs @@ -25,6 +25,11 @@ impl Value { pub fn int(v: i32) -> Value { v.into() } + pub fn try_int>(n: N) -> EvalResult { + n.try_into() + .map(Self::int) + .map_err(|_| EvalError::NumericRange) + } pub fn bool(v: bool) -> Value { v.into() } @@ -337,6 +342,11 @@ impl Default for ObjectValue { Self::new() } } +impl From for Value { + fn from(value: ObjectValue) -> Self { + RefValue::Object(GcCell::new(value)).into() + } +} impl ObjectValue { pub fn new() -> ObjectValue { diff --git a/tree-sitter-borlang/rules.js b/tree-sitter-borlang/rules.js index df0efbf..7bf11f0 100644 --- a/tree-sitter-borlang/rules.js +++ b/tree-sitter-borlang/rules.js @@ -38,6 +38,7 @@ const common_rules = { $.expr_app, $.expr_fun, $.expr_if, + $.expr_for, $.expr_prop, $.expr_prop_opt, $.expr_index, @@ -139,6 +140,13 @@ const common_rules = { field('el', $.expr_block), )), ), + expr_for: $ => seq( + 'for', + field('name', $.ident), + 'in', + field('target', $._expr), + field('body', $.expr_block), + ), expr_prop: $ => prec(Prec.prop, seq( field('expr', $._expr), '.',