diff options
-rw-r--r-- | src/builtins.rs | 304 | ||||
-rw-r--r-- | 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}; | |||
3 | use crate::{ | 3 | use 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 | ||
9 | pub static BUILTINS: LazyLock<HashMap<&'static str, Box<dyn Builtin + Sync + Send>>> = | 9 | macro_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 | |||
27 | pub(crate) trait Builtin | ||
28 | where | ||
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 | ||
46 | fn get_args<const N: usize>(args: &[ast::Expr]) -> std::result::Result<&[ast::Expr; N], Error> { | 25 | builtins! { |
47 | args.try_into().map_err(|_| Error::IncorrectArgFormat { | 26 | print, |
48 | wanted: N, | ||
49 | got: args.len(), | ||
50 | }) | ||
51 | } | ||
52 | 27 | ||
53 | struct Print; | 28 | // string |
54 | impl 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 | ||
68 | struct IsUpper; | 39 | // list |
69 | impl 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 | ||
84 | struct IsLower; | 44 | fn print(ctx: &mut Context, args: &[ast::Expr]) -> Result { |
85 | impl 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 { | 52 | fn 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 | ||
100 | struct Substr; | 61 | fn islower(ctx: &mut Context, args: &[ast::Expr]) -> Result { |
101 | impl 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 { | 70 | fn 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 | ||
137 | struct Text; | 100 | fn text(ctx: &mut Context, args: &[ast::Expr]) -> Result { |
138 | impl 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 | ||
154 | struct Parent; | 110 | fn parent(ctx: &mut Context, args: &[ast::Expr]) -> Result { |
155 | impl 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 | ||
171 | struct Children; | 120 | fn children(ctx: &mut Context, args: &[ast::Expr]) -> Result { |
172 | impl 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 | ||
189 | struct Length; | 131 | fn length(ctx: &mut Context, args: &[ast::Expr]) -> Result { |
190 | impl 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 { | 137 | fn 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 | ||
202 | struct Kind; | 144 | fn member(ctx: &mut Context, args: &[ast::Expr]) -> Result { |
203 | impl 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 { | 155 | fn 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 { |