use crate::utils::*;
use crate::value;
use crate::value::*;
use std::fs::File;
use std::io::Read;
use std::process::Command;
pub(crate) fn testcase(filename: &'static str) {
let mut code = String::new();
match File::open(filename) {
Ok(mut file) => {
file.read_to_string(&mut code).unwrap();
}
Err(error) => {
panic!("Error opening file {}: {}", filename, error);
}
}
if let Some((_, data)) = code.split_once("#---\n") {
let mut params = vec![filename];
let mut result = data;
let tmp;
if let Some((i, o)) = data.split_once("#---\n") {
let input: Vec<&str> = i
.split("\n")
.filter(|line| line.starts_with("#"))
.map(|line| &line[1..])
.collect();
tmp = input.join("\n");
params.extend(vec!["--", &tmp]);
result = o;
}
let program = "target/debug/tokay";
let output = Command::new(program)
.args(¶ms)
.output()
.expect(&format!(
"Failed to run '{} {}'; You have to `cargo run` first!",
program, filename
));
let mut out: Vec<String> = String::from_utf8(output.stdout)
.expect("Not UTF-8")
.split("\n")
.map(|line| line.to_string())
.collect();
let mut err: Vec<String> = String::from_utf8(output.stderr)
.expect("Not UTF-8")
.split("\n")
.map(|line| line.to_string())
.collect();
println!("out = {:?}", out);
println!("err = {:?}", err);
for line in result.trim().split("\n").into_iter() {
assert!(
line.starts_with("#"),
"Lines in result must start with a #-comment"
);
if line.starts_with("#ERR:") {
assert_eq!(err.remove(0), line[5..].to_string());
} else {
assert_eq!(out.remove(0), line[1..].to_string());
}
}
assert!(out.len() == 1, "Some output not consumed: {:?}", out);
assert!(err.len() == 1, "Some errors not consumed: {:?}", err);
} else {
panic!("Testcase invalid, require for a '#---' delimiter.")
}
}
#[test]
fn literal() {
assert_eq!(
run(
"\
1337 \
23.5 \
true \
false \
\"Hello World\" \
",
""
),
Ok(Some(value!([1337, 23.5, true, false, "Hello World"])))
);
}
#[test]
fn expression() {
assert_eq!(
run(
"\
1 + 2 \
100 - 99 \
7 * 3 \
21 / 3 \
1 + 2 * 3 + 4 \
",
""
),
Ok(Some(value!([3, 1, 21, 7, 11])))
);
assert_eq!(
run(
"\
.2 + .3 \
1.9 - 1.1 \
2.1 * 6.5 \
13.6 / 15.1 \
1.1 + 2.2 * 3.3 + 4.4 \
",
""
),
Ok(Some(value!([
0.5,
0.7999999999999998,
13.65,
0.9006622516556292,
12.76
])))
);
assert_eq!(
run(
"\
\"a\" + \"b\" \
\"a\" + 2 \
\"a\" + 1.337 \
\"a\" + \"b\" * 3 \
\"a\" * 9 \
\"a\" * 3.3 \
",
""
),
Ok(Some(value!([
"ab",
"a2",
"a1.337",
"abbb",
"aaaaaaaaa",
"aaa"
])))
);
assert_eq!(
run(
"\
x = 42 \
42 == 42 \
x == 42 \
x == x \
42 != 42 \
x != 42 \
x != x \
42 != 23 \
42 == 23 \
1.3 != 3.7 \
1.3 == 3.7 \
1.12345 == 1.12345 \
1.12345 != 1.12345 \
\"a\" == \"a\" \
\"a\" != \"a\" \
\"a\" != \"a\" * 2 \
\"a\" == \"a\" * 2 \
",
""
),
Ok(Some(value!([
true, true, true, false, false, false, true, false, true, false, true, false, true,
false, true, false
])))
);
assert_eq!(
run(
"\
42 >= 42 \
42 <= 42 \
42 < 42 \
42 > 42 \
\
42.23 >= 42.23 \
42.23 <= 42.23 \
42.23 < 42.23 \
42.23 > 42.23 \
\
\"42.23\" >= 42.23 \
\"42.23\" <= 42.23 \
\"42.23\" < 42.23 \
\"42.23\" > 42.23 \
",
""
),
Ok(Some(value!([
true, true, false, false, true, true, false, false, true, false, false, true
])))
);
assert_eq!(
run(
"\
a = true
b = false
\
a && a \
a && b \
a || b \
b || a \
\
b || a && a \
b || b && a \
b && b
",
""
),
Ok(Some(value!([true, false, true, true, true, false, false])))
);
assert_eq!(
run(
"\
-1 \
(-(-1)) \
!true \
!!true
",
""
),
Ok(Some(value![[(-1), 1, false, true]]))
);
}
#[test]
fn operations() {
assert_eq!(
run(
"
a = true a \
a + 2 == 3 \
a += 30 a \
a -= 9 a \
a *= 3 a \
a /= 6 a \
a /= 2 a \
a *= 10 a
",
""
),
Ok(Some(value![[true, true, 31, 22, 66, 11, 5.5, 55.0]]))
);
assert_eq!(
run(
"
a = 1 \
a \
a++ \
++a \
a++ a++ \
a++ + ++a \
a-- - --a \
a-- - - --a \
a",
""
),
Ok(Some(value![[1, 1, 3, 3, 4, 12, 2, 8, 3]]))
);
assert_eq!(
run(
"
i = 2 \
i *= i \
i
",
""
),
Ok(Some(value!(4)))
);
assert_eq!(
run(
"
a = (1,2) \
b = (3,4) \
a + b \
a \
b \
a += b \
a \
b
",
""
),
Ok(Some(value!([
[1, 2, 3, 4],
[1, 2],
[3, 4],
[1, 2, 3, 4],
[3, 4]
])))
);
}
#[test]
fn variables() {
assert_eq!(
run(
"
a = b = 10
a++
a b
",
""
),
Ok(Some(value![[11, 10]]))
);
assert_eq!(
run(
"
f : @{
a = b = 10
a++
a b
}
f
",
""
),
Ok(Some(value![[11, 10]]))
);
assert_eq!(
run(
"
10 20 $1 = $2 = 30 ++$1 $2
",
""
),
Ok(Some(value![[31, 30, 31, 30]]))
);
assert_eq!(
run(
"
a => 10 b => 20 $a = $b = 30 c => ++$a d => $b
",
""
),
Ok(Some(value![["a" => 31, "b" => 30, "c" => 31, "d" => 30]]))
);
}
#[test]
fn scoping() {
assert_eq!(
run(include_str!("../tests/test_scopes.tok"), ""),
Ok(Some(value![[10, 2000, 1072]]))
);
}
#[test]
fn collections() {
assert_eq!(
run(
"
(1 2 3) \
(1, 2, 3) \
(42, \"Hello\", 23.5, true, false)
",
""
),
Ok(Some(value!([
[1, 2, 3],
[1, 2, 3],
[42, "Hello", 23.5, true, false]
])))
);
assert_eq!(
run(
"
x = 10
(a => 1 b => 2 c => 3) \
(a => 1, b => 2, c => 3) \
(a => 42, x * 2 => \"Hello\", c => 23.5)
",
""
),
Ok(Some(value!([
["a" => 1, "b" => 2, "c" => 3],
["a" => 1, "b" => 2, "c" => 3],
["a" => 42, "20" => "Hello", "c" => 23.5]
])))
);
}
#[test]
fn token_modifiers() {
let s = "ab abbb bb 123 ABC 456 'def'";
assert_eq!(
run("'a' 'b'", s),
Ok(Some(value![[["a", "b"], ["a", "b"]]]))
);
assert_eq!(run("'a' ''b''", s), Ok(Some(value![["b", "b"]])));
assert_eq!(
run("'a' ''b''+", s),
Ok(Some(value![["b", ["b", "b", "b"]]]))
);
assert_eq!(
run("''a''* ''b''+", s),
Ok(Some(value![[
["a", "b"],
["a", ["b", "b", "b"]],
["b", "b"]
]]))
);
assert_eq!(
run("'a'* ''b''+", s),
Ok(Some(value![["b", ["b", "b", "b"], ["b", "b"]]]))
);
assert_eq!(run("[a-z]", &s[..2]), Ok(Some(value![["a", "b"]])));
assert_eq!(
run("[a-z]+", s),
Ok(Some(value![["ab", "abbb", "bb", "def"]]))
);
assert_eq!(
run("[^ ']+", s),
Ok(Some(value![[
"ab", "abbb", "bb", "123", "ABC", "456", "def"
]]))
);
assert_eq!(run("Int", s), Ok(Some(value![[123, 456]])));
assert_eq!(
run("''a'' {''b'' ''c''}* ''d''", "abcbcd"),
Ok(Some(value![["a", [["b", "c"], ["b", "c"]], "d"]]))
);
assert_eq!(
run("''a'' {''b'' ''c''}+ ''d''", "abcbcd"),
Ok(Some(value![["a", [["b", "c"], ["b", "c"]], "d"]]))
);
assert_eq!(
run("''a'' {''b'' ''c''}* ''d''", "ad"),
Ok(Some(value![["a", "d"]]))
);
assert_eq!(run("''a'' {''b'' ''c''}+ ''d''", "ad"), Ok(None));
assert_eq!(
run("{ Word { ',' _ }? }+", "Hello, World, Beta, Test"),
Ok(Some(value![["Hello", "World", "Beta", "Test"]]))
);
}
#[test]
fn examples() {
assert_eq!(
run(
include_str!("../examples/planets.tok"),
"Mercury Venus Earth Mars"
),
Ok(Some(value!([
"Hello Mercury",
"Hello Venus",
"Hello World",
"Hello Mars"
])))
);
assert_eq!(
run(include_str!("../examples/expr.tok"), "1+2*3+4"),
Ok(Some(value!(11)))
);
assert_eq!(
run(include_str!("../examples/expr_with_ast.tok"), "1+2*3+4"),
Ok(None)
);
assert_eq!(
run(
include_str!("../examples/expr_with_spaces.tok"),
"1 + \t 2 \n * 3 + 4"
),
Ok(Some(value!(11)))
);
assert_eq!(
run(include_str!("../examples/faculty.tok"), ""),
Ok(Some(value!(24)))
);
testcase("tests/test_piped_grammar.tok");
}
#[test]
fn if_else() {
assert_eq!(
run(
"
if true 1 \
if false 2 \
if $1 3 else 4 \
if !$2 5 else 6",
""
),
Ok(Some(value!([1, 3, 5])))
);
assert_eq!(
run(
"
b = true
nb = false
if b 1 \
if nb 2 \
if $1 3 else 4 \
if !$2 5 else 6",
""
),
Ok(Some(value!([1, 3, 5])))
);
testcase("tests/test_if.tok");
}
#[test]
fn push_next() {
assert_eq!(
run(
"
1 2 3 next
4 5 6 push 7
",
""
),
Ok(Some(value!(7)))
);
}
#[test]
fn loops() {
testcase("tests/test_loop.tok");
testcase("tests/test_for.tok");
testcase("tests/err_break_continue.tok");
}