From 7b5f9b93376a6532a39a87107b3c0b6763317fbf Mon Sep 17 00:00:00 2001 From: Akshay Date: Sun, 8 Sep 2024 22:44:39 +0100 Subject: simplify builtins more --- src/builtins.rs | 304 +++++++++++++++++++++++--------------------------------- src/eval.rs | 8 +- 2 files changed, 129 insertions(+), 183 deletions(-) diff --git a/src/builtins.rs b/src/builtins.rs index 8f15603..6fa1110 100644 --- a/src/builtins.rs +++ b/src/builtins.rs @@ -3,212 +3,158 @@ use std::{collections::HashMap, sync::LazyLock}; use crate::{ ast, eval::{Context, Error, Result, Value}, - Wrap + Wrap, }; -pub static BUILTINS: LazyLock>> = - LazyLock::new(|| { - [ - Print.boxed(), - IsUpper.boxed(), - IsLower.boxed(), - Substr.boxed(), - Text.boxed(), - Parent.boxed(), - Children.boxed(), - Length.boxed(), - Kind.boxed(), - ] - .into_iter() - .map(|b| (b.id(), b)) - .collect() - }); - -pub(crate) trait Builtin -where - Self: 'static, -{ - /// Name of this function - fn id(&self) -> &'static str; - - /// Function description - fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result; - - /// Anything that is Builtin can be turned into a trait object - fn boxed(self) -> Box - where - Self: Sized + Sync + Send, - { - Box::new(self) as Box +macro_rules! builtins { + ($($f:ident),* $(,)?) => { + pub static BUILTINS: LazyLock Result + Sync + Send>>> = + LazyLock::new(|| { + [ + $(( + stringify!($f), + Box::new($f) as Box Result + Sync + Send>, + )),* + ] + .into_iter() + .collect() + }); } } -fn get_args(args: &[ast::Expr]) -> std::result::Result<&[ast::Expr; N], Error> { - args.try_into().map_err(|_| Error::IncorrectArgFormat { - wanted: N, - got: args.len(), - }) -} +builtins! { + print, -struct Print; -impl Builtin for Print { - fn id(&self) -> &'static str { - "print" - } + // string + isupper, + islower, + substr, - fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { - for arg in args { - let val = ctx.eval_expr(arg)?; - print!("{val}"); - } - Ok(Value::Unit) - } -} + // node + text, + parent, + children, + kind, -struct IsUpper; -impl Builtin for IsUpper { - fn id(&self) -> &'static str { - "isupper" - } - - fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { - Ok(ctx - .eval_expr(&get_args::<1>(args)?[0])? - .as_str()? - .chars() - .all(|c| c.is_ascii_uppercase()) - .into()) - } + // list + length, + member, } -struct IsLower; -impl Builtin for IsLower { - fn id(&self) -> &'static str { - "islower" +fn print(ctx: &mut Context, args: &[ast::Expr]) -> Result { + for arg in args { + let val = ctx.eval_expr(arg)?; + print!("{val}"); } + Ok(Value::Unit) +} - fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { - Ok(ctx - .eval_expr(&get_args::<1>(args)?[0])? - .as_str()? - .chars() - .all(|c| c.is_ascii_lowercase()) - .into()) - } +fn isupper(ctx: &mut Context, args: &[ast::Expr]) -> Result { + Ok(ctx + .eval_expr(&get_args::<1>(args)?[0])? + .as_str()? + .chars() + .all(|c| c.is_ascii_uppercase()) + .into()) } -struct Substr; -impl Builtin for Substr { - fn id(&self) -> &'static str { - "substr" - } +fn islower(ctx: &mut Context, args: &[ast::Expr]) -> Result { + Ok(ctx + .eval_expr(&get_args::<1>(args)?[0])? + .as_str()? + .chars() + .all(|c| c.is_ascii_lowercase()) + .into()) +} - fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { - if let Ok([string, start, end]) = get_args::<3>(args) { - let v = ctx.eval_expr(string)?; - let s = v.as_str()?; - let start = ctx.eval_expr(start)?.as_int()?; - let end = ctx.eval_expr(end)?.as_int()?; - if start < 0 || start >= s.len() as i128 || end >= s.len() as i128 || start > end { - return Err(Error::InvalidStringSlice { - length: s.len(), - start, - end, - }); - } - Ok(s[start as usize..end as usize].into()) - } else { - let [string, end] = get_args::<2>(args)?; - let v = ctx.eval_expr(string)?; - let s = v.as_str()?; - let end = ctx.eval_expr(end)?.as_int()?; - if end >= s.len() as i128 { - return Err(Error::InvalidStringSlice { - length: s.len(), - start: 0, - end, - }); - } - Ok(s[..end as usize].into()) +fn substr(ctx: &mut Context, args: &[ast::Expr]) -> Result { + if let Ok([string, start, end]) = get_args::<3>(args) { + let v = ctx.eval_expr(string)?; + let s = v.as_str()?; + let start = ctx.eval_expr(start)?.as_int()?; + let end = ctx.eval_expr(end)?.as_int()?; + if start < 0 || start >= s.len() as i128 || end >= s.len() as i128 || start > end { + return Err(Error::InvalidStringSlice { + length: s.len(), + start, + end, + }); } + Ok(s[start as usize..end as usize].into()) + } else { + let [string, end] = get_args::<2>(args)?; + let v = ctx.eval_expr(string)?; + let s = v.as_str()?; + let end = ctx.eval_expr(end)?.as_int()?; + if end >= s.len() as i128 { + return Err(Error::InvalidStringSlice { + length: s.len(), + start: 0, + end, + }); + } + Ok(s[..end as usize].into()) } } -struct Text; -impl Builtin for Text { - fn id(&self) -> &'static str { - "text" - } - - fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { - let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; - let id = v.as_node()?; - let node = ctx.get_node_by_id(id).unwrap(); - let text = node - .utf8_text(ctx.input_src.as_ref().unwrap().as_bytes()) - .unwrap(); - text.to_owned().wrap(Value::String).wrap(Ok) - } +fn text(ctx: &mut Context, args: &[ast::Expr]) -> Result { + let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; + let id = v.as_node()?; + let node = ctx.get_node_by_id(id).unwrap(); + let text = node + .utf8_text(ctx.input_src.as_ref().unwrap().as_bytes()) + .unwrap(); + text.to_owned().wrap(Value::String).wrap(Ok) } -struct Parent; -impl Builtin for Parent { - fn id(&self) -> &'static str { - "parent" - } - - fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { - let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; - let id = v.as_node()?; - let node = ctx.get_node_by_id(id).unwrap(); - let parent = node.parent(); - parent - .map(|n| Value::Node(n.id())) - .ok_or(Error::NoParentNode(node)) - } +fn parent(ctx: &mut Context, args: &[ast::Expr]) -> Result { + let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; + let id = v.as_node()?; + let node = ctx.get_node_by_id(id).unwrap(); + let parent = node.parent(); + parent + .map(|n| Value::Node(n.id())) + .ok_or(Error::NoParentNode(node)) } -struct Children; -impl Builtin for Children { - fn id(&self) -> &'static str { - "children" - } - - fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { - let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; - let id = v.as_node()?; - let node = ctx.get_node_by_id(id).unwrap(); - let children = node - .children(&mut node.walk()) - .map(|c| Value::Node(c.id())) - .collect::>(); - Ok(Value::List(children)) - } +fn children(ctx: &mut Context, args: &[ast::Expr]) -> Result { + let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; + let id = v.as_node()?; + let node = ctx.get_node_by_id(id).unwrap(); + let children = node + .children(&mut node.walk()) + .map(|c| Value::Node(c.id())) + .collect::>(); + Ok(Value::List(children)) } -struct Length; -impl Builtin for Length { - fn id(&self) -> &'static str { - "length" - } +fn length(ctx: &mut Context, args: &[ast::Expr]) -> Result { + let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; + let l = v.as_list()?; + (l.len() as i128).wrap(Value::Integer).wrap(Ok) +} - fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { - let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; - let l = v.as_list()?; - (l.len() as i128).wrap(Value::Integer).wrap(Ok) - } +fn kind(ctx: &mut Context, args: &[ast::Expr]) -> Result { + let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; + let id = v.as_node()?; + let node = ctx.get_node_by_id(id).unwrap(); + node.kind().to_owned().wrap(Value::String).wrap(Ok) } -struct Kind; -impl Builtin for Kind { - fn id(&self) -> &'static str { - "kind" - } +fn member(ctx: &mut Context, args: &[ast::Expr]) -> Result { + let [list_expr, element_expr] = get_args::<2>(args)?; + let list = ctx.eval_expr(&list_expr)?; + let v = list.as_list()?; + let element = ctx.eval_expr(&element_expr)?; + v.iter() + .any(|item| item == &element) + .wrap(Value::Boolean) + .wrap(Ok) +} - fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { - let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; - let id = v.as_node()?; - let node = ctx.get_node_by_id(id).unwrap(); - node.kind().to_owned().wrap(Value::String).wrap(Ok) - } +fn get_args(args: &[ast::Expr]) -> std::result::Result<&[ast::Expr; N], Error> { + args.try_into().map_err(|_| Error::IncorrectArgFormat { + wanted: N, + got: args.len(), + }) } diff --git a/src/eval.rs b/src/eval.rs index 35cba82..b92bf97 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -696,10 +696,10 @@ impl Context { } fn eval_call(&mut self, call: &ast::Call) -> Result { - (&*crate::builtins::BUILTINS) - .get(call.function.as_str()) - .ok_or_else(|| Error::FailedLookup(call.function.to_owned()))? - .eval(self, call.parameters.as_slice()) + ((&*crate::builtins::BUILTINS) + .get(call.function.as_str()) + .ok_or_else(|| Error::FailedLookup(call.function.to_owned()))?) + (self, call.parameters.as_slice()) } fn eval_list(&mut self, list: &ast::List) -> Result { -- cgit v1.2.3