#![cfg(test)]
use std::collections::{HashMap, HashSet};
use crate::{
errors::Error,
func::{AvidFunc, Builtin},
parser::{Lexer, Parser, TokenData},
stack::Stack,
typing::{Object, ObjectType},
Avid, Builder, ErrorKind, Location, Ast,
};
fn call_ops<T: AvidFunc>(ops: &mut [T]) -> crate::Result<Stack, ErrorKind> {
let mut stack = Stack::new();
for op in ops {
op.call(&mut stack)?;
}
Ok(stack)
}
#[test]
fn add() {
let mut ops = [Builtin::PushInt(34), Builtin::PushInt(35), Builtin::Sum];
let mut stack = call_ops(&mut ops).unwrap();
assert_eq!(Object::Num(69), stack.pop::<1>().unwrap()[0])
}
#[test]
fn subtract() {
let mut ops = [
Builtin::PushInt(34),
Builtin::PushInt(35),
Builtin::Subtract,
];
let mut stack = call_ops(&mut ops).unwrap();
assert_eq!(Object::Num(-1), stack.pop::<1>().unwrap()[0])
}
#[test]
fn not_enough_on_stack() {
let mut ops = [Builtin::Print];
let res = call_ops(&mut ops);
assert_eq!(
res,
Err(ErrorKind::NotEnoughArgs {
expected: 1,
got: 0
})
)
}
#[test]
fn incorrect_types() {
let mut ops = [
Builtin::PushInt(5),
Builtin::PushStr("kfdj".to_string()),
Builtin::Sum,
];
let res = call_ops(&mut ops);
assert_eq!(
res,
Err(ErrorKind::IncorrectType {
expected: &[ObjectType::Num, ObjectType::Num],
got: vec![ObjectType::Num, ObjectType::String]
})
)
}
#[test]
fn parses_correctly() {
let lexer = Lexer::new("1 2 3 - + -fgh -22 \"Hello, World!\" print", None);
let toks = lexer.map(|tok| tok.unwrap().data).collect::<Vec<_>>();
let expected = vec![
TokenData::Int(1),
TokenData::Int(2),
TokenData::Int(3),
TokenData::Promise("-".to_string()),
TokenData::Promise("+".to_string()),
TokenData::Promise("-fgh".to_string()),
TokenData::Int(-22),
TokenData::String("Hello, World!".to_string()),
TokenData::Promise("print".to_string()),
];
assert_eq!(toks, expected)
}
#[test]
fn handles_unfinished_string() {
let mut lexer = Lexer::new("\"hi", None);
let tok = lexer.next().unwrap();
assert_eq!(
tok,
Err(Error::new(
ErrorKind::UnclosedString,
Location {
line: 1,
col: 3,
file_name: None
}
))
)
}
#[test]
fn handles_incorrect_number() {
let mut lexer = Lexer::new("2a", None);
let tok = lexer.next().unwrap().map_err(|x| x.kind);
assert_eq!(tok, Err(ErrorKind::IncorrectNumber))
}
#[test]
fn runs_correctly() {
let src = "32 35 + 2 +";
let avid = Builder::new(src).build().unwrap();
let mut stack = avid.run(None).unwrap();
assert_eq!(stack.pop::<1>(), Ok([Object::Num(69)]))
}
#[test]
fn pop_untyped() {
let mut stack = Stack::new();
stack.push(Object::Num(1));
stack.push(Object::Num(2));
let res = stack.pop::<2>();
assert_eq!(res, Ok([Object::Num(1), Object::Num(2)]));
let res = stack.pop::<5>();
assert_eq!(
res,
Err(ErrorKind::NotEnoughArgs {
expected: 5,
got: 0
})
);
}
#[test]
fn pop_typed() {
let mut stack = Stack::new();
stack.push(Object::String("sus".to_string()));
stack.push(Object::Num(2));
const EXPECTED_TYPES: [ObjectType; 2] = [ObjectType::Num, ObjectType::Num];
let res = stack.pop_typed(&EXPECTED_TYPES);
assert_eq!(
res,
Err(ErrorKind::IncorrectType {
expected: &EXPECTED_TYPES,
got: vec![ObjectType::String, ObjectType::Num]
})
);
let res = stack.pop::<5>();
assert_eq!(
res,
Err(ErrorKind::NotEnoughArgs {
expected: 5,
got: 2
})
);
}
#[test]
fn lexer_unclosed_string() {
let mut lexer = Lexer::new("\"Hi", None);
let res = lexer.next().map(|x| x.map_err(|e| e.kind));
assert_eq!(res, Some(Err(ErrorKind::UnclosedString)))
}
#[test]
fn lexer_string() {
let mut lexer = Lexer::new("\"Hi\"", None);
let res = lexer.next().map(|x| x.map(|t| t.data));
assert_eq!(res, Some(Ok(TokenData::String("Hi".to_string()))))
}
#[test]
fn lexer_number() {
let lexer = Lexer::new("420 -69 0b101 0o11 0xF6 0", None);
let res: Vec<TokenData> = lexer.map(|r| r.unwrap().data).collect();
let expected: Vec<TokenData> = vec![420, -69, 5, 9, 246, 0]
.into_iter()
.map(TokenData::Int)
.collect();
assert_eq!(res, expected);
}
#[test]
fn lexer_bad_number() {
let mut lexer = Lexer::new("5zgy", None);
let res = lexer.next().unwrap().unwrap_err().kind;
assert_eq!(res, ErrorKind::IncorrectNumber);
}
#[test]
fn handles_unknown_var() {
let src = "amogus";
let provided = HashMap::new();
let parser = Parser::<Ast>::new(src, None, HashSet::new(), provided);
let res = parser.parse().map_err(|e| e.kind).unwrap_err();
assert_eq!(
res,
ErrorKind::UnknownVar {
name: "amogus".to_string()
}
)
}
#[test]
fn handles_unmatched_end() {
let src = "end";
let res = Builder::new(src).build().map_err(|e| e.kind);
assert_eq!(res, Err(ErrorKind::UnpairedEnd))
}
#[test]
fn handles_basic_if() {
let src = "1 true if 2 end false if 3 end";
let avid = Builder::new(src).build().unwrap();
let stack = avid.run(None).unwrap().into_objects();
assert_eq!(stack, vec![Object::Num(1), Object::Num(2)])
}
#[test]
fn unclosed_if() {
let src = "true if";
let res = Builder::new(src).build().map_err(|e| e.kind);
assert_eq!(res, Err(ErrorKind::UnclosedIf));
}
#[test]
fn parse_into_ast() {
let src = "push-a print";
let a = "Hello from the other thread!!".to_string();
let ast = Builder::new_ast(src)
.register_fn("push-a", |s: &mut Stack| {
let b = &a;
s.push(Object::String(b.clone()));
Ok(())
})
.build()
.unwrap();
Avid::from_ast(ast).run(None).unwrap();
}
#[test]
fn test_error_reporting() {
let src = [
"+",
"1 \n\"b\" +",
"\n\n\njb",
"unknown",
"end",
"if",
"while\n do\n",
"while",
"do",
"\"",
"8fjdd",
"\\j"
];
fn map_fn(line: &str) -> crate::Result<()> {
Builder::new(line)
.src_name("example")
.promise("unknown")
.build()?
.run(None)?;
Ok(())
}
let res = src
.into_iter()
.map(|line| {
format!("{}", map_fn(line).unwrap_err())
})
.collect::<Vec<_>>();
let expected = [
"example:1:1: Error: Not Enough Arguments: expected 2, got 0!\n",
"example:2:5: Error: Incorrect Argument Type: expected [Num, Num], got [Num, String]!\n",
"example:4:1: Error: Unknown Variable: \"jb\"!\n",
"example:1:1: Error: Unfulfilled Promise: \"unknown\"!\nNote: This is ususally the fault of the program interpreting the code. You should file a bug report if possible.\n",
"example:1:1: Error: `End` Without Start of Block!\n",
"example:1:1: Error: `if` Statement Without Matching `end`!\n",
"example:2:2: Error: `while` Statement Without Matching `end`!\n",
"example:1:1: Error: `while` Statement Without Matching `end`!\n",
"example:1:1: Error: `while` Statement Without Matching `end`!\n",
"example:1:1: Error: Unclosed String!\n",
"example:1:1: Error: Unparseable Number!\n",
"example:1:1: Error: Unknown Variable: \"\\j\"!\n",
].into_iter().map(String::from).collect::<Vec<_>>();
assert_eq!(res, expected, "\nRes: {:#?}\nExpected: {:#?}", res, expected)
}