extern crate erl_parse;
extern crate erl_pp;
extern crate erl_tokenize;
#[macro_use]
extern crate trackable;
use erl_parse::cst::{Expr, Form, Pattern, Type};
use erl_parse::{Parser, TokenReader};
use erl_pp::Preprocessor;
use erl_tokenize::{Lexer, PositionRange};
macro_rules! parse_expr {
($text:expr) => {
let mut parser = Parser::new(TokenReader::new(Preprocessor::new(Lexer::new($text))));
let value: Expr = track_try_unwrap!(parser.parse(), "text={:?}", $text);
assert_eq!(value.end_position().offset(), $text.len());
};
}
macro_rules! parse_pattern {
($text:expr) => {
let mut parser = Parser::new(TokenReader::new(Preprocessor::new(Lexer::new($text))));
let value: Pattern = track_try_unwrap!(parser.parse(), "text={:?}", $text);
assert_eq!(value.end_position().offset(), $text.len());
};
}
macro_rules! parse_type {
($text:expr) => {
let mut parser = Parser::new(TokenReader::new(Preprocessor::new(Lexer::new($text))));
let value: Type = track_try_unwrap!(parser.parse(), "text={:?}", $text);
assert_eq!(value.end_position().offset(), $text.len());
};
}
macro_rules! parse_form {
($text:expr) => {
let mut parser = Parser::new(TokenReader::new(Preprocessor::new(Lexer::new($text))));
let value: Form = track_try_unwrap!(parser.parse(), "text={:?}", $text);
assert_eq!(value.end_position().offset(), $text.len());
};
}
#[test]
fn parse_expr_works() {
parse_expr!("foo");
parse_expr!("$c");
parse_expr!("1.2");
parse_expr!("123");
parse_expr!(r#""foo""#);
parse_expr!(r#""foo" "bar" "baz""#);
parse_expr!("Foo");
parse_expr!("{}");
parse_expr!("{1}");
parse_expr!("{1, 2, 3}");
parse_expr!("#{}");
parse_expr!("#{a => b}");
parse_expr!("#{a := b}");
parse_expr!("#{a => b, 1 := 2}");
parse_expr!("M#{}");
parse_expr!("(#{a => 10})#{a := b, 1 => 2}");
parse_expr!("#foo{}");
parse_expr!("#foo{a = b}");
parse_expr!("#foo{a = b, _ = 10}");
parse_expr!("R#foo{bar = 10}");
parse_expr!("(#foo{})#foo{bar = 10, baz = 20}");
parse_expr!("R#foo.bar");
parse_expr!("(#foo{})#foo.bar");
parse_expr!("#foo.bar");
parse_expr!("[]");
parse_expr!("[1]");
parse_expr!("[1, 2, 3]");
parse_expr!("[1 | 2]");
parse_expr!("[1, 2 | 3]");
parse_expr!("[x || _ <- [1,2,3]]");
parse_expr!("[x || X <- [1,2,3], filter(X), _ <= <<1,2,3>>]");
parse_expr!("<<>>");
parse_expr!("<<10>>");
parse_expr!("<<1, 2, 3>>");
parse_expr!("<<100:2>>");
parse_expr!("<<1/little>>");
parse_expr!("<<1:2/little-unit:8>>");
parse_expr!("<< <<x>> || _ <- [1,2,3]>>");
parse_expr!("<< <<x>> || X <- [1,2,3], filter(X), _ <= <<1,2,3>> >>");
parse_expr!("( 1 )");
parse_expr!("foo()");
parse_expr!("Foo(1)");
parse_expr!(r#"(list_to_atom("foo"))(1, 2, [3])"#);
parse_expr!("foo:bar()");
parse_expr!("Foo:Bar(1)");
parse_expr!(r#"(list_to_atom("foo")):bar(1, 2, [3])"#);
parse_expr!("fun foo/2");
parse_expr!("fun foo:bar/2");
parse_expr!("fun Foo:Bar/Baz");
parse_expr!("fun () -> ok end");
parse_expr!("fun (a) -> ok; (B) -> err end");
parse_expr!("fun (a) when true -> ok; (B) -> err end");
parse_expr!("fun Foo() -> ok end");
parse_expr!("fun Foo(a) -> ok; Foo(B) -> err end");
parse_expr!("fun Foo(a) when true -> Foo(b); Foo(B) -> err end");
parse_expr!("+10");
parse_expr!("-20");
parse_expr!("not false");
parse_expr!("bnot Foo");
parse_expr!("1 =:= 2");
parse_expr!("Pid ! [1, 2] ++ [3] -- [1]");
parse_expr!("foo() ++ bar()");
parse_expr!("1 = 2");
parse_expr!("1 = 2 = 3");
parse_expr!("[A, 2, {}] = [10 | B]");
parse_expr!("begin 1, 2, 3 end");
parse_expr!("catch [1,2,3]");
parse_expr!("if true -> 1, 2, 3 end");
parse_expr!("if true; false, true -> 1, 2, 3 end");
parse_expr!("if true -> 1; false -> 2; _ -> ok end");
parse_expr!("case 1 of 2 -> 3 end");
parse_expr!("case 1 of 2 -> 3; _ -> ok end");
parse_expr!("case 1 of A when A == 2; true; 1, 2, false -> 3 end");
parse_expr!("receive foo -> bar end");
parse_expr!("receive Foo when Foo == 2 -> bar; 1 -> 2 end");
parse_expr!("receive Foo when Foo == 2 -> bar; 1 -> 2 after 10 * 2 -> foo(), done end");
parse_expr!("try foo, bar catch baz -> 1; _ -> qux end");
parse_expr!("try foo, bar of 10 -> 2; _ -> 30 catch baz -> 1; _:_ -> qux end");
parse_expr!("try foo, bar after baz, qux end");
parse_expr!("try foo of _ -> 1 catch throw:_ -> ok end");
parse_expr!("try foo catch _:Expr:StackTrace -> ok end");
parse_expr!("try foo of _ -> 1 after ok end");
parse_expr!("try foo of _ -> 1 catch _ -> err after ok end");
}
#[test]
fn parse_pattern_works() {
parse_pattern!("foo");
parse_pattern!("$c");
parse_pattern!("1.2");
parse_pattern!("123");
parse_pattern!(r#""foo""#);
parse_pattern!("Foo");
parse_pattern!("<<>>");
parse_pattern!("<<10>>");
parse_pattern!("<<1, 2, 3>>");
parse_pattern!("<<100:2>>");
parse_pattern!("<<1/little>>");
parse_pattern!("<<1:2/little-unit:8>>");
parse_pattern!("[]");
parse_pattern!("[1]");
parse_pattern!("[1, 2, 3]");
parse_pattern!("[1 | 2]");
parse_pattern!("[1, 2 | 3]");
parse_pattern!("#{}");
parse_pattern!("#{a := b}");
parse_pattern!("#{a := B, 1 := 2}");
parse_pattern!("{}");
parse_pattern!("{1}");
parse_pattern!("{1, 2, 3}");
parse_pattern!("+10");
parse_pattern!("-20");
parse_pattern!("[1] ++ [2,3]");
parse_pattern!("( [1,2,3] )");
parse_pattern!("#foo{}");
parse_pattern!("#foo{a = b}");
parse_pattern!("#foo{a = b, _ = 10}");
parse_pattern!("#foo.bar");
parse_pattern!("{A, B = 2, 3} = {1, 2, 3}");
}
#[test]
fn parse_type_works() {
parse_type!("10");
parse_type!("-10");
parse_type!("(3)");
parse_type!("(10 - 2)");
parse_type!("1 + 2 - 3 rem 4");
parse_type!("(1 + 2) - 3");
parse_type!("1 + (2 - -3)");
parse_type!("0..10");
parse_type!("-10..+10");
parse_type!("(1 + 2)..(10 * 30 - 1)");
parse_type!("A :: 10");
parse_type!("[]");
parse_type!("[foo]");
parse_type!("[foo, ...]");
parse_type!("([10])");
parse_type!("{1, 2, 3}");
parse_type!("#{a => 10, b := 20}");
parse_type!("#foo{bar = integer()}");
parse_type!("<<>>");
parse_type!("<<_:1>>");
parse_type!("<<_:_*3>>");
parse_type!("<<_:10,_:_*3>>");
parse_type!("foo()");
parse_type!("foo:bar(1,2,3)");
parse_type!("fun ()");
parse_type!("fun ((...) -> number())");
parse_type!("fun ((A, b) -> c:d())");
parse_type!("10 | 1 + 2 | (foo | {a, b, c}) | baz");
}
#[test]
fn parse_form_works() {
parse_form!("-module(foo).");
parse_form!("-export([]).");
parse_form!("-export([foo/0, bar/2]).");
parse_form!("-export_type([foo/0]).");
parse_form!("-export_type([foo/0, bar/2]).");
parse_form!("-import(foo, []).");
parse_form!("-import(foo, [bar/0, baz/5]).");
parse_form!(r#"-file("/path/to/file", 10)."#);
parse_form!("-my_attr([1, {2, 3}, #{}]).");
parse_form!("-spec foo () -> ok.");
parse_form!("-spec foo (a) -> ok; (b) -> err.");
parse_form!("-spec foo (a) -> ok; (B) -> err when B :: integer().");
parse_form!("-spec foo (A) -> ok when A :: integer(), is_subtype(A, list()).");
parse_form!("-spec foo:bar () -> ok.");
parse_form!("-spec foo:bar (a) -> ok when B :: integer(); (B) -> err.");
parse_form!("-callback foo () -> ok.");
parse_form!("-callback foo (a) -> ok; (b) -> err.");
parse_form!("-callback foo (a) -> ok; (B) -> err when B :: integer().");
parse_form!("foo () -> ok.");
parse_form!("foo (A, {B, _}) -> A + B.");
parse_form!("foo (A) when is_integer(A) -> ok; foo (B) -> {error, B}.");
parse_form!("-record(foo, {}).");
parse_form!("-record(foo, {a, b, c}).");
parse_form!("-record(foo, {a = 10, b :: integer(), c = d :: atom()}).");
parse_form!("-type foo() :: integer().");
parse_form!("-type foo(A, B) :: {A, B}.");
parse_form!("-opaque foo() :: integer().");
}