aboutsummaryrefslogtreecommitdiff
path: root/src/builtins.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/builtins.rs')
-rw-r--r--src/builtins.rs214
1 files changed, 214 insertions, 0 deletions
diff --git a/src/builtins.rs b/src/builtins.rs
new file mode 100644
index 0000000..8f15603
--- /dev/null
+++ b/src/builtins.rs
@@ -0,0 +1,214 @@
1use std::{collections::HashMap, sync::LazyLock};
2
3use crate::{
4 ast,
5 eval::{Context, Error, Result, Value},
6 Wrap
7};
8
9pub static BUILTINS: LazyLock<HashMap<&'static str, Box<dyn Builtin + Sync + Send>>> =
10 LazyLock::new(|| {
11 [
12 Print.boxed(),
13 IsUpper.boxed(),
14 IsLower.boxed(),
15 Substr.boxed(),
16 Text.boxed(),
17 Parent.boxed(),
18 Children.boxed(),
19 Length.boxed(),
20 Kind.boxed(),
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 }
44}
45
46fn get_args<const N: usize>(args: &[ast::Expr]) -> std::result::Result<&[ast::Expr; N], Error> {
47 args.try_into().map_err(|_| Error::IncorrectArgFormat {
48 wanted: N,
49 got: args.len(),
50 })
51}
52
53struct Print;
54impl Builtin for Print {
55 fn id(&self) -> &'static str {
56 "print"
57 }
58
59 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
60 for arg in args {
61 let val = ctx.eval_expr(arg)?;
62 print!("{val}");
63 }
64 Ok(Value::Unit)
65 }
66}
67
68struct IsUpper;
69impl Builtin for IsUpper {
70 fn id(&self) -> &'static str {
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}
83
84struct IsLower;
85impl Builtin for IsLower {
86 fn id(&self) -> &'static str {
87 "islower"
88 }
89
90 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
91 Ok(ctx
92 .eval_expr(&get_args::<1>(args)?[0])?
93 .as_str()?
94 .chars()
95 .all(|c| c.is_ascii_lowercase())
96 .into())
97 }
98}
99
100struct Substr;
101impl Builtin for Substr {
102 fn id(&self) -> &'static str {
103 "substr"
104 }
105
106 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
107 if let Ok([string, start, end]) = get_args::<3>(args) {
108 let v = ctx.eval_expr(string)?;
109 let s = v.as_str()?;
110 let start = ctx.eval_expr(start)?.as_int()?;
111 let end = ctx.eval_expr(end)?.as_int()?;
112 if start < 0 || start >= s.len() as i128 || end >= s.len() as i128 || start > end {
113 return Err(Error::InvalidStringSlice {
114 length: s.len(),
115 start,
116 end,
117 });
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 }
134 }
135}
136
137struct Text;
138impl Builtin for Text {
139 fn id(&self) -> &'static str {
140 "text"
141 }
142
143 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
144 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
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}
153
154struct Parent;
155impl Builtin for Parent {
156 fn id(&self) -> &'static str {
157 "parent"
158 }
159
160 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
161 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
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}
170
171struct Children;
172impl Builtin for Children {
173 fn id(&self) -> &'static str {
174 "children"
175 }
176
177 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
178 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
179 let id = v.as_node()?;
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}
188
189struct Length;
190impl Builtin for Length {
191 fn id(&self) -> &'static str {
192 "length"
193 }
194
195 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
196 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
197 let l = v.as_list()?;
198 (l.len() as i128).wrap(Value::Integer).wrap(Ok)
199 }
200}
201
202struct Kind;
203impl Builtin for Kind {
204 fn id(&self) -> &'static str {
205 "kind"
206 }
207
208 fn eval(&self, ctx: &mut Context, args: &[ast::Expr]) -> Result {
209 let v = ctx.eval_expr(&get_args::<1>(args)?[0])?;
210 let id = v.as_node()?;
211 let node = ctx.get_node_by_id(id).unwrap();
212 node.kind().to_owned().wrap(Value::String).wrap(Ok)
213 }
214}