aboutsummaryrefslogtreecommitdiff
path: root/src/eval/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval/mod.rs')
-rw-r--r--src/eval/mod.rs345
1 files changed, 81 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}