2022-07-08 01:31:00 +03:00
|
|
|
//! Parse expressions and compare their results to expected values.
|
|
|
|
|
2022-10-05 07:45:31 +03:00
|
|
|
// === Features ===
|
|
|
|
#![feature(let_else)]
|
2022-07-08 01:31:00 +03:00
|
|
|
// === Non-Standard Linter Configuration ===
|
|
|
|
#![allow(clippy::option_map_unit_fn)]
|
|
|
|
#![allow(clippy::precedence)]
|
|
|
|
#![allow(dead_code)]
|
|
|
|
#![deny(non_ascii_idents)]
|
|
|
|
#![deny(unconditional_recursion)]
|
|
|
|
#![warn(unsafe_code)]
|
|
|
|
#![warn(missing_copy_implementations)]
|
|
|
|
#![warn(missing_debug_implementations)]
|
|
|
|
#![warn(missing_docs)]
|
|
|
|
#![warn(trivial_casts)]
|
|
|
|
#![warn(trivial_numeric_casts)]
|
|
|
|
#![warn(unused_import_braces)]
|
|
|
|
#![warn(unused_qualifications)]
|
|
|
|
|
2022-09-14 21:09:58 +03:00
|
|
|
|
|
|
|
|
2022-09-03 06:15:27 +03:00
|
|
|
mod metadata;
|
|
|
|
|
2022-07-08 01:31:00 +03:00
|
|
|
use lexpr::sexp;
|
2022-07-20 17:53:20 +03:00
|
|
|
use lexpr::Value;
|
2022-07-08 01:31:00 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ===========================
|
|
|
|
// === Test support macros ===
|
|
|
|
// ===========================
|
|
|
|
|
2022-07-20 17:53:20 +03:00
|
|
|
/// Parses input as a sequence of S-expressions, and wraps it in a `BodyBlock`.
|
2022-07-08 01:31:00 +03:00
|
|
|
macro_rules! block {
|
2022-07-20 17:53:20 +03:00
|
|
|
( $($statements:tt)* ) => {
|
|
|
|
sexp![(BodyBlock #( $( $statements )* ) )]
|
2022-07-08 01:31:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// =============
|
|
|
|
// === Tests ===
|
|
|
|
// =============
|
|
|
|
|
2022-07-20 17:53:20 +03:00
|
|
|
#[test]
|
|
|
|
fn nothing() {
|
|
|
|
test("", block![()]);
|
|
|
|
}
|
|
|
|
|
2022-07-08 01:31:00 +03:00
|
|
|
#[test]
|
|
|
|
fn application() {
|
|
|
|
test("a b c", block![(App (App (Ident a) (Ident b)) (Ident c))]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-10-05 07:45:31 +03:00
|
|
|
fn parentheses() {
|
|
|
|
test("(a b)", block![(Group (App (Ident a) (Ident b)))]);
|
|
|
|
test("x)", block![(App (Ident x) (Invalid))]);
|
|
|
|
test("(x", block![(App (Invalid) (Ident x))]);
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(Group
|
|
|
|
(App (Group (App (Ident a) (Ident b)))
|
|
|
|
(Ident c)))];
|
|
|
|
test("((a b) c)", expected);
|
2022-07-20 17:53:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn section_simple() {
|
2022-10-05 07:45:31 +03:00
|
|
|
let expected_lhs = block![(OprSectionBoundary 1 (OprApp () (Ok "+") (Ident a)))];
|
2022-07-20 17:53:20 +03:00
|
|
|
test("+ a", expected_lhs);
|
2022-10-05 07:45:31 +03:00
|
|
|
let expected_rhs = block![(OprSectionBoundary 1 (OprApp (Ident a) (Ok "+") ()))];
|
2022-07-20 17:53:20 +03:00
|
|
|
test("a +", expected_rhs);
|
|
|
|
}
|
|
|
|
|
2022-07-08 01:31:00 +03:00
|
|
|
#[test]
|
2022-07-28 20:17:33 +03:00
|
|
|
fn comments() {
|
2022-10-05 07:45:31 +03:00
|
|
|
test("# a b c", block![()()]);
|
2022-07-28 20:17:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === Type Definitions ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn type_definition_no_body() {
|
2022-10-05 07:45:31 +03:00
|
|
|
test("type Bool", block![(TypeDef type Bool #() #() #())]);
|
|
|
|
test("type Option a", block![(TypeDef type Option #((() (Ident a) () ())) #() #())]);
|
|
|
|
test("type Option (a)", block![
|
|
|
|
(TypeDef type Option #((() (Ident a) () ())) #() #())]);
|
|
|
|
test("type Foo (a : Int)", block![
|
|
|
|
(TypeDef type Foo #((() (Ident a) (":" (Ident Int)) ())) #() #())]);
|
|
|
|
test("type A a=0", block![
|
|
|
|
(TypeDef type A #((() (Ident a) () ("=" (Number () "0" ())))) #() #())]);
|
|
|
|
test("type Existing_Headers (column_names : Vector Text)", block![
|
|
|
|
(TypeDef type Existing_Headers #(
|
|
|
|
(() (Ident column_names) (":" (App (Ident Vector) (Ident Text))) ())) #() #())]);
|
2022-07-28 20:17:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn type_constructors() {
|
|
|
|
let code = [
|
|
|
|
"type Geo",
|
|
|
|
" Circle",
|
|
|
|
" radius",
|
2022-09-14 21:09:58 +03:00
|
|
|
" x",
|
2022-07-28 20:17:33 +03:00
|
|
|
" Rectangle width height",
|
|
|
|
" Point",
|
|
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(TypeDef type Geo #()
|
|
|
|
#(((Circle #() #(((() (Ident radius) () ())) ((() (Ident x) () ())))))
|
|
|
|
((Rectangle #((() (Ident width) () ()) (() (Ident height) () ())) #()))
|
2022-07-28 20:17:33 +03:00
|
|
|
((Point #() #())))
|
|
|
|
#())
|
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn type_methods() {
|
2022-09-14 21:09:58 +03:00
|
|
|
let code = ["type Geo", " number =", " x", " area self = x + x"];
|
2022-07-28 20:17:33 +03:00
|
|
|
#[rustfmt::skip]
|
2022-10-05 07:45:31 +03:00
|
|
|
let expected = block![
|
|
|
|
(TypeDef type Geo #() #()
|
2022-09-14 21:09:58 +03:00
|
|
|
#((Function number #() "=" (BodyBlock #((Ident x))))
|
2022-09-15 08:32:28 +03:00
|
|
|
(Function area #((() (Ident self) () ())) "=" (OprApp (Ident x) (Ok "+") (Ident x)))))
|
2022-07-28 20:17:33 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
2022-07-08 01:31:00 +03:00
|
|
|
}
|
|
|
|
|
2022-10-05 07:45:31 +03:00
|
|
|
#[test]
|
|
|
|
fn type_operator_methods() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let code = [
|
|
|
|
"type Foo",
|
|
|
|
" + : Foo -> Foo -> Foo",
|
|
|
|
" + self b = b",
|
|
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(TypeDef type Foo #() #()
|
|
|
|
#((TypeSignature #"+" ":"
|
|
|
|
(OprApp (Ident Foo) (Ok "->") (OprApp (Ident Foo) (Ok "->") (Ident Foo))))
|
|
|
|
(Function #"+" #((() (Ident self) () ()) (() (Ident b) () ()))
|
|
|
|
"=" (Ident b))))];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
2022-07-28 20:17:33 +03:00
|
|
|
#[test]
|
|
|
|
fn type_def_full() {
|
|
|
|
let code = [
|
|
|
|
"type Geo",
|
|
|
|
" Circle",
|
|
|
|
" radius : float",
|
2022-09-14 21:09:58 +03:00
|
|
|
" x",
|
2022-07-28 20:17:33 +03:00
|
|
|
" Rectangle width height",
|
|
|
|
" Point",
|
|
|
|
"",
|
|
|
|
" number =",
|
2022-09-14 21:09:58 +03:00
|
|
|
" x",
|
|
|
|
" area self = x + x",
|
2022-07-28 20:17:33 +03:00
|
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(TypeDef type Geo #()
|
|
|
|
#(((Circle #() #(
|
|
|
|
((() (Ident radius) (":" (Ident float)) ()))
|
|
|
|
((() (Ident x) () ())))))
|
|
|
|
((Rectangle #((() (Ident width) () ()) (() (Ident height) () ())) #()))
|
2022-07-28 20:17:33 +03:00
|
|
|
((Point #() #()))
|
|
|
|
(()))
|
2022-09-14 21:09:58 +03:00
|
|
|
#((Function number #() "=" (BodyBlock #((Ident x))))
|
2022-09-15 08:32:28 +03:00
|
|
|
(Function area #((() (Ident self) () ())) "=" (OprApp (Ident x) (Ok "+") (Ident x)))))
|
2022-07-28 20:17:33 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
2022-10-05 07:45:31 +03:00
|
|
|
#[test]
|
|
|
|
fn type_def_defaults() {
|
|
|
|
let code = ["type Result error ok=Nothing", " Ok value:ok = Nothing"];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(TypeDef type Result #((() (Ident error) () ())
|
|
|
|
(() (Ident ok) () ("=" (Ident Nothing))))
|
|
|
|
#(((Ok #((() (Ident value) (":" (Ident ok)) ("=" (Ident Nothing))))
|
|
|
|
#())))
|
|
|
|
#())
|
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
2022-08-02 18:09:20 +03:00
|
|
|
#[test]
|
|
|
|
fn type_def_nested() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let code = [
|
|
|
|
"type Foo",
|
|
|
|
" type Bar",
|
|
|
|
" type Baz",
|
|
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(TypeDef type Foo #() #()
|
|
|
|
#((TypeDef type Bar #() #() #())
|
|
|
|
(TypeDef type Baz #() #() #())))
|
2022-08-02 18:09:20 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
2022-07-28 20:17:33 +03:00
|
|
|
|
|
|
|
// === Variable Assignment ===
|
|
|
|
|
2022-07-08 01:31:00 +03:00
|
|
|
#[test]
|
|
|
|
fn assignment_simple() {
|
2022-09-14 21:09:58 +03:00
|
|
|
test("foo = x", block![(Assignment (Ident foo) "=" (Ident x))]);
|
2022-07-08 01:31:00 +03:00
|
|
|
}
|
|
|
|
|
2022-07-28 20:17:33 +03:00
|
|
|
|
|
|
|
// === Functions ===
|
|
|
|
|
2022-07-08 01:31:00 +03:00
|
|
|
#[test]
|
|
|
|
fn function_inline_simple_args() {
|
2022-10-05 07:45:31 +03:00
|
|
|
test(" foo a = x", block![(Function foo #((() (Ident a) () ())) "=" (Ident x))]);
|
2022-09-15 08:32:28 +03:00
|
|
|
#[rustfmt::skip]
|
|
|
|
test("foo a b = x",
|
|
|
|
block![(Function foo #((() (Ident a) () ()) (() (Ident b) () ())) "=" (Ident x))]);
|
|
|
|
#[rustfmt::skip]
|
|
|
|
test(
|
|
|
|
"foo a b c = x", block![
|
|
|
|
(Function foo
|
|
|
|
#((() (Ident a) () ()) (() (Ident b) () ()) (() (Ident c) () ()))
|
|
|
|
"=" (Ident x))],
|
|
|
|
);
|
2022-10-05 07:45:31 +03:00
|
|
|
test(" foo _ = x", block![(Function foo #((() (Wildcard -1) () ())) "=" (Ident x))]);
|
2022-07-08 01:31:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn function_block_noargs() {
|
|
|
|
test("foo =", block![(Function foo #() "=" ())]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn function_block_simple_args() {
|
2022-09-15 08:32:28 +03:00
|
|
|
test("foo a =", block![(Function foo #((() (Ident a) () ())) "=" ())]);
|
2022-10-05 07:45:31 +03:00
|
|
|
test("foo a b =", block![(Function foo #((() (Ident a) () ())
|
|
|
|
(() (Ident b) () ())) "=" ())]);
|
2022-09-15 08:32:28 +03:00
|
|
|
#[rustfmt::skip]
|
|
|
|
test(
|
|
|
|
"foo a b c =", block![
|
|
|
|
(Function foo
|
|
|
|
#((() (Ident a) () ()) (() (Ident b) () ()) (() (Ident c) () ()))
|
|
|
|
"=" ())],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === Named arguments ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn named_arguments() {
|
|
|
|
let cases = [
|
2022-10-05 07:45:31 +03:00
|
|
|
("f x=y", block![(NamedApp (Ident f) x "=" (Ident y))]),
|
|
|
|
("f (x = y)", block![(NamedApp (Ident f) x "=" (Ident y))]),
|
2022-09-15 08:32:28 +03:00
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === Default arguments ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_app() {
|
|
|
|
let cases = [("f default", block![(DefaultApp (Ident f) default)])];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn default_arguments() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let cases = [
|
|
|
|
("f x=1 = x",
|
2022-10-05 07:45:31 +03:00
|
|
|
block![(Function f #((() (Ident x) () ("=" (Number () "1" ())))) "=" (Ident x))]),
|
2022-09-15 08:32:28 +03:00
|
|
|
("f (x = 1) = x",
|
2022-10-05 07:45:31 +03:00
|
|
|
block![(Function f #((() (Ident x) () ("=" (Number () "1" ())))) "=" (Ident x))]),
|
2022-09-15 08:32:28 +03:00
|
|
|
// Pattern in LHS:
|
|
|
|
("f ~x=1 = x", block![
|
|
|
|
(Function f
|
2022-10-05 07:45:31 +03:00
|
|
|
#(("~" (Ident x) () ("=" (Number () "1" ()))))
|
2022-09-15 08:32:28 +03:00
|
|
|
"=" (Ident x))]),
|
|
|
|
("f (~x = 1) = x", block![
|
|
|
|
(Function f
|
2022-10-05 07:45:31 +03:00
|
|
|
#(("~" (Ident x) () ("=" (Number () "1" ()))))
|
2022-09-15 08:32:28 +03:00
|
|
|
"=" (Ident x))]),
|
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
2022-07-08 01:31:00 +03:00
|
|
|
}
|
|
|
|
|
2022-07-28 20:17:33 +03:00
|
|
|
|
|
|
|
// === Code Blocks ===
|
|
|
|
|
2022-07-20 17:53:20 +03:00
|
|
|
#[test]
|
|
|
|
fn code_block_body() {
|
2022-09-14 21:09:58 +03:00
|
|
|
let code = ["main =", " x"];
|
|
|
|
test(&code.join("\n"), block![(Function main #() "=" (BodyBlock #((Ident x))))]);
|
|
|
|
let code = ["main =", " ", " x"];
|
|
|
|
test(&code.join("\n"), block![(Function main #() "=" (BodyBlock #(() (Ident x))))]);
|
|
|
|
let code = ["main =", " ", " x"];
|
|
|
|
test(&code.join("\n"), block![(Function main #() "=" (BodyBlock #(() (Ident x))))]);
|
|
|
|
let code = ["main =", " ", " x"];
|
|
|
|
test(&code.join("\n"), block![(Function main #() "=" (BodyBlock #(() (Ident x))))]);
|
|
|
|
let code = ["main =", "", " x"];
|
|
|
|
test(&code.join("\n"), block![(Function main #() "=" (BodyBlock #(() (Ident x))))]);
|
2022-07-20 17:53:20 +03:00
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let code = [
|
|
|
|
"main =",
|
2022-09-14 21:09:58 +03:00
|
|
|
" +x",
|
|
|
|
" print x",
|
2022-07-20 17:53:20 +03:00
|
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expect = block![
|
|
|
|
(Function main #() "=" (BodyBlock #(
|
2022-10-05 07:45:31 +03:00
|
|
|
(OprSectionBoundary 1 (OprApp () (Ok "+") (Ident x)))
|
2022-09-14 21:09:58 +03:00
|
|
|
(App (Ident print) (Ident x)))))
|
2022-07-20 17:53:20 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expect);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn code_block_operator() {
|
|
|
|
let code = ["value = nums", " * each random", " + constant"];
|
|
|
|
let expect = block![
|
|
|
|
(Assignment (Ident value) "="
|
|
|
|
(OperatorBlockApplication (Ident nums)
|
|
|
|
#(((Ok "*") (App (Ident each) (Ident random)))
|
|
|
|
((Ok "+") (Ident constant)))
|
|
|
|
#()))
|
|
|
|
];
|
|
|
|
test(&code.join("\n"), expect);
|
|
|
|
}
|
|
|
|
|
2022-10-05 07:45:31 +03:00
|
|
|
#[test]
|
|
|
|
//#[ignore] // WIP
|
|
|
|
fn dot_operator_blocks() {
|
|
|
|
let code = ["rect1", " . width = 7", " . center", " + x"];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(OperatorBlockApplication (Ident rect1)
|
|
|
|
#(((Ok ".") (OprApp (Ident width) (Ok "=") (Number () "7" ())))
|
|
|
|
((Ok ".") (OperatorBlockApplication (Ident center)
|
|
|
|
#(((Ok "+") (Ident x))) #()))) #())];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
2022-07-20 17:53:20 +03:00
|
|
|
#[test]
|
|
|
|
fn code_block_argument_list() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let code = [
|
|
|
|
"value = foo",
|
|
|
|
" bar",
|
|
|
|
];
|
|
|
|
let expect = block![
|
|
|
|
(Assignment (Ident value) "=" (ArgumentBlockApplication (Ident foo) #((Ident bar))))
|
|
|
|
];
|
|
|
|
test(&code.join("\n"), expect);
|
|
|
|
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let code = [
|
|
|
|
"value = foo",
|
2022-09-14 21:09:58 +03:00
|
|
|
" +x",
|
2022-07-20 17:53:20 +03:00
|
|
|
" bar",
|
|
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expect = block![
|
|
|
|
(Assignment (Ident value) "="
|
|
|
|
(ArgumentBlockApplication (Ident foo) #(
|
2022-10-05 07:45:31 +03:00
|
|
|
(OprSectionBoundary 1 (OprApp () (Ok "+") (Ident x)))
|
2022-07-20 17:53:20 +03:00
|
|
|
(Ident bar))))
|
|
|
|
];
|
|
|
|
test(&code.join("\n"), expect);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn code_block_empty() {
|
|
|
|
// The first line here should parse as a function with no body expression (which is an error).
|
|
|
|
// No input would parse as an empty `ArgumentBlock` or `OperatorBlock`, because those types are
|
|
|
|
// distinguished from a body continuation by the presence of non-empty indented lines.
|
|
|
|
let code = ["foo =", "bar"];
|
|
|
|
test(&code.join("\n"), block![(Function foo #() "=" ()) (Ident bar)]);
|
|
|
|
// This parses similarly to above; a line with no non-whitespace content does not create a code
|
|
|
|
// block.
|
|
|
|
let code = ["foo =", " ", "bar"];
|
|
|
|
test(&code.join("\n"), block![(Function foo #() "=" ()) () (Ident bar)]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn code_block_bad_indents1() {
|
|
|
|
let code = ["main =", " foo", " bar", " baz"];
|
|
|
|
let expected = block![
|
|
|
|
(Function main #() "=" (BodyBlock #((Ident foo) (Ident bar) (Ident baz))))
|
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn code_block_bad_indents2() {
|
|
|
|
let code = ["main =", " foo", " bar", "baz"];
|
|
|
|
let expected = block![
|
|
|
|
(Function main #() "=" (BodyBlock #((Ident foo) (Ident bar))))
|
|
|
|
(Ident baz)
|
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn code_block_with_following_statement() {
|
|
|
|
let code = ["main =", " foo", "bar"];
|
|
|
|
let expected = block![
|
|
|
|
(Function main #() "=" (BodyBlock #((Ident foo))))
|
|
|
|
(Ident bar)
|
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
2022-10-05 07:45:31 +03:00
|
|
|
#[test]
|
|
|
|
fn operator_block_nested() {
|
|
|
|
let code = ["foo", " + bar", " - baz"];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(OperatorBlockApplication (Ident foo)
|
|
|
|
#(((Ok "+") (OperatorBlockApplication (Ident bar) #(((Ok "-") (Ident baz))) #())))
|
|
|
|
#())];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn operator_section_in_operator_block() {
|
|
|
|
let code = ["foo", " + bar +"];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(OperatorBlockApplication (Ident foo)
|
|
|
|
#(((Ok "+") (OprSectionBoundary 1 (OprApp (Ident bar) (Ok "+") ()))))
|
|
|
|
#())];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
2022-07-08 01:31:00 +03:00
|
|
|
|
2022-07-28 20:17:33 +03:00
|
|
|
// === Binary Operators ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multiple_operator_error() {
|
2022-09-14 21:09:58 +03:00
|
|
|
let code = ["x + + x"];
|
2022-07-28 20:17:33 +03:00
|
|
|
let expected = block![
|
2022-09-14 21:09:58 +03:00
|
|
|
(OprApp (Ident x) (Err (#("+" "+"))) (Ident x))
|
2022-07-28 20:17:33 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
2022-09-14 21:09:58 +03:00
|
|
|
let code = ["x + + + x"];
|
2022-07-28 20:17:33 +03:00
|
|
|
let expected = block![
|
2022-09-14 21:09:58 +03:00
|
|
|
(OprApp (Ident x) (Err (#("+" "+" "+"))) (Ident x))
|
2022-07-28 20:17:33 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn precedence() {
|
2022-09-14 21:09:58 +03:00
|
|
|
let code = ["x * y + z"];
|
|
|
|
let expected = block![
|
|
|
|
(OprApp (OprApp (Ident x) (Ok "*") (Ident y)) (Ok "+") (Ident z))
|
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn right_associative_operators() {
|
|
|
|
let code = ["x --> y ---> z"];
|
2022-07-28 20:17:33 +03:00
|
|
|
let expected = block![
|
2022-09-14 21:09:58 +03:00
|
|
|
(OprApp (Ident x) (Ok "-->") (OprApp (Ident y) (Ok "--->") (Ident z)))
|
2022-07-28 20:17:33 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
2022-09-14 21:09:58 +03:00
|
|
|
#[test]
|
|
|
|
fn pipeline_operators() {
|
|
|
|
test("f <| a", block![(OprApp (Ident f) (Ok "<|") (Ident a))]);
|
|
|
|
test("a |> f", block![(OprApp (Ident a) (Ok "|>") (Ident f))]);
|
|
|
|
}
|
|
|
|
|
2022-10-05 07:45:31 +03:00
|
|
|
#[test]
|
|
|
|
fn accessor_operator() {
|
|
|
|
// Test that the accessor operator `.` is treated like any other operator.
|
|
|
|
let cases = [
|
|
|
|
("Console.", block![(OprSectionBoundary 1 (OprApp (Ident Console) (Ok ".") ()))]),
|
|
|
|
(".", block![(OprSectionBoundary 2 (OprApp () (Ok ".") ()))]),
|
|
|
|
(".log", block![(OprSectionBoundary 1 (OprApp () (Ok ".") (Ident log)))]),
|
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn operator_sections() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
test(".map (+2 * 3) *7", block![
|
|
|
|
(OprSectionBoundary 1
|
|
|
|
(App (App (OprApp () (Ok ".") (Ident map))
|
|
|
|
(Group
|
|
|
|
(OprSectionBoundary 1 (OprApp (OprApp () (Ok "+") (Number () "2" ()))
|
|
|
|
(Ok "*") (Number () "3" ())))))
|
|
|
|
(OprSectionBoundary 1 (OprApp () (Ok "*") (Number () "7" ())))))]);
|
|
|
|
#[rustfmt::skip]
|
|
|
|
test(".sum 1", block![
|
|
|
|
(OprSectionBoundary 1 (App (OprApp () (Ok ".") (Ident sum)) (Number () "1" ())))]);
|
|
|
|
#[rustfmt::skip]
|
|
|
|
test("+1 + x", block![
|
|
|
|
(OprSectionBoundary 1 (OprApp (OprApp () (Ok "+") (Number () "1" ()))
|
|
|
|
(Ok "+") (Ident x)))]);
|
|
|
|
#[rustfmt::skip]
|
|
|
|
test("increment = 1 +", block![
|
|
|
|
(Assignment (Ident increment) "="
|
|
|
|
(OprSectionBoundary 1 (OprApp (Number () "1" ()) (Ok "+") ())))]);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn template_functions() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
test("_.map (_+2 * 3) _*7", block![
|
|
|
|
(TemplateFunction 1
|
|
|
|
(App (App (OprApp (Wildcard 0) (Ok ".") (Ident map))
|
|
|
|
(Group
|
|
|
|
(TemplateFunction 1
|
|
|
|
(OprApp (OprApp (Wildcard 0) (Ok "+") (Number () "2" ()))
|
|
|
|
(Ok "*") (Number () "3" ())))))
|
|
|
|
(TemplateFunction 1 (OprApp (Wildcard 0) (Ok "*") (Number () "7" ())))))]);
|
|
|
|
#[rustfmt::skip]
|
|
|
|
test("_.sum 1", block![
|
|
|
|
(TemplateFunction 1 (App (OprApp (Wildcard 0) (Ok ".") (Ident sum)) (Number () "1" ())))]);
|
|
|
|
#[rustfmt::skip]
|
|
|
|
test("_+1 + x", block![
|
|
|
|
(TemplateFunction 1 (OprApp (OprApp (Wildcard 0) (Ok "+") (Number () "1" ()))
|
|
|
|
(Ok "+") (Ident x)))]);
|
|
|
|
}
|
|
|
|
|
2022-07-28 20:17:33 +03:00
|
|
|
|
|
|
|
// === Unary Operators ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn unevaluated_argument() {
|
2022-09-14 21:09:58 +03:00
|
|
|
let code = ["main ~foo = x"];
|
2022-07-28 20:17:33 +03:00
|
|
|
let expected = block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(Function main #(("~" (Ident foo) () ())) "=" (Ident x))
|
2022-07-28 20:17:33 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn unary_operator_missing_operand() {
|
2022-09-14 21:09:58 +03:00
|
|
|
let code = ["main ~ = x"];
|
2022-07-28 20:17:33 +03:00
|
|
|
let expected = block![
|
2022-09-15 08:32:28 +03:00
|
|
|
(Function main #((() (UnaryOprApp "~" ()) () ())) "=" (Ident x))
|
2022-07-28 20:17:33 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn unary_operator_at_end_of_expression() {
|
|
|
|
let code = ["foo ~"];
|
|
|
|
let expected = block![
|
|
|
|
(App (Ident foo) (UnaryOprApp "~" ()))
|
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn plus_negative() {
|
2022-09-14 21:09:58 +03:00
|
|
|
let code = ["x = x+-x"];
|
2022-07-28 20:17:33 +03:00
|
|
|
let expected = block![
|
2022-09-14 21:09:58 +03:00
|
|
|
(Assignment (Ident x) "=" (OprApp (Ident x) (Ok "+") (UnaryOprApp "-" (Ident x))))
|
2022-07-28 20:17:33 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
2022-08-09 23:31:23 +03:00
|
|
|
#[test]
|
|
|
|
fn minus_binary() {
|
|
|
|
let cases = [
|
2022-09-14 21:09:58 +03:00
|
|
|
("x - x", block![(OprApp (Ident x) (Ok "-") (Ident x))]),
|
|
|
|
("x-x", block![(OprApp (Ident x) (Ok "-") (Ident x))]),
|
2022-08-09 23:31:23 +03:00
|
|
|
("x.-y", block![(OprApp (Ident x) (Ok ".") (UnaryOprApp "-" (Ident y)))]),
|
|
|
|
("x.~y", block![(OprApp (Ident x) (Ok ".") (UnaryOprApp "~" (Ident y)))]),
|
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn minus_section() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let cases = [
|
2022-10-05 07:45:31 +03:00
|
|
|
("- x", block![(OprSectionBoundary 1 (OprApp () (Ok "-") (Ident x)))]),
|
|
|
|
("(- x)", block![(Group (OprSectionBoundary 1 (OprApp () (Ok "-") (Ident x))))]),
|
2022-09-14 21:09:58 +03:00
|
|
|
("- (x * x)", block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(OprSectionBoundary 1 (OprApp () (Ok "-")
|
|
|
|
(Group (OprApp (Ident x) (Ok "*") (Ident x)))))]),
|
2022-08-09 23:31:23 +03:00
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn minus_unary() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let cases = [
|
2022-09-14 21:09:58 +03:00
|
|
|
("f -x", block![(App (Ident f) (UnaryOprApp "-" (Ident x)))]),
|
|
|
|
("-x", block![(UnaryOprApp "-" (Ident x))]),
|
2022-10-05 07:45:31 +03:00
|
|
|
("(-x)", block![(Group (UnaryOprApp "-" (Ident x)))]),
|
2022-09-14 21:09:58 +03:00
|
|
|
("-(x * x)", block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(UnaryOprApp "-" (Group (OprApp (Ident x) (Ok "*") (Ident x))))]),
|
2022-09-14 21:09:58 +03:00
|
|
|
("x=-x", block![(Assignment (Ident x) "=" (UnaryOprApp "-" (Ident x)))]),
|
|
|
|
("-x+x", block![(OprApp (UnaryOprApp "-" (Ident x)) (Ok "+") (Ident x))]),
|
|
|
|
("-x*x", block![(OprApp (UnaryOprApp "-" (Ident x)) (Ok "*") (Ident x))]),
|
|
|
|
("-1.x", block![(OprApp (UnaryOprApp "-" (Number () "1" ())) (Ok ".") (Ident x))]),
|
2022-08-09 23:31:23 +03:00
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
2022-07-28 20:17:33 +03:00
|
|
|
|
2022-09-03 09:38:06 +03:00
|
|
|
// === Import/Export ===
|
2022-08-02 18:09:20 +03:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn import() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let cases = [
|
|
|
|
("import project.IO", block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(Import () () ((Ident import) (OprApp (Ident project) (Ok ".") (Ident IO))) () () ())]),
|
2022-08-02 18:09:20 +03:00
|
|
|
("import Standard.Base as Enso_List", block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(Import () ()
|
2022-08-02 18:09:20 +03:00
|
|
|
((Ident import) (OprApp (Ident Standard) (Ok ".") (Ident Base)))
|
2022-10-05 07:45:31 +03:00
|
|
|
()
|
2022-08-02 18:09:20 +03:00
|
|
|
((Ident as) (Ident Enso_List))
|
|
|
|
())]),
|
|
|
|
("from Standard.Base import all", block![
|
|
|
|
(Import ()
|
|
|
|
((Ident from) (OprApp (Ident Standard) (Ok ".") (Ident Base)))
|
2022-10-05 07:45:31 +03:00
|
|
|
((Ident import) ())
|
|
|
|
all () ())]),
|
2022-08-02 18:09:20 +03:00
|
|
|
("from Standard.Base import all hiding Number, Boolean", block![
|
|
|
|
(Import ()
|
|
|
|
((Ident from) (OprApp (Ident Standard) (Ok ".") (Ident Base)))
|
2022-10-05 07:45:31 +03:00
|
|
|
((Ident import) ())
|
|
|
|
all
|
2022-08-02 18:09:20 +03:00
|
|
|
()
|
2022-09-03 09:38:06 +03:00
|
|
|
((Ident hiding) (OprApp (Ident Number) (Ok ",") (Ident Boolean))))]),
|
2022-08-02 18:09:20 +03:00
|
|
|
("polyglot java import java.lang.Float", block![
|
|
|
|
(Import
|
|
|
|
((Ident polyglot) (Ident java))
|
|
|
|
()
|
|
|
|
((Ident import)
|
|
|
|
(OprApp (OprApp (Ident java) (Ok ".") (Ident lang)) (Ok ".") (Ident Float)))
|
2022-10-05 07:45:31 +03:00
|
|
|
() () ())]),
|
2022-08-02 18:09:20 +03:00
|
|
|
("polyglot java import java.net.URI as Java_URI", block![
|
|
|
|
(Import
|
|
|
|
((Ident polyglot) (Ident java))
|
|
|
|
()
|
|
|
|
((Ident import)
|
|
|
|
(OprApp (OprApp (Ident java) (Ok ".") (Ident net)) (Ok ".") (Ident URI)))
|
2022-10-05 07:45:31 +03:00
|
|
|
()
|
2022-08-02 18:09:20 +03:00
|
|
|
((Ident as) (Ident Java_URI))
|
|
|
|
())]),
|
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
2022-09-03 09:38:06 +03:00
|
|
|
#[test]
|
|
|
|
fn export() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let cases = [
|
2022-09-14 21:09:58 +03:00
|
|
|
("export prj.Data.Foo", block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(Export ()
|
2022-09-14 21:09:58 +03:00
|
|
|
((Ident export)
|
|
|
|
(OprApp (OprApp (Ident prj) (Ok ".") (Ident Data)) (Ok ".") (Ident Foo)))
|
2022-10-05 07:45:31 +03:00
|
|
|
() () ())]),
|
2022-09-03 09:38:06 +03:00
|
|
|
("export Foo as Bar", block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(Export () ((Ident export) (Ident Foo)) () ((Ident as) (Ident Bar)) ())]),
|
2022-09-03 09:38:06 +03:00
|
|
|
("from Foo export Bar, Baz", block![
|
|
|
|
(Export
|
|
|
|
((Ident from) (Ident Foo))
|
|
|
|
((Ident export) (OprApp (Ident Bar) (Ok ",") (Ident Baz)))
|
2022-10-05 07:45:31 +03:00
|
|
|
() () ())]),
|
2022-09-03 09:38:06 +03:00
|
|
|
("from Foo export all hiding Bar, Baz", block![
|
|
|
|
(Export
|
|
|
|
((Ident from) (Ident Foo))
|
2022-10-05 07:45:31 +03:00
|
|
|
((Ident export) ())
|
|
|
|
all
|
2022-09-03 09:38:06 +03:00
|
|
|
()
|
|
|
|
((Ident hiding) (OprApp (Ident Bar) (Ok ",") (Ident Baz))))]),
|
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
2022-08-02 18:09:20 +03:00
|
|
|
|
2022-09-03 06:15:27 +03:00
|
|
|
// === Metadata ===
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn metadata_raw() {
|
|
|
|
let code = [
|
2022-09-14 21:09:58 +03:00
|
|
|
"x",
|
2022-09-03 06:15:27 +03:00
|
|
|
"#### METADATA ####",
|
|
|
|
r#"[[{"index":{"value":7},"size":{"value":8}},"5bad897e-099b-4b00-9348-64092636746d"]]"#,
|
|
|
|
];
|
|
|
|
let code = code.join("\n");
|
|
|
|
let (_meta, code) = enso_parser::metadata::parse(&code).unwrap();
|
|
|
|
let expected = block![
|
2022-09-14 21:09:58 +03:00
|
|
|
(Ident x)
|
2022-09-03 06:15:27 +03:00
|
|
|
()
|
|
|
|
];
|
|
|
|
test(code, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn metadata_parsing() {
|
|
|
|
let code = metadata::ORDERS_WITH_METADATA;
|
|
|
|
let (meta, code) = enso_parser::metadata::parse(code).unwrap();
|
|
|
|
let _ast = enso_parser::Parser::new().run(code);
|
|
|
|
let _meta: enso_parser::metadata::Metadata = meta.unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === Type annotations and signatures ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn type_signatures() {
|
|
|
|
let cases = [
|
|
|
|
("val : Bool", block![(TypeSignature val ":" (Ident Bool))]),
|
|
|
|
("val : List Int", block![(TypeSignature val ":" (App (Ident List) (Ident Int)))]),
|
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn type_annotations() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let cases = [
|
2022-09-14 21:09:58 +03:00
|
|
|
("val = x : Int", block![
|
|
|
|
(Assignment (Ident val) "=" (TypeAnnotated (Ident x) ":" (Ident Int)))]),
|
|
|
|
("val = foo (x : Int)", block![
|
2022-09-03 06:15:27 +03:00
|
|
|
(Assignment (Ident val) "="
|
|
|
|
(App (Ident foo)
|
2022-10-05 07:45:31 +03:00
|
|
|
(Group (TypeAnnotated (Ident x) ":" (Ident Int)))))]),
|
|
|
|
("(x : My_Type _)", block![
|
|
|
|
(Group (TypeAnnotated (Ident x) ":" (App (Ident My_Type) (Wildcard -1))))]),
|
|
|
|
("x : List Int -> Int", block![
|
|
|
|
(TypeSignature x ":" (OprApp (App (Ident List) (Ident Int)) (Ok "->") (Ident Int)))]),
|
2022-09-03 06:15:27 +03:00
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-09-03 09:38:06 +03:00
|
|
|
// === Text Literals ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn inline_text_literals() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let cases = [
|
|
|
|
(r#""I'm an inline raw text!""#, block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(TextLiteral #((Section "I'm an inline raw text!")))]),
|
2022-09-03 09:38:06 +03:00
|
|
|
(r#"zero_length = """#, block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(Assignment (Ident zero_length) "=" (TextLiteral #()))]),
|
|
|
|
(r#""type""#, block![(TextLiteral #((Section "type")))]),
|
|
|
|
(r#"unclosed = ""#, block![(Assignment (Ident unclosed) "=" (TextLiteral #()))]),
|
2022-09-03 09:38:06 +03:00
|
|
|
(r#"unclosed = "a"#, block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(Assignment (Ident unclosed) "=" (TextLiteral #((Section "a"))))]),
|
|
|
|
(r#"'Other quote type'"#, block![(TextLiteral #((Section "Other quote type")))]),
|
|
|
|
(r#""Non-escape: \n""#, block![(TextLiteral #((Section "Non-escape: \\n")))]),
|
2022-09-03 09:38:06 +03:00
|
|
|
(r#""String with \" escape""#, block![
|
|
|
|
(TextLiteral
|
2022-10-05 07:45:31 +03:00
|
|
|
#((Section "String with ") (Escape '\"') (Section " escape")))]),
|
|
|
|
(r#"'\u0915\u094D\u0937\u093F'"#, block![(TextLiteral #(
|
|
|
|
(Escape '\u{0915}') (Escape '\u{094D}') (Escape '\u{0937}') (Escape '\u{093F}')))]),
|
|
|
|
(r#"('\n')"#, block![(Group (TextLiteral #((Escape '\n'))))]),
|
|
|
|
(r#"`"#, block![(Invalid)]),
|
|
|
|
(r#"(")")"#, block![(Group (TextLiteral #((Section ")"))))]),
|
2022-09-03 09:38:06 +03:00
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multiline_text_literals() {
|
2022-10-05 07:45:31 +03:00
|
|
|
test("'''", block![(TextLiteral #())]);
|
|
|
|
let code = r#""""
|
2022-09-03 09:38:06 +03:00
|
|
|
part of the string
|
|
|
|
3-spaces indented line, part of the Text Block
|
|
|
|
this does not end the string -> '''
|
|
|
|
|
2022-10-05 07:45:31 +03:00
|
|
|
`also` part of the string
|
2022-09-03 09:38:06 +03:00
|
|
|
|
2022-09-14 21:09:58 +03:00
|
|
|
x"#;
|
2022-09-03 09:38:06 +03:00
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(TextLiteral
|
2022-10-05 07:45:31 +03:00
|
|
|
#((Section "part of the string\n")
|
|
|
|
(Section " 3-spaces indented line, part of the Text Block\n")
|
|
|
|
(Section "this does not end the string -> '''\n")
|
2022-09-15 08:32:28 +03:00
|
|
|
(Section "\n")
|
2022-10-05 07:45:31 +03:00
|
|
|
(Section "`also` part of the string\n")))
|
|
|
|
(Ident x)
|
|
|
|
];
|
|
|
|
test(code, expected);
|
|
|
|
let code = r#""""
|
|
|
|
multiline string that doesn't end in a newline
|
|
|
|
x"#;
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(TextLiteral #((Section "multiline string that doesn't end in a newline")))
|
2022-09-14 21:09:58 +03:00
|
|
|
(Ident x)
|
2022-09-03 09:38:06 +03:00
|
|
|
];
|
2022-10-05 07:45:31 +03:00
|
|
|
test(code, expected);
|
2022-09-03 09:38:06 +03:00
|
|
|
}
|
|
|
|
|
2022-09-15 08:32:28 +03:00
|
|
|
#[test]
|
|
|
|
fn interpolated_literals_in_inline_text() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let cases = [
|
2022-10-05 07:45:31 +03:00
|
|
|
(r#"'Simple case.'"#, block![(TextLiteral #((Section "Simple case.")))]),
|
2022-09-15 08:32:28 +03:00
|
|
|
(r#"'With a `splice`.'"#, block![(TextLiteral
|
|
|
|
#((Section "With a ")
|
2022-10-05 07:45:31 +03:00
|
|
|
(Splice (Ident splice))
|
|
|
|
(Section ".")))]),
|
|
|
|
(r#"'` SpliceWithLeadingWhitespace`'"#, block![(TextLiteral
|
|
|
|
#((Splice (Ident SpliceWithLeadingWhitespace))))]),
|
2022-09-15 08:32:28 +03:00
|
|
|
(r#"'String with \n escape'"#, block![
|
|
|
|
(TextLiteral
|
2022-10-05 07:45:31 +03:00
|
|
|
#((Section "String with ") (Escape '\n') (Section " escape")))]),
|
2022-09-15 08:32:28 +03:00
|
|
|
(r#"'\x0Aescape'"#, block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(TextLiteral #((Escape '\n') (Section "escape")))]),
|
2022-09-15 08:32:28 +03:00
|
|
|
(r#"'\u000Aescape'"#, block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(TextLiteral #((Escape '\n') (Section "escape")))]),
|
2022-09-15 08:32:28 +03:00
|
|
|
(r#"'\u{0000A}escape'"#, block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(TextLiteral #((Escape '\n') (Section "escape")))]),
|
2022-09-15 08:32:28 +03:00
|
|
|
(r#"'\U0000000Aescape'"#, block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(TextLiteral #((Escape '\n') (Section "escape")))]),
|
2022-09-15 08:32:28 +03:00
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn interpolated_literals_in_multiline_text() {
|
2022-10-05 07:45:31 +03:00
|
|
|
let code = r#"'''
|
|
|
|
`splice` at start"#;
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(TextLiteral #((Splice (Ident splice)) (Section " at start")))];
|
|
|
|
test(code, expected);
|
|
|
|
|
|
|
|
let code = r#"'''
|
2022-09-15 08:32:28 +03:00
|
|
|
text with a `splice`
|
|
|
|
and some \u000Aescapes\'"#;
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(TextLiteral
|
2022-10-05 07:45:31 +03:00
|
|
|
#((Section "text with a ") (Splice (Ident splice)) (Section "\n")
|
|
|
|
(Section "and some ") (Escape '\n') (Section "escapes") (Escape '\'')))];
|
|
|
|
test(code, expected);
|
2022-09-15 08:32:28 +03:00
|
|
|
}
|
|
|
|
|
2022-09-03 09:38:06 +03:00
|
|
|
|
2022-09-14 21:09:58 +03:00
|
|
|
// === Lambdas ===
|
|
|
|
|
|
|
|
#[test]
|
2022-10-05 07:45:31 +03:00
|
|
|
fn new_lambdas() {
|
2022-09-14 21:09:58 +03:00
|
|
|
let cases = [
|
2022-10-05 07:45:31 +03:00
|
|
|
(r#"\v -> v"#, block![(Lambda "\\" (OprApp (Ident v) (Ok "->") (Ident v)))]),
|
|
|
|
(r#"\a b -> x"#, block![
|
|
|
|
(Lambda "\\" (OprApp (App (Ident a) (Ident b)) (Ok "->") (Ident x)))]),
|
2022-09-14 21:09:58 +03:00
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
2022-10-05 07:45:31 +03:00
|
|
|
#[test]
|
|
|
|
fn old_lambdas() {
|
|
|
|
let cases = [("v -> v", block![(OprApp (Ident v) (Ok "->") (Ident v))])];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
2022-09-14 21:09:58 +03:00
|
|
|
|
|
|
|
// === Pattern Matching ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn pattern_irrefutable() {
|
|
|
|
let code = "Point x_val = my_point";
|
|
|
|
let expected = block![(Assignment (App (Ident Point) (Ident x_val)) "=" (Ident my_point))];
|
|
|
|
test(code, expected);
|
|
|
|
|
2022-10-05 07:45:31 +03:00
|
|
|
test("Vector _ = x", block![(Assignment (App (Ident Vector) (Wildcard -1)) "=" (Ident x))]);
|
2022-09-14 21:09:58 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn case_expression() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let code = [
|
|
|
|
"case a of",
|
|
|
|
" Some -> x",
|
|
|
|
" Int ->",
|
|
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(CaseOf (Ident a) #(
|
|
|
|
(((Ident Some) "->" (Ident x)))
|
|
|
|
(((Ident Int) "->" ()))))
|
2022-09-14 21:09:58 +03:00
|
|
|
];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let code = [
|
|
|
|
"case a of",
|
|
|
|
" Vector_2d x y -> x",
|
|
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(CaseOf (Ident a) #(
|
|
|
|
(((App (App (Ident Vector_2d) (Ident x)) (Ident y)) "->" (Ident x)))))];
|
2022-09-14 21:09:58 +03:00
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let code = [
|
|
|
|
"case self of",
|
|
|
|
" Vector_2d -> x",
|
|
|
|
" _ -> x",
|
|
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(CaseOf (Ident self) #(
|
|
|
|
(((Ident Vector_2d) "->" (Ident x)))
|
|
|
|
(((Wildcard -1) "->" (Ident x)))))];
|
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let code = [
|
|
|
|
"case foo of",
|
|
|
|
" v:My_Type -> x",
|
|
|
|
" v:(My_Type _ _) -> x",
|
2022-09-14 21:09:58 +03:00
|
|
|
];
|
2022-10-05 07:45:31 +03:00
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
|
|
|
(CaseOf (Ident foo) #(
|
|
|
|
(((TypeAnnotated (Ident v) ":" (Ident My_Type)) "->" (Ident x)))
|
|
|
|
(((TypeAnnotated (Ident v) ":"
|
|
|
|
(Group (App (App (Ident My_Type) (Wildcard -1)) (Wildcard -1))))
|
|
|
|
"->" (Ident x)))))];
|
2022-09-14 21:09:58 +03:00
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn pattern_match_auto_scope() {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let code = [
|
|
|
|
"case self of",
|
|
|
|
" Vector_2d ... -> x",
|
|
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let expected = block![
|
2022-10-05 07:45:31 +03:00
|
|
|
(CaseOf (Ident self) #((((App (Ident Vector_2d) (AutoScope)) "->" (Ident x)))))];
|
2022-09-14 21:09:58 +03:00
|
|
|
test(&code.join("\n"), expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === Array/tuple literals ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn array_literals() {
|
|
|
|
let cases = [
|
2022-10-05 07:45:31 +03:00
|
|
|
("[]", block![(Array () #())]),
|
|
|
|
("[x]", block![(Array (Ident x) #())]),
|
|
|
|
("[x, y]", block![(Array (Ident x) #(("," (Ident y))))]),
|
|
|
|
("[x, y, z]", block![(Array (Ident x) #(("," (Ident y)) ("," (Ident z))))]),
|
|
|
|
("[ x , y ]", block![(Array (Ident x) #(("," (Ident y))))]),
|
|
|
|
("[ x , y , z ]", block![(Array (Ident x) #(("," (Ident y)) ("," (Ident z))))]),
|
2022-09-14 21:09:58 +03:00
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn tuple_literals() {
|
|
|
|
let cases = [
|
2022-10-05 07:45:31 +03:00
|
|
|
("{}", block![(Tuple () #())]),
|
|
|
|
("{x}", block![(Tuple (Ident x) #())]),
|
|
|
|
("{x, y}", block![(Tuple (Ident x) #(("," (Ident y))))]),
|
2022-09-14 21:09:58 +03:00
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === Numeric literals ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn numbers() {
|
|
|
|
let cases = [
|
|
|
|
("100_000", block![(Number () "100_000" ())]),
|
|
|
|
("10_000.99", block![(Number () "10_000" ("." "99"))]),
|
2022-10-05 07:45:31 +03:00
|
|
|
("1 . 0", block![(OprApp (Number () "1" ()) (Ok ".") (Number () "0" ()))]),
|
|
|
|
("1 .0", block![(App (Number () "1" ()) (OprSectionBoundary 1 (OprApp () (Ok ".") (Number () "0" ()))))]),
|
|
|
|
("1. 0", block![(OprSectionBoundary 1 (App (OprApp (Number () "1" ()) (Ok ".") ()) (Number () "0" ())))]),
|
2022-09-14 21:09:58 +03:00
|
|
|
("0b10101010", block![(Number "0b" "10101010" ())]),
|
|
|
|
("0o122137", block![(Number "0o" "122137" ())]),
|
|
|
|
("0xAE2F14", block![(Number "0x" "AE2F14" ())]),
|
2022-10-05 07:45:31 +03:00
|
|
|
("pi = 3.14", block![(Assignment (Ident pi) "=" (Number () "3" ("." "14")))])
|
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === Whitespace ===
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn trailing_whitespace() {
|
|
|
|
let cases = [
|
|
|
|
("a ", block![(Ident a) ()]),
|
|
|
|
("a \n", block![(Ident a) ()]),
|
|
|
|
("a = \n x", block![(Function a #() "=" (BodyBlock #((Ident x))))]),
|
2022-09-14 21:09:58 +03:00
|
|
|
];
|
|
|
|
cases.into_iter().for_each(|(code, expected)| test(code, expected));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-07-08 01:31:00 +03:00
|
|
|
|
|
|
|
// ====================
|
|
|
|
// === Test Support ===
|
|
|
|
// ====================
|
|
|
|
|
|
|
|
use enso_metamodel_lexpr::ToSExpr;
|
|
|
|
use enso_reflect::Reflect;
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
|
|
|
/// Given a block of input Enso code, test that:
|
|
|
|
/// - The given code parses to the AST represented by the given S-expression.
|
|
|
|
/// - The AST pretty-prints back to the original code.
|
2022-07-25 17:24:21 +03:00
|
|
|
/// - Rust's deserialization is compatible with Rust's serialization for the type. (The Java format
|
|
|
|
/// tests check Java's deserialization against Rust's deserialization).
|
2022-07-08 01:31:00 +03:00
|
|
|
///
|
|
|
|
/// The S-expression format is as documented for [`enso_metamodel_lexpr`], with some
|
|
|
|
/// postprocessing:
|
|
|
|
/// - For concision, field names are stripped (as if all structs were tuple structs).
|
|
|
|
/// - Most token types are represented as their contents, rather than as a token struct. For
|
|
|
|
/// example, a `token::Number` may be represented like: `sexp![10]`, and a `token::Ident` may look
|
|
|
|
/// like `sexp![foo]`.
|
2022-07-20 17:53:20 +03:00
|
|
|
fn test(code: &str, expect: Value) {
|
2022-07-08 01:31:00 +03:00
|
|
|
let ast = enso_parser::Parser::new().run(code);
|
|
|
|
let ast_s_expr = to_s_expr(&ast, code);
|
2022-07-20 17:53:20 +03:00
|
|
|
assert_eq!(ast_s_expr.to_string(), expect.to_string(), "{:?}", &ast);
|
|
|
|
assert_eq!(ast.code(), code, "{:?}", &ast);
|
2022-07-25 17:24:21 +03:00
|
|
|
let serialized = enso_parser::serialization::serialize_tree(&ast).unwrap();
|
|
|
|
let deserialized = enso_parser::serialization::deserialize_tree(&serialized);
|
|
|
|
deserialized.unwrap();
|
2022-07-08 01:31:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// =====================
|
|
|
|
// === S-expressions ===
|
|
|
|
// =====================
|
|
|
|
|
|
|
|
/// Produce an S-expression representation of the input AST type.
|
2022-07-20 17:53:20 +03:00
|
|
|
pub fn to_s_expr<T>(value: &T, code: &str) -> Value
|
2022-07-08 01:31:00 +03:00
|
|
|
where T: serde::Serialize + Reflect {
|
2022-07-20 17:53:20 +03:00
|
|
|
use enso_parser::syntax::token;
|
|
|
|
use enso_parser::syntax::tree;
|
2022-07-08 01:31:00 +03:00
|
|
|
let (graph, rust_to_meta) = enso_metamodel::rust::to_meta(value.reflect_type());
|
|
|
|
let ast_ty = rust_to_meta[&value.reflect_type().id];
|
|
|
|
let base = code.as_bytes().as_ptr() as usize;
|
|
|
|
let code: Box<str> = Box::from(code);
|
|
|
|
let mut to_s_expr = ToSExpr::new(&graph);
|
|
|
|
to_s_expr.mapper(ast_ty, strip_hidden_fields);
|
2022-07-20 17:53:20 +03:00
|
|
|
let ident_token = rust_to_meta[&token::variant::Ident::reflect().id];
|
|
|
|
let operator_token = rust_to_meta[&token::variant::Operator::reflect().id];
|
2022-10-05 07:45:31 +03:00
|
|
|
let open_symbol_token = rust_to_meta[&token::variant::OpenSymbol::reflect().id];
|
|
|
|
let close_symbol_token = rust_to_meta[&token::variant::CloseSymbol::reflect().id];
|
2022-09-14 21:09:58 +03:00
|
|
|
let number_token = rust_to_meta[&token::variant::Digits::reflect().id];
|
|
|
|
let number_base_token = rust_to_meta[&token::variant::NumberBase::reflect().id];
|
2022-07-20 17:53:20 +03:00
|
|
|
let newline_token = rust_to_meta[&token::variant::Newline::reflect().id];
|
2022-09-03 09:38:06 +03:00
|
|
|
let text_start_token = rust_to_meta[&token::variant::TextStart::reflect().id];
|
|
|
|
let text_end_token = rust_to_meta[&token::variant::TextEnd::reflect().id];
|
|
|
|
let text_section_token = rust_to_meta[&token::variant::TextSection::reflect().id];
|
2022-10-05 07:45:31 +03:00
|
|
|
let text_escape_token = rust_to_meta[&token::variant::TextEscape::reflect().id];
|
2022-09-14 21:09:58 +03:00
|
|
|
let wildcard_token = rust_to_meta[&token::variant::Wildcard::reflect().id];
|
|
|
|
let autoscope_token = rust_to_meta[&token::variant::AutoScope::reflect().id];
|
2022-07-20 17:53:20 +03:00
|
|
|
// TODO: Implement `#[reflect(flag = "enso::concrete")]`, which just attaches user data to the
|
|
|
|
// type info; then filter by flag here instead of hard-coding these simplifications.
|
|
|
|
let token_to_str = move |token: Value| {
|
2022-07-08 01:31:00 +03:00
|
|
|
let range = token_code_range(&token, base);
|
|
|
|
code[range].to_owned().into_boxed_str()
|
|
|
|
};
|
|
|
|
let token_to_str_ = token_to_str.clone();
|
2022-07-20 17:53:20 +03:00
|
|
|
to_s_expr.mapper(ident_token, move |token| Value::symbol(token_to_str_(token)));
|
2022-07-08 01:31:00 +03:00
|
|
|
let token_to_str_ = token_to_str.clone();
|
2022-07-20 17:53:20 +03:00
|
|
|
to_s_expr.mapper(operator_token, move |token| Value::string(token_to_str_(token)));
|
|
|
|
let token_to_str_ = token_to_str.clone();
|
2022-09-03 09:38:06 +03:00
|
|
|
to_s_expr.mapper(text_section_token, move |token| Value::string(token_to_str_(token)));
|
2022-09-14 21:09:58 +03:00
|
|
|
let token_to_str_ = token_to_str.clone();
|
|
|
|
to_s_expr.mapper(number_token, move |token| Value::string(token_to_str_(token)));
|
2022-07-08 01:31:00 +03:00
|
|
|
let token_to_str_ = token_to_str;
|
2022-09-14 21:09:58 +03:00
|
|
|
to_s_expr.mapper(number_base_token, move |token| Value::string(token_to_str_(token)));
|
2022-07-20 17:53:20 +03:00
|
|
|
let into_car = |cons| match cons {
|
|
|
|
Value::Cons(cons) => cons.into_pair().0,
|
|
|
|
_ => panic!(),
|
|
|
|
};
|
2022-09-14 21:09:58 +03:00
|
|
|
let simplify_case = |list| {
|
|
|
|
let list = strip_hidden_fields(list);
|
|
|
|
let (_, list) = match list {
|
|
|
|
Value::Cons(cons) => cons.into_pair(),
|
|
|
|
_ => panic!(),
|
|
|
|
};
|
|
|
|
let (expression, list) = match list {
|
|
|
|
Value::Cons(cons) => cons.into_pair(),
|
|
|
|
_ => panic!(),
|
|
|
|
};
|
|
|
|
let (_, list) = match list {
|
|
|
|
Value::Cons(cons) => cons.into_pair(),
|
|
|
|
_ => panic!(),
|
|
|
|
};
|
|
|
|
Value::cons(expression, list)
|
|
|
|
};
|
2022-10-05 07:45:31 +03:00
|
|
|
let simplify_escape = |mut list| {
|
|
|
|
let mut last = None;
|
|
|
|
while let Value::Cons(cons) = list {
|
|
|
|
let (car, cdr) = cons.into_pair();
|
|
|
|
last = Some(car);
|
|
|
|
list = cdr;
|
|
|
|
}
|
|
|
|
last.unwrap()
|
|
|
|
};
|
|
|
|
let strip_invalid = |list| {
|
|
|
|
let Value::Cons(cons) = list else { unreachable!() };
|
|
|
|
let (car, _) = cons.into_pair();
|
|
|
|
Value::cons(car, Value::Null)
|
|
|
|
};
|
2022-09-14 21:09:58 +03:00
|
|
|
let line = rust_to_meta[&tree::block::Line::reflect().id];
|
|
|
|
let operator_line = rust_to_meta[&tree::block::OperatorLine::reflect().id];
|
2022-10-05 07:45:31 +03:00
|
|
|
let case = rust_to_meta[&tree::CaseOf::reflect().id];
|
|
|
|
let invalid = rust_to_meta[&tree::Invalid::reflect().id];
|
2022-07-20 17:53:20 +03:00
|
|
|
to_s_expr.mapper(line, into_car);
|
|
|
|
to_s_expr.mapper(operator_line, into_car);
|
2022-09-14 21:09:58 +03:00
|
|
|
to_s_expr.mapper(case, simplify_case);
|
2022-10-05 07:45:31 +03:00
|
|
|
to_s_expr.mapper(invalid, strip_invalid);
|
|
|
|
to_s_expr.mapper(text_escape_token, simplify_escape);
|
2022-07-20 17:53:20 +03:00
|
|
|
to_s_expr.skip(newline_token);
|
2022-09-14 21:09:58 +03:00
|
|
|
to_s_expr.skip(wildcard_token);
|
|
|
|
to_s_expr.skip(autoscope_token);
|
2022-09-15 08:32:28 +03:00
|
|
|
to_s_expr.skip(text_start_token);
|
|
|
|
to_s_expr.skip(text_end_token);
|
2022-10-05 07:45:31 +03:00
|
|
|
to_s_expr.skip(open_symbol_token);
|
|
|
|
to_s_expr.skip(close_symbol_token);
|
2022-07-08 01:31:00 +03:00
|
|
|
tuplify(to_s_expr.value(ast_ty, &value))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Strip certain fields that should be excluded from output.
|
2022-07-20 17:53:20 +03:00
|
|
|
fn strip_hidden_fields(tree: Value) -> Value {
|
|
|
|
let hidden_tree_fields = [
|
|
|
|
":spanLeftOffsetVisible",
|
|
|
|
":spanLeftOffsetCodeReprBegin",
|
|
|
|
":spanLeftOffsetCodeReprLen",
|
2022-09-14 21:09:58 +03:00
|
|
|
":spanLeftOffsetCodeUtf16",
|
|
|
|
":spanCodeLengthUtf8",
|
|
|
|
":spanCodeLengthUtf16",
|
2022-07-20 17:53:20 +03:00
|
|
|
];
|
2022-07-08 01:31:00 +03:00
|
|
|
let hidden_tree_fields: HashSet<_> = hidden_tree_fields.into_iter().collect();
|
2022-07-20 17:53:20 +03:00
|
|
|
Value::list(tree.to_vec().unwrap().into_iter().filter(|val| match val {
|
|
|
|
Value::Cons(cons) => match cons.car() {
|
|
|
|
Value::Symbol(symbol) => !hidden_tree_fields.contains(symbol.as_ref()),
|
2022-07-08 01:31:00 +03:00
|
|
|
_ => panic!(),
|
|
|
|
},
|
|
|
|
_ => true,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Given an S-expression representation of a [`Token`] and the base address for `Code` `Cow`s,
|
|
|
|
/// return the range of the input code the token references.
|
2022-07-20 17:53:20 +03:00
|
|
|
fn token_code_range(token: &Value, base: usize) -> std::ops::Range<usize> {
|
|
|
|
let get_u32 =
|
|
|
|
|field| fields(token).find(|(name, _)| *name == field).unwrap().1.as_u64().unwrap() as u32;
|
|
|
|
let begin = get_u32(":codeReprBegin");
|
|
|
|
let len = get_u32(":codeReprLen");
|
2022-07-08 01:31:00 +03:00
|
|
|
let begin = (begin as u64) | (base as u64 & !0xFFFF_FFFF);
|
|
|
|
let begin = if begin < (base as u64) { begin + 0x1_0000_0000 } else { begin };
|
|
|
|
let begin = begin as usize - base;
|
2022-07-20 17:53:20 +03:00
|
|
|
let len = len as usize;
|
2022-07-08 01:31:00 +03:00
|
|
|
begin..(begin + len)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Iterate the field `(name, value)` pairs of the S-expression of a struct with named fields.
|
2022-07-20 17:53:20 +03:00
|
|
|
fn fields(value: &'_ Value) -> impl Iterator<Item = (&'_ str, &'_ Value)> {
|
2022-07-08 01:31:00 +03:00
|
|
|
value.list_iter().unwrap().filter_map(|value| match value {
|
2022-07-20 17:53:20 +03:00
|
|
|
Value::Cons(cons) => match cons.car() {
|
|
|
|
Value::Symbol(symbol) => Some((&symbol[..], cons.cdr())),
|
2022-07-08 01:31:00 +03:00
|
|
|
_ => None,
|
|
|
|
},
|
|
|
|
_ => None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Strip field names from struct representations, so that they are printed more concisely, as if
|
|
|
|
/// they were tuple-structs.
|
2022-07-20 17:53:20 +03:00
|
|
|
fn tuplify(value: Value) -> Value {
|
2022-07-08 01:31:00 +03:00
|
|
|
let (car, cdr) = match value {
|
2022-07-20 17:53:20 +03:00
|
|
|
Value::Cons(cons) => cons.into_pair(),
|
|
|
|
Value::Vector(mut vector) => {
|
2022-07-08 01:31:00 +03:00
|
|
|
for value in vector.iter_mut() {
|
2022-07-20 17:53:20 +03:00
|
|
|
let original = std::mem::replace(value, Value::Nil);
|
2022-07-08 01:31:00 +03:00
|
|
|
*value = tuplify(original);
|
|
|
|
}
|
2022-07-20 17:53:20 +03:00
|
|
|
return Value::Vector(vector);
|
2022-07-08 01:31:00 +03:00
|
|
|
}
|
|
|
|
value => return value,
|
|
|
|
};
|
2022-07-20 17:53:20 +03:00
|
|
|
if let Value::Symbol(symbol) = &car {
|
2022-07-08 01:31:00 +03:00
|
|
|
if let Some(':') = symbol.chars().next() {
|
|
|
|
return tuplify(cdr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let car = tuplify(car);
|
|
|
|
let cdr = tuplify(cdr);
|
2022-07-20 17:53:20 +03:00
|
|
|
Value::Cons(lexpr::Cons::new(car, cdr))
|
2022-07-08 01:31:00 +03:00
|
|
|
}
|