From 3ea84fb77b24af7a1e897bf23fcb376b938f6d9e Mon Sep 17 00:00:00 2001 From: Matthew Pope Date: Thu, 12 Dec 2024 21:59:01 -0800 Subject: [PATCH 1/2] Adds initial TDL test cases --- conformance/README.md | 1 + conformance/tdl/data_model_values.ion | 4 + conformance/tdl/expression_groups.ion | 54 ++++++++++ conformance/tdl/for.ion | 19 ++++ conformance/tdl/if_multi.ion | 132 +++++++++++++++++++++++++ conformance/tdl/if_none.ion | 132 +++++++++++++++++++++++++ conformance/tdl/if_single.ion | 132 +++++++++++++++++++++++++ conformance/tdl/if_some.ion | 132 +++++++++++++++++++++++++ conformance/tdl/literal.ion | 17 ++++ conformance/tdl/macro_invocation.ion | 13 +++ conformance/tdl/variable_expansion.ion | 17 ++++ 11 files changed, 653 insertions(+) create mode 100644 conformance/tdl/data_model_values.ion create mode 100644 conformance/tdl/expression_groups.ion create mode 100644 conformance/tdl/for.ion create mode 100644 conformance/tdl/if_multi.ion create mode 100644 conformance/tdl/if_none.ion create mode 100644 conformance/tdl/if_single.ion create mode 100644 conformance/tdl/if_some.ion create mode 100644 conformance/tdl/literal.ion create mode 100644 conformance/tdl/macro_invocation.ion create mode 100644 conformance/tdl/variable_expansion.ion diff --git a/conformance/README.md b/conformance/README.md index 7a0f21c..b669409 100644 --- a/conformance/README.md +++ b/conformance/README.md @@ -10,6 +10,7 @@ declarative domain-specific language. * `ion_encoding/` – test cases for encoding directives * `module/` * `system_macros/` – test cases for each of the system macros + * `tdl/` – test cases for the template definition language and special forms * `demos/` – contains demonstrations of interesting and/or useful strategies > [!WARNING] diff --git a/conformance/tdl/data_model_values.ion b/conformance/tdl/data_model_values.ion new file mode 100644 index 0000000..50d9081 --- /dev/null +++ b/conformance/tdl/data_model_values.ion @@ -0,0 +1,4 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Test each type of data model value in a template body. diff --git a/conformance/tdl/expression_groups.ion b/conformance/tdl/expression_groups.ion new file mode 100644 index 0000000..f00cd99 --- /dev/null +++ b/conformance/tdl/expression_groups.ion @@ -0,0 +1,54 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +(ion_1_1 "expression groups may not" + (each "have an annotation on the s-expression" + (mactab (macro foo () (.values foo::(.. 1 2)))) + "have an annotation on the '..' operator" + (mactab (macro foo () (.values (foo::'..' 1 2)))) + "contain another expression group" + (mactab (macro foo () (.values (.. (..))))) + (mactab (macro foo () (.values (.. 1 (..) 2)))) + (mactab (macro foo () (.values (.. (.. 1) 2)))) + (mactab (macro foo () (.values (.. 1 (.. 2))))) + (mactab (macro foo () (.values (.. (.. 1 2))))) + "be used as rest arguments" + // ...you can have rest arguments or an expression group, but not both. + (mactab (macro foo () (.values (.. 1) (.. 2)))) + "be the template body expression" + (mactab (macro foo () (..))) + (mactab (macro foo () (.. 1))) + "occur in a list" + (mactab (macro foo () [(..)])) + (mactab (macro foo () [(.. 1)])) + (mactab (macro foo () [0, (..), 2])) + (mactab (macro foo () [0, (.. 1), 2])) + "occur in a sexp" + (mactab (macro foo () ( (..) ) )) + (mactab (macro foo () (0 (..) 2) )) + (mactab (macro foo () ( (.. 1) ) )) + (mactab (macro foo () (0 (.. 1) 1) )) + "occur in a struct" + (mactab (macro foo () { a: (..) } )) + (mactab (macro foo () { a: 1, b: (..), c: 2 } )) + (mactab (macro foo () { a: (.. 1) } )) + (mactab (macro foo () { a: 0, b: (.. 1), c:2 } )) + (signals "invalid macro definition"))) + +(ion_1_1 "expression groups" + (then "may be empty" + (mactab (macro foo () (.values (..)))) + (toplevel 0 ('#$:foo') 1) + (produces 0 1)) + (then "may contain one value" + (mactab (macro foo () (.values (.. 2)))) + (toplevel 0 ('#$:foo') 1) + (produces 0 2 1)) + (then "may contain many values" + (mactab (macro foo () (.values (.. 2 3)))) + (toplevel 0 ('#$:foo') 1) + (produces 0 2 3 1)) + (then "may contain macro invocations" + (mactab (macro foo () (.values (.. (.values 2))))) + (toplevel 0 ('#$:foo') 1) + (produces 0 2 1))) diff --git a/conformance/tdl/for.ion b/conformance/tdl/for.ion new file mode 100644 index 0000000..6b6a402 --- /dev/null +++ b/conformance/tdl/for.ion @@ -0,0 +1,19 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// * Test cases for `for` +// * binding names, keywords, etc. cannot be annotated +// * Can iterate one stream +// * Can iterate more than one stream +// * Can iterate empty stream +// * Can iterate non-empty stream +// * Can use grouping syntax for avoiding ambiguity +// * Creates a new binding for the stream iteration +// * Bindings can shadow the macro variables +// * Bindings can shadow an outer `for` +// * Iteration ends when the shorter stream is complete +// * When there's one stream +// * When there's multiple streams, and the first stream is shortest +// * When there's multiple streams, and the second (or later) is the shortest +// * When two streams have the same length +// * `for` should have exactly one template body expression diff --git a/conformance/tdl/if_multi.ion b/conformance/tdl/if_multi.ion new file mode 100644 index 0000000..0be817a --- /dev/null +++ b/conformance/tdl/if_multi.ion @@ -0,0 +1,132 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +(ion_1_1 "if_multi special form cannot be" + (each "invoked as an unqualified e-expression" + (text "(:if_multi 1 2 3)") + "invoked as a qualified e-expression" + (text "(:$ion::if_multi 1 2 3)") + "exported" + (mactab (export if_multi)) + (mactab (export $ion::if_multi)) + (signals "no such macro"))) + +(ion_1_1 "when invoked in TDL," + // TODO: Can be invoked qualified and unqualified + // Can be shadowed + (each "each argument can be elided because they are optional trailing parameters" + (mactab (macro foo () (.$ion::if_multi))) + (mactab (macro foo () (.$ion::if_multi (..)))) + (mactab (macro foo () (.$ion::if_multi (..) 3))) + (then (toplevel 0 ('#$:foo') 1) + (produces 0 1)))) + +(ion_1_1 "when the first argument is" + (each "an empty expression group" + (mactab (macro is_multi () (.$ion::if_multi (..) true false))) + "a macro that produces nothing" + (mactab (macro is_multi () (.$ion::if_multi (.$ion::none) true false))) + "an expression group with one macro that produces nothing" + (mactab (macro is_multi () (.$ion::if_multi (..(.$ion::none)) true false))) + "an expression group with multiple macros that produce nothing" + (mactab (macro is_multi () (.$ion::if_multi (..(.$ion::none)(.$ion::none)) true false))) + "a single value" + (mactab (macro is_multi () (.$ion::if_multi 0 true false))) + "an expression group with a single value" + (mactab (macro is_multi () (.$ion::if_multi (..0) true false))) + "a macro that produces a single value" + (mactab (macro is_multi () (.$ion::if_multi (.values 0) true false))) + "an expression group with a single macro that produces a single value" + (mactab (macro is_multi () (.$ion::if_multi (.. (.values 0)) true false))) + "an expression group with a multiple macros that collectively produce a single value" + (mactab (macro is_multi () (.$ion::if_multi (.. (.values 0) (.none)) true false))) + (mactab (macro is_multi () (.$ion::if_multi (.. (.none) (.values 0)) true false))) + "an expression group with a single value and a macro produces nothing" + (mactab (macro is_multi () (.$ion::if_multi (.. 0 (.none)) true false))) + (mactab (macro is_multi () (.$ion::if_multi (.. (.none) 0) true false))) + (then "then if_multi should produce the false branch" + (toplevel ('#$:is_multi')) + (produces false))) + (each "an expression group with multiple values" + (mactab (macro is_multi () (.$ion::if_multi (.. 0 1 2) true false))) + "a macro that produces multiple values" + (mactab (macro is_multi () (.$ion::if_multi (.values (.. 0 1 2)) true false))) + "an expression group with a single macro that produces multiple values" + (mactab (macro is_multi () (.$ion::if_multi (..(.values (.. 0 1 2))) true false))) + "an expression group with multiple macros " + (mactab (macro is_multi () (.$ion::if_multi (..(.values 0) (.values 1)) true false))) + "an expression group with multiple values and some macros that produce nothing" + (mactab (macro is_multi () (.$ion::if_multi (.. 0 1 2 (.none)) true false))) + "an expression group with multiple values that starts with a macro that produces nothing" + (mactab (macro is_multi () (.$ion::if_multi (.. (.none) 0 1 2) true false))) + (then "then if_multi should produce the true branch" + (toplevel ('#$:is_multi')) + (produces true)))) + +(ion_1_1 "when the first argument is multiple values" + (then "the 'true_branch' argument can be" + (each "an empty expression group" + (mactab (macro is_multi () (.$ion::if_multi (.. "two" "things") (..) false))) + "a macro that produces no values" + (mactab (macro is_multi () (.$ion::if_multi (.. "two" "things") (.none) false))) + "an expression group with macros that produces no values" + (mactab (macro is_multi () (.$ion::if_multi (.. "two" "things") (.. (.none) (.none)) false))) + (then (toplevel 1 ('#$:is_multi') 2) + (produces 1 2))) + (each "one value" + (mactab (macro is_multi () (.$ion::if_multi (.. "two" "things") true false))) + "an expression group with one value" + (mactab (macro is_multi () (.$ion::if_multi (.. "two" "things") (.. true) false))) + "a macro that produces one value" + (mactab (macro is_multi () (.$ion::if_multi (.. "two" "things") (.$ion::values true) false))) + (then (toplevel 1 ('#$:is_multi') 2) + (produces 1 true 2))) + (each "an expression group with multiple values" + (mactab (macro is_multi () (.$ion::if_multi (.. "two" "things") (.. true true) false))) + "a macro that produces multiple values" + (mactab (macro is_multi () (.$ion::if_multi (.. "two" "things") (.$ion::values (.. true true)) false))) + (then (toplevel 1 ('#$:is_multi') 2) + (produces 1 true true 2)))) + (then "the 'false_branch' argument is not evaluated" + // Note the false branch here must be something that is a RUNTIME evaluation error. + // We use `make_string`, assuming that it is a macro that will be implemented early on. + // We wrap the invalid argument in a `values` invocation so that the test won't cause + // a compilation error if implementations try to have compile time analysis of macro arguments. + (mactab (macro always_true () (.$ion::if_multi (.. "two" "things") true (.$ion::make_string (.$ion::values null))))) + (then (toplevel 1 ('#$:always_true') 2) + (produces 1 true 2)))) + +(ion_1_1 "when the first argument is none" + (then "the 'false_branch' argument can be" + (each "an empty expression group" + (mactab (macro is_multi () (.$ion::if_multi (..) true (..)))) + "a macro that produces no values" + (mactab (macro is_multi () (.$ion::if_multi (..) true (.none)))) + "an expression group with macros that produces no values" + (mactab (macro is_multi () (.$ion::if_multi (..) true (.. (.none) (.none))))) + (then (toplevel 1 ('#$:is_multi') 2) + (produces 1 2))) + (each "one value" + (mactab (macro is_multi () (.$ion::if_multi (..) true false))) + "an expression group with one value" + (mactab (macro is_multi () (.$ion::if_multi (..) true (.. false)))) + "a macro that produces one value" + (mactab (macro is_multi () (.$ion::if_multi (..) true (.$ion::values false)))) + (then (toplevel 1 ('#$:is_multi') 2) + (produces 1 false 2))) + (each "an expression group with multiple values" + (mactab (macro is_multi () (.$ion::if_multi (..) true (.. false false)))) + "a macro that produces multiple values" + (mactab (macro is_multi () (.$ion::if_multi (..) true (.$ion::values (.. false false))))) + "multiple values as implicit rest args" + (mactab (macro is_multi () (.$ion::if_multi (..) true false false))) + (then (toplevel 1 ('#$:is_multi') 2) + (produces 1 false false 2)))) + (then "the 'true_branch' argument is not evaluated" + // Note the true branch here must be something that is a RUNTIME evaluation error. + // We use `make_string`, assuming that it is a macro that will be implemented early on. + // We wrap the invalid argument in a `values` invocation so that the test won't cause + // a compilation error if implementations try to have compile time analysis of macro arguments. + (mactab (macro always_false () (.$ion::if_multi (..) (.$ion::make_string (.$ion::values null)) false))) + (then (toplevel 1 ('#$:always_false') 2) + (produces 1 false 2)))) diff --git a/conformance/tdl/if_none.ion b/conformance/tdl/if_none.ion new file mode 100644 index 0000000..b104391 --- /dev/null +++ b/conformance/tdl/if_none.ion @@ -0,0 +1,132 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +(ion_1_1 "if_none special form cannot be" + (each "invoked as an unqualified e-expression" + (text "(:if_none 1 2 3)") + "invoked as a qualified e-expression" + (text "(:$ion::if_none 1 2 3)") + "exported" + (mactab (export if_none)) + (mactab (export $ion::if_none)) + (signals "no such macro"))) + +(ion_1_1 "when invoked in TDL," + // TODO: Can be invoked qualified and unqualified + // Can be shadowed + (each "each argument can be elided because they are optional trailing parameters" + (mactab (macro foo () (.$ion::if_none))) + (mactab (macro foo () (.$ion::if_none 2))) + (mactab (macro foo () (.$ion::if_none 2 3))) + (then (toplevel 0 ('$#:foo') 1) + (produces 0 1)))) + +(ion_1_1 "when the first argument is" + (each "an empty expression group" + (mactab (macro is_none () (.$ion::if_none (..) true false))) + "a macro that produces nothing" + (mactab (macro is_none () (.$ion::if_none (.$ion::none) true false))) + "an expression group with one macro that produces nothing" + (mactab (macro is_none () (.$ion::if_none (..(.$ion::none)) true false))) + "an expression group with multiple macros that produce nothing" + (mactab (macro is_none () (.$ion::if_none (..(.$ion::none)(.$ion::none)) true false))) + (then "then if_none should produce the true branch" + (toplevel ('#$:is_none')) + (produces true))) + (each "a single value" + (mactab (macro is_none () (.$ion::if_none 0 true false))) + "an expression group with a single value" + (mactab (macro is_none () (.$ion::if_none (..0) true false))) + "a macro that produces a single value" + (mactab (macro is_none () (.$ion::if_none (.values 0) true false))) + "an expression group with a single macro that produces a single value" + (mactab (macro is_none () (.$ion::if_none (.. (.values 0)) true false))) + "an expression group with a multiple macros that collectively produce a single value" + (mactab (macro is_none () (.$ion::if_none (.. (.values 0) (.none)) true false))) + (mactab (macro is_none () (.$ion::if_none (.. (.none) (.values 0)) true false))) + "an expression group with a single value and a macro produces nothing" + (mactab (macro is_none () (.$ion::if_none (.. 0 (.none)) true false))) + (mactab (macro is_none () (.$ion::if_none (.. (.none) 0) true false))) + "an expression group with multiple values" + (mactab (macro is_none () (.$ion::if_none (.. 0 1 2) true false))) + "a macro that produces multiple values" + (mactab (macro is_none () (.$ion::if_none (.values (.. 0 1 2)) true false))) + "an expression group with a single macro that produces multiple values" + (mactab (macro is_none () (.$ion::if_none (..(.values (.. 0 1 2))) true false))) + "an expression group with multiple macros " + (mactab (macro is_none () (.$ion::if_none (..(.values 0) (.values 1)) true false))) + "an expression group with multiple values and some macros that produce nothing" + (mactab (macro is_none () (.$ion::if_none (.. 0 1 2 (.none)) true false))) + "an expression group with multiple values that starts with a macro that produces nothing" + (mactab (macro is_none () (.$ion::if_none (.. (.none) 0 1 2) true false))) + (then "then if_none should produce the false branch" + (toplevel ('#$:is_none')) + (produces false)))) + +(ion_1_1 "when the first argument is none" + (then "the 'true_branch' argument can be" + (each "an empty expression group" + (mactab (macro is_none () (.$ion::if_none (..) (..) false))) + "a macro that produces no values" + (mactab (macro is_none () (.$ion::if_none (..) (.none) false))) + "an expression group with macros that produces no values" + (mactab (macro is_none () (.$ion::if_none (..) (.. (.none) (.none)) false))) + (then (toplevel 1 ('#$:is_none') 2) + (produces 1 2))) + (each "one value" + (mactab (macro is_none () (.$ion::if_none (..) true false))) + "an expression group with one value" + (mactab (macro is_none () (.$ion::if_none (..) (.. true) false))) + "a macro that produces one value" + (mactab (macro is_none () (.$ion::if_none (..) (.$ion::values true) false))) + (then (toplevel 1 ('#$:is_none') 2) + (produces 1 true 2))) + (each "an expression group with multiple values" + (mactab (macro is_none () (.$ion::if_none (..) (.. true true) false))) + "a macro that produces multiple values" + (mactab (macro is_none () (.$ion::if_none (..) (.$ion::values (.. true true)) false))) + (then (toplevel 1 ('#$:is_none') 2) + (produces 1 true true 2)))) + (then "the 'false_branch' argument is not evaluated" + // Note the false branch here must be something that is a RUNTIME evaluation error. + // We use `make_string`, assuming that it is a macro that will be implemented early on. + // We wrap the invalid argument in a `values` invocation so that the test won't cause + // a compilation error if implementations try to have compile time analysis of macro arguments. + (mactab (macro always_true () (.$ion::if_none (..) true (.$ion::make_string (.$ion::values null))))) + (then (toplevel 1 ('#$:always_true') 2) + (produces 1 true 2)))) + +(ion_1_1 "when the first argument is some" + (then "the 'false_branch' argument can be" + (each "an empty expression group" + (mactab (macro is_none () (.$ion::if_none "something" true (..)))) + "a macro that produces no values" + (mactab (macro is_none () (.$ion::if_none "something" true (.none)))) + "an expression group with macros that produces no values" + (mactab (macro is_none () (.$ion::if_none "something" true (.. (.none) (.none))))) + (then (toplevel 1 ('#$:is_none') 2) + (produces 1 2))) + (each "one value" + (mactab (macro is_none () (.$ion::if_none "something" true false))) + "an expression group with one value" + (mactab (macro is_none () (.$ion::if_none "something" true (.. false)))) + "a macro that produces one value" + (mactab (macro is_none () (.$ion::if_none "something" true (.$ion::values false)))) + (then (toplevel 1 ('#$:is_none') 2) + (produces 1 false 2))) + (each "an expression group with multiple values" + (mactab (macro is_none () (.$ion::if_none "something" true (.. false false)))) + "a macro that produces multiple values" + (mactab (macro is_none () (.$ion::if_none "something" true (.$ion::values (.. false false))))) + "multiple values as implicit rest args" + (mactab (macro is_none () (.$ion::if_none "something" true false false))) + (then (toplevel 1 ('#$:is_none') 2) + (produces 1 false false 2)))) + (then "the 'true_branch' argument is not evaluated" + // Note the true branch here must be something that is a RUNTIME evaluation error. + // We use `make_string`, assuming that it is a macro that will be implemented early on. + // We wrap the invalid argument in a `values` invocation so that the test won't cause + // a compilation error if implementations try to have compile time analysis of macro arguments. + (mactab (macro always_false () (.$ion::if_none "something" (.$ion::make_string (.$ion::values null)) false))) + (then (toplevel 1 ('#$:always_false') 2) + (produces 1 false 2)))) diff --git a/conformance/tdl/if_single.ion b/conformance/tdl/if_single.ion new file mode 100644 index 0000000..982c4c0 --- /dev/null +++ b/conformance/tdl/if_single.ion @@ -0,0 +1,132 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +(ion_1_1 "if_single special form cannot be" + (each "invoked as an unqualified e-expression" + (text "(:if_single 1 2 3)") + "invoked as a qualified e-expression" + (text "(:$ion::if_single 1 2 3)") + "exported" + (mactab (export if_single)) + (mactab (export $ion::if_single)) + (signals "no such macro"))) + +(ion_1_1 "when invoked in TDL," + // TODO: Can be invoked qualified and unqualified + // Can be shadowed + (each "each argument can be elided because they are optional trailing parameters" + (mactab (macro foo () (.$ion::if_single))) + (mactab (macro foo () (.$ion::if_single (..)))) + (mactab (macro foo () (.$ion::if_single (..) 3))) + (then (toplevel 0 ('$#:foo') 1) + (produces 0 1)))) + +(ion_1_1 "when the first argument is" + (each "an empty expression group" + (mactab (macro is_single () (.$ion::if_single (..) true false))) + "a macro that produces nothing" + (mactab (macro is_single () (.$ion::if_single (.$ion::none) true false))) + "an expression group with one macro that produces nothing" + (mactab (macro is_single () (.$ion::if_single (..(.$ion::none)) true false))) + "an expression group with multiple macros that produce nothing" + (mactab (macro is_single () (.$ion::if_single (..(.$ion::none)(.$ion::none)) true false))) + "an expression group with multiple values" + (mactab (macro is_single () (.$ion::if_single (.. 0 1 2) true false))) + "a macro that produces multiple values" + (mactab (macro is_single () (.$ion::if_single (.values (.. 0 1 2)) true false))) + "an expression group with a single macro that produces multiple values" + (mactab (macro is_single () (.$ion::if_single (..(.values (.. 0 1 2))) true false))) + "an expression group with multiple macros " + (mactab (macro is_single () (.$ion::if_single (..(.values 0) (.values 1)) true false))) + "an expression group with multiple values and some macros that produce nothing" + (mactab (macro is_single () (.$ion::if_single (.. 0 1 2 (.none)) true false))) + "an expression group with multiple values that starts with a macro that produces nothing" + (mactab (macro is_single () (.$ion::if_single (.. (.none) 0 1 2) true false))) + (then "then if_single should produce the false branch" + (toplevel ('#$:is_single')) + (produces false))) + (each "a single value" + (mactab (macro is_single () (.$ion::if_single 0 true false))) + "an expression group with a single value" + (mactab (macro is_single () (.$ion::if_single (..0) true false))) + "a macro that produces a single value" + (mactab (macro is_single () (.$ion::if_single (.values 0) true false))) + "an expression group with a single macro that produces a single value" + (mactab (macro is_single () (.$ion::if_single (.. (.values 0)) true false))) + "an expression group with a multiple macros that collectively produce a single value" + (mactab (macro is_single () (.$ion::if_single (.. (.values 0) (.none)) true false))) + (mactab (macro is_single () (.$ion::if_single (.. (.none) (.values 0)) true false))) + "an expression group with a single value and a macro produces nothing" + (mactab (macro is_single () (.$ion::if_single (.. 0 (.none)) true false))) + (mactab (macro is_single () (.$ion::if_single (.. (.none) 0) true false))) + (then "then if_single should produce the true branch" + (toplevel ('#$:is_single')) + (produces true)))) + +(ion_1_1 "when the first argument is some" + (then "the 'true_branch' argument can be" + (each "an empty expression group" + (mactab (macro is_single () (.$ion::if_single "something" (..) false))) + "a macro that produces no values" + (mactab (macro is_single () (.$ion::if_single "something" (.none) false))) + "an expression group with macros that produces no values" + (mactab (macro is_single () (.$ion::if_single "something" (.. (.none) (.none)) false))) + (then (toplevel 1 ('#$:is_single') 2) + (produces 1 2))) + (each "one value" + (mactab (macro is_single () (.$ion::if_single "something" true false))) + "an expression group with one value" + (mactab (macro is_single () (.$ion::if_single "something" (.. true) false))) + "a macro that produces one value" + (mactab (macro is_single () (.$ion::if_single "something" (.$ion::values true) false))) + (then (toplevel 1 ('#$:is_single') 2) + (produces 1 true 2))) + (each "an expression group with multiple values" + (mactab (macro is_single () (.$ion::if_single "something" (.. true true) false))) + "a macro that produces multiple values" + (mactab (macro is_single () (.$ion::if_single "something" (.$ion::values (.. true true)) false))) + (then (toplevel 1 ('#$:is_single') 2) + (produces 1 true true 2)))) + (then "the 'false_branch' argument is not evaluated" + // Note the false branch here must be something that is a RUNTIME evaluation error. + // We use `make_string`, assuming that it is a macro that will be implemented early on. + // We wrap the invalid argument in a `values` invocation so that the test won't cause + // a compilation error if implementations try to have compile time analysis of macro arguments. + (mactab (macro always_true () (.$ion::if_single "something" true (.$ion::make_string (.$ion::values null))))) + (then (toplevel 1 ('#$:always_true') 2) + (produces 1 true 2)))) + +(ion_1_1 "when the first argument is none" + (then "the 'false_branch' argument can be" + (each "an empty expression group" + (mactab (macro is_single () (.$ion::if_single (..) true (..)))) + "a macro that produces no values" + (mactab (macro is_single () (.$ion::if_single (..) true (.none)))) + "an expression group with macros that produces no values" + (mactab (macro is_single () (.$ion::if_single (..) true (.. (.none) (.none))))) + (then (toplevel 1 ('#$:is_single') 2) + (produces 1 2))) + (each "one value" + (mactab (macro is_single () (.$ion::if_single (..) true false))) + "an expression group with one value" + (mactab (macro is_single () (.$ion::if_single (..) true (.. false)))) + "a macro that produces one value" + (mactab (macro is_single () (.$ion::if_single (..) true (.$ion::values false)))) + (then (toplevel 1 ('#$:is_single') 2) + (produces 1 false 2))) + (each "an expression group with multiple values" + (mactab (macro is_single () (.$ion::if_single (..) true (.. false false)))) + "a macro that produces multiple values" + (mactab (macro is_single () (.$ion::if_single (..) true (.$ion::values (.. false false))))) + "multiple values as implicit rest args" + (mactab (macro is_single () (.$ion::if_single (..) true false false))) + (then (toplevel 1 ('#$:is_single') 2) + (produces 1 false false 2)))) + (then "the 'true_branch' argument is not evaluated" + // Note the true branch here must be something that is a RUNTIME evaluation error. + // We use `make_string`, assuming that it is a macro that will be implemented early on. + // We wrap the invalid argument in a `values` invocation so that the test won't cause + // a compilation error if implementations try to have compile time analysis of macro arguments. + (mactab (macro always_false () (.$ion::if_single (..) (.$ion::make_string (.$ion::values null)) false))) + (then (toplevel 1 ('#$:always_false') 2) + (produces 1 false 2)))) diff --git a/conformance/tdl/if_some.ion b/conformance/tdl/if_some.ion new file mode 100644 index 0000000..4894a1c --- /dev/null +++ b/conformance/tdl/if_some.ion @@ -0,0 +1,132 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +(ion_1_1 "if_some special form cannot be" + (each "invoked as an unqualified e-expression" + (text "(:if_some 1 2 3)") + "invoked as a qualified e-expression" + (text "(:$ion::if_some 1 2 3)") + "exported" + (mactab (export if_some)) + (mactab (export $ion::if_some)) + (signals "no such macro"))) + +(ion_1_1 "when invoked in TDL," + // TODO: Can be invoked qualified and unqualified + // Can be shadowed + (each "each argument can be elided because they are optional trailing parameters" + (mactab (macro foo () (.$ion::if_some))) + (mactab (macro foo () (.$ion::if_some (..)))) + (mactab (macro foo () (.$ion::if_some (..) 3))) + (then (toplevel 0 ('$#:foo') 1) + (produces 0 1)))) + +(ion_1_1 "when the first argument is" + (each "an empty expression group" + (mactab (macro is_some () (.$ion::if_some (..) true false))) + "a macro that produces nothing" + (mactab (macro is_some () (.$ion::if_some (.$ion::none) true false))) + "an expression group with one macro that produces nothing" + (mactab (macro is_some () (.$ion::if_some (..(.$ion::none)) true false))) + "an expression group with multiple macros that produce nothing" + (mactab (macro is_some () (.$ion::if_some (..(.$ion::none)(.$ion::none)) true false))) + (then "then if_some should produce the false branch" + (toplevel ('#$:is_some')) + (produces false))) + (each "a single value" + (mactab (macro is_some () (.$ion::if_some 0 true false))) + "an expression group with a single value" + (mactab (macro is_some () (.$ion::if_some (..0) true false))) + "a macro that produces a single value" + (mactab (macro is_some () (.$ion::if_some (.values 0) true false))) + "an expression group with a single macro that produces a single value" + (mactab (macro is_some () (.$ion::if_some (.. (.values 0)) true false))) + "an expression group with a multiple macros that collectively produce a single value" + (mactab (macro is_some () (.$ion::if_some (.. (.values 0) (.none)) true false))) + (mactab (macro is_some () (.$ion::if_some (.. (.none) (.values 0)) true false))) + "an expression group with a single value and a macro produces nothing" + (mactab (macro is_some () (.$ion::if_some (.. 0 (.none)) true false))) + (mactab (macro is_some () (.$ion::if_some (.. (.none) 0) true false))) + "an expression group with multiple values" + (mactab (macro is_some () (.$ion::if_some (.. 0 1 2) true false))) + "a macro that produces multiple values" + (mactab (macro is_some () (.$ion::if_some (.values (.. 0 1 2)) true false))) + "an expression group with a single macro that produces multiple values" + (mactab (macro is_some () (.$ion::if_some (..(.values (.. 0 1 2))) true false))) + "an expression group with multiple macros " + (mactab (macro is_some () (.$ion::if_some (..(.values 0) (.values 1)) true false))) + "an expression group with multiple values and some macros that produce nothing" + (mactab (macro is_some () (.$ion::if_some (.. 0 1 2 (.none)) true false))) + "an expression group with multiple values that starts with a macro that produces nothing" + (mactab (macro is_some () (.$ion::if_some (.. (.none) 0 1 2) true false))) + (then "then if_some should produce the true branch" + (toplevel ('#$:is_some')) + (produces true)))) + +(ion_1_1 "when the first argument is some" + (then "the 'true_branch' argument can be" + (each "an empty expression group" + (mactab (macro is_some () (.$ion::if_some "something" (..) false))) + "a macro that produces no values" + (mactab (macro is_some () (.$ion::if_some "something" (.none) false))) + "an expression group with macros that produces no values" + (mactab (macro is_some () (.$ion::if_some "something" (.. (.none) (.none)) false))) + (then (toplevel 1 ('#$:is_some') 2) + (produces 1 2))) + (each "one value" + (mactab (macro is_some () (.$ion::if_some "something" true false))) + "an expression group with one value" + (mactab (macro is_some () (.$ion::if_some "something" (.. true) false))) + "a macro that produces one value" + (mactab (macro is_some () (.$ion::if_some "something" (.$ion::values true) false))) + (then (toplevel 1 ('#$:is_some') 2) + (produces 1 true 2))) + (each "an expression group with multiple values" + (mactab (macro is_some () (.$ion::if_some "something" (.. true true) false))) + "a macro that produces multiple values" + (mactab (macro is_some () (.$ion::if_some "something" (.$ion::values (.. true true)) false))) + (then (toplevel 1 ('#$:is_some') 2) + (produces 1 true true 2)))) + (then "the 'false_branch' argument is not evaluated" + // Note the false branch here must be something that is a RUNTIME evaluation error. + // We use `make_string`, assuming that it is a macro that will be implemented early on. + // We wrap the invalid argument in a `values` invocation so that the test won't cause + // a compilation error if implementations try to have compile time analysis of macro arguments. + (mactab (macro always_true () (.$ion::if_some "something" true (.$ion::make_string (.$ion::values null))))) + (then (toplevel 1 ('#$:always_true') 2) + (produces 1 true 2)))) + +(ion_1_1 "when the first argument is none" + (then "the 'false_branch' argument can be" + (each "an empty expression group" + (mactab (macro is_some () (.$ion::if_some (..) true (..)))) + "a macro that produces no values" + (mactab (macro is_some () (.$ion::if_some (..) true (.none)))) + "an expression group with macros that produces no values" + (mactab (macro is_some () (.$ion::if_some (..) true (.. (.none) (.none))))) + (then (toplevel 1 ('#$:is_some') 2) + (produces 1 2))) + (each "one value" + (mactab (macro is_some () (.$ion::if_some (..) true false))) + "an expression group with one value" + (mactab (macro is_some () (.$ion::if_some (..) true (.. false)))) + "a macro that produces one value" + (mactab (macro is_some () (.$ion::if_some (..) true (.$ion::values false)))) + (then (toplevel 1 ('#$:is_some') 2) + (produces 1 false 2))) + (each "an expression group with multiple values" + (mactab (macro is_some () (.$ion::if_some (..) true (.. false false)))) + "a macro that produces multiple values" + (mactab (macro is_some () (.$ion::if_some (..) true (.$ion::values (.. false false))))) + "multiple values as implicit rest args" + (mactab (macro is_some () (.$ion::if_some (..) true false false))) + (then (toplevel 1 ('#$:is_some') 2) + (produces 1 false false 2)))) + (then "the 'true_branch' argument is not evaluated" + // Note the true branch here must be something that is a RUNTIME evaluation error. + // We use `make_string`, assuming that it is a macro that will be implemented early on. + // We wrap the invalid argument in a `values` invocation so that the test won't cause + // a compilation error if implementations try to have compile time analysis of macro arguments. + (mactab (macro always_false () (.$ion::if_some (..) (.$ion::make_string (.$ion::values null)) false))) + (then (toplevel 1 ('#$:always_false') 2) + (produces 1 false 2)))) diff --git a/conformance/tdl/literal.ion b/conformance/tdl/literal.ion new file mode 100644 index 0000000..d77cd2d --- /dev/null +++ b/conformance/tdl/literal.ion @@ -0,0 +1,17 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// TODO: +// - literal can wrap +// - each type of scalar value +// - the `literal` special form +// - variable-expansion-like syntax +// - things that would be invalid syntax, such as annotations on the `%` operator +// - macro-like syntax +// - both names and addresses +// - qualified and unqualified +// - things that would be invalid syntax, such as annotations on the `.` operator +// - expression-group-like syntax +// - things that would be invalid syntax, such as annotations on the `..` operator +// - literal does not prevent evaluation of e-expressions +// - literal does not prevent SIDs from being interpreted diff --git a/conformance/tdl/macro_invocation.ion b/conformance/tdl/macro_invocation.ion new file mode 100644 index 0000000..41b69cc --- /dev/null +++ b/conformance/tdl/macro_invocation.ion @@ -0,0 +1,13 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// * Macro invocations +// * Must be invoked with `.` +// * Can be invoked by name +// * Can be invoked by number +// * Can be invoked by qualified names +// * Can be invoked by qualified addresses +// * Can be invoked using implicit rest args +// * Can be invoked with elided, trailing, optional args +// * May not have annotations on the `.` or enclosing sexp +// diff --git a/conformance/tdl/variable_expansion.ion b/conformance/tdl/variable_expansion.ion new file mode 100644 index 0000000..23c81de --- /dev/null +++ b/conformance/tdl/variable_expansion.ion @@ -0,0 +1,17 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// DECLARATION TEST CASES + +// May not have annotations on the `%`, enclosing sexp, or variable name +// May not have other values inside the enclosing sexp +// Variable name must be a symbol and correspond to a valid, in-scope variable + +// EVALUATION TEST CASES (Many of these tests should also be repeated for e-expression invocations) + +// Cardinality checks for zero-or-one +// Cardinality checks for exactly-one +// Cardinality checks for zero-or-more +// Cardinality checks for one-or-more +// The variable cardinality should not be checked if the variable is unused. +// A variable should not be expanded unless it is actually being used. From e46f1401e46c3698039df97fbb4751873c5c2e89 Mon Sep 17 00:00:00 2001 From: Matthew Pope Date: Fri, 13 Dec 2024 12:29:56 -0800 Subject: [PATCH 2/2] Adds suggested changes --- conformance/tdl/if_multi.ion | 2 +- conformance/tdl/if_none.ion | 2 +- conformance/tdl/if_single.ion | 4 ++-- conformance/tdl/if_some.ion | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/conformance/tdl/if_multi.ion b/conformance/tdl/if_multi.ion index 0be817a..02dac27 100644 --- a/conformance/tdl/if_multi.ion +++ b/conformance/tdl/if_multi.ion @@ -41,7 +41,7 @@ "an expression group with a multiple macros that collectively produce a single value" (mactab (macro is_multi () (.$ion::if_multi (.. (.values 0) (.none)) true false))) (mactab (macro is_multi () (.$ion::if_multi (.. (.none) (.values 0)) true false))) - "an expression group with a single value and a macro produces nothing" + "an expression group with a single value and a macro that produces nothing" (mactab (macro is_multi () (.$ion::if_multi (.. 0 (.none)) true false))) (mactab (macro is_multi () (.$ion::if_multi (.. (.none) 0) true false))) (then "then if_multi should produce the false branch" diff --git a/conformance/tdl/if_none.ion b/conformance/tdl/if_none.ion index b104391..a0f7b02 100644 --- a/conformance/tdl/if_none.ion +++ b/conformance/tdl/if_none.ion @@ -44,7 +44,7 @@ "an expression group with a multiple macros that collectively produce a single value" (mactab (macro is_none () (.$ion::if_none (.. (.values 0) (.none)) true false))) (mactab (macro is_none () (.$ion::if_none (.. (.none) (.values 0)) true false))) - "an expression group with a single value and a macro produces nothing" + "an expression group with a single value and a macro that produces nothing" (mactab (macro is_none () (.$ion::if_none (.. 0 (.none)) true false))) (mactab (macro is_none () (.$ion::if_none (.. (.none) 0) true false))) "an expression group with multiple values" diff --git a/conformance/tdl/if_single.ion b/conformance/tdl/if_single.ion index 982c4c0..a539dfa 100644 --- a/conformance/tdl/if_single.ion +++ b/conformance/tdl/if_single.ion @@ -56,14 +56,14 @@ "an expression group with a multiple macros that collectively produce a single value" (mactab (macro is_single () (.$ion::if_single (.. (.values 0) (.none)) true false))) (mactab (macro is_single () (.$ion::if_single (.. (.none) (.values 0)) true false))) - "an expression group with a single value and a macro produces nothing" + "an expression group with a single value and a macro that produces nothing" (mactab (macro is_single () (.$ion::if_single (.. 0 (.none)) true false))) (mactab (macro is_single () (.$ion::if_single (.. (.none) 0) true false))) (then "then if_single should produce the true branch" (toplevel ('#$:is_single')) (produces true)))) -(ion_1_1 "when the first argument is some" +(ion_1_1 "when the first argument is a single value" (then "the 'true_branch' argument can be" (each "an empty expression group" (mactab (macro is_single () (.$ion::if_single "something" (..) false))) diff --git a/conformance/tdl/if_some.ion b/conformance/tdl/if_some.ion index 4894a1c..3664c57 100644 --- a/conformance/tdl/if_some.ion +++ b/conformance/tdl/if_some.ion @@ -44,7 +44,7 @@ "an expression group with a multiple macros that collectively produce a single value" (mactab (macro is_some () (.$ion::if_some (.. (.values 0) (.none)) true false))) (mactab (macro is_some () (.$ion::if_some (.. (.none) (.values 0)) true false))) - "an expression group with a single value and a macro produces nothing" + "an expression group with a single value and a macro that produces nothing" (mactab (macro is_some () (.$ion::if_some (.. 0 (.none)) true false))) (mactab (macro is_some () (.$ion::if_some (.. (.none) 0) true false))) "an expression group with multiple values"