aboutsummaryrefslogtreecommitdiff
path: root/src/luax/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/luax/tests')
-rw-r--r--src/luax/tests/lexer.rs197
-rw-r--r--src/luax/tests/mod.rs2
-rw-r--r--src/luax/tests/preprocessor.rs198
3 files changed, 397 insertions, 0 deletions
diff --git a/src/luax/tests/lexer.rs b/src/luax/tests/lexer.rs
new file mode 100644
index 0000000..997dc2c
--- /dev/null
+++ b/src/luax/tests/lexer.rs
@@ -0,0 +1,197 @@
+use crate::luax::lexer::*;
+use crate::luax::tokens::*;
+use color_eyre::Result;
+
+fn compare_tokens<'a>(lua: &'a str, expected: Vec<Token<'a>>) -> Result<()> {
+ let mut lexer = Lexer::new(lua);
+ let mut tokens = Vec::new();
+ loop {
+ let token = lexer.next_token()?;
+ if token.is_none() {
+ break;
+ }
+ tokens.push(token.unwrap());
+ }
+
+ assert_eq!(tokens, expected);
+
+ Ok(())
+}
+
+#[test]
+fn empty() -> Result<()> {
+ compare_tokens("", vec![Token::Eof])
+}
+
+#[test]
+fn number() -> Result<()> {
+ compare_tokens("123", vec![Token::Number("123"), Token::Eof])
+}
+
+#[test]
+fn string() -> Result<()> {
+ compare_tokens(
+ "\"hello world\"",
+ vec![Token::String("hello world", StringType::Double), Token::Eof],
+ )
+}
+
+#[test]
+fn empty_string() -> Result<()> {
+ compare_tokens(
+ "\"\"",
+ vec![Token::String("", StringType::Double), Token::Eof],
+ )
+}
+
+#[test]
+fn all_string_types() -> Result<()> {
+ compare_tokens(
+ "\"hello world\"'hello world'[[hello world]]",
+ vec![
+ Token::String("hello world", StringType::Double),
+ Token::String("hello world", StringType::Single),
+ Token::String("hello world", StringType::DoubleBracket),
+ Token::Eof,
+ ],
+ )
+}
+
+#[test]
+fn table() -> Result<()> {
+ compare_tokens(
+ "{1, 2, 3}",
+ vec![
+ Token::OpenBrace,
+ Token::Number("1"),
+ Token::Comma,
+ Token::Number("2"),
+ Token::Comma,
+ Token::Number("3"),
+ Token::CloseBrace,
+ Token::Eof,
+ ],
+ )
+}
+
+#[test]
+fn table_with_string_keys() -> Result<()> {
+ compare_tokens(
+ "{[\"a\"]=1, [\"b\"]=2, [\"c\"]=3}",
+ vec![
+ Token::OpenBrace,
+ Token::OpenBracket,
+ Token::String("a", StringType::Double),
+ Token::CloseBracket,
+ Token::Eq,
+ Token::Number("1"),
+ Token::Comma,
+ Token::OpenBracket,
+ Token::String("b", StringType::Double),
+ Token::CloseBracket,
+ Token::Eq,
+ Token::Number("2"),
+ Token::Comma,
+ Token::OpenBracket,
+ Token::String("c", StringType::Double),
+ Token::CloseBracket,
+ Token::Eq,
+ Token::Number("3"),
+ Token::CloseBrace,
+ Token::Eof,
+ ],
+ )
+}
+
+#[test]
+fn operators() -> Result<()> {
+ compare_tokens(
+ "+ - * / // ^ % & ~ | << >> .. < <= > >= == ~= and or # not",
+ vec![
+ Token::Plus,
+ Token::Minus,
+ Token::Star,
+ Token::Slash,
+ Token::SlashSlash,
+ Token::Hat,
+ Token::Percent,
+ Token::Amp,
+ Token::Tilde,
+ Token::Pipe,
+ Token::LtLt,
+ Token::GtGt,
+ Token::DotDot,
+ Token::Lt,
+ Token::Le,
+ Token::Gt,
+ Token::Ge,
+ Token::EqEq,
+ Token::TildeEq,
+ Token::And,
+ Token::Or,
+ Token::Hash,
+ Token::Not,
+ Token::Eof,
+ ],
+ )
+}
+
+#[test]
+fn keywords() -> Result<()> {
+ compare_tokens(
+ "and break do else elseif end false for function goto if in local nil not or repeat return then true until while",
+ vec![
+ Token::And,
+ Token::Break,
+ Token::Do,
+ Token::Else,
+ Token::ElseIf,
+ Token::End,
+ Token::False,
+ Token::For,
+ Token::Function,
+ Token::Goto,
+ Token::If,
+ Token::In,
+ Token::Local,
+ Token::Nil,
+ Token::Not,
+ Token::Or,
+ Token::Repeat,
+ Token::Return,
+ Token::Then,
+ Token::True,
+ Token::Until,
+ Token::While,
+ Token::Eof,
+ ],
+ )
+}
+
+#[test]
+fn html() -> Result<()> {
+ compare_tokens(
+ "<div class=\"hello\">hello world</div><img src=\"hello.png\"/>",
+ vec![
+ Token::Lt,
+ Token::Identifier("div"),
+ Token::Identifier("class"),
+ Token::Eq,
+ Token::String("hello", StringType::Double),
+ Token::Gt,
+ Token::Identifier("hello"),
+ Token::Identifier("world"),
+ Token::OpenClosingTag,
+ Token::Identifier("div"),
+ Token::Gt,
+ Token::Lt,
+ Token::Identifier("img"),
+ Token::Identifier("src"),
+ Token::Eq,
+ Token::String("hello.png", StringType::Double),
+ Token::Slash,
+ Token::Gt,
+ Token::Eof,
+ ],
+ )
+}
diff --git a/src/luax/tests/mod.rs b/src/luax/tests/mod.rs
new file mode 100644
index 0000000..f2fed46
--- /dev/null
+++ b/src/luax/tests/mod.rs
@@ -0,0 +1,2 @@
+mod lexer;
+mod preprocessor;
diff --git a/src/luax/tests/preprocessor.rs b/src/luax/tests/preprocessor.rs
new file mode 100644
index 0000000..d5c2f42
--- /dev/null
+++ b/src/luax/tests/preprocessor.rs
@@ -0,0 +1,198 @@
+use crate::luax::{lexer::Lexer, tokens::Token, *};
+use color_eyre::Result;
+
+fn tokenize(input: &str) -> Result<Vec<Token>> {
+ let mut lexer = Lexer::new(input);
+ let mut tokens = Vec::new();
+
+ while let Some(token) = lexer.next_token()? {
+ tokens.push(token);
+ }
+
+ Ok(tokens)
+}
+
+fn compare_output(input: &str, expected: &str) -> Result<()> {
+ let output = preprocess(input)?;
+
+ // collect the tokens from each string
+ let expected_tokens = tokenize(expected)?;
+ let output_tokens = tokenize(&output)?;
+
+ if !expected_tokens
+ .iter()
+ .zip(output_tokens.iter())
+ .all(|(a, b)| a == b)
+ {
+ println!("expected: {:?}", expected_tokens);
+ println!("output: {:?}", output_tokens);
+ let first_mismatch = expected_tokens
+ .iter()
+ .zip(output_tokens.iter())
+ .enumerate()
+ .find(|(_, (a, b))| a != b)
+ .unwrap();
+ println!(
+ "first mismatch: index {}: {:?} != {:?}",
+ first_mismatch.0, first_mismatch.1.0, first_mismatch.1.1
+ );
+ println!("expected: {}", expected);
+ println!("output: {}", output);
+ panic!("output did not match expected");
+ }
+
+ Ok(())
+}
+
+#[test]
+fn empty() -> Result<()> {
+ compare_output("", "")
+}
+
+#[test]
+fn no_preprocessor() -> Result<()> {
+ compare_output("hello", "hello")
+}
+
+#[test]
+fn lua_function() -> Result<()> {
+ compare_output("function hello() end", "function hello ( ) end")
+}
+
+#[test]
+fn lua_function_with_args() -> Result<()> {
+ compare_output(
+ "function hello(a, b, c) end",
+ "function hello ( a , b , c ) end",
+ )
+}
+
+#[test]
+fn variable() -> Result<()> {
+ compare_output(
+ "local hello = 123 hello = 456",
+ "local hello = 123 hello = 456",
+ )
+}
+
+#[test]
+fn call() -> Result<()> {
+ compare_output("hello()", "hello ( )")
+}
+
+#[test]
+fn call_with_args() -> Result<()> {
+ compare_output("hello(1, 2, 3)", "hello ( 1 , 2 , 3 )")
+}
+
+#[test]
+fn call_with_args_and_assign() -> Result<()> {
+ compare_output("local a = hello(1, 2, 3)", "local a = hello ( 1 , 2 , 3 )")
+}
+
+#[test]
+fn call_with_string_arg() -> Result<()> {
+ compare_output("hello \"world\"", "hello \"world\"")
+}
+
+#[test]
+fn call_with_table_arg() -> Result<()> {
+ compare_output("hello {1, 2, 3}", "hello { 1 , 2 , 3 }")
+}
+
+#[test]
+fn long_call_and_access() -> Result<()> {
+ compare_output("a.b.c()[1].d()().e", "a . b . c ( ) [ 1 ] . d ( ) ( ) . e")
+}
+
+#[test]
+fn simple_html() -> Result<()> {
+ compare_output(
+ "return <div></div>",
+ "return { tag=\"div\", attrs={}, children={} }",
+ )
+}
+
+#[test]
+fn html_with_text() -> Result<()> {
+ compare_output(
+ "return <div>hello</div>",
+ "return { tag=\"div\", attrs={}, children={ \"hello\",} }",
+ )
+}
+
+#[test]
+fn html_with_weird_text() -> Result<()> {
+ compare_output(
+ "return <div>hello #world!</div>",
+ "return { tag=\"div\", attrs={}, children={ \"hello #world!\",} }",
+ )
+}
+
+#[test]
+fn html_with_attrs() -> Result<()> {
+ compare_output(
+ "return <div class=\"hello\" id=\"world\"></div>",
+ "return { tag=\"div\", attrs={class=\"hello\", id=\"world\", }, children={} }",
+ )
+}
+
+#[test]
+fn nested_html() -> Result<()> {
+ compare_output(
+ "return <div><span></span></div>",
+ "return { tag=\"div\", attrs={}, children={ { tag=\"span\", attrs={}, children={} },} }",
+ )
+}
+
+#[test]
+fn nested_html_with_text() -> Result<()> {
+ compare_output(
+ "return <div><span>hello</span></div>",
+ "return { tag=\"div\", attrs={}, children={ { tag=\"span\", attrs={}, children={ \"hello\",} },} }",
+ )
+}
+
+#[test]
+fn nested_html_with_attrs() -> Result<()> {
+ compare_output(
+ "return <div><span class=\"hello\" id=\"world\"></span></div>",
+ "return { tag=\"div\", attrs={}, children={ { tag=\"span\", attrs={class=\"hello\", id=\"world\", }, children={} },} }",
+ )
+}
+
+#[test]
+fn self_closing_tag() -> Result<()> {
+ compare_output(
+ "return <div><span /><span /></div>",
+ "return { tag=\"div\", attrs={}, children={ { tag=\"span\", attrs={}, children={} }, { tag=\"span\", attrs={}, children={} },} }",
+ )
+}
+
+#[test]
+fn site() -> Result<()> {
+ compare_output(
+ r#"
+ return <html>
+ <head>
+ <title>My Site</title>
+ </head>
+ <body>
+ <h1>My Site</h1>
+ <p>My site is the best site.</p>
+ </body>
+ </html>
+ "#,
+ r#"
+ return { tag="html", attrs={}, children={
+ { tag="head", attrs={}, children={
+ { tag="title", attrs={}, children={ "My Site",} },
+ } },
+ { tag="body", attrs={}, children={
+ { tag="h1", attrs={}, children={ "My Site",} },
+ { tag="p", attrs={}, children={ "My site is the best site.",} },
+ } },
+ } }
+ "#,
+ )
+}