#[macro_use] extern crate assert_matches; extern crate ketos; use ketos::{CompileError, Error, ExecError, Interpreter, FromValue, Value}; fn eval(s: &str) -> Result { let interp = Interpreter::new(); let v = interp.run_single_expr(s, None)?; Ok(interp.format_value(&v)) } fn eval_str(s: &str) -> Result { let interp = Interpreter::new(); let v = interp.run_single_expr(s, None)?; let s = FromValue::from_value(v).unwrap(); Ok(s) } fn run(s: &str) -> Result, Error> { let interp = Interpreter::new(); let c = interp.compile_exprs(s)?; c.into_iter().map(|c| interp.execute(c) .map(|v| interp.format_value(&v))).collect() } #[test] fn test_const() { assert_eq!(run(" (const foo 2) (const bar (* foo 3)) (const baz (+ bar 1)) baz ").unwrap(), ["foo", "bar", "baz", "7"]); assert_eq!(run(" (const foo `(foo 1 2 3)) (const bar `(bar ,foo)) (const baz `(baz ,@foo)) foo bar baz ").unwrap(), ["foo", "bar", "baz", "(foo 1 2 3)", "(bar (foo 1 2 3))", "(baz foo 1 2 3)"]); assert_eq!(run(" (const a 123) (const b 456) (const c (if (< a b) a b)) c ").unwrap(), ["a", "b", "c", "123"]); } #[test] fn test_const_error() { assert_matches!(run(" (define (foo) ()) (const bar (foo)) ").unwrap_err(), Error::CompileError(CompileError::NotConstant(_))); assert_matches!(run(" (define foo 1) (const bar foo) ").unwrap_err(), Error::CompileError(CompileError::NotConstant(_))); } #[test] fn test_docs() { assert_eq!(run(r#" (use code (documentation)) (const foo "foo doc" 0) (define bar "bar doc" 0) (define (baz) "baz doc" 0) (documentation 'foo) (documentation 'bar) (documentation 'baz) (documentation (lambda () "lambda doc" 0)) "#).unwrap(), ["()", "foo", "bar", "baz", r#""foo doc""#, r#""bar doc""#, r#""baz doc""#, r#""lambda doc""#]); assert_eq!(run(r#" (use code (documentation)) ;; foo doc (const foo 0) ;; bar doc ;; with multiple lines (define bar 0) ;; baz doc ;; ;; with indentation (struct baz ()) (documentation 'foo) (documentation 'bar) (documentation 'baz) (documentation ;; lambda doc (lambda () 0)) "#).unwrap(), ["()", "foo", "bar", "baz", r#""foo doc\n""#, r#""bar doc\nwith multiple lines\n""#, r#""baz doc\n\n with indentation\n""#, r#""lambda doc\n""# ]); } #[test] fn test_integer() { assert_eq!(eval("123").unwrap(), "123"); assert_eq!(eval("-123").unwrap(), "-123"); assert_eq!(eval("0xfaff").unwrap(), "64255"); assert_eq!(eval("0o777").unwrap(), "511"); assert_eq!(eval("0b101101").unwrap(), "45"); } #[test] fn test_path() { assert_eq!(eval(r#"#p"foo""#).unwrap(), r#"#p"foo""#); assert_eq!(eval(r#"(path "foo")"#).unwrap(), r#"#p"foo""#); assert_eq!(eval(r#"(path #p"bar")"#).unwrap(), r#"#p"bar""#); } #[test] fn test_quasiquote() { assert_eq!(eval("`foo").unwrap(), "foo"); assert_eq!(eval("``foo").unwrap(), "`foo"); assert_eq!(eval("```foo").unwrap(), "``foo"); assert_eq!(eval("`(foo ,(id 1))").unwrap(), "(foo 1)"); assert_eq!(eval("``(foo ,(id ,(id 1)))").unwrap(), "`(foo ,(id 1))"); assert_eq!(eval("```(foo ,(id ,(id ,(id 1))))").unwrap(), "``(foo ,(id ,(id 1)))"); assert_eq!(eval("`(,@(list 1 2 3))").unwrap(), "(1 2 3)"); assert_eq!(eval("`(foo ,@(list 1 2 3))").unwrap(), "(foo 1 2 3)"); assert_eq!(eval("`(foo ,@(list 1 2 3) bar)").unwrap(), "(foo 1 2 3 bar)"); assert_eq!(eval("`(foo ,@(list 1 2 3) bar ,@(list 4 5 6))").unwrap(), "(foo 1 2 3 bar 4 5 6)"); assert_eq!(eval("`(foo ,@(list 1 2 3) bar ,@(list 4 5 6) baz)").unwrap(), "(foo 1 2 3 bar 4 5 6 baz)"); assert_eq!(eval("`(foo ,1)").unwrap(), "(foo 1)"); assert_eq!(eval("``(foo ,,1)").unwrap(), "`(foo 1)"); assert_eq!(eval("```(foo ,,,1)").unwrap(), "``(foo 1)"); } #[test] fn test_struct() { assert_eq!(run(" (struct foo ()) (struct bar ()) (define my-foo (new foo)) (is-instance foo my-foo) (is-instance bar my-foo) ").unwrap(), ["foo", "bar", "my-foo", "true", "false"]); assert_eq!(run(" (struct foo ((a integer) (b list) (c bool))) (define my-foo (new foo :a 123 :b '(4 5 6) :c true)) (. my-foo :a) (. my-foo :b) (. my-foo :c) (define new-foo (.= my-foo :a 456 :b '(7 8 9) :c false)) (. new-foo :a) (. new-foo :b) (. new-foo :c) ").unwrap(), ["foo", "my-foo", "123", "(4 5 6)", "true", "new-foo", "456", "(7 8 9)", "false"]); assert_eq!(run(" (struct foo ((a number))) (new foo :a 1) (new foo :a 1.0) ").unwrap(), ["foo", "foo { a: 1 }", "foo { a: 1.0 }"]); assert_eq!(run(" (struct foo ()) (define my-foo (new foo)) (is-instance foo my-foo) (struct foo ()) (is-instance foo my-foo) ").unwrap(), ["foo", "my-foo", "true", "foo", "false"]); assert_matches!(run(" (struct foo ((a integer))) (new foo) ").unwrap_err(), Error::ExecError(ExecError::MissingField{..})); assert_matches!(run(" (struct foo ((a integer))) (new foo :a 1.0) ").unwrap_err(), Error::ExecError(ExecError::FieldTypeError{..})); assert_matches!(run(" (struct foo ((a integer))) (.= (new foo :a 1) :a 1.0) ").unwrap_err(), Error::ExecError(ExecError::FieldTypeError{..})); assert_matches!(run(" (struct foo ((a integer))) (. (new foo :a 1) :b) ").unwrap_err(), Error::ExecError(ExecError::FieldError{..})); assert_matches!(run(" (struct foo ((a integer))) (.= (new foo :a 1) :b 0) ").unwrap_err(), Error::ExecError(ExecError::FieldError{..})); assert_eq!(run(" (struct foo ((a number))) (= (new foo :a 1) (new foo :a 1.0)) ").unwrap(), ["foo", "true"]); } #[test] fn test_format() { assert_eq!(eval_str(r#"(format "foo")"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "foo~a" "bar")"#).unwrap(), "foobar"); assert_eq!(eval_str(r#"(format "foo~s" "bar")"#).unwrap(), "foo\"bar\""); assert_eq!(eval_str(r#"(format "~5a" "foo")"#).unwrap(), "foo "); assert_eq!(eval_str(r#"(format "~5@a" "foo")"#).unwrap(), " foo"); assert_eq!(eval_str(r#"(format "~7s" "foo")"#).unwrap(), "\"foo\" "); assert_eq!(eval_str(r#"(format "~7@s" "foo")"#).unwrap(), " \"foo\""); assert_eq!(eval_str(r#"(format "~5,4a" "foo")"#).unwrap(), "foo "); assert_eq!(eval_str(r#"(format "~5,4@a" "foo")"#).unwrap(), " foo"); assert_eq!(eval_str(r#"(format "~,,3,'-a" "foo")"#).unwrap(), "foo---"); assert_eq!(eval_str(r#"(format "~a" 123)"#).unwrap(), "123"); assert_eq!(eval_str(r#"(format "~s" 123)"#).unwrap(), "123"); assert_eq!(eval_str(r#"(format "~s" 'foo)"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "~a" #'a')"#).unwrap(), "a"); assert_eq!(eval_str(r#"(format "~s" #'a')"#).unwrap(), "#'a'"); assert_eq!(eval_str(r#"(format "~a" #p"foo")"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "~s" #p"foo")"#).unwrap(), "#p\"foo\""); assert_eq!(eval_str(r#"(format "~a" #b"foo")"#).unwrap(), r#"#b"foo""#); assert_eq!(eval_str(r#"(format "~s" #b"foo")"#).unwrap(), r#"#b"foo""#); assert_eq!(eval_str(r#"(format "~a" #b"foo\xff")"#).unwrap(), r#"#b"foo\xff""#); assert_eq!(eval_str(r#"(format "foo~c~c~c" #'b' #'a' #'r')"#).unwrap(), "foobar"); assert_eq!(eval_str(r#"(format "~f" 1.0)"#).unwrap(), "1"); assert_eq!(eval_str(r#"(format "~e" 1.0)"#).unwrap(), "1e0"); assert_eq!(eval_str(r#"(format "~d" 123)"#).unwrap(), "123"); assert_eq!(eval_str(r#"(format "~d" 1.2)"#).unwrap(), "1"); assert_eq!(eval_str(r#"(format "~d" 11/10)"#).unwrap(), "1"); assert_eq!(eval_str(r#"(format "~f" 1)"#).unwrap(), "1"); assert_eq!(eval_str(r#"(format "~f" 1/2)"#).unwrap(), "0.5"); assert_eq!(eval_str(r#"(format "~4f" 1.0)"#).unwrap(), " 1"); assert_eq!(eval_str(r#"(format "~4f" -1.0)"#).unwrap(), " -1"); assert_eq!(eval_str(r#"(format "~,3f" 1.0)"#).unwrap(), "1.000"); assert_eq!(eval_str(r#"(format "~4,,'*f" 1.0)"#).unwrap(), "***1"); assert_eq!(eval_str(r#"(format "~4,,'*e" 1.0)"#).unwrap(), "*1e0"); assert_eq!(eval_str(r#"(format "~@d" 0)"#).unwrap(), "+0"); assert_eq!(eval_str(r#"(format "~@d" 1)"#).unwrap(), "+1"); assert_eq!(eval_str(r#"(format "~@d" -1)"#).unwrap(), "-1"); assert_eq!(eval_str(r#"(format "~5d" 123)"#).unwrap(), " 123"); assert_eq!(eval_str(r#"(format "~:d" 1234567890)"#).unwrap(), "1,234,567,890"); assert_eq!(eval_str(r#"(format "~:d" -123456)"#).unwrap(), "-123,456"); assert_eq!(eval_str(r#"(format "~,,' ,2:d" 12345)"#).unwrap(), "1 23 45"); assert_matches!(eval_str(r#"(format "~v,vd" 5 6 123)"#).unwrap_err(), Error::ExecError(ExecError::FormatError{..})); assert_eq!(eval_str(r#"(format "~v,vd" 5 #'*' 123)"#).unwrap(), "**123"); assert_eq!(eval_str(r#"(format "~,,v,v:d" #'.' 2 123)"#).unwrap(), "1.23"); assert_eq!(eval_str(r#"(format "~v,v,v,v:d" 5 #'*' #'.' 2 123)"#).unwrap(), "*1.23"); assert_eq!(eval_str(r#"(format "~4@f" 1.0)"#).unwrap(), " +1"); assert_eq!(eval_str(r#"(format "~,3@f" 1.0)"#).unwrap(), "+1.000"); assert_eq!(eval_str(r#"(format "~4,,'*@f" 1.0)"#).unwrap(), "**+1"); assert_eq!(eval_str(r#"(format "~d thing~:p" 0)"#).unwrap(), "0 things"); assert_eq!(eval_str(r#"(format "~d thing~:p" 1)"#).unwrap(), "1 thing"); assert_eq!(eval_str(r#"(format "~d thing~:p" 2)"#).unwrap(), "2 things"); assert_eq!(eval_str(r#"(format "~d thing~:@p" 0)"#).unwrap(), "0 thingies"); assert_eq!(eval_str(r#"(format "~d thing~:@p" 1)"#).unwrap(), "1 thingy"); assert_eq!(eval_str(r#"(format "~d thing~:@p" 2)"#).unwrap(), "2 thingies"); assert_eq!(eval_str(r#"(format "~d thing~p" 1 2)"#).unwrap(), "1 things"); assert_eq!(eval_str(r#"(format "~t")"#).unwrap(), " "); assert_eq!(eval_str(r#"(format "~4t")"#).unwrap(), " "); assert_eq!(eval_str(r#"(format "ab~4t")"#).unwrap(), "ab "); assert_eq!(eval_str(r#"(format "abc~4t")"#).unwrap(), "abc "); assert_eq!(eval_str(r#"(format "abcd~4t")"#).unwrap(), "abcd "); assert_eq!(eval_str(r#"(format "abcde~4,4t")"#).unwrap(), "abcde "); assert_eq!(eval_str(r#"(format "abcdef~4,4t")"#).unwrap(), "abcdef "); assert_eq!(eval_str(r#"(format "abcdefg~4,4t")"#).unwrap(), "abcdefg "); assert_eq!(eval_str(r#"(format "abcdefgh~4,4t")"#).unwrap(), "abcdefgh "); assert_eq!(eval_str(r#"(format "ab~2,4@t")"#).unwrap(), "ab "); assert_eq!(eval_str(r#"(format "abc~2,4@t")"#).unwrap(), "abc "); assert_eq!(eval_str(r#"(format "~~")"#).unwrap(), "~"); assert_eq!(eval_str(r#"(format "~3~")"#).unwrap(), "~~~"); assert_eq!(eval_str(r#"(format "~v~" 5)"#).unwrap(), "~~~~~"); assert_eq!(eval_str(r#"(format "~#~" 1 2)"#).unwrap(), "~~"); assert_eq!(eval_str(r#"(format "~%")"#).unwrap(), "\n"); assert_eq!(eval_str(r#"(format "foo~ bar")"#).unwrap(), "foobar"); assert_eq!(eval_str(r#"(format "~&")"#).unwrap(), ""); assert_eq!(eval_str(r#"(format "a~&b")"#).unwrap(), "a\nb"); assert_eq!(eval_str(r#"(format "a~%~&b")"#).unwrap(), "a\nb"); assert_eq!(eval_str(r#"(format "~2&")"#).unwrap(), "\n"); assert_eq!(eval_str(r#"(format "a~2&b")"#).unwrap(), "a\n\nb"); assert_eq!(eval_str(r#"(format "a~%~2&b")"#).unwrap(), "a\n\nb"); assert_eq!(eval_str(r#"(format "~a ~*~a" 0 1 2)"#).unwrap(), "0 2"); assert_eq!(eval_str(r#"(format "~a ~:*~a" 0)"#).unwrap(), "0 0"); assert_matches!(eval_str(r#"(format "~*~*~*~a" 0 1 2)"#).unwrap_err(), Error::ExecError(ExecError::FormatError{..})); assert_eq!(eval_str(r#"(format "~a ~@*~a" 0)"#).unwrap(), "0 0"); assert_eq!(eval_str(r#"(format "~a ~2@*~a" 0 1 2)"#).unwrap(), "0 2"); assert_eq!(eval_str(r#"(format "~?" "~a ~a" '(1 2))"#).unwrap(), "1 2"); assert_eq!(eval_str(r#"(format "~@?" "~a ~a" 1 2)"#).unwrap(), "1 2"); assert_eq!(eval_str(r#"(format "FOO~(BAR~)")"#).unwrap(), "FOObar"); assert_eq!(eval_str(r#"(format "foo~:@(bar~)")"#).unwrap(), "fooBAR"); assert_eq!(eval_str(r#"(format "foo ~@(bar baz~)")"#).unwrap(), "foo Bar baz"); assert_eq!(eval_str(r#"(format "foo~@( bar baz~)")"#).unwrap(), "foo Bar baz"); assert_eq!(eval_str(r#"(format "foo ~:(bar baz~)")"#).unwrap(), "foo Bar Baz"); assert_eq!(eval_str(r#"(format "~(FOO~^BAR~)")"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "~[foo~;bar~;baz~]" 0)"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "~[foo~;bar~;baz~]" 1)"#).unwrap(), "bar"); assert_eq!(eval_str(r#"(format "~[foo~;bar~;baz~]" 2)"#).unwrap(), "baz"); assert_eq!(eval_str(r#"(format "~[foo~;bar~;baz~]" 3)"#).unwrap(), ""); assert_eq!(eval_str(r#"(format "~[foo~;bar~:;baz~]" 3)"#).unwrap(), "baz"); assert_eq!(eval_str(r#"(format "~[foo~;bar~:;baz~;quux~]" 3)"#).unwrap(), "baz"); assert_eq!(eval_str(r#"(format "~:[foo~;bar~]" false)"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "~:[foo~;bar~]" true)"#).unwrap(), "bar"); assert_matches!(eval_str(r#"(format "~:[foo~;bar~]" 'other)"#).unwrap_err(), Error::ExecError(ExecError::FormatError{..})); assert_eq!(eval_str(r#"(format "~@[~a~]" ())"#).unwrap(), ""); assert_eq!(eval_str(r#"(format "~@[~a~]" "foo")"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "~@[~a~]" false)"#).unwrap(), "false"); assert_eq!(eval_str(r#"(format "~{~a~}" '(1 2 3))"#).unwrap(), "123"); assert_eq!(eval_str(r#"(format "~{~}" "~a" '(1 2 3))"#).unwrap(), "123"); assert_eq!(eval_str(r#"(format "~{~{~a ~a~}~^, ~}" '((a b) (c d)))"#).unwrap(), "a b, c d"); assert_eq!(eval_str(r#"(format "~{foo~^~a~:}" '())"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "~{foo~^~a~:}" '(1 2))"#).unwrap(), "foo1foo2"); assert_eq!(eval_str(r#"(format "~0{foo~:}" '())"#).unwrap(), ""); assert_eq!(eval_str(r#"(format "~2{~a~:}" '(1 2 3))"#).unwrap(), "12"); assert_eq!(eval_str(r#"(format "~{foo~0^~:}" '(1 2 3))"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "~:{~@?~^:~}" '(("a") ("b")))"#).unwrap(), "ab"); assert_eq!(eval_str(r#"(format "~:{~@?~:^:~}" '(("a") ("b")))"#).unwrap(), "a:b"); assert_eq!(eval_str(r#"(format "~:{foo~a~0^bar~:}" '((1 2) (3 4)))"#).unwrap(), "foo1foo3"); assert_eq!(eval_str(r#"(format "~:{foo~a~0:^bar~:}" '((1 2) (3 4)))"#).unwrap(), "foo1"); assert_matches!(eval_str(r#"(format "~{infinite-loop~}" '(1 2 3))"#).unwrap_err(), Error::ExecError(ExecError::FormatError{..})); assert_matches!(eval_str(r#"(format "~@{infinite-loop~}" 1 2 3)"#).unwrap_err(), Error::ExecError(ExecError::FormatError{..})); assert_eq!(eval_str(r#"(format "~?baz" "foo~^bar" ())"#).unwrap(), "foobaz"); assert_eq!(eval_str(r#"(format "foo~^bar")"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "~:@(foo~^bar~)baz")"#).unwrap(), "FOO"); assert_eq!(eval_str(r#"(format "~[foo~^bar~;baz~]quux" 0)"#).unwrap(), "foo"); assert_eq!(eval_str(r#"(format "~[foo~^bar~;baz~]quux" 1)"#).unwrap(), "bazquux"); assert_eq!(eval_str(r#"(format "~[foo~^bar~;baz~]quux" 2)"#).unwrap(), "quux"); assert_eq!(eval_str(r#"(format "~{foo ~a ~^bar ~}baz" '())"#).unwrap(), "baz"); assert_eq!(eval_str(r#"(format "~{foo ~a ~^bar ~}baz" '(1))"#).unwrap(), "foo 1 baz"); assert_eq!(eval_str(r#"(format "~{foo ~a ~^bar ~}baz" '(1 2))"#).unwrap(), "foo 1 bar foo 2 baz"); assert_eq!(eval_str(r#"(format "~{a ~a~^ b ~a~^ ~}" '())"#).unwrap(), ""); assert_eq!(eval_str(r#"(format "~{a ~a~^ b ~a~^ ~}" '(1))"#).unwrap(), "a 1"); assert_eq!(eval_str(r#"(format "~{a ~a~^ b ~a~^ ~}" '(1 2))"#).unwrap(), "a 1 b 2"); assert_eq!(eval_str(r#"(format "~{a ~a~^ b ~a~^ ~}" '(1 2 3))"#).unwrap(), "a 1 b 2 a 3"); assert_eq!(eval_str(r#"(format "~<~>")"#).unwrap(), ""); assert_eq!(eval_str(r#"(format "~")"#).unwrap(), "a"); assert_eq!(eval_str(r#"(format "~5")"#).unwrap(), " a"); assert_eq!(eval_str(r#"(format "~5@")"#).unwrap(), "a "); assert_eq!(eval_str(r#"(format "~5:")"#).unwrap(), " a"); assert_eq!(eval_str(r#"(format "~5:@")"#).unwrap(), " a "); assert_eq!(eval_str(r#"(format "~5")"#).unwrap(), "a b c"); assert_eq!(eval_str(r#"(format "~6")"#).unwrap(), "a b c"); assert_eq!(eval_str(r#"(format "~7")"#).unwrap(), "a b c"); assert_eq!(eval_str(r#"(format "~")"#).unwrap(), "aaaaa"); assert_eq!(eval_str(r#"(format "~")"#).unwrap(), "foo:aaaaaa"); assert_matches!(eval_str(r#"(format "~(~]")"#).unwrap_err(), Error::ExecError(ExecError::FormatError{..})); assert_matches!(eval_str(r#"(format "~}")"#).unwrap_err(), Error::ExecError(ExecError::FormatError{..})); } #[test] fn test_format_radix() { assert_matches!(eval_str(r#"(format "~@r" 0)"#).unwrap_err(), Error::ExecError(ExecError::FormatError{..})); assert_matches!(eval_str(r#"(format "~@:r" 0)"#).unwrap_err(), Error::ExecError(ExecError::FormatError{..})); assert_eq!(eval_str(r#"(format "~10r" 0)"#).unwrap(), "0"); assert_eq!(eval_str(r#"(format "~10r" 123)"#).unwrap(), "123"); assert_eq!(eval_str(r#"(format "~vr" 10 123)"#).unwrap(), "123"); assert_eq!(eval_str(r#"(format "~10,5,'*r" 123)"#).unwrap(), "**123"); assert_eq!(eval_str(r#"(format "~2r" 0b111000111000)"#).unwrap(), "111000111000"); assert_eq!(eval_str(r#"(format "~8r" 0o777)"#).unwrap(), "777"); assert_eq!(eval_str(r#"(format "~16r" 0xdeadbeef)"#).unwrap(), "deadbeef"); assert_eq!(eval_str(r#"(format "~16r" (- 0xff))"#).unwrap(), "-ff"); assert_eq!(eval_str(r#"(format "~36r" 158401117505150402526146910133459374380313782878846610247207502179)"#) .unwrap(), "omghowdidthisgethereiamnotgoodwithcomputer"); assert_eq!(eval_str(r#"(format "~b" 0b1010)"#).unwrap(), "1010"); assert_eq!(eval_str(r#"(format "~o" 0o321)"#).unwrap(), "321"); assert_eq!(eval_str(r#"(format "~x" 0xabcdef)"#).unwrap(), "abcdef"); assert_eq!(eval_str(r#"(format "~2,,,' ,4:r" 0b101011001111)"#).unwrap(), "1010 1100 1111"); assert_eq!(eval_str(r#"(format "~r" 0)"#).unwrap(), "zero"); assert_eq!(eval_str(r#"(format "~r" 1)"#).unwrap(), "one"); assert_eq!(eval_str(r#"(format "~r" 2)"#).unwrap(), "two"); assert_eq!(eval_str(r#"(format "~r" 3)"#).unwrap(), "three"); assert_eq!(eval_str(r#"(format "~r" 15)"#).unwrap(), "fifteen"); assert_eq!(eval_str(r#"(format "~r" 41)"#).unwrap(), "forty-one"); assert_eq!(eval_str(r#"(format "~r" 101)"#).unwrap(), "one hundred one"); assert_eq!(eval_str(r#"(format "~r" 1204)"#).unwrap(), "one thousand two hundred four"); assert_eq!(eval_str(r#"(format "~:r" 0)"#).unwrap(), "zeroth"); assert_eq!(eval_str(r#"(format "~:r" 1)"#).unwrap(), "first"); assert_eq!(eval_str(r#"(format "~:r" 2)"#).unwrap(), "second"); assert_eq!(eval_str(r#"(format "~:r" 3)"#).unwrap(), "third"); assert_eq!(eval_str(r#"(format "~:r" 15)"#).unwrap(), "fifteenth"); assert_eq!(eval_str(r#"(format "~:r" 41)"#).unwrap(), "forty-first"); assert_eq!(eval_str(r#"(format "~:r" 101)"#).unwrap(), "one hundred first"); assert_eq!(eval_str(r#"(format "~:r" 1204)"#).unwrap(), "one thousand two hundred fourth"); assert_eq!(eval_str(r#"(format "~@r" 1)"#).unwrap(), "I"); assert_eq!(eval_str(r#"(format "~@r" 2)"#).unwrap(), "II"); assert_eq!(eval_str(r#"(format "~@r" 3)"#).unwrap(), "III"); assert_eq!(eval_str(r#"(format "~@r" 15)"#).unwrap(), "XV"); assert_eq!(eval_str(r#"(format "~@r" 41)"#).unwrap(), "XLI"); assert_eq!(eval_str(r#"(format "~@r" 101)"#).unwrap(), "CI"); assert_eq!(eval_str(r#"(format "~@r" 1204)"#).unwrap(), "MCCIV"); assert_eq!(eval_str(r#"(format "~@:r" 1)"#).unwrap(), "I"); assert_eq!(eval_str(r#"(format "~@:r" 2)"#).unwrap(), "II"); assert_eq!(eval_str(r#"(format "~@:r" 3)"#).unwrap(), "III"); assert_eq!(eval_str(r#"(format "~@:r" 15)"#).unwrap(), "XV"); assert_eq!(eval_str(r#"(format "~@:r" 41)"#).unwrap(), "XXXXI"); assert_eq!(eval_str(r#"(format "~@:r" 101)"#).unwrap(), "CI"); assert_eq!(eval_str(r#"(format "~@:r" 1204)"#).unwrap(), "MCCIIII"); } #[test] fn test_add() { assert_eq!(eval("(+)").unwrap(), "0"); assert_eq!(eval("(+ 1 2)").unwrap(), "3"); assert_eq!(eval("(+ 1 2 3 4)").unwrap(), "10"); assert_eq!(eval("(+ (+ 1 2) (+ 3 4))").unwrap(), "10"); assert_eq!(eval("(+ 1.0 2.0)").unwrap(), "3.0"); assert_eq!(eval("(+ 1/3 1/2)").unwrap(), "5/6"); assert_eq!(eval("(+ 1 1/2)").unwrap(), "3/2"); assert_eq!(eval("(+ 1 0.5)").unwrap(), "1.5"); assert_eq!(eval("(+ 1/4 0.5)").unwrap(), "0.75"); } #[test] fn test_sub() { assert_eq!(eval("(- 3 2)").unwrap(), "1"); assert_eq!(eval("(- 5 4 3)").unwrap(), "-2"); assert_eq!(eval("(- 1 3/4)").unwrap(), "1/4"); assert_eq!(eval("(- 1.0 3/4)").unwrap(), "0.25"); } #[test] fn test_mul() { assert_eq!(eval("(*)").unwrap(), "1"); assert_eq!(eval("(* 3 2)").unwrap(), "6"); assert_eq!(eval("(* 100 200 0)").unwrap(), "0"); assert_eq!(eval("(* 1/2 1/2)").unwrap(), "1/4"); assert_eq!(eval("(* 0.5 1/2)").unwrap(), "0.25"); assert_eq!(eval("(* 2 0.5)").unwrap(), "1.0"); assert_eq!(eval("(* 2 1/2)").unwrap(), "1/1"); } #[test] fn test_pow() { assert_eq!(eval("(^ 2 10)").unwrap(), "1024"); assert_eq!(eval("(^ 2 -1)").unwrap(), "0.5"); assert_eq!(eval("(^ 2.0 -1.0)").unwrap(), "0.5"); assert_eq!(eval("(^ 2/3 10)").unwrap(), "1024/59049"); assert_eq!(eval("(^ 2/3 10/1)").unwrap(), "1024/59049"); assert_eq!(eval("(^ 2/3 0)").unwrap(), "1/1"); assert_eq!(eval("(^ 2/3 -1)").unwrap(), "1.5"); } #[test] fn test_div() { assert_eq!(eval("(/ 1)").unwrap(), "1"); assert_eq!(eval("(/ 10)").unwrap(), "1/10"); assert_eq!(eval("(/ 10.0)").unwrap(), "0.1"); assert_eq!(eval("(/ 1/10)").unwrap(), "10/1"); assert_eq!(eval("(/ 10 2)").unwrap(), "5"); assert_eq!(eval("(/ 12 2 2)").unwrap(), "3"); assert_eq!(eval("(/ 5 2)").unwrap(), "5/2"); assert_eq!(eval("(// 5 2)").unwrap(), "2"); assert_eq!(eval("(/ 1.0 2.0)").unwrap(), "0.5"); assert_eq!(eval("(/ 1/2 3/4)").unwrap(), "2/3"); assert_eq!(eval("(/ 2 4)").unwrap(), "1/2"); assert_eq!(eval("(/ 2 4.0)").unwrap(), "0.5"); assert_matches!(eval("(/ 1 0)").unwrap_err(), Error::ExecError(ExecError::DivideByZero)); assert_eq!(eval("(/ 1.0 0.0)").unwrap(), "inf"); assert_matches!(eval("(/ 1/1 0/1)").unwrap_err(), Error::ExecError(ExecError::DivideByZero)); } #[test] fn test_rem() { assert_eq!(eval("(rem 10 3)").unwrap(), "1"); assert_matches!(eval("(rem 1 0)").unwrap_err(), Error::ExecError(ExecError::DivideByZero)); assert_eq!(eval("(rem 1.0 0.0)").unwrap(), "NaN"); assert_matches!(eval("(rem 1/1 0/1)").unwrap_err(), Error::ExecError(ExecError::DivideByZero)); } #[test] fn test_shift() { assert_eq!(eval("(<< 1 10)").unwrap(), "1024"); assert_eq!(eval("(>> 128 7)").unwrap(), "1"); assert_matches!(eval("(<< 1 -1)").unwrap_err(), Error::ExecError(ExecError::Overflow)); assert_matches!(eval("(<< 1 10000000000000)").unwrap_err(), Error::ExecError(ExecError::Overflow)); assert_matches!(eval("(>> 1 -1)").unwrap_err(), Error::ExecError(ExecError::Overflow)); assert_matches!(eval("(>> 1 10000000000000)").unwrap_err(), Error::ExecError(ExecError::Overflow)); } #[test] fn test_bitwise() { assert_eq!(eval("(bit&)").unwrap(), "-1"); assert_eq!(eval("(bit& 15 6)").unwrap(), "6"); assert_eq!(eval("(bit& 5 15 1)").unwrap(), "1"); assert_eq!(eval("(bit& 5 15 2)").unwrap(), "0"); assert_eq!(eval("(bit|)").unwrap(), "0"); assert_eq!(eval("(bit| 5 20)").unwrap(), "21"); assert_eq!(eval("(bit| 5 20 29)").unwrap(), "29"); assert_eq!(eval("(bit^)").unwrap(), "0"); assert_eq!(eval("(bit^ 2 3)").unwrap(), "1"); assert_eq!(eval("(bit^ 2 3 6 9)").unwrap(), "14"); assert_eq!(eval("(bit! 0)").unwrap(), "-1"); assert_eq!(eval("(bit! -5)").unwrap(), "4"); } #[test] fn test_eq() { assert_eq!(eval("(= 1 1)").unwrap(), "true"); assert_eq!(eval("(= 1 1 1 1)").unwrap(), "true"); assert_eq!(eval("(= 1 2)").unwrap(), "false"); assert_eq!(eval("(= 1 1 1 2)").unwrap(), "false"); // Require optimal short-circuit behavior assert_eq!(eval("(= '(a 123) '(b ()))").unwrap(), "false"); assert_eq!(eval("(= (nan) (nan))").unwrap(), "false"); assert_eq!(eval(r#"(= "a" "a")"#).unwrap(), "true"); assert_eq!(eval(r#"(= "a" "b")"#).unwrap(), "false"); assert_eq!(eval(r"(= #'a' #'a')").unwrap(), "true"); assert_eq!(eval(r"(= #'a' #'b')").unwrap(), "false"); assert_eq!(eval(r#"(= #p"a" #p"a")"#).unwrap(), "true"); assert_eq!(eval(r#"(= #p"a" #p"b")"#).unwrap(), "false"); assert_eq!(eval(r#"(= #b"a" #b"a")"#).unwrap(), "true"); assert_eq!(eval(r#"(= #b"a" #b"b")"#).unwrap(), "false"); assert_eq!(eval("(= 'a 'a)").unwrap(), "true"); assert_eq!(eval("(= 'a 'b)").unwrap(), "false"); assert_eq!(eval("(= :a :a)").unwrap(), "true"); assert_eq!(eval("(= :a :b)").unwrap(), "false"); assert_eq!(eval("(= = =)").unwrap(), "true"); assert_eq!(eval("(= id =)").unwrap(), "false"); assert_eq!(run(" (define (foo n) (+ n 1)) (define (bar n) (+ n 1)) (= foo foo) (= foo bar) ").unwrap(), ["foo", "bar", "true", "false"]); assert_eq!(eval("(= 1 1.0)").unwrap(), "true"); assert_eq!(eval("(= 2 1.0)").unwrap(), "false"); assert_eq!(eval("(= 1/1 1)").unwrap(), "true"); assert_eq!(eval("(= 1/1 1.0)").unwrap(), "true"); } #[test] fn test_ne() { assert_eq!(eval("(/= 1 2 3 4)").unwrap(), "true"); assert_eq!(eval("(/= 1 2 3 1)").unwrap(), "false"); assert_eq!(eval("(/= 1 2 3 2)").unwrap(), "false"); // Require optimal short-circuit behavior assert_eq!(eval("(/= '(a 123) '(b ()))").unwrap(), "true"); assert_eq!(eval("(/= (nan) (nan))").unwrap(), "true"); } #[test] fn test_weak_eq() { assert_eq!(eval("(eq 1 1)").unwrap(), "true"); assert_eq!(eval("(eq () 1)").unwrap(), "false"); assert_eq!(eval("(ne 1 1)").unwrap(), "false"); assert_eq!(eval("(ne () 1)").unwrap(), "true"); } #[test] fn test_cmp() { assert_eq!(eval("(> 5 4 3 2 1)").unwrap(), "true"); assert_eq!(eval("(> 5 4 3 2 3)").unwrap(), "false"); assert_eq!(eval("(< 1 2 3 4 5)").unwrap(), "true"); assert_eq!(eval("(<= 1 1 2 2 3)").unwrap(), "true"); assert_eq!(eval("(< 1 1.5)").unwrap(), "true"); assert_eq!(eval("(< 1/2 1)").unwrap(), "true"); assert_eq!(eval("(< #'a' #'b')").unwrap(), "true"); assert_eq!(eval(r#"(< "a" "b")"#).unwrap(), "true"); assert_eq!(eval(r#"(< #p"a" #p"b")"#).unwrap(), "true"); assert_eq!(eval(r#"(< #b"a" #b"b")"#).unwrap(), "true"); assert_eq!(eval("(< () '(0))").unwrap(), "true"); assert_eq!(eval("(> '(0) ())").unwrap(), "true"); // Ordering is not predictable, but it should succeed. assert!(eval("(< 'a 'b)").is_ok()); assert!(eval("(< :a :b)").is_ok()); assert_matches!(eval("(< < <)").unwrap_err(), Error::ExecError(ExecError::CannotCompare("function"))); assert_matches!(eval("(< 1.0 (nan))").unwrap_err(), Error::ExecError(ExecError::CompareNaN)); assert_matches!(eval("(< 1 (nan))").unwrap_err(), Error::ExecError(ExecError::CompareNaN)); assert_matches!(eval("(< 1/2 (nan))").unwrap_err(), Error::ExecError(ExecError::CompareNaN)); } #[test] fn test_cmp_overflow() { // Comparing ints too big to convert to float assert_eq!(eval("(< 0.0 (^ 10 309))").unwrap(), "true"); assert_eq!(eval("(> 0.0 (^ -10 309))").unwrap(), "true"); assert_eq!(eval("(< 0.0 (rat (^ 10 309) 1))").unwrap(), "true"); assert_eq!(eval("(> 0.0 (rat (^ -10 309) 1))").unwrap(), "true"); // Comparing big ints to infinity assert_eq!(eval("(> (inf) (^ 10 309))").unwrap(), "true"); assert_eq!(eval("(> (inf) (^ -10 309))").unwrap(), "true"); assert_eq!(eval("(> (inf) (rat (^ 10 309) 1))").unwrap(), "true"); assert_eq!(eval("(> (inf) (rat (^ -10 309) 1))").unwrap(), "true"); // Comparing big ints to negative infinity assert_eq!(eval("(< (- (inf)) (^ 10 309))").unwrap(), "true"); assert_eq!(eval("(< (- (inf)) (^ -10 309))").unwrap(), "true"); assert_eq!(eval("(< (- (inf)) (rat (^ 10 309) 1))").unwrap(), "true"); assert_eq!(eval("(< (- (inf)) (rat (^ -10 309) 1))").unwrap(), "true"); // Now, all of those comparisons, in reverse. // Comparing ints too big to convert to float assert_eq!(eval("(> (^ 10 309) 0.0)").unwrap(), "true"); assert_eq!(eval("(< (^ -10 309) 0.0)").unwrap(), "true"); assert_eq!(eval("(> (rat (^ 10 309) 1) 0.0)").unwrap(), "true"); assert_eq!(eval("(< (rat (^ -10 309) 1) 0.0)").unwrap(), "true"); // Comparing big ints to infinity assert_eq!(eval("(< (^ 10 309) (inf))").unwrap(), "true"); assert_eq!(eval("(< (^ -10 309) (inf))").unwrap(), "true"); assert_eq!(eval("(< (rat (^ 10 309) 1) (inf))").unwrap(), "true"); assert_eq!(eval("(< (rat (^ -10 309) 1) (inf))").unwrap(), "true"); // Comparing big ints to negative infinity assert_eq!(eval("(> (^ 10 309) (- (inf)))").unwrap(), "true"); assert_eq!(eval("(> (^ -10 309) (- (inf)))").unwrap(), "true"); assert_eq!(eval("(> (rat (^ 10 309) 1) (- (inf)))").unwrap(), "true"); assert_eq!(eval("(> (rat (^ -10 309) 1) (- (inf)))").unwrap(), "true"); } #[test] fn test_zero() { assert_eq!(eval("(zero 0 0 0)").unwrap(), "true"); assert_eq!(eval("(zero 0 1 0)").unwrap(), "false"); assert_eq!(eval("(zero 0 0.0 0/1)").unwrap(), "true"); assert_matches!(eval("(zero ())").unwrap_err(), Error::ExecError( ExecError::TypeError{ expected: "number", found: "unit", value: Some(Value::Unit), })); } #[test] fn test_min_max() { assert_eq!(eval("(max 1 2 3 2 1)").unwrap(), "3"); assert_eq!(eval("(min 3 2 1 2 3)").unwrap(), "1"); } #[test] fn test_and() { assert_eq!(eval("(and true true)").unwrap(), "true"); assert_eq!(eval("(and true false)").unwrap(), "false"); assert_eq!(eval("(and false true)").unwrap(), "false"); assert_eq!(eval("(and false (panic))").unwrap(), "false"); assert_matches!(eval("(and () true)").unwrap_err(), Error::ExecError( ExecError::TypeError{ expected: "bool", found: "unit", value: Some(Value::Unit), })); } #[test] fn test_or() { assert_eq!(eval("(or false false)").unwrap(), "false"); assert_eq!(eval("(or true false)").unwrap(), "true"); assert_eq!(eval("(or false true)").unwrap(), "true"); assert_eq!(eval("(or true (panic))").unwrap(), "true"); assert_matches!(eval("(or () true)").unwrap_err(), Error::ExecError( ExecError::TypeError{ expected: "bool", found: "unit", value: Some(Value::Unit), })); } #[test] fn test_xor() { assert_eq!(eval("(xor true true)").unwrap(), "false"); assert_eq!(eval("(xor false true)").unwrap(), "true"); assert_eq!(eval("(xor true false)").unwrap(), "true"); assert_eq!(eval("(xor false false)").unwrap(), "false"); assert_matches!(eval("(xor () ())").unwrap_err(), Error::ExecError( ExecError::TypeError{ expected: "bool", found: "unit", value: Some(Value::Unit), })); } #[test] fn test_not() { assert_eq!(eval("(not true)").unwrap(), "false"); assert_eq!(eval("(not false)").unwrap(), "true"); assert_matches!(eval("(not ())").unwrap_err(), Error::ExecError( ExecError::TypeError{ expected: "bool", found: "unit", value: Some(Value::Unit), })); } #[test] fn test_if() { assert_eq!(eval("(if true 1 (panic))").unwrap(), "1"); assert_eq!(eval("(if false (panic) 1)").unwrap(), "1"); assert_eq!(eval("(if (= 0 0) 'a 'b)").unwrap(), "a"); assert_eq!(eval("(if (/= 0 0) 'a 'b)").unwrap(), "b"); assert_eq!(eval("(if (= 1 0) 'a 'b)").unwrap(), "b"); assert_eq!(eval("(if (/= 1 0) 'a 'b)").unwrap(), "a"); assert_matches!(eval("(if 0 () ())").unwrap_err(), Error::ExecError( ExecError::TypeError{ expected: "bool", found: "integer", value: Some(Value::Integer(_)), })); } #[test] fn test_case() { assert_eq!(eval("(case 1 ((0) 'a))").unwrap(), "()"); assert_eq!(eval("(case 1 ((0 1 2) 'a))").unwrap(), "a"); assert_eq!(eval("(case 1 ((0) 'a) (else 'b))").unwrap(), "b"); assert_eq!(eval("(case 2 ((0) 'a) ((1) 'b) ((2) 'c))").unwrap(), "c"); assert_matches!(eval("(case 0 ((0) 'a) (else 'b) ((1) 'c))").unwrap_err(), Error::CompileError(_)); } #[test] fn test_cond() { assert_eq!(eval("(cond (false 'a) (true 'b))").unwrap(), "b"); assert_eq!(eval("(cond (true 'a) (true 'b))").unwrap(), "a"); assert_eq!(eval("(cond (false 'a) (false 'b))").unwrap(), "()"); assert_eq!(eval("(cond (false 'a) (false 'b) (else 'c))").unwrap(), "c"); assert_matches!(eval("(cond (false 'a) (else 'b) (true 'c))").unwrap_err(), Error::CompileError(_)); } #[test] fn test_let() { assert_eq!(eval(" (let ((a 1) (b 2)) (+ a b)) ").unwrap(), "3"); // Standard names CAN be overriden with let assert_eq!(eval("(let ((id 0)) id)").unwrap(), "0"); } #[test] fn test_chars() { assert_eq!(eval(r#"(chars "")"#).unwrap(), "()"); assert_eq!(eval(r#"(chars "foo")"#).unwrap(), "(#'f' #'o' #'o')"); assert_eq!(eval(r#"(chars "halo thar")"#).unwrap(), "(#'h' #'a' #'l' #'o' #' ' #'t' #'h' #'a' #'r')"); } #[test] fn test_string() { assert_eq!(eval(r#"(string #'a')"#).unwrap(), r#""a""#); assert_eq!(eval(r#"(string "foo")"#).unwrap(), r#""foo""#); assert_eq!(eval(r#"(string 'bar)"#).unwrap(), r#""bar""#); } #[test] fn test_bytes() { assert_eq!(eval(r#"(bytes ())"#).unwrap(), r#"#b"""#); assert_eq!(eval(r#"(bytes '(97 98 99))"#).unwrap(), r#"#b"abc""#); assert_eq!(eval(r#"(bytes '(#b'a' #b'b' #b'c'))"#).unwrap(), r#"#b"abc""#); assert_eq!(eval(r#"(bytes "foo")"#).unwrap(), r#"#b"foo""#); assert_eq!(eval(r#"(bytes #b"bar")"#).unwrap(), r#"#b"bar""#); } #[test] fn test_slice() { assert_eq!(eval("(slice () 0 0)").unwrap(), "()"); assert_eq!(eval("(slice () 0)").unwrap(), "()"); assert_matches!(eval("(slice () 0 1)").unwrap_err(), Error::ExecError(ExecError::OutOfBounds(1))); assert_matches!(eval("(slice () 1)").unwrap_err(), Error::ExecError(ExecError::OutOfBounds(1))); assert_eq!(eval("(slice '(1 2 3) 0 2)").unwrap(), "(1 2)"); assert_eq!(eval("(slice '(1 2 3) 1 3)").unwrap(), "(2 3)"); assert_eq!(eval("(slice '(1 2 3) 1)").unwrap(), "(2 3)"); assert_eq!(eval(r#"(slice "" 0 0)"#).unwrap(), r#""""#); assert_eq!(eval(r#"(slice "" 0)"#).unwrap(), r#""""#); assert_eq!(eval(r#"(slice "foobar" 3 6)"#).unwrap(), r#""bar""#); assert_eq!(eval(r#"(slice "foobar" 3)"#).unwrap(), r#""bar""#); assert_matches!(eval(r#"(slice "a\u{2022}" 1 2)"#).unwrap_err(), Error::ExecError(ExecError::NotCharBoundary(2))); assert_matches!(eval(r#"(slice "a\u{2022}" 2)"#).unwrap_err(), Error::ExecError(ExecError::NotCharBoundary(2))); } #[test] fn test_append() { assert_eq!(eval("(append () 1 2)").unwrap(), "(1 2)"); assert_eq!(eval("(append '(1 2) 3 4)").unwrap(), "(1 2 3 4)"); } #[test] fn test_elt() { assert_eq!(eval("(elt '(1 2) 0)").unwrap(), "1"); assert_eq!(eval(r#"(elt #b"abc" 0)"#).unwrap(), "97"); assert_eq!(eval(r#"(elt #b"abc" 1)"#).unwrap(), "98"); assert_matches!(eval("(elt '(1 2) 2)").unwrap_err(), Error::ExecError(ExecError::OutOfBounds(2))); assert_matches!(eval("(elt () -1)").unwrap_err(), Error::ExecError(ExecError::Overflow)); } #[test] fn test_concat() { assert_eq!(eval("(concat '(1 2) () '(3 4))").unwrap(), "(1 2 3 4)"); assert_eq!(eval(r#"(concat "foo" "" "bar")"#).unwrap(), r#""foobar""#); assert_eq!(eval(r#"(concat #'a' #'b')"#).unwrap(), r#""ab""#); assert_eq!(eval(r#"(concat "foo" #'b' #'a' #'r')"#).unwrap(), r#""foobar""#); assert_eq!(eval(r#"(concat #p"foo" #p"bar")"#).unwrap(), r#"#p"foo/bar""#); assert_eq!(eval(r#"(concat #b"foo" #b"bar")"#).unwrap(), r#"#b"foobar""#); } #[test] fn test_join() { assert_eq!(eval("(join '(0) '(1 2) () '(3 4))").unwrap(), "(1 2 0 0 3 4)"); assert_eq!(eval("(join '(0))").unwrap(), "()"); assert_eq!(eval(r#"(join ":")"#).unwrap(), r#""""#); assert_eq!(eval(r#"(join ":" "foo" "" "bar")"#).unwrap(), r#""foo::bar""#); assert_eq!(eval(r#"(join #':' "foo" "" "bar")"#).unwrap(), r#""foo::bar""#); assert_eq!(eval(r#"(join #':' #'a' #'b')"#).unwrap(), r#""a:b""#); assert_eq!(eval(r#"(join ":" "foo" #'a' #'b' "bar")"#).unwrap(), r#""foo:a:b:bar""#); assert_eq!(eval(r#"(join #b":" #b"foo" #b"" #b"bar")"#).unwrap(), r#"#b"foo::bar""#); } #[test] fn test_len() { assert_eq!(eval("(len ())").unwrap(), "0"); assert_eq!(eval("(len '(1 2 3))").unwrap(), "3"); assert_eq!(eval(r#"(len "")"#).unwrap(), "0"); assert_eq!(eval(r#"(len "foo")"#).unwrap(), "3"); assert_eq!(eval(r#"(len #b"foo")"#).unwrap(), "3"); assert_matches!(eval("(len 123)").unwrap_err(), Error::ExecError(ExecError::TypeError{..})); } #[test] fn test_first_second() { assert_eq!(eval("(first '(1 2))").unwrap(), "1"); assert_eq!(eval("(second '(1 2))").unwrap(), "2"); assert_matches!(eval("(first ())").unwrap_err(), Error::ExecError(_)); assert_matches!(eval("(second '(1))").unwrap_err(), Error::ExecError(ExecError::OutOfBounds(1))); } #[test] fn test_list_fns() { assert_eq!(eval("(last '(1 2 3))").unwrap(), "3"); assert_eq!(eval("(init '(1 2 3))").unwrap(), "(1 2)"); assert_eq!(eval("(tail '(1 2 3))").unwrap(), "(2 3)"); assert_eq!(eval("(init '(1))").unwrap(), "()"); assert_eq!(eval("(tail '(1))").unwrap(), "()"); } #[test] fn test_str_fns() { assert_eq!(eval(r#"(first "abc")"#).unwrap(), r#"#'a'"#); assert_eq!(eval(r#"(last "abc")"#).unwrap(), r#"#'c'"#); assert_eq!(eval(r#"(init "abc")"#).unwrap(), r#""ab""#); assert_eq!(eval(r#"(tail "abc")"#).unwrap(), r#""bc""#); assert_eq!(eval(r#"(init "x")"#).unwrap(), r#""""#); assert_eq!(eval(r#"(tail "x")"#).unwrap(), r#""""#); assert_matches!(eval(r#"(first "")"#).unwrap_err(), Error::ExecError(ExecError::OutOfBounds(0))); assert_matches!(eval(r#"(last "")"#).unwrap_err(), Error::ExecError(ExecError::OutOfBounds(0))); assert_matches!(eval(r#"(init "")"#).unwrap_err(), Error::ExecError(ExecError::OutOfBounds(0))); assert_matches!(eval(r#"(tail "")"#).unwrap_err(), Error::ExecError(ExecError::OutOfBounds(0))); } #[test] fn test_bytes_fns() { assert_eq!(eval(r#"(first #b"abc")"#).unwrap(), r#"97"#); assert_eq!(eval(r#"(last #b"abc")"#).unwrap(), r#"99"#); assert_eq!(eval(r#"(init #b"abc")"#).unwrap(), r#"#b"ab""#); assert_eq!(eval(r#"(tail #b"abc")"#).unwrap(), r#"#b"bc""#); assert_eq!(eval(r#"(init #b"x")"#).unwrap(), r#"#b"""#); assert_eq!(eval(r#"(tail #b"x")"#).unwrap(), r#"#b"""#); assert_matches!(eval(r#"(first #b"")"#).unwrap_err(), Error::ExecError(ExecError::OutOfBounds(0))); assert_matches!(eval(r#"(last #b"")"#).unwrap_err(), Error::ExecError(ExecError::OutOfBounds(0))); assert_matches!(eval(r#"(init #b"")"#).unwrap_err(), Error::ExecError(ExecError::OutOfBounds(0))); assert_matches!(eval(r#"(tail #b"")"#).unwrap_err(), Error::ExecError(ExecError::OutOfBounds(0))); } #[test] fn test_list() { assert_eq!(eval("(list 1 2 (+ 1 2))").unwrap(), "(1 2 3)"); } #[test] fn test_reverse() { assert_eq!(eval("(reverse ())").unwrap(), "()"); assert_eq!(eval("(reverse '(1 2 3))").unwrap(), "(3 2 1)"); } #[test] fn test_abs() { assert_eq!(eval("(abs -1)").unwrap(), "1"); assert_eq!(eval("(abs -1/2)").unwrap(), "1/2"); assert_eq!(eval("(abs -1.5)").unwrap(), "1.5"); assert_eq!(eval("(abs (- (inf)))").unwrap(), "inf"); } #[test] fn test_ceil() { assert_eq!(eval("(ceil 1)").unwrap(), "1"); assert_eq!(eval("(ceil 1/2)").unwrap(), "1/1"); assert_eq!(eval("(ceil 1.3)").unwrap(), "2.0"); } #[test] fn test_floor() { assert_eq!(eval("(floor 1)").unwrap(), "1"); assert_eq!(eval("(floor 1/2)").unwrap(), "0/1"); assert_eq!(eval("(floor -1.3)").unwrap(), "-2.0"); } #[test] fn test_trunc() { assert_eq!(eval("(trunc 1)").unwrap(), "1"); assert_eq!(eval("(trunc -1.3)").unwrap(), "-1.0"); assert_eq!(eval("(trunc 5/2)").unwrap(), "2/1"); } #[test] fn test_int() { assert_eq!(eval("(int 123)").unwrap(), "123"); assert_eq!(eval("(int 123.0)").unwrap(), "123"); assert_eq!(eval("(int 123/1)").unwrap(), "123"); assert_matches!(eval("(int (inf))").unwrap_err(), Error::ExecError(ExecError::Overflow)); assert_matches!(eval("(int (nan))").unwrap_err(), Error::ExecError(ExecError::Overflow)); } #[test] fn test_float() { assert_eq!(eval("(float 123)").unwrap(), "123.0"); assert_eq!(eval("(float 123.0)").unwrap(), "123.0"); assert_eq!(eval("(float 123/1)").unwrap(), "123.0"); assert_matches!(eval("(float (^ 10 309))").unwrap_err(), Error::ExecError(ExecError::Overflow)); } #[test] fn test_inf() { assert_eq!(eval("(inf)").unwrap(), "inf"); assert_eq!(eval("(- (inf))").unwrap(), "-inf"); assert_eq!(eval("(inf (inf) (inf))").unwrap(), "true"); assert_eq!(eval("(inf (- (inf)))").unwrap(), "true"); assert_eq!(eval("(inf (inf) 1.0)").unwrap(), "false"); } #[test] fn test_nan() { assert_eq!(eval("(nan)").unwrap(), "NaN"); assert_eq!(eval("(nan (nan) (nan))").unwrap(), "true"); assert_eq!(eval("(nan (nan) 1.0)").unwrap(), "false"); } #[test] fn test_fract() { assert_eq!(eval("(fract 1.5)").unwrap(), "0.5"); assert_eq!(eval("(fract 5/2)").unwrap(), "1/2"); } #[test] fn test_recip() { assert_eq!(eval("(recip 3)").unwrap(), "1/3"); assert_eq!(eval("(recip 10.0)").unwrap(), "0.1"); assert_eq!(eval("(recip 2/3)").unwrap(), "3/2"); assert_matches!(eval("(recip 0)").unwrap_err(), Error::ExecError(ExecError::DivideByZero)); assert_matches!(eval("(recip 0/1)").unwrap_err(), Error::ExecError(ExecError::DivideByZero)); } #[test] fn test_ratio() { assert_eq!(eval("(denom 1/2)").unwrap(), "2"); assert_eq!(eval("(numer 1/2)").unwrap(), "1"); assert_eq!(eval("(denom 123)").unwrap(), "1"); assert_eq!(eval("(numer 123)").unwrap(), "123"); assert_eq!(eval("(rat 1.5)").unwrap(), "3/2"); assert_eq!(eval("(rat 1/3)").unwrap(), "1/3"); assert_eq!(eval("(rat 3)").unwrap(), "3/1"); assert_eq!(eval("(rat 1 2)").unwrap(), "1/2"); assert_matches!(eval("(rat (inf))").unwrap_err(), Error::ExecError(ExecError::Overflow)); assert_matches!(eval("(rat (nan))").unwrap_err(), Error::ExecError(ExecError::Overflow)); } #[test] fn test_id() { assert_eq!(eval("(id 1)").unwrap(), "1"); assert_eq!(eval("(id #'a')").unwrap(), "#'a'"); assert_eq!(eval("(id \"a\")").unwrap(), "\"a\""); assert_eq!(eval("(id '(1 2 3))").unwrap(), "(1 2 3)"); } #[test] fn test_is() { assert_eq!(eval("(is 'integer 1)").unwrap(), "true"); assert_eq!(eval("(is 'ratio 1/1)").unwrap(), "true"); assert_eq!(eval("(is 'float 1.0)").unwrap(), "true"); assert_eq!(eval("(is 'list '(1))").unwrap(), "true"); assert_eq!(eval("(is 'list ())").unwrap(), "true"); assert_eq!(eval("(is 'unit ())").unwrap(), "true"); assert_eq!(eval("(is 'name 'a)").unwrap(), "true"); assert_eq!(eval("(is 'keyword :a)").unwrap(), "true"); assert_eq!(eval("(is 'string \"a\")").unwrap(), "true"); assert_eq!(eval("(is 'path #p\"a\")").unwrap(), "true"); assert_eq!(eval("(is 'bytes #b\"a\")").unwrap(), "true"); assert_eq!(eval("(is 'function is)").unwrap(), "true"); assert_eq!(eval("(is 'lambda (lambda () ()))").unwrap(), "true"); assert_eq!(eval("(is 'number 1)").unwrap(), "true"); assert_eq!(eval("(is 'number 1/1)").unwrap(), "true"); assert_eq!(eval("(is 'number 1.0)").unwrap(), "true"); assert_eq!(eval("(is 'integer 1.0)").unwrap(), "false"); assert_eq!(eval("(is 'list 'foo)").unwrap(), "false"); assert_eq!(eval("(is 'foo ())").unwrap(), "false"); } #[test] fn test_null() { assert_eq!(eval("(null ())").unwrap(), "true"); assert_eq!(eval("(null 'a)").unwrap(), "false"); assert_eq!(eval("(null 1)").unwrap(), "false"); } #[test] fn test_type_of() { assert_eq!(eval("(type-of ())").unwrap(), "unit"); assert_eq!(eval("(type-of true)").unwrap(), "bool"); assert_eq!(eval("(type-of 1)").unwrap(), "integer"); assert_eq!(eval("(type-of 1/1)").unwrap(), "ratio"); assert_eq!(eval("(type-of 1.0)").unwrap(), "float"); assert_eq!(eval("(type-of 'a)").unwrap(), "name"); assert_eq!(eval("(type-of :a)").unwrap(), "keyword"); assert_eq!(eval("(type-of #'a')").unwrap(), "char"); assert_eq!(eval("(type-of \"a\")").unwrap(), "string"); assert_eq!(eval("(type-of #p\"a\")").unwrap(), "path"); assert_eq!(eval("(type-of #b\"a\")").unwrap(), "bytes"); assert_eq!(eval("(type-of ''a)").unwrap(), "object"); assert_eq!(eval("(type-of '(1))").unwrap(), "list"); assert_eq!(eval("(type-of id)").unwrap(), "function"); assert_eq!(eval("(type-of (lambda () ()))").unwrap(), "lambda"); } #[test] fn test_define() { assert_eq!(run("(define foo 123) foo").unwrap(), ["foo", "123"]); // Standard names cannot be overriden in global scope assert_matches!(run("(define (=) ())").unwrap_err(), Error::CompileError(_)); assert_matches!(run("(define (if) ())").unwrap_err(), Error::CompileError(_)); assert_eq!(run("(define (foo n) (* n n)) (foo 3)").unwrap(), ["foo", "9"]); assert_eq!(run(" (define (foo a b :optional c d) (list a b c d)) (foo 1 2) (foo 1 2 3) (foo 1 2 3 4) ").unwrap(), ["foo", "(1 2 () ())", "(1 2 3 ())", "(1 2 3 4)"]); assert_eq!(run(" (define (foo a b :key c d) (list a b c d)) (foo 1 2) (foo 1 2 :c 3) (foo 1 2 :d 3) (foo 1 2 :c 3 :d 4) ").unwrap(), ["foo", "(1 2 () ())", "(1 2 3 ())", "(1 2 () 3)", "(1 2 3 4)"]); assert_eq!(run(" (define (foo a b :key (c 10) d) (list a b c d)) (foo 1 2) (foo 1 2 :c 3) (foo 1 2 :d 3) (foo 1 2 :c 3 :d 4) ").unwrap(), ["foo", "(1 2 10 ())", "(1 2 3 ())", "(1 2 10 3)", "(1 2 3 4)"]); assert_eq!(run(" (define (foo a b :key c (d 10)) (list a b c d)) (foo 1 2) (foo 1 2 :c 3) (foo 1 2 :d 3) (foo 1 2 :c 3 :d 4) ").unwrap(), ["foo", "(1 2 () 10)", "(1 2 3 10)", "(1 2 () 3)", "(1 2 3 4)"]); assert_eq!(run(" (define (foo a b :rest rest) (list a b rest)) (foo 1 2) (foo 1 2 3) (foo 1 2 3 4) ").unwrap(), ["foo", "(1 2 ())", "(1 2 (3))", "(1 2 (3 4))"]); assert_eq!(run(" (define (foo a b :optional c d :rest rest) (list a b c d rest)) (foo 1 2) (foo 1 2 3) (foo 1 2 3 4) (foo 1 2 3 4 5) (foo 1 2 3 4 5 6) ").unwrap(), ["foo", "(1 2 () () ())", "(1 2 3 () ())", "(1 2 3 4 ())", "(1 2 3 4 (5))", "(1 2 3 4 (5 6))"]); assert_eq!(run(" (define (foo a b :optional (c 10) d) (list a b c d)) (foo 1 2) (foo 1 2 3) (foo 1 2 3 4) ").unwrap(), ["foo", "(1 2 10 ())", "(1 2 3 ())", "(1 2 3 4)"]); assert_eq!(run(" (define (foo a b :optional c (d 10)) (list a b c d)) (foo 1 2) (foo 1 2 3) (foo 1 2 3 4) ").unwrap(), ["foo", "(1 2 () 10)", "(1 2 3 10)", "(1 2 3 4)"]); assert_eq!(run(" (define (foo a b :optional (c 10) (d 20) :rest rest) (list a b c d rest)) (foo 1 2) (foo 1 2 3) (foo 1 2 3 4) (foo 1 2 3 4 5) (foo 1 2 3 4 5 6) ").unwrap(), ["foo", "(1 2 10 20 ())", "(1 2 3 20 ())", "(1 2 3 4 ())", "(1 2 3 4 (5))", "(1 2 3 4 (5 6))"]); assert_matches!(run(" (define (foo a :optional b :key c) ()) ").unwrap_err(), Error::CompileError(CompileError::SyntaxError(_))); assert_matches!(run(" (define (foo a :key b :optional c) ()) ").unwrap_err(), Error::CompileError(CompileError::SyntaxError(_))); assert_matches!(run(" (define (foo a :key b :rest rest) ()) ").unwrap_err(), Error::CompileError(CompileError::SyntaxError(_))); } #[test] fn test_const_define() { // Test that a non-capturing lambda is available to macros at compile time assert_eq!(run(" (macro (foo) `(do ,(bar))) (define (bar) 123) (foo) ").unwrap(), ["foo", "bar", "123"]); } #[test] fn test_lambda() { assert_eq!(eval("((lambda (n) n) 1)").unwrap(), "1"); assert_eq!(eval("((lambda (:rest rest) rest) 1 2 3)").unwrap(), "(1 2 3)"); } #[test] fn test_do() { assert_eq!(eval("(do 1 2 3)").unwrap(), "3"); } #[test] fn test_macro() { assert_eq!(run(" (macro (quote a) `',a) (quote foo) ").unwrap(), ["quote", "foo"]); assert_matches!(eval("(define (foo a) (macro (bar) a))").unwrap_err(), Error::CompileError(_)); assert_matches!(run(" (macro (foo) '(bar)) (macro (bar) '(foo)) (foo) ").unwrap_err(), Error::CompileError(CompileError::MacroRecursionExceeded)); } #[test] fn test_apply() { assert_eq!(eval("(apply + '(1 2 3))").unwrap(), "6"); assert_eq!(eval("(apply + 1 2 3 '(4 5 6))").unwrap(), "21"); } #[test] fn test_call_self() { assert_eq!(eval(" (let ((factorial (lambda (n :optional (acc 1)) (if (<= n 1) acc (call-self (- n 1) (* n acc)))))) (factorial 10)) ").unwrap(), "3628800"); assert_eq!(run(" (define (factorial n :optional (acc 1)) (if (<= n 1) acc (call-self (- n 1) (* n acc)))) (factorial 9) ").unwrap(), ["factorial", "362880"]); assert_matches!(eval("(call-self 1)").unwrap_err(), Error::CompileError(CompileError::SyntaxError(_))); assert_matches!(eval("(if true (call-self))").unwrap_err(), Error::CompileError(CompileError::SyntaxError(_))); } #[test] fn test_panic() { assert_matches!(eval("(panic)").unwrap_err(), Error::ExecError(ExecError::Panic(None))); assert_matches!(eval("(panic 123)").unwrap_err(), Error::ExecError(ExecError::Panic(Some(Value::Integer(ref i)))) if i.to_u32() == Some(123)); assert_matches!(eval("(panic \"foo\")").unwrap_err(), Error::ExecError(ExecError::Panic(Some(Value::String(ref s)))) if s == "foo"); } #[test] fn test_use() { assert_eq!(run(" (use math (sqrt)) (sqrt 4.0) ").unwrap(), ["()", "2.0"]); assert_eq!(run(" (use math :all) (sqrt 4.0) ").unwrap(), ["()", "2.0"]); assert_eq!(run(" (use math :self) (math/sqrt 16.0) ").unwrap(), ["()", "4.0"]); assert_matches!(run(" (use math (does-not-exist)) ").unwrap_err(), Error::CompileError(CompileError::ImportError{..})); }