diff options
Diffstat (limited to 'src/builtins.rs')
-rw-r--r-- | src/builtins.rs | 214 |
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 @@ | |||
1 | use std::{collections::HashMap, sync::LazyLock}; | ||
2 | |||
3 | use crate::{ | ||
4 | ast, | ||
5 | eval::{Context, Error, Result, Value}, | ||
6 | Wrap | ||
7 | }; | ||
8 | |||
9 | pub 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 | |||
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 | } | ||
44 | } | ||
45 | |||
46 | fn 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 | |||
53 | struct Print; | ||
54 | impl 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 | |||
68 | struct IsUpper; | ||
69 | impl 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 | |||
84 | struct IsLower; | ||
85 | impl 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 | |||
100 | struct Substr; | ||
101 | impl 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 | |||
137 | struct Text; | ||
138 | impl 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 | |||
154 | struct Parent; | ||
155 | impl 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 | |||
171 | struct Children; | ||
172 | impl 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 | |||
189 | struct Length; | ||
190 | impl 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 | |||
202 | struct Kind; | ||
203 | impl 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 | } | ||