Skip to content

Commit

Permalink
obj spread
Browse files Browse the repository at this point in the history
  • Loading branch information
todesking committed Apr 14, 2024
1 parent e57e948 commit dd2fb8a
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 13 deletions.
10 changes: 9 additions & 1 deletion src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,18 @@ pub struct ArrayItem {
pub expr: Box<Expr>,
}

#[derive(Debug, PartialEq, Eq, Clone, Deserialize)]
pub enum ObjItem {
#[serde(rename = "obj_item_kv")]
Kv(Ident, Box<Expr>),
#[serde(rename = "obj_item_spread")]
Spread(Box<Expr>),
}

#[derive(Debug, PartialEq, Eq, Clone, Deserialize)]
pub enum Expr {
#[serde(rename = "expr_object")]
Object { name: Vec<Ident>, expr: Vec<Expr> },
Object(Vec<ObjItem>),
#[serde(rename = "expr_array")]
Array(Vec<ArrayItem>),
#[serde(rename = "expr_int")]
Expand Down
27 changes: 20 additions & 7 deletions src/evaluator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::ast::ArrayItem;
use crate::ast::Ident;
use crate::ast::ObjItem;
use crate::ast::TopTerm;
use crate::value::AtomValue;
use crate::value::LocalEnv;
Expand Down Expand Up @@ -127,14 +128,26 @@ impl Env {
match expr {
Expr::Int(v) => Ok(AtomValue::Int(*v).into()),
Expr::Str { content } => Ok(AtomValue::Str(content.clone()).into()),
Expr::Object { name, expr } => {
assert!(name.len() == expr.len());
let mut obj = ObjectValue::new();
for (name, expr) in name.iter().zip(expr) {
let value = self.eval_expr(expr, local_env)?;
obj.insert(name.clone(), value);
Expr::Object(items) => {
let mut buf = ObjectValue::new();
for item in items {
match item {
ObjItem::Kv(name, expr) => {
let value = self.eval_expr(expr, local_env)?;
buf.insert(name.clone(), value);
}
ObjItem::Spread(expr) => {
let value = self.eval_expr(expr, local_env)?;
value.use_object(|obj| {
for (name, value) in obj.iter() {
buf.insert(name.clone(), value.clone());
}
Ok(())
})?;
}
}
}
Ok(Value::object(obj))
Ok(Value::object(buf))
}
Expr::Array(items) => {
let mut buf = Vec::new();
Expand Down
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,15 @@ mod test {
);
}

#[test]
fn obj_spread() {
assert_eval_ok!(
"{a: 1, ..{}, b: 2, ..{}, ..{a: 11, c: 13}}",
object_value! {a: 11, b: 2, c: 13}
);
assert_eval_err!("{..[]}", EvalError::type_error("Object", array_value![]));
}

#[test]
fn function() {
assert_eval_ok!("{let f = fn() => 123; f()}", 123);
Expand Down
30 changes: 30 additions & 0 deletions src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ impl Value {
},
}
}
pub fn use_object<T, F: FnOnce(&ObjectValue) -> EvalResult<T>>(&self, f: F) -> EvalResult<T> {
match self {
Value::Ref(ref_value) => match &**ref_value {
RefValue::Object(obj) => f(&obj.borrow()),
_ => Err(EvalError::type_error("Object", self.clone())),
},
_ => Err(EvalError::type_error("Object", self.clone())),
}
}
pub fn use_object_mut<T, F: FnOnce(&mut ObjectValue) -> EvalResult<T>>(
&self,
f: F,
Expand Down Expand Up @@ -341,6 +350,27 @@ impl ObjectValue {
pub fn get(&self, name: &Ident) -> Option<&Value> {
self.values.get(name)
}
pub fn iter(&self) -> impl Iterator<Item = (&Ident, &Value)> {
ObjectIter {
values: &self.values,
names_it: self.names.iter(),
}
}
}
pub struct ObjectIter<'a, I: Iterator<Item = &'a Ident>> {
names_it: I,
values: &'a HashMap<Ident, Value>,
}
impl<'a, I: Iterator<Item = &'a Ident>> Iterator for ObjectIter<'a, I> {
type Item = (&'a Ident, &'a Value);

fn next(&mut self) -> Option<Self::Item> {
let Some(name) = self.names_it.next() else {
return None;
};
let value = self.values.get(name).unwrap();
Some((name, value))
}
}
unsafe impl Trace for ObjectValue {
gc::custom_trace!(this, {
Expand Down
16 changes: 11 additions & 5 deletions tree-sitter-borlang/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,21 @@ const common_rules = {
expr_object: $ => prec(Prec.object, seq(
'{',
repsep(
seq(
field('name', $.ident),
':',
field('expr', $._expr),
),
$._obj_item,
',',
),
'}',
)),
_obj_item: $ => choice($.obj_item_kv, $.obj_item_spread),
obj_item_kv: $ => seq(
field('name', $.ident),
':',
field('expr', $._expr),
),
obj_item_spread: $ => seq(
'..',
$._expr,
),
expr_block: $ => prec(Prec.block, seq(
'{',
repeat(seq(field('terms', $._expr), ';')),
Expand Down

0 comments on commit dd2fb8a

Please sign in to comment.