diff options
author | Akshay <[email protected]> | 2024-10-07 16:18:23 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2024-10-07 16:18:23 +0100 |
commit | 1c33b810521b2ffe5108d8cef32439aef975c48d (patch) | |
tree | 7e936f8283acd14a7c8b6e3da9dc75bf6268bd32 /src/eval | |
parent | 7c9c73af1bc6cb4bbf9eff077bb524ca21031082 (diff) |
refactor; start adding expect tests
Diffstat (limited to 'src/eval')
-rw-r--r-- | src/eval/builtins.rs | 256 | ||||
-rw-r--r-- | src/eval/mod.rs | 1051 |
2 files changed, 1307 insertions, 0 deletions
diff --git a/src/eval/builtins.rs b/src/eval/builtins.rs new file mode 100644 index 0000000..7820a8e --- /dev/null +++ b/src/eval/builtins.rs | |||
@@ -0,0 +1,256 @@ | |||
1 | use std::{collections::HashMap, sync::LazyLock}; | ||
2 | |||
3 | use crate::{ | ||
4 | ast, | ||
5 | eval::{Context, Error, Result, Value}, | ||
6 | Wrap, | ||
7 | }; | ||
8 | |||
9 | macro_rules! builtins { | ||
10 | ($($f:ident),* $(,)?) => { | ||
11 | pub static BUILTINS: LazyLock<HashMap<&'static str, Box<dyn Fn(&mut Context, &[ast::Expr]) -> Result + Sync + Send>>> = | ||
12 | LazyLock::new(|| { | ||
13 | [ | ||
14 | $(( | ||
15 | stringify!($f), | ||
16 | Box::new($f) as Box<dyn Fn(&mut Context, &[ast::Expr]) -> Result + Sync + Send>, | ||
17 | )),* | ||
18 | ] | ||
19 | .into_iter() | ||
20 | .collect() | ||
21 | }); | ||
22 | } | ||
23 | } | ||
24 | |||
25 | builtins! { | ||
26 | print, | ||
27 | println, | ||
28 | |||
29 | // string | ||
30 | isupper, | ||
31 | islower, | ||
32 | substr, | ||
33 | |||
34 | // node | ||
35 | text, | ||
36 | parent, | ||
37 | children, | ||
38 | kind, | ||
39 | |||
40 | // list | ||
41 | length, | ||
42 | member, | ||
43 | push, | ||
44 | pop, | ||
45 | isempty, | ||
46 | } | ||
47 | |||
48 | fn print(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
49 | for arg in args { | ||
50 | let val = ctx.eval_expr(arg)?; | ||
51 | let mut default_stream =Box::new(std::io::stdout()) as Box<dyn std::io::Write> ; | ||
52 | let stream = ctx | ||
53 | .output_stream | ||
54 | .as_mut() | ||
55 | .unwrap_or(&mut default_stream); | ||
56 | write!(stream, "{val}").unwrap(); | ||
57 | } | ||
58 | Ok(Value::Unit) | ||
59 | } | ||
60 | |||
61 | fn println(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
62 | print(ctx, args)?; | ||
63 | print(ctx, &[ast::Expr::Lit(ast::Literal::Str("\n".to_owned()))]) | ||
64 | } | ||
65 | |||
66 | fn isupper(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
67 | Ok(ctx | ||
68 | .eval_expr(&get_args::<1>(args)?[0])? | ||
69 | .as_str()? | ||
70 | .chars() | ||
71 | .all(|c| c.is_ascii_uppercase()) | ||
72 | .into()) | ||
73 | } | ||
74 | |||
75 | fn islower(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
76 | Ok(ctx | ||
77 | .eval_expr(&get_args::<1>(args)?[0])? | ||
78 | .as_str()? | ||
79 | .chars() | ||
80 | .all(|c| c.is_ascii_lowercase()) | ||
81 | .into()) | ||
82 | } | ||
83 | |||
84 | fn substr(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
85 | if let Ok([string, start, end]) = get_args::<3>(args) { | ||
86 | let v = ctx.eval_expr(string)?; | ||
87 | let s = v.as_str()?; | ||
88 | let start = ctx.eval_expr(start)?.as_int()?; | ||
89 | let end = ctx.eval_expr(end)?.as_int()?; | ||
90 | if start < 0 || start >= s.len() as i128 || end >= s.len() as i128 || start > end { | ||
91 | return Err(Error::InvalidStringSlice { | ||
92 | length: s.len(), | ||
93 | start, | ||
94 | end, | ||
95 | }); | ||
96 | } | ||
97 | Ok(s[start as usize..end as usize].into()) | ||
98 | } else { | ||
99 | let [string, end] = get_args::<2>(args)?; | ||
100 | let v = ctx.eval_expr(string)?; | ||
101 | let s = v.as_str()?; | ||
102 | let end = ctx.eval_expr(end)?.as_int()?; | ||
103 | if end >= s.len() as i128 { | ||
104 | return Err(Error::InvalidStringSlice { | ||
105 | length: s.len(), | ||
106 | start: 0, | ||
107 | end, | ||
108 | }); | ||
109 | } | ||
110 | Ok(s[..end as usize].into()) | ||
111 | } | ||
112 | } | ||
113 | |||
114 | fn text(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
115 | let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; | ||
116 | let id = v.as_node()?; | ||
117 | let node = ctx.get_node_by_id(id).unwrap(); | ||
118 | let text = node | ||
119 | .utf8_text(ctx.input_src.as_ref().unwrap().as_bytes()) | ||
120 | .unwrap(); | ||
121 | text.to_owned().wrap(Value::String).wrap(Ok) | ||
122 | } | ||
123 | |||
124 | fn parent(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
125 | let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; | ||
126 | let id = v.as_node()?; | ||
127 | let node = ctx.get_node_by_id(id).unwrap(); | ||
128 | let parent = node.parent(); | ||
129 | parent | ||
130 | .map(|n| Value::Node(n.id())) | ||
131 | .ok_or(Error::NoParentNode(node)) | ||
132 | } | ||
133 | |||
134 | fn children(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
135 | let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; | ||
136 | let id = v.as_node()?; | ||
137 | let node = ctx.get_node_by_id(id).unwrap(); | ||
138 | let children = node | ||
139 | .children(&mut node.walk()) | ||
140 | .map(|c| Value::Node(c.id())) | ||
141 | .collect::<Vec<_>>(); | ||
142 | Ok(Value::List(children)) | ||
143 | } | ||
144 | |||
145 | fn length(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
146 | let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; | ||
147 | let l = v.as_list()?; | ||
148 | (l.len() as i128).wrap(Value::Integer).wrap(Ok) | ||
149 | } | ||
150 | |||
151 | fn kind(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
152 | let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; | ||
153 | let id = v.as_node()?; | ||
154 | let node = ctx.get_node_by_id(id).unwrap(); | ||
155 | node.kind().to_owned().wrap(Value::String).wrap(Ok) | ||
156 | } | ||
157 | |||
158 | fn member(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
159 | let [list_expr, element_expr] = get_args::<2>(args)?; | ||
160 | let list = ctx.eval_expr(&list_expr)?; | ||
161 | let v = list.as_list()?; | ||
162 | let element = ctx.eval_expr(&element_expr)?; | ||
163 | v.iter() | ||
164 | .any(|item| item == &element) | ||
165 | .wrap(Value::Boolean) | ||
166 | .wrap(Ok) | ||
167 | } | ||
168 | |||
169 | fn push(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
170 | let [lhs, rhs] = get_args::<2>(args)?; | ||
171 | let ast::Expr::Ident(ident) = lhs else { | ||
172 | return Err(Error::MalformedExpr(format!( | ||
173 | "malformed assigment, lhs: {:?}", | ||
174 | lhs | ||
175 | ))); | ||
176 | }; | ||
177 | let element = ctx.eval_expr(&rhs)?; | ||
178 | let variable = ctx.lookup_mut(ident)?; | ||
179 | variable.mutate(|v| match &mut v.value { | ||
180 | Value::List(l) => { | ||
181 | l.push(element); | ||
182 | Ok(Value::Unit) | ||
183 | } | ||
184 | _ => Err(v.ty().expected([ast::Type::List])), | ||
185 | }) | ||
186 | } | ||
187 | |||
188 | fn pop(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
189 | let [lhs] = get_args::<1>(args)?; | ||
190 | let ast::Expr::Ident(ident) = lhs else { | ||
191 | return Err(Error::MalformedExpr(format!( | ||
192 | "malformed assigment, lhs: {:?}", | ||
193 | lhs | ||
194 | ))); | ||
195 | }; | ||
196 | let variable = ctx.lookup_mut(ident)?; | ||
197 | variable.mutate(|v| match &mut v.value { | ||
198 | Value::List(l) => l | ||
199 | .pop() | ||
200 | .ok_or_else(|| Error::ArrayOutOfBounds { idx: 0, len: 0 }), | ||
201 | _ => Err(v.ty().expected([ast::Type::List])), | ||
202 | }) | ||
203 | } | ||
204 | |||
205 | fn isempty(ctx: &mut Context, args: &[ast::Expr]) -> Result { | ||
206 | let v = ctx.eval_expr(&get_args::<1>(args)?[0])?; | ||
207 | match v.ty() { | ||
208 | ast::Type::List => v | ||
209 | .as_list() | ||
210 | .unwrap() | ||
211 | .is_empty() | ||
212 | .wrap(Value::Boolean) | ||
213 | .wrap(Ok), | ||
214 | ast::Type::String => v | ||
215 | .as_str() | ||
216 | .unwrap() | ||
217 | .is_empty() | ||
218 | .wrap(Value::Boolean) | ||
219 | .wrap(Ok), | ||
220 | _ => Err(v.ty().expected([ast::Type::List])), | ||
221 | } | ||
222 | } | ||
223 | |||
224 | fn get_args<const N: usize>(args: &[ast::Expr]) -> std::result::Result<&[ast::Expr; N], Error> { | ||
225 | args.try_into().map_err(|_| Error::IncorrectArgFormat { | ||
226 | wanted: N, | ||
227 | got: args.len(), | ||
228 | }) | ||
229 | } | ||
230 | |||
231 | #[cfg(test)] | ||
232 | mod test { | ||
233 | use super::*; | ||
234 | use crate::{ast::*, eval::*}; | ||
235 | |||
236 | #[test] | ||
237 | fn test_ts_builtins() { | ||
238 | let language = tree_sitter_python::language(); | ||
239 | let mut ctx = Context::new(language).with_program(Program::new()); | ||
240 | |||
241 | assert_eq!( | ||
242 | ctx.eval_block(&Block { | ||
243 | body: vec![Statement::decl(Type::List, "a", Expr::list([Expr::int(5)]),)] | ||
244 | }), | ||
245 | Ok(Value::Unit) | ||
246 | ); | ||
247 | assert_eq!( | ||
248 | ctx.lookup(&String::from("a")).unwrap().clone(), | ||
249 | Variable { | ||
250 | ty: Type::List, | ||
251 | name: "a".to_owned(), | ||
252 | value: vec![5usize.into()].into(), | ||
253 | } | ||
254 | ); | ||
255 | } | ||
256 | } | ||
diff --git a/src/eval/mod.rs b/src/eval/mod.rs new file mode 100644 index 0000000..c4460c0 --- /dev/null +++ b/src/eval/mod.rs | |||
@@ -0,0 +1,1051 @@ | |||
1 | //! tree walking interpreter for tbsp | ||
2 | |||
3 | use crate::{ast, Wrap}; | ||
4 | use std::{collections::HashMap, fmt, io}; | ||
5 | |||
6 | mod builtins; | ||
7 | |||
8 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
9 | pub struct Variable { | ||
10 | pub ty: ast::Type, | ||
11 | pub name: ast::Identifier, | ||
12 | pub value: Value, | ||
13 | } | ||
14 | |||
15 | impl Variable { | ||
16 | fn value(&self) -> &Value { | ||
17 | &self.value | ||
18 | } | ||
19 | |||
20 | pub(crate) fn ty(&self) -> ast::Type { | ||
21 | self.ty | ||
22 | } | ||
23 | |||
24 | fn assign(&mut self, value: Value) -> Result { | ||
25 | if self.ty() == value.ty() { | ||
26 | self.value = value; | ||
27 | Ok(self.value.clone()) | ||
28 | } else { | ||
29 | Err(value.ty().expected([self.ty()])) | ||
30 | } | ||
31 | } | ||
32 | |||
33 | pub(crate) fn mutate(&mut self, f: impl FnOnce(&mut Self) -> Result) -> Result { | ||
34 | f(self) | ||
35 | } | ||
36 | } | ||
37 | |||
38 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] | ||
39 | pub enum Value { | ||
40 | Unit, | ||
41 | Integer(i128), | ||
42 | String(String), | ||
43 | Boolean(bool), | ||
44 | Node(NodeId), | ||
45 | List(Vec<Value>), | ||
46 | } | ||
47 | |||
48 | type NodeId = usize; | ||
49 | |||
50 | impl Value { | ||
51 | pub(crate) fn ty(&self) -> ast::Type { | ||
52 | match self { | ||
53 | Self::Unit => ast::Type::Unit, | ||
54 | Self::Integer(_) => ast::Type::Integer, | ||
55 | Self::String(_) => ast::Type::String, | ||
56 | Self::Boolean(_) => ast::Type::Boolean, | ||
57 | Self::Node(_) => ast::Type::Node, | ||
58 | Self::List(_) => ast::Type::List, | ||
59 | } | ||
60 | } | ||
61 | |||
62 | fn default(ty: ast::Type) -> Self { | ||
63 | match ty { | ||
64 | ast::Type::Unit => Self::Unit, | ||
65 | ast::Type::Integer => Self::default_int(), | ||
66 | ast::Type::String => Self::default_string(), | ||
67 | ast::Type::Boolean => Self::default_bool(), | ||
68 | ast::Type::Node => unreachable!(), | ||
69 | ast::Type::List => Self::default_list(), | ||
70 | } | ||
71 | } | ||
72 | |||
73 | fn default_int() -> Self { | ||
74 | Self::Integer(0) | ||
75 | } | ||
76 | |||
77 | fn default_bool() -> Self { | ||
78 | Self::Boolean(false) | ||
79 | } | ||
80 | |||
81 | fn default_string() -> Self { | ||
82 | Self::String(String::default()) | ||
83 | } | ||
84 | |||
85 | fn default_list() -> Self { | ||
86 | Self::List(Vec::new()) | ||
87 | } | ||
88 | |||
89 | fn as_boolean(&self) -> std::result::Result<bool, Error> { | ||
90 | match self { | ||
91 | Self::Boolean(b) => Ok(*b), | ||
92 | v => Err(v.ty().expected([ast::Type::Boolean])), | ||
93 | } | ||
94 | } | ||
95 | |||
96 | pub(crate) fn as_str(&self) -> std::result::Result<&str, Error> { | ||
97 | match self { | ||
98 | Self::String(s) => Ok(s.as_str()), | ||
99 | v => Err(v.ty().expected([ast::Type::String])), | ||
100 | } | ||
101 | } | ||
102 | |||
103 | pub(crate) fn as_int(&self) -> std::result::Result<i128, Error> { | ||
104 | match self { | ||
105 | Self::Integer(i) => Ok(*i), | ||
106 | v => Err(v.ty().expected([ast::Type::Integer])), | ||
107 | } | ||
108 | } | ||
109 | |||
110 | pub(crate) fn as_node(&self) -> std::result::Result<NodeId, Error> { | ||
111 | match self { | ||
112 | Self::Node(id) => Ok(*id), | ||
113 | v => Err(v.ty().expected([ast::Type::Node])), | ||
114 | } | ||
115 | } | ||
116 | |||
117 | pub(crate) fn as_list(&self) -> std::result::Result<Vec<Value>, Error> { | ||
118 | match self { | ||
119 | Self::List(values) => Ok(values.clone()), | ||
120 | v => Err(v.ty().expected([ast::Type::List])), | ||
121 | } | ||
122 | } | ||
123 | |||
124 | fn add(&self, other: &Self) -> Result { | ||
125 | match (self, other) { | ||
126 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Integer(*s + *o)), | ||
127 | (Self::String(s), Self::String(o)) => Ok(Self::String(format!("{s}{o}"))), | ||
128 | (Self::List(l), o) => Ok(Self::List(l.iter().cloned().chain([o.clone()]).collect())), | ||
129 | _ => Err(Error::UndefinedBinOp( | ||
130 | ast::BinOp::Arith(ast::ArithOp::Add), | ||
131 | self.ty(), | ||
132 | other.ty(), | ||
133 | )), | ||
134 | } | ||
135 | } | ||
136 | |||
137 | fn sub(&self, other: &Self) -> Result { | ||
138 | match (self, other) { | ||
139 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Integer(*s - *o)), | ||
140 | (Self::String(s), Self::String(o)) => { | ||
141 | Ok(Self::String(s.strip_suffix(o).unwrap_or(s).to_owned())) | ||
142 | } | ||
143 | _ => Err(Error::UndefinedBinOp( | ||
144 | ast::BinOp::Arith(ast::ArithOp::Sub), | ||
145 | self.ty(), | ||
146 | other.ty(), | ||
147 | )), | ||
148 | } | ||
149 | } | ||
150 | |||
151 | fn mul(&self, other: &Self) -> Result { | ||
152 | match (self, other) { | ||
153 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Integer(*s * *o)), | ||
154 | (Self::Integer(s), Self::String(o)) => Ok(Self::String(o.repeat(*s as usize))), | ||
155 | (Self::String(_), Self::Integer(_)) => other.mul(self), | ||
156 | _ => Err(Error::UndefinedBinOp( | ||
157 | ast::BinOp::Arith(ast::ArithOp::Mul), | ||
158 | self.ty(), | ||
159 | other.ty(), | ||
160 | )), | ||
161 | } | ||
162 | } | ||
163 | |||
164 | fn div(&self, other: &Self) -> Result { | ||
165 | match (self, other) { | ||
166 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Integer(*s / *o)), | ||
167 | _ => Err(Error::UndefinedBinOp( | ||
168 | ast::BinOp::Arith(ast::ArithOp::Div), | ||
169 | self.ty(), | ||
170 | other.ty(), | ||
171 | )), | ||
172 | } | ||
173 | } | ||
174 | |||
175 | fn mod_(&self, other: &Self) -> Result { | ||
176 | match (self, other) { | ||
177 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Integer(*s % *o)), | ||
178 | _ => Err(Error::UndefinedBinOp( | ||
179 | ast::BinOp::Arith(ast::ArithOp::Mod), | ||
180 | self.ty(), | ||
181 | other.ty(), | ||
182 | )), | ||
183 | } | ||
184 | } | ||
185 | |||
186 | fn equals(&self, other: &Self) -> Result { | ||
187 | match (self, other) { | ||
188 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Boolean(s == o)), | ||
189 | (Self::String(s), Self::String(o)) => Ok(Self::Boolean(s == o)), | ||
190 | (Self::Boolean(s), Self::Boolean(o)) => Ok(Self::Boolean(s == o)), | ||
191 | _ => Err(Error::UndefinedBinOp( | ||
192 | ast::BinOp::Cmp(ast::CmpOp::Eq), | ||
193 | self.ty(), | ||
194 | other.ty(), | ||
195 | )), | ||
196 | } | ||
197 | } | ||
198 | |||
199 | fn greater_than(&self, other: &Self) -> Result { | ||
200 | match (self, other) { | ||
201 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Boolean(s > o)), | ||
202 | (Self::String(s), Self::String(o)) => Ok(Self::Boolean(s.cmp(o).is_gt())), | ||
203 | _ => Err(Error::UndefinedBinOp( | ||
204 | ast::BinOp::Cmp(ast::CmpOp::Gt), | ||
205 | self.ty(), | ||
206 | other.ty(), | ||
207 | )), | ||
208 | } | ||
209 | } | ||
210 | |||
211 | fn less_than(&self, other: &Self) -> Result { | ||
212 | match (self, other) { | ||
213 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Boolean(s < o)), | ||
214 | (Self::String(s), Self::String(o)) => Ok(Self::Boolean(s.cmp(o).is_lt())), | ||
215 | _ => Err(Error::UndefinedBinOp( | ||
216 | ast::BinOp::Cmp(ast::CmpOp::Lt), | ||
217 | self.ty(), | ||
218 | other.ty(), | ||
219 | )), | ||
220 | } | ||
221 | } | ||
222 | |||
223 | fn greater_than_equals(&self, other: &Self) -> Result { | ||
224 | match (self, other) { | ||
225 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Boolean(s >= o)), | ||
226 | (Self::String(s), Self::String(o)) => Ok(Self::Boolean(s.cmp(o).is_ge())), | ||
227 | (Self::Boolean(s), Self::Boolean(o)) => Ok(Self::Boolean(s == o)), | ||
228 | _ => Err(Error::UndefinedBinOp( | ||
229 | ast::BinOp::Cmp(ast::CmpOp::Gte), | ||
230 | self.ty(), | ||
231 | other.ty(), | ||
232 | )), | ||
233 | } | ||
234 | } | ||
235 | |||
236 | fn less_than_equals(&self, other: &Self) -> Result { | ||
237 | match (self, other) { | ||
238 | (Self::Integer(s), Self::Integer(o)) => Ok(Self::Boolean(s <= o)), | ||
239 | (Self::String(s), Self::String(o)) => Ok(Self::Boolean(s.cmp(o).is_le())), | ||
240 | (Self::Boolean(s), Self::Boolean(o)) => Ok(Self::Boolean(s == o)), | ||
241 | _ => Err(Error::UndefinedBinOp( | ||
242 | ast::BinOp::Cmp(ast::CmpOp::Lte), | ||
243 | self.ty(), | ||
244 | other.ty(), | ||
245 | )), | ||
246 | } | ||
247 | } | ||
248 | |||
249 | fn not(&self) -> Result { | ||
250 | match self { | ||
251 | Self::Boolean(s) => Ok(Self::Boolean(!s)), | ||
252 | _ => Err(Error::UndefinedUnaryOp(ast::UnaryOp::Not, self.ty())), | ||
253 | } | ||
254 | } | ||
255 | |||
256 | fn and(&self, other: &Self) -> Result { | ||
257 | match (self, other) { | ||
258 | (Self::Boolean(s), Self::Boolean(o)) => Ok(Self::Boolean(*s && *o)), | ||
259 | _ => Err(Error::UndefinedBinOp( | ||
260 | ast::BinOp::Logic(ast::LogicOp::And), | ||
261 | self.ty(), | ||
262 | other.ty(), | ||
263 | )), | ||
264 | } | ||
265 | } | ||
266 | |||
267 | fn or(&self, other: &Self) -> Result { | ||
268 | match (self, other) { | ||
269 | (Self::Boolean(s), Self::Boolean(o)) => Ok(Self::Boolean(*s || *o)), | ||
270 | _ => Err(Error::UndefinedBinOp( | ||
271 | ast::BinOp::Logic(ast::LogicOp::Or), | ||
272 | self.ty(), | ||
273 | other.ty(), | ||
274 | )), | ||
275 | } | ||
276 | } | ||
277 | } | ||
278 | |||
279 | impl fmt::Display for Value { | ||
280 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
281 | match self { | ||
282 | Self::Unit => write!(f, "()"), | ||
283 | Self::Integer(i) => write!(f, "{i}"), | ||
284 | Self::String(s) => write!(f, "{s}"), | ||
285 | Self::Boolean(b) => write!(f, "{b}"), | ||
286 | Self::Node(id) => write!(f, "<node #{id}>"), | ||
287 | Self::List(items) => { | ||
288 | write!(f, "[")?; | ||
289 | let mut iterable = items.iter().peekable(); | ||
290 | while let Some(item) = iterable.next() { | ||
291 | if iterable.peek().is_none() { | ||
292 | write!(f, "{item}")?; | ||
293 | } else { | ||
294 | write!(f, "{item}, ")?; | ||
295 | } | ||
296 | } | ||
297 | write!(f, "]") | ||
298 | } | ||
299 | } | ||
300 | } | ||
301 | } | ||
302 | |||
303 | impl From<bool> for Value { | ||
304 | fn from(value: bool) -> Self { | ||
305 | Self::Boolean(value) | ||
306 | } | ||
307 | } | ||
308 | |||
309 | impl From<i128> for Value { | ||
310 | fn from(value: i128) -> Self { | ||
311 | Self::Integer(value) | ||
312 | } | ||
313 | } | ||
314 | |||
315 | impl From<usize> for Value { | ||
316 | fn from(value: usize) -> Self { | ||
317 | (value as i128).into() | ||
318 | } | ||
319 | } | ||
320 | |||
321 | impl From<&str> for Value { | ||
322 | fn from(value: &str) -> Self { | ||
323 | Self::String(value.to_owned()) | ||
324 | } | ||
325 | } | ||
326 | |||
327 | impl From<Vec<Value>> for Value { | ||
328 | fn from(value: Vec<Value>) -> Self { | ||
329 | Self::List(value) | ||
330 | } | ||
331 | } | ||
332 | |||
333 | #[derive(Debug, PartialEq, Eq)] | ||
334 | pub enum Error { | ||
335 | FailedLookup(ast::Identifier), | ||
336 | TypeMismatch { | ||
337 | expected: Vec<ast::Type>, | ||
338 | got: ast::Type, | ||
339 | }, | ||
340 | UndefinedBinOp(ast::BinOp, ast::Type, ast::Type), | ||
341 | UndefinedUnaryOp(ast::UnaryOp, ast::Type), | ||
342 | AlreadyBound(ast::Identifier), | ||
343 | MalformedExpr(String), | ||
344 | InvalidNodeKind(String), | ||
345 | NoParentNode(tree_sitter::Node<'static>), | ||
346 | IncorrectArgFormat { | ||
347 | wanted: usize, | ||
348 | got: usize, | ||
349 | }, | ||
350 | InvalidStringSlice { | ||
351 | length: usize, | ||
352 | start: i128, | ||
353 | end: i128, | ||
354 | }, | ||
355 | ArrayOutOfBounds { | ||
356 | idx: i128, | ||
357 | len: usize, | ||
358 | }, | ||
359 | // current node is only set in visitors, not in BEGIN or END blocks | ||
360 | CurrentNodeNotPresent, | ||
361 | } | ||
362 | |||
363 | impl ast::Type { | ||
364 | pub fn expected<const N: usize>(self, expected: [Self; N]) -> Error { | ||
365 | Error::TypeMismatch { | ||
366 | expected: expected.to_vec(), | ||
367 | got: self, | ||
368 | } | ||
369 | } | ||
370 | } | ||
371 | |||
372 | pub type Result = std::result::Result<Value, Error>; | ||
373 | |||
374 | pub struct Context<'s> { | ||
375 | variables: HashMap<ast::Identifier, Variable>, | ||
376 | language: tree_sitter::Language, | ||
377 | program: ast::Program, | ||
378 | pub(crate) input_src: Option<String>, | ||
379 | cursor: Option<tree_sitter::TreeCursor<'static>>, | ||
380 | tree: Option<&'static tree_sitter::Tree>, | ||
381 | cache: HashMap<NodeId, tree_sitter::Node<'static>>, | ||
382 | output_stream: Option<Box<dyn io::Write + 's>>, | ||
383 | } | ||
384 | |||
385 | impl<'s> fmt::Debug for Context<'s> { | ||
386 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
387 | f.debug_struct("Context") | ||
388 | .field("variables", &self.variables) | ||
389 | .field("language", &self.language) | ||
390 | .field("input_src", &self.input_src) | ||
391 | .field( | ||
392 | "cursor", | ||
393 | if self.cursor.is_some() { | ||
394 | &"Some(<cursor>)" | ||
395 | } else { | ||
396 | &"None" | ||
397 | }, | ||
398 | ) | ||
399 | .finish() | ||
400 | } | ||
401 | } | ||
402 | |||
403 | impl<'s> Context<'s> { | ||
404 | pub fn new(language: tree_sitter::Language) -> Self { | ||
405 | Self { | ||
406 | program: Default::default(), | ||
407 | variables: Default::default(), | ||
408 | language, | ||
409 | input_src: None, | ||
410 | cursor: None, | ||
411 | tree: None, | ||
412 | cache: HashMap::default(), | ||
413 | output_stream: Some(Box::new(io::stdout()) as Box<dyn io::Write + 's>), | ||
414 | } | ||
415 | } | ||
416 | |||
417 | pub fn cache_node(&mut self, node: tree_sitter::Node<'static>) { | ||
418 | self.cache.entry(node.id()).or_insert(node); | ||
419 | } | ||
420 | |||
421 | pub fn get_node_by_id(&mut self, id: usize) -> Option<tree_sitter::Node<'static>> { | ||
422 | let root_node = self.tree.as_ref().map(|t| t.root_node())?; | ||
423 | self.get_node_by_id_helper(root_node, id) | ||
424 | } | ||
425 | |||
426 | fn get_node_by_id_helper( | ||
427 | &mut self, | ||
428 | start: tree_sitter::Node<'static>, | ||
429 | id: usize, | ||
430 | ) -> Option<tree_sitter::Node<'static>> { | ||
431 | self.cache_node(start); | ||
432 | |||
433 | if let Some(found) = self.cache.get(&id) { | ||
434 | return Some(*found); | ||
435 | } | ||
436 | |||
437 | if start.id() == id { | ||
438 | return Some(start); | ||
439 | } else { | ||
440 | for child in start.children(&mut start.walk()) { | ||
441 | if let Some(n) = self.get_node_by_id_helper(child, id) { | ||
442 | return Some(n); | ||
443 | }; | ||
444 | } | ||
445 | } | ||
446 | |||
447 | None | ||
448 | } | ||
449 | |||
450 | pub fn with_program(mut self, program: ast::Program) -> Self { | ||
451 | self.program = program; | ||
452 | self | ||
453 | } | ||
454 | |||
455 | pub fn with_input(mut self, src: String) -> Self { | ||
456 | self.input_src = Some(src); | ||
457 | self | ||
458 | } | ||
459 | |||
460 | pub fn with_tree(mut self, tree: tree_sitter::Tree) -> Self { | ||
461 | let tree = Box::leak(Box::new(tree)); | ||
462 | self.cursor = Some(tree.walk()); | ||
463 | self.tree = Some(tree); | ||
464 | self | ||
465 | } | ||
466 | |||
467 | pub fn with_output_stream(mut self, stream: Box<dyn io::Write + 's>) -> Self { | ||
468 | self.output_stream = Some(stream); | ||
469 | self | ||
470 | } | ||
471 | |||
472 | pub(crate) fn eval_expr(&mut self, expr: &ast::Expr) -> Result { | ||
473 | match expr { | ||
474 | ast::Expr::Unit => Ok(Value::Unit), | ||
475 | ast::Expr::Lit(lit) => self.eval_lit(lit), | ||
476 | ast::Expr::Ident(ident) => self.lookup(ident).map(Variable::value).cloned(), | ||
477 | ast::Expr::Bin(lhs, op, rhs) => self.eval_bin(&*lhs, *op, &*rhs), | ||
478 | ast::Expr::Unary(expr, op) => self.eval_unary(&*expr, *op), | ||
479 | ast::Expr::Call(call) => self.eval_call(&*call), | ||
480 | ast::Expr::List(list) => self.eval_list(&*list), | ||
481 | ast::Expr::Index(target, idx) => self.eval_index(&*target, &*idx), | ||
482 | ast::Expr::If(if_expr) => self.eval_if(if_expr), | ||
483 | ast::Expr::Block(block) => self.eval_block(block), | ||
484 | ast::Expr::Node => self.eval_node(), | ||
485 | ast::Expr::FieldAccess(expr, items) => self.eval_field_access(expr, items), | ||
486 | } | ||
487 | } | ||
488 | |||
489 | fn eval_lit(&mut self, lit: &ast::Literal) -> Result { | ||
490 | match lit { | ||
491 | ast::Literal::Str(s) => Ok(Value::String(s.to_owned())), | ||
492 | ast::Literal::Int(i) => Ok(Value::Integer(*i)), | ||
493 | ast::Literal::Bool(b) => Ok(Value::Boolean(*b)), | ||
494 | } | ||
495 | } | ||
496 | |||
497 | fn eval_node(&mut self) -> Result { | ||
498 | self.cursor | ||
499 | .as_ref() | ||
500 | .ok_or(Error::CurrentNodeNotPresent)? | ||
501 | .node() | ||
502 | .id() | ||
503 | .wrap(Value::Node) | ||
504 | .wrap_ok() | ||
505 | } | ||
506 | |||
507 | pub(crate) fn lookup( | ||
508 | &mut self, | ||
509 | ident: &ast::Identifier, | ||
510 | ) -> std::result::Result<&Variable, Error> { | ||
511 | self.variables | ||
512 | .get(ident) | ||
513 | .ok_or_else(|| Error::FailedLookup(ident.to_owned())) | ||
514 | } | ||
515 | |||
516 | pub(crate) fn lookup_mut( | ||
517 | &mut self, | ||
518 | ident: &ast::Identifier, | ||
519 | ) -> std::result::Result<&mut Variable, Error> { | ||
520 | self.variables | ||
521 | .get_mut(ident) | ||
522 | .ok_or_else(|| Error::FailedLookup(ident.to_owned())) | ||
523 | } | ||
524 | |||
525 | fn bind( | ||
526 | &mut self, | ||
527 | ident: &ast::Identifier, | ||
528 | ty: ast::Type, | ||
529 | ) -> std::result::Result<&mut Variable, Error> { | ||
530 | if self.lookup(ident).is_err() { | ||
531 | self.variables | ||
532 | .entry(ident.to_owned()) | ||
533 | .or_insert_with(|| Variable { | ||
534 | name: ident.to_owned(), | ||
535 | value: Value::default(ty), | ||
536 | ty, | ||
537 | }) | ||
538 | .wrap_ok() | ||
539 | } else { | ||
540 | Err(Error::AlreadyBound(ident.to_owned())) | ||
541 | } | ||
542 | } | ||
543 | |||
544 | fn eval_bin(&mut self, lhs: &ast::Expr, op: ast::BinOp, rhs: &ast::Expr) -> Result { | ||
545 | match op { | ||
546 | ast::BinOp::Assign(op) => self.eval_assign(lhs, op, rhs), | ||
547 | ast::BinOp::Arith(op) => self.eval_arith(lhs, op, rhs), | ||
548 | ast::BinOp::Cmp(op) => self.eval_cmp(lhs, op, rhs), | ||
549 | ast::BinOp::Logic(op) => self.eval_logic(lhs, op, rhs), | ||
550 | } | ||
551 | } | ||
552 | |||
553 | fn eval_assign( | ||
554 | &mut self, | ||
555 | lhs: &ast::Expr, | ||
556 | ast::AssignOp { op }: ast::AssignOp, | ||
557 | rhs: &ast::Expr, | ||
558 | ) -> Result { | ||
559 | let ast::Expr::Ident(ident) = lhs else { | ||
560 | return Err(Error::MalformedExpr(format!( | ||
561 | "malformed assigment, lhs: {:?}", | ||
562 | lhs | ||
563 | ))); | ||
564 | }; | ||
565 | let value = self.eval_expr(rhs)?; | ||
566 | let variable = self.lookup_mut(ident)?; | ||
567 | match op { | ||
568 | None => variable.assign(value), | ||
569 | Some(ast::ArithOp::Add) => variable.assign(variable.value().add(&value)?), | ||
570 | Some(ast::ArithOp::Sub) => variable.assign(variable.value().sub(&value)?), | ||
571 | Some(ast::ArithOp::Mul) => variable.assign(variable.value().mul(&value)?), | ||
572 | Some(ast::ArithOp::Div) => variable.assign(variable.value().div(&value)?), | ||
573 | Some(ast::ArithOp::Mod) => variable.assign(variable.value().mod_(&value)?), | ||
574 | } | ||
575 | } | ||
576 | |||
577 | fn eval_arith(&mut self, lhs: &ast::Expr, op: ast::ArithOp, rhs: &ast::Expr) -> Result { | ||
578 | let l = self.eval_expr(lhs)?; | ||
579 | let r = self.eval_expr(rhs)?; | ||
580 | match op { | ||
581 | ast::ArithOp::Add => l.add(&r), | ||
582 | ast::ArithOp::Sub => l.sub(&r), | ||
583 | ast::ArithOp::Mul => l.mul(&r), | ||
584 | ast::ArithOp::Div => l.div(&r), | ||
585 | ast::ArithOp::Mod => l.mod_(&r), | ||
586 | } | ||
587 | } | ||
588 | |||
589 | fn eval_cmp(&mut self, lhs: &ast::Expr, op: ast::CmpOp, rhs: &ast::Expr) -> Result { | ||
590 | let l = self.eval_expr(lhs)?; | ||
591 | let r = self.eval_expr(rhs)?; | ||
592 | |||
593 | match op { | ||
594 | ast::CmpOp::Eq => l.equals(&r), | ||
595 | ast::CmpOp::Gt => l.greater_than(&r), | ||
596 | ast::CmpOp::Lt => l.less_than(&r), | ||
597 | ast::CmpOp::Neq => l.equals(&r).and_then(|v| v.not()), | ||
598 | ast::CmpOp::Gte => l.greater_than_equals(&r), | ||
599 | ast::CmpOp::Lte => l.less_than_equals(&r), | ||
600 | } | ||
601 | } | ||
602 | |||
603 | fn eval_logic(&mut self, lhs: &ast::Expr, op: ast::LogicOp, rhs: &ast::Expr) -> Result { | ||
604 | let l = self.eval_expr(lhs)?; | ||
605 | |||
606 | // short-circuit | ||
607 | let l_value = l.as_boolean()?; | ||
608 | |||
609 | match op { | ||
610 | ast::LogicOp::Or => { | ||
611 | if l_value { | ||
612 | return Ok(l); | ||
613 | } else { | ||
614 | let r = self.eval_expr(rhs)?; | ||
615 | l.or(&r) | ||
616 | } | ||
617 | } | ||
618 | ast::LogicOp::And => { | ||
619 | if !l_value { | ||
620 | return Ok(l); | ||
621 | } else { | ||
622 | let r = self.eval_expr(rhs)?; | ||
623 | l.and(&r) | ||
624 | } | ||
625 | } | ||
626 | } | ||
627 | } | ||
628 | |||
629 | fn eval_unary(&mut self, expr: &ast::Expr, op: ast::UnaryOp) -> Result { | ||
630 | let val = self.eval_expr(expr)?; | ||
631 | match op { | ||
632 | ast::UnaryOp::Not => val.not(), | ||
633 | } | ||
634 | } | ||
635 | |||
636 | fn eval_if(&mut self, if_expr: &ast::IfExpr) -> Result { | ||
637 | let cond = self.eval_expr(&if_expr.condition)?; | ||
638 | |||
639 | if cond.as_boolean()? { | ||
640 | self.eval_block(&if_expr.then) | ||
641 | } else { | ||
642 | self.eval_block(&if_expr.else_) | ||
643 | } | ||
644 | } | ||
645 | |||
646 | fn eval_call(&mut self, call: &ast::Call) -> Result { | ||
647 | ((&*builtins::BUILTINS) | ||
648 | .get(call.function.as_str()) | ||
649 | .ok_or_else(|| Error::FailedLookup(call.function.to_owned()))?)( | ||
650 | self, | ||
651 | call.parameters.as_slice(), | ||
652 | ) | ||
653 | } | ||
654 | |||
655 | fn eval_list(&mut self, list: &ast::List) -> Result { | ||
656 | let mut vals = vec![]; | ||
657 | for i in &list.items { | ||
658 | vals.push(self.eval_expr(i)?); | ||
659 | } | ||
660 | Ok(vals.into()) | ||
661 | } | ||
662 | |||
663 | fn eval_index(&mut self, target: &ast::Expr, idx: &ast::Expr) -> Result { | ||
664 | let mut target = self.eval_expr(target)?.as_list()?; | ||
665 | let idx = self.eval_expr(idx)?.as_int()?; | ||
666 | if idx < 0 || idx >= target.len() as i128 { | ||
667 | Err(Error::ArrayOutOfBounds { | ||
668 | idx, | ||
669 | len: target.len(), | ||
670 | }) | ||
671 | } else { | ||
672 | Ok(target.remove(idx as usize)) | ||
673 | } | ||
674 | } | ||
675 | |||
676 | fn eval_declaration(&mut self, decl: &ast::Declaration) -> Result { | ||
677 | let initial_value = match decl.init.as_ref() { | ||
678 | Some(init) => Some(self.eval_expr(&*init)?), | ||
679 | None => None, | ||
680 | }; | ||
681 | let variable = self.bind(&decl.name, decl.ty)?; | ||
682 | |||
683 | if let Some(init) = initial_value { | ||
684 | variable.assign(init)?; | ||
685 | } | ||
686 | |||
687 | Ok(Value::Unit) | ||
688 | } | ||
689 | |||
690 | fn eval_statement(&mut self, stmt: &ast::Statement) -> Result { | ||
691 | match stmt { | ||
692 | ast::Statement::Bare(expr) => self.eval_expr(expr), | ||
693 | ast::Statement::Declaration(decl) => self.eval_declaration(decl), | ||
694 | } | ||
695 | } | ||
696 | |||
697 | fn eval_block(&mut self, block: &ast::Block) -> Result { | ||
698 | for stmt in block.body.iter() { | ||
699 | self.eval_statement(stmt)?; | ||
700 | } | ||
701 | Ok(Value::Unit) | ||
702 | } | ||
703 | |||
704 | fn eval_field_access(&mut self, expr: &ast::Expr, field: &ast::Identifier) -> Result { | ||
705 | let v = self.eval_expr(expr)?; | ||
706 | let base = v.as_node()?; | ||
707 | let base_node = self.get_node_by_id(base).unwrap(); | ||
708 | base_node | ||
709 | .child_by_field_name(field) | ||
710 | .ok_or(Error::InvalidNodeKind(field.clone())) | ||
711 | .map(|n| n.id()) | ||
712 | .map(Value::Node) | ||
713 | } | ||
714 | |||
715 | fn goto_first_child(&mut self) -> bool { | ||
716 | self.cursor | ||
717 | .as_mut() | ||
718 | .map(|c| c.goto_first_child()) | ||
719 | .unwrap_or_default() | ||
720 | } | ||
721 | |||
722 | pub fn eval(&mut self) -> Result { | ||
723 | let program = std::mem::take(&mut self.program); | ||
724 | let mut has_next = true; | ||
725 | let mut postorder = Vec::new(); | ||
726 | |||
727 | // BEGIN block | ||
728 | if let Some(block) = program.begin() { | ||
729 | self.eval_block(block)?; | ||
730 | } | ||
731 | |||
732 | while has_next { | ||
733 | let current_node = self.cursor.as_ref().unwrap().node(); | ||
734 | postorder.push(current_node); | ||
735 | |||
736 | if let Some(block) = program.stanza_by_node(current_node, ast::Modifier::Enter) { | ||
737 | self.eval_block(block)?; | ||
738 | } | ||
739 | |||
740 | has_next = self.goto_first_child(); | ||
741 | |||
742 | if !has_next { | ||
743 | has_next = self.cursor.as_mut().unwrap().goto_next_sibling(); | ||
744 | if let Some(block) = postorder | ||
745 | .pop() | ||
746 | .and_then(|n| program.stanza_by_node(n, ast::Modifier::Leave)) | ||
747 | { | ||
748 | self.eval_block(block)?; | ||
749 | }; | ||
750 | } | ||
751 | |||
752 | while !has_next && self.cursor.as_mut().unwrap().goto_parent() { | ||
753 | has_next = self.cursor.as_mut().unwrap().goto_next_sibling(); | ||
754 | if let Some(block) = postorder | ||
755 | .pop() | ||
756 | .and_then(|n| program.stanza_by_node(n, ast::Modifier::Leave)) | ||
757 | { | ||
758 | self.eval_block(block)?; | ||
759 | }; | ||
760 | } | ||
761 | } | ||
762 | |||
763 | // END block | ||
764 | if let Some(block) = program.end() { | ||
765 | self.eval_block(block)?; | ||
766 | } | ||
767 | |||
768 | Ok(Value::Unit) | ||
769 | } | ||
770 | } | ||
771 | |||
772 | pub fn evaluate(file: &str, program: &str, language: tree_sitter::Language) -> Result { | ||
773 | let mut parser = tree_sitter::Parser::new(); | ||
774 | let _ = parser.set_language(&language); | ||
775 | |||
776 | let tree = parser.parse(file, None).unwrap(); | ||
777 | |||
778 | let program = ast::Program::new().from_str(program).unwrap(); | ||
779 | let mut ctx = Context::new(language) | ||
780 | .with_input(file.to_owned()) | ||
781 | .with_tree(tree) | ||
782 | .with_output_stream(Box::new(io::stdout())) | ||
783 | .with_program(program); | ||
784 | |||
785 | ctx.eval() | ||
786 | } | ||
787 | |||
788 | #[cfg(test)] | ||
789 | mod test { | ||
790 | use super::*; | ||
791 | use crate::ast::*; | ||
792 | use expect_test::{expect, Expect}; | ||
793 | |||
794 | fn gen_test(file: &str, program: &str, expected: Expect) { | ||
795 | let language = tree_sitter_python::language(); | ||
796 | let mut parser = tree_sitter::Parser::new(); | ||
797 | let _ = parser.set_language(&language); | ||
798 | let tree = parser.parse(file, None).unwrap(); | ||
799 | let program = ast::Program::new().from_str(program).unwrap(); | ||
800 | |||
801 | let mut output = Vec::new(); | ||
802 | let mut ctx = Context::new(language) | ||
803 | .with_input(file.to_owned()) | ||
804 | .with_tree(tree) | ||
805 | .with_program(program) | ||
806 | .with_output_stream(Box::new(&mut output) as Box<dyn io::Write>); | ||
807 | |||
808 | ctx.eval().unwrap(); | ||
809 | |||
810 | drop(ctx); | ||
811 | |||
812 | expected.assert_eq(&String::from_utf8(output).unwrap()) | ||
813 | } | ||
814 | |||
815 | #[test] | ||
816 | fn bin() { | ||
817 | let language = tree_sitter_python::language(); | ||
818 | let mut ctx = Context::new(language).with_program(Program::new()); | ||
819 | assert_eq!( | ||
820 | ctx.eval_expr(&Expr::bin(Expr::int(5), "+", Expr::int(10),)), | ||
821 | Ok(Value::Integer(15)) | ||
822 | ); | ||
823 | assert_eq!( | ||
824 | ctx.eval_expr(&Expr::bin(Expr::int(5), "==", Expr::int(10),)), | ||
825 | Ok(Value::Boolean(false)) | ||
826 | ); | ||
827 | assert_eq!( | ||
828 | ctx.eval_expr(&Expr::bin(Expr::int(5), "<", Expr::int(10),)), | ||
829 | Ok(Value::Boolean(true)) | ||
830 | ); | ||
831 | assert_eq!( | ||
832 | ctx.eval_expr(&Expr::bin( | ||
833 | Expr::bin(Expr::int(5), "<", Expr::int(10),), | ||
834 | "&&", | ||
835 | Expr::false_(), | ||
836 | )), | ||
837 | Ok(Value::Boolean(false)) | ||
838 | ); | ||
839 | } | ||
840 | |||
841 | #[test] | ||
842 | fn test_evaluate_blocks() { | ||
843 | let language = tree_sitter_python::language(); | ||
844 | let mut ctx = Context::new(language).with_program(Program::new()); | ||
845 | assert_eq!( | ||
846 | ctx.eval_block(&Block { | ||
847 | body: vec![ | ||
848 | Statement::Declaration(Declaration { | ||
849 | ty: Type::Integer, | ||
850 | name: "a".to_owned(), | ||
851 | init: None, | ||
852 | }), | ||
853 | Statement::Bare(Expr::bin(Expr::ident("a"), "+=", Expr::int(5),)), | ||
854 | ] | ||
855 | }), | ||
856 | Ok(Value::Unit) | ||
857 | ); | ||
858 | assert_eq!( | ||
859 | ctx.lookup(&String::from("a")).unwrap().clone(), | ||
860 | Variable { | ||
861 | ty: Type::Integer, | ||
862 | name: "a".to_owned(), | ||
863 | value: Value::Integer(5) | ||
864 | } | ||
865 | ); | ||
866 | } | ||
867 | |||
868 | #[test] | ||
869 | fn test_evaluate_if() { | ||
870 | let language = tree_sitter_python::language(); | ||
871 | let mut ctx = Context::new(language).with_program(Program::new()); | ||
872 | assert_eq!( | ||
873 | ctx.eval_block(&Block { | ||
874 | body: vec![ | ||
875 | Statement::Declaration(Declaration { | ||
876 | ty: Type::Integer, | ||
877 | name: "a".to_owned(), | ||
878 | init: Some(Expr::int(1).boxed()), | ||
879 | }), | ||
880 | Statement::Bare(Expr::If(IfExpr { | ||
881 | condition: Expr::true_().boxed(), | ||
882 | then: Block { | ||
883 | body: vec![Statement::Bare(Expr::bin( | ||
884 | Expr::Ident("a".to_owned()), | ||
885 | "+=", | ||
886 | Expr::int(5), | ||
887 | ))] | ||
888 | }, | ||
889 | else_: Block { | ||
890 | body: vec![Statement::Bare(Expr::bin( | ||
891 | Expr::ident("a"), | ||
892 | "+=", | ||
893 | Expr::int(10), | ||
894 | ))] | ||
895 | } | ||
896 | })) | ||
897 | ] | ||
898 | }), | ||
899 | Ok(Value::Unit) | ||
900 | ); | ||
901 | assert_eq!( | ||
902 | ctx.lookup(&String::from("a")).unwrap().clone(), | ||
903 | Variable { | ||
904 | ty: Type::Integer, | ||
905 | name: "a".to_owned(), | ||
906 | value: Value::Integer(6) | ||
907 | } | ||
908 | ); | ||
909 | } | ||
910 | |||
911 | #[test] | ||
912 | fn test_substring() { | ||
913 | let language = tree_sitter_python::language(); | ||
914 | let mut ctx = Context::new(language).with_program(Program::new()); | ||
915 | assert_eq!( | ||
916 | ctx.eval_block(&Block { | ||
917 | body: vec![ | ||
918 | Statement::Declaration(Declaration { | ||
919 | ty: Type::String, | ||
920 | name: "a".to_owned(), | ||
921 | init: Some(Expr::str("foobar").boxed()), | ||
922 | }), | ||
923 | Statement::Declaration(Declaration { | ||
924 | ty: Type::String, | ||
925 | name: "b".to_owned(), | ||
926 | init: Some( | ||
927 | Expr::call( | ||
928 | "substr", | ||
929 | [Expr::Ident("a".to_owned()), Expr::int(0), Expr::int(3),] | ||
930 | ) | ||
931 | .boxed() | ||
932 | ), | ||
933 | }), | ||
934 | ] | ||
935 | }), | ||
936 | Ok(Value::Unit) | ||
937 | ); | ||
938 | assert_eq!( | ||
939 | ctx.lookup(&String::from("b")).unwrap().clone(), | ||
940 | Variable { | ||
941 | ty: Type::String, | ||
942 | name: "b".to_owned(), | ||
943 | value: "foo".into() | ||
944 | } | ||
945 | ); | ||
946 | } | ||
947 | |||
948 | #[test] | ||
949 | fn test_list() { | ||
950 | let language = tree_sitter_python::language(); | ||
951 | let mut ctx = Context::new(language).with_program(Program::new()); | ||
952 | assert_eq!( | ||
953 | ctx.eval_block(&Block { | ||
954 | body: vec![Statement::Declaration(Declaration { | ||
955 | ty: Type::List, | ||
956 | name: "a".to_owned(), | ||
957 | init: Some( | ||
958 | Expr::List(List { | ||
959 | items: vec![Expr::int(5)] | ||
960 | }) | ||
961 | .boxed() | ||
962 | ), | ||
963 | }),] | ||
964 | }), | ||
965 | Ok(Value::Unit) | ||
966 | ); | ||
967 | assert_eq!( | ||
968 | ctx.lookup(&String::from("a")).unwrap().clone(), | ||
969 | Variable { | ||
970 | ty: Type::List, | ||
971 | name: "a".to_owned(), | ||
972 | value: vec![5usize.into()].into(), | ||
973 | } | ||
974 | ); | ||
975 | } | ||
976 | |||
977 | #[test] | ||
978 | fn list_builtins() { | ||
979 | gen_test( | ||
980 | "", | ||
981 | "BEGIN { | ||
982 | list a = [5]; | ||
983 | print(a); | ||
984 | } | ||
985 | ", | ||
986 | expect!["[5]"] | ||
987 | ); | ||
988 | |||
989 | gen_test( | ||
990 | "", | ||
991 | "BEGIN { | ||
992 | list a = [5, 4, 3]; | ||
993 | print(length(a)); | ||
994 | } | ||
995 | ", | ||
996 | expect!["3"] | ||
997 | ); | ||
998 | |||
999 | gen_test( | ||
1000 | "", | ||
1001 | r#"BEGIN { | ||
1002 | list a = [5, 4, 3]; | ||
1003 | print(member(a, 3)); | ||
1004 | print(", "); | ||
1005 | print(member(a, 6)); | ||
1006 | } | ||
1007 | "#, | ||
1008 | expect!["true, false"] | ||
1009 | ); | ||
1010 | |||
1011 | gen_test( | ||
1012 | "", | ||
1013 | r#"BEGIN { | ||
1014 | list a = [5]; | ||
1015 | println(a); | ||
1016 | push(a, 4); | ||
1017 | println(a); | ||
1018 | push(a, 3); | ||
1019 | println(a); | ||
1020 | pop(a); | ||
1021 | println(a); | ||
1022 | pop(a); | ||
1023 | println(a); | ||
1024 | } | ||
1025 | "#, | ||
1026 | expect![[r#" | ||
1027 | [5] | ||
1028 | [5, 4] | ||
1029 | [5, 4, 3] | ||
1030 | [5, 4] | ||
1031 | [5] | ||
1032 | "#]] | ||
1033 | ); | ||
1034 | |||
1035 | gen_test( | ||
1036 | "", | ||
1037 | r#"BEGIN { | ||
1038 | list a = [5]; | ||
1039 | println(isempty(a)); | ||
1040 | pop(a); | ||
1041 | println(isempty(a)); | ||
1042 | } | ||
1043 | "#, | ||
1044 | expect![[r#" | ||
1045 | false | ||
1046 | true | ||
1047 | "#]] | ||
1048 | ); | ||
1049 | |||
1050 | } | ||
1051 | } | ||