aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/builtins.rs304
-rw-r--r--src/eval.rs8
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};
3use crate::{ 3use crate::{
4 ast, 4 ast,
5 eval::{Context, Error, Result, Value}, 5 eval::{Context, Error, Result, Value},
6 Wrap 6 Wrap,
7}; 7};
8 8
9pub static BUILTINS: LazyLock<HashMap<&'static str, Box<dyn Builtin + Sync + Send>>> = 9macro_rules! builtins {
10 LazyLock::new(|| { 10 ($($f:ident),* $(,)?) => {
11 [ 11 pub static BUILTINS: LazyLock<HashMap<&'static str, Box<dyn Fn(&mut Context, &[ast::Expr]) -> Result + Sync + Send>>> =
12 Print.boxed(), 12 LazyLock::new(|| {
13 IsUpper.boxed(), 13 [
14 IsLower.boxed(), 14 $((
15 Substr.boxed(), 15 stringify!($f),
16 Text.boxed(), 16 Box::new($f) as Box<dyn Fn(&mut Context, &[ast::Expr]) -> Result + Sync + Send>,
17 Parent.boxed(), 17 )),*
18 Children.boxed(), 18 ]
19 Length.boxed(), 19 .into_iter()
20 Kind.boxed(), 20 .collect()
21 ] 21 });
22 .into_iter()
23 .map(|b| (b.id(), b))
24 .collect()
25 });
26
27pub(crate) trait Builtin
28where
29 Self: 'static,
30{
31 /// Name of this function
32 fn id(&self) -> &'static str;
33
34 /// Function description
35 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result;
36
37 /// Anything that is Builtin can be turned into a trait object
38 fn boxed(self) -> Box<dyn Builtin + Sync + Send>
39 where
40 Self: Sized + Sync + Send,
41 {
42 Box::new(self) as Box<dyn Builtin + Sync + Send>
43 } 22 }
44} 23}
45 24
46fn get_args<const N: usize>(args: &[ast::Expr]) -> std::result::Result<&[ast::Expr; N], Error> { 25builtins! {
47 args.try_into().map_err(|_| Error::IncorrectArgFormat { 26 print,
48 wanted: N,
49 got: args.len(),
50 })
51}
52 27
53struct Print; 28 // string
54impl Builtin for Print { 29 isupper,
55 fn id(&self) -> &'static str { 30 islower,
56 "print" 31 substr,
57 }
58 32
59 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { 33 // node
60 for arg in args { 34 text,
61 let val = ctx.eval_expr(arg)?; 35 parent,
62 print!("{val}"); 36 children,
63 } 37 kind,
64 Ok(Value::Unit)
65 }
66}
67 38
68struct IsUpper; 39 // list
69impl Builtin for IsUpper { 40 length,
70 fn id(&self) -> &'static str { 41 member,
71 "isupper"
72 }
73
74 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
75 Ok(ctx
76 .eval_expr(&get_args::<1>(args)?[0])?
77 .as_str()?
78 .chars()
79 .all(|c| c.is_ascii_uppercase())
80 .into())
81 }
82} 42}
83 43
84struct IsLower; 44fn print(ctx: &mut Context, args: &[ast::Expr]) -> Result {
85impl Builtin for IsLower { 45 for arg in args {
86 fn id(&self) -> &'static str { 46 let val = ctx.eval_expr(arg)?;
87 "islower" 47 print!("{val}");
88 } 48 }
49 Ok(Value::Unit)
50}
89 51
90 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { 52fn isupper(ctx: &mut Context, args: &[ast::Expr]) -> Result {
91 Ok(ctx 53 Ok(ctx
92 .eval_expr(&get_args::<1>(args)?[0])? 54 .eval_expr(&get_args::<1>(args)?[0])?
93 .as_str()? 55 .as_str()?
94 .chars() 56 .chars()
95 .all(|c| c.is_ascii_lowercase()) 57 .all(|c| c.is_ascii_uppercase())
96 .into()) 58 .into())
97 }
98} 59}
99 60
100struct Substr; 61fn islower(ctx: &mut Context, args: &[ast::Expr]) -> Result {
101impl Builtin for Substr { 62 Ok(ctx
102 fn id(&self) -> &'static str { 63 .eval_expr(&get_args::<1>(args)?[0])?
103 "substr" 64 .as_str()?
104 } 65 .chars()
66 .all(|c| c.is_ascii_lowercase())
67 .into())
68}
105 69
106 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { 70fn substr(ctx: &mut Context, args: &[ast::Expr]) -> Result {
107 if let Ok([string, start, end]) = get_args::<3>(args) { 71 if let Ok([string, start, end]) = get_args::<3>(args) {
108 let v = ctx.eval_expr(string)?; 72 let v = ctx.eval_expr(string)?;
109 let s = v.as_str()?; 73 let s = v.as_str()?;
110 let start = ctx.eval_expr(start)?.as_int()?; 74 let start = ctx.eval_expr(start)?.as_int()?;
111 let end = ctx.eval_expr(end)?.as_int()?; 75 let end = ctx.eval_expr(end)?.as_int()?;
112 if start < 0 || start >= s.len() as i128 || end >= s.len() as i128 || start > end { 76 if start < 0 || start >= s.len() as i128 || end >= s.len() as i128 || start > end {
113 return Err(Error::InvalidStringSlice { 77 return Err(Error::InvalidStringSlice {
114 length: s.len(), 78 length: s.len(),
115 start, 79 start,
116 end, 80 end,
117 }); 81 });
118 }
119 Ok(s[start as usize..end as usize].into())
120 } else {
121 let [string, end] = get_args::<2>(args)?;
122 let v = ctx.eval_expr(string)?;
123 let s = v.as_str()?;
124 let end = ctx.eval_expr(end)?.as_int()?;
125 if end >= s.len() as i128 {
126 return Err(Error::InvalidStringSlice {
127 length: s.len(),
128 start: 0,
129 end,
130 });
131 }
132 Ok(s[..end as usize].into())
133 } 82 }
83 Ok(s[start as usize..end as usize].into())
84 } else {
85 let [string, end] = get_args::<2>(args)?;
86 let v = ctx.eval_expr(string)?;
87 let s = v.as_str()?;
88 let end = ctx.eval_expr(end)?.as_int()?;
89 if end >= s.len() as i128 {
90 return Err(Error::InvalidStringSlice {
91 length: s.len(),
92 start: 0,
93 end,
94 });
95 }
96 Ok(s[..end as usize].into())
134 } 97 }
135} 98}
136 99
137struct Text; 100fn text(ctx: &mut Context, args: &[ast::Expr]) -> Result {
138impl Builtin for Text { 101 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
139 fn id(&self) -> &'static str { 102 let id = v.as_node()?;
140 "text" 103 let node = ctx.get_node_by_id(id).unwrap();
141 } 104 let text = node
142 105 .utf8_text(ctx.input_src.as_ref().unwrap().as_bytes())
143 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { 106 .unwrap();
144 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; 107 text.to_owned().wrap(Value::String).wrap(Ok)
145 let id = v.as_node()?;
146 let node = ctx.get_node_by_id(id).unwrap();
147 let text = node
148 .utf8_text(ctx.input_src.as_ref().unwrap().as_bytes())
149 .unwrap();
150 text.to_owned().wrap(Value::String).wrap(Ok)
151 }
152} 108}
153 109
154struct Parent; 110fn parent(ctx: &mut Context, args: &[ast::Expr]) -> Result {
155impl Builtin for Parent { 111 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
156 fn id(&self) -> &'static str { 112 let id = v.as_node()?;
157 "parent" 113 let node = ctx.get_node_by_id(id).unwrap();
158 } 114 let parent = node.parent();
159 115 parent
160 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { 116 .map(|n| Value::Node(n.id()))
161 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; 117 .ok_or(Error::NoParentNode(node))
162 let id = v.as_node()?;
163 let node = ctx.get_node_by_id(id).unwrap();
164 let parent = node.parent();
165 parent
166 .map(|n| Value::Node(n.id()))
167 .ok_or(Error::NoParentNode(node))
168 }
169} 118}
170 119
171struct Children; 120fn children(ctx: &mut Context, args: &[ast::Expr]) -> Result {
172impl Builtin for Children { 121 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
173 fn id(&self) -> &'static str { 122 let id = v.as_node()?;
174 "children" 123 let node = ctx.get_node_by_id(id).unwrap();
175 } 124 let children = node
176 125 .children(&mut node.walk())
177 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { 126 .map(|c| Value::Node(c.id()))
178 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; 127 .collect::<Vec<_>>();
179 let id = v.as_node()?; 128 Ok(Value::List(children))
180 let node = ctx.get_node_by_id(id).unwrap();
181 let children = node
182 .children(&mut node.walk())
183 .map(|c| Value::Node(c.id()))
184 .collect::<Vec<_>>();
185 Ok(Value::List(children))
186 }
187} 129}
188 130
189struct Length; 131fn length(ctx: &mut Context, args: &[ast::Expr]) -> Result {
190impl Builtin for Length { 132 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
191 fn id(&self) -> &'static str { 133 let l = v.as_list()?;
192 "length" 134 (l.len() as i128).wrap(Value::Integer).wrap(Ok)
193 } 135}
194 136
195 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { 137fn kind(ctx: &mut Context, args: &[ast::Expr]) -> Result {
196 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; 138 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
197 let l = v.as_list()?; 139 let id = v.as_node()?;
198 (l.len() as i128).wrap(Value::Integer).wrap(Ok) 140 let node = ctx.get_node_by_id(id).unwrap();
199 } 141 node.kind().to_owned().wrap(Value::String).wrap(Ok)
200} 142}
201 143
202struct Kind; 144fn member(ctx: &mut Context, args: &[ast::Expr]) -> Result {
203impl Builtin for Kind { 145 let [list_expr, element_expr] = get_args::<2>(args)?;
204 fn id(&self) -> &'static str { 146 let list = ctx.eval_expr(&list_expr)?;
205 "kind" 147 let v = list.as_list()?;
206 } 148 let element = ctx.eval_expr(&element_expr)?;
149 v.iter()
150 .any(|item| item == &element)
151 .wrap(Value::Boolean)
152 .wrap(Ok)
153}
207 154
208 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result { 155fn get_args<const N: usize>(args: &[ast::Expr]) -> std::result::Result<&[ast::Expr; N], Error> {
209 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; 156 args.try_into().map_err(|_| Error::IncorrectArgFormat {
210 let id = v.as_node()?; 157 wanted: N,
211 let node = ctx.get_node_by_id(id).unwrap(); 158 got: args.len(),
212 node.kind().to_owned().wrap(Value::String).wrap(Ok) 159 })
213 }
214} 160}
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 {
696 } 696 }
697 697
698 fn eval_call(&mut self, call: &ast::Call) -> Result { 698 fn eval_call(&mut self, call: &ast::Call) -> Result {
699 (&*crate::builtins::BUILTINS) 699 ((&*crate::builtins::BUILTINS)
700 .get(call.function.as_str()) 700 .get(call.function.as_str())
701 .ok_or_else(|| Error::FailedLookup(call.function.to_owned()))? 701 .ok_or_else(|| Error::FailedLookup(call.function.to_owned()))?)
702 .eval(self, call.parameters.as_slice()) 702 (self, call.parameters.as_slice())
703 } 703 }
704 704
705 fn eval_list(&mut self, list: &ast::List) -> Result { 705 fn eval_list(&mut self, list: &ast::List) -> Result {