aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAkshay <[email protected]>2024-10-11 03:55:17 +0100
committerAkshay <[email protected]>2024-10-11 03:55:17 +0100
commitb84a1a3598f68c1575a92bcd2ba68e9d1322256d (patch)
tree6aa47862c6f5ae054b5b3f4ddfb0826d5b3bb7d0 /src
parent013dc07b5d3e28c77b0efc51d18f4ffc371fa382 (diff)
improve tests
Diffstat (limited to 'src')
-rw-r--r--src/eval/mod.rs345
-rw-r--r--src/eval/test.rs371
2 files changed, 452 insertions, 264 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 9455704..589be37 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -4,6 +4,9 @@ use crate::{ast, Wrap};
4use std::{collections::HashMap, fmt, io}; 4use std::{collections::HashMap, fmt, io};
5 5
6mod builtins; 6mod builtins;
7mod analysis;
8#[cfg(test)]
9mod test;
7 10
8#[derive(Debug, PartialEq, Eq, Clone)] 11#[derive(Debug, PartialEq, Eq, Clone)]
9pub struct Variable { 12pub struct Variable {
@@ -324,6 +327,12 @@ impl From<&str> for Value {
324 } 327 }
325} 328}
326 329
330impl From<String> for Value {
331 fn from(value: String) -> Self {
332 Self::String(value.to_owned())
333 }
334}
335
327impl From<Vec<Value>> for Value { 336impl From<Vec<Value>> for Value {
328 fn from(value: Vec<Value>) -> Self { 337 fn from(value: Vec<Value>) -> Self {
329 Self::List(value) 338 Self::List(value)
@@ -358,6 +367,71 @@ pub enum Error {
358 }, 367 },
359 // current node is only set in visitors, not in BEGIN or END blocks 368 // current node is only set in visitors, not in BEGIN or END blocks
360 CurrentNodeNotPresent, 369 CurrentNodeNotPresent,
370 Analysis,
371}
372
373impl Error {
374 pub fn code(self) -> i32 {
375 match self {
376 Self::FailedLookup(_) => 30,
377 Self::TypeMismatch { .. } => 31,
378 Self::UndefinedBinOp(..) => 32,
379 Self::UndefinedUnaryOp(..) => 33,
380 Self::AlreadyBound(..) => 34,
381 Self::MalformedExpr(..) => 35,
382 Self::InvalidNodeKind(..) => 36,
383 Self::NoParentNode(..) => 37,
384 Self::IncorrectArgFormat { .. } => 38,
385 Self::InvalidStringSlice { .. } => 39,
386 Self::ArrayOutOfBounds { .. } => 40,
387 Self::CurrentNodeNotPresent => 41,
388 Self::Analysis => 50,
389 }
390 }
391}
392
393impl std::fmt::Display for Error {
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 match self {
396 Self::FailedLookup(ident) => write!(f, "failed to lookup variable `{ident}`"),
397 Self::TypeMismatch { expected, got } => write!(
398 f,
399 "mismatched types, expected one of `{}`, got `{got}`",
400 expected
401 .iter()
402 .map(|t| t.to_string())
403 .collect::<Vec<_>>()
404 .join(", ")
405 ),
406 Self::UndefinedBinOp(op, ltype, rtype) => write!(
407 f,
408 "undefined operation `{op}` between `{ltype}` and `{rtype}`"
409 ),
410 Self::UndefinedUnaryOp(op, ty) => write!(f, "undefined operation `{op}` on `{ty}`"),
411 Self::AlreadyBound(ident) => write!(f, "variable `{ident}` is aleady declared"),
412 Self::MalformedExpr(s) => write!(f, "{s}"),
413 Self::InvalidNodeKind(s) => write!(f, "invalid node kind `{s}`"),
414 Self::NoParentNode(n) => write!(
415 f,
416 "current node (with kind {}) has no parent node ",
417 n.kind()
418 ),
419 Self::IncorrectArgFormat { wanted, got } => write!(
420 f,
421 "incorrect number of arguments, wanted {wanted} but got {got}"
422 ),
423 Self::InvalidStringSlice { length, start, end } => write!(
424 f,
425 "invalid string-slice operation, length was {length}, but slice was {start}:{end}"
426 ),
427 Self::ArrayOutOfBounds { idx, len } => write!(
428 f,
429 "array index out of bounds, length was {len} but index was {idx}"
430 ),
431 Self::CurrentNodeNotPresent => write!(f, "no current node captured in stanza"),
432 Self::Analysis => write!(f, "encountered analysis errors"),
433 }
434 }
361} 435}
362 436
363impl ast::Type { 437impl ast::Type {
@@ -719,6 +793,12 @@ impl<'s> Context<'s> {
719 .unwrap_or_default() 793 .unwrap_or_default()
720 } 794 }
721 795
796 pub fn analyze(&mut self) -> Result {
797 let analysis = analysis::run(&*self);
798 analysis.print();
799 Err(Error::Analysis)
800 }
801
722 pub fn eval(&mut self) -> Result { 802 pub fn eval(&mut self) -> Result {
723 let program = std::mem::take(&mut self.program); 803 let program = std::mem::take(&mut self.program);
724 let mut has_next = true; 804 let mut has_next = true;
@@ -782,269 +862,6 @@ pub fn evaluate(file: &str, program: &str, language: tree_sitter::Language) -> R
782 .with_output_stream(Box::new(io::stdout())) 862 .with_output_stream(Box::new(io::stdout()))
783 .with_program(program); 863 .with_program(program);
784 864
865 ctx.analyze()?;
785 ctx.eval() 866 ctx.eval()
786} 867}
787
788#[cfg(test)]
789mod 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().with_file(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}
diff --git a/src/eval/test.rs b/src/eval/test.rs
new file mode 100644
index 0000000..83749e0
--- /dev/null
+++ b/src/eval/test.rs
@@ -0,0 +1,371 @@
1use super::*;
2use crate::ast::*;
3use std::io::Write;
4use expect_test::{expect, Expect};
5
6fn gen_test(file: &str, program: &str, expected: Expect) {
7 let language = tree_sitter_python::language();
8 let mut parser = tree_sitter::Parser::new();
9 let _ = parser.set_language(&language);
10 let tree = parser.parse(file, None).unwrap();
11 let program = ast::Program::new().with_file(program).unwrap();
12
13 let mut output = Vec::new();
14 let result;
15
16 {
17 let mut ctx = Context::new(language)
18 .with_input(file.to_owned())
19 .with_tree(tree)
20 .with_program(program)
21 .with_output_stream(Box::new(&mut output) as Box<dyn io::Write>);
22
23 result = ctx.eval();
24 }
25
26 if let Err(e) = result {
27 writeln!(output, "{e:?}").unwrap();
28 }
29 expected.assert_eq(&String::from_utf8(output).unwrap())
30}
31
32#[test]
33fn bin() {
34 let language = tree_sitter_python::language();
35 let mut ctx = Context::new(language).with_program(Program::new());
36 assert_eq!(
37 ctx.eval_expr(&Expr::bin(Expr::int(5), "+", Expr::int(10),)),
38 Ok(Value::Integer(15))
39 );
40 assert_eq!(
41 ctx.eval_expr(&Expr::bin(Expr::int(5), "==", Expr::int(10),)),
42 Ok(Value::Boolean(false))
43 );
44 assert_eq!(
45 ctx.eval_expr(&Expr::bin(Expr::int(5), "<", Expr::int(10),)),
46 Ok(Value::Boolean(true))
47 );
48 assert_eq!(
49 ctx.eval_expr(&Expr::bin(
50 Expr::bin(Expr::int(5), "<", Expr::int(10),),
51 "&&",
52 Expr::false_(),
53 )),
54 Ok(Value::Boolean(false))
55 );
56}
57
58#[test]
59fn test_evaluate_blocks() {
60 let language = tree_sitter_python::language();
61 let mut ctx = Context::new(language).with_program(Program::new());
62 assert_eq!(
63 ctx.eval_block(&Block {
64 body: vec![
65 Statement::Declaration(Declaration {
66 ty: Type::Integer,
67 name: "a".to_owned(),
68 init: None,
69 }),
70 Statement::Bare(Expr::bin(Expr::ident("a"), "+=", Expr::int(5),)),
71 ]
72 }),
73 Ok(Value::Unit)
74 );
75 assert_eq!(
76 ctx.lookup(&String::from("a")).unwrap().clone(),
77 Variable {
78 ty: Type::Integer,
79 name: "a".to_owned(),
80 value: Value::Integer(5)
81 }
82 );
83}
84
85#[test]
86fn test_evaluate_if() {
87 let language = tree_sitter_python::language();
88 let mut ctx = Context::new(language).with_program(Program::new());
89 assert_eq!(
90 ctx.eval_block(&Block {
91 body: vec![
92 Statement::Declaration(Declaration {
93 ty: Type::Integer,
94 name: "a".to_owned(),
95 init: Some(Expr::int(1).boxed()),
96 }),
97 Statement::Bare(Expr::If(IfExpr {
98 condition: Expr::true_().boxed(),
99 then: Block {
100 body: vec![Statement::Bare(Expr::bin(
101 Expr::Ident("a".to_owned()),
102 "+=",
103 Expr::int(5),
104 ))]
105 },
106 else_: Block {
107 body: vec![Statement::Bare(Expr::bin(
108 Expr::ident("a"),
109 "+=",
110 Expr::int(10),
111 ))]
112 }
113 }))
114 ]
115 }),
116 Ok(Value::Unit)
117 );
118 assert_eq!(
119 ctx.lookup(&String::from("a")).unwrap().clone(),
120 Variable {
121 ty: Type::Integer,
122 name: "a".to_owned(),
123 value: Value::Integer(6)
124 }
125 );
126}
127
128#[test]
129fn test_substring() {
130 let language = tree_sitter_python::language();
131 let mut ctx = Context::new(language).with_program(Program::new());
132 assert_eq!(
133 ctx.eval_block(&Block {
134 body: vec![
135 Statement::Declaration(Declaration {
136 ty: Type::String,
137 name: "a".to_owned(),
138 init: Some(Expr::str("foobar").boxed()),
139 }),
140 Statement::Declaration(Declaration {
141 ty: Type::String,
142 name: "b".to_owned(),
143 init: Some(
144 Expr::call(
145 "substr",
146 [Expr::Ident("a".to_owned()), Expr::int(0), Expr::int(3),]
147 )
148 .boxed()
149 ),
150 }),
151 ]
152 }),
153 Ok(Value::Unit)
154 );
155 assert_eq!(
156 ctx.lookup(&String::from("b")).unwrap().clone(),
157 Variable {
158 ty: Type::String,
159 name: "b".to_owned(),
160 value: "foo".into()
161 }
162 );
163}
164
165#[test]
166fn test_list() {
167 let language = tree_sitter_python::language();
168 let mut ctx = Context::new(language).with_program(Program::new());
169 assert_eq!(
170 ctx.eval_block(&Block {
171 body: vec![Statement::Declaration(Declaration {
172 ty: Type::List,
173 name: "a".to_owned(),
174 init: Some(
175 Expr::List(List {
176 items: vec![Expr::int(5)]
177 })
178 .boxed()
179 ),
180 }),]
181 }),
182 Ok(Value::Unit)
183 );
184 assert_eq!(
185 ctx.lookup(&String::from("a")).unwrap().clone(),
186 Variable {
187 ty: Type::List,
188 name: "a".to_owned(),
189 value: vec![5usize.into()].into(),
190 }
191 );
192}
193
194#[test]
195fn list_1() {
196 gen_test(
197 "",
198 "BEGIN {
199 list a = [5];
200 print(a);
201 }
202 ",
203 expect!["[5]"],
204 );
205}
206
207#[test]
208fn list_2() {
209 gen_test(
210 "",
211 "BEGIN {
212 list a = [5, 4, 3];
213 print(length(a));
214 }
215 ",
216 expect!["3"],
217 );
218}
219
220#[test]
221fn list_3() {
222 gen_test(
223 "",
224 r#"BEGIN {
225 list a = [5, 4, 3];
226 print(member(a, 3));
227 print(", ");
228 print(member(a, 6));
229 }
230 "#,
231 expect!["true, false"],
232 );
233}
234
235#[test]
236fn list_4() {
237 gen_test(
238 "",
239 r#"BEGIN {
240 list a = [5];
241 println(a);
242 push(a, 4);
243 println(a);
244 push(a, 3);
245 println(a);
246 pop(a);
247 println(a);
248 pop(a);
249 println(a);
250 }
251 "#,
252 expect![[r#"
253 [5]
254 [5, 4]
255 [5, 4, 3]
256 [5, 4]
257 [5]
258 "#]],
259 );
260}
261
262#[test]
263fn list_5() {
264 gen_test(
265 "",
266 r#"BEGIN {
267 list a = [5];
268 println(isempty(a));
269 pop(a);
270 println(isempty(a));
271 }
272 "#,
273 expect![[r#"
274 false
275 true
276 "#]],
277 );
278}
279
280#[test]
281fn string_1() {
282 gen_test(
283 "",
284 r#"BEGIN {
285 string a = "Foo";
286 println(toupper(a));
287 println(tolower(a));
288 }
289 "#,
290 expect![[r#"
291 FOO
292 foo
293 "#]],
294 );
295}
296
297#[test]
298fn string_2() {
299 gen_test(
300 "",
301 r#"BEGIN {
302 string a = "foo";
303 println(a, " is upper? ", isupper(a));
304 println(a, " is lower? ", islower(a));
305
306 string b = "Foo";
307 println(b, " is upper? ", isupper(b));
308 println(b, " is lower? ", islower(b));
309
310 string c = "FOO";
311 println(c, " is upper? ", isupper(c));
312 println(c, " is lower? ", islower(c));
313 }
314 "#,
315 expect![[r#"
316 foo is upper? false
317 foo is lower? true
318 Foo is upper? false
319 Foo is lower? false
320 FOO is upper? true
321 FOO is lower? false
322 "#]],
323 );
324}
325
326#[test]
327fn string_3() {
328 gen_test(
329 "",
330 r#"BEGIN {
331 string a = "foo bar baz";
332 println("a[3:5]: `", substr(a, 3, 5), "`");
333 println("a[2:9]: `", substr(a, 2, 9), "`");
334 }
335 "#,
336 expect![[r#"
337 a[3:5]: ` b`
338 a[2:9]: `o bar b`
339 "#]],
340 );
341}
342
343#[test]
344fn string_4() {
345 gen_test(
346 "",
347 r#"BEGIN {
348 string a = "foo bar baz";
349 println("a[9:20]: `", substr(a, 9, 20), "`");
350 }
351 "#,
352 expect![[r#"
353 a[9:20]: `InvalidStringSlice { length: 11, start: 9, end: 20 }
354 "#]],
355 );
356}
357
358#[test]
359fn node_1() {
360 gen_test(
361 "def foo(a, b): hello()",
362 r#"enter function_definition {
363 println(text(node));
364 println(text(node.name));
365 }"#,
366 expect![[r#"
367 def foo(a, b): hello()
368 foo
369 "#]],
370 );
371}