Skip to content

Commit

Permalink
Adds tests for annotate, flatten, and parse_ion system macros
Browse files Browse the repository at this point in the history
  • Loading branch information
popematt committed Nov 25, 2024
1 parent fd9013a commit 4d4720a
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 14 deletions.
2 changes: 1 addition & 1 deletion conformance/grammar.isl
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ type::{
type::{
name: model_annot,
ordered_elements: [
{ valid_values: [Annot, "Annot"] },
{ valid_values: [annot, "annot"] },
model_content,
{ type: model_symtok, occurs: range::[0, max] }
]
Expand Down
90 changes: 86 additions & 4 deletions conformance/system_macros/annotate.ion
Original file line number Diff line number Diff line change
@@ -1,9 +1,91 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// Test Cases:
// annotate can be invoked using any type of macro reference.
// annotate adds annotations to a value
// if the value has existing annotations, they are preserved and annotate prepends annotations to the existing annotations of the value
(ion_1_1 "annotate can be invoked"
(each "in text with an unqualified macro name"
(text " (:annotate (::) 0) ")
"in text with an unqualified macro address"
(text " (:2 (::) 0) ")
"in text with a qualified macro name"
(text " (:$ion::annotate (::) 0) ")
"in text using qualified system macro address 2"
(text " (:$ion::2 (::) 0) ")
"in binary using system macro address 2"
(binary "EF 02 00 60")
"in binary with a user macro address"
(binary "02 00 60")
(produces 0)))

(ion_1_1 "annotate can add"
(each "0 annotations"
(text " (:annotate (::) a::b::c::d::e::0) ")
"1 annotations"
(text " (:annotate (:: a) b::c::d::e::0) ")
"2 annotations"
(text " (:annotate (:: a b) c::d::e::0) ")
"3 annotations"
(text " (:annotate (:: a b c) d::e::0) ")
"4 annotations"
(text " (:annotate (:: a b c d) e::0) ")
"5 annotations"
(text " (:annotate (:: a b c d e) 0) ")
(produces a::b::c::d::e::0)))

(ion_1_1 "annotate can add annotations to"
(then "null" (text "(:annotate (:: a) null )") (produces a::null))
(then "bool" (text "(:annotate (:: a) true )") (produces a::true))
(then "int" (text "(:annotate (:: a) 2 )") (produces a::2))
(then "float" (text "(:annotate (:: a) 3e0 )") (produces a::3e0))
(then "decimal" (text "(:annotate (:: a) 4d0 )") (produces a::4d0))
(then "timestamp" (text "(:annotate (:: a) 2024T )") (produces a::2024T))
(then "string" (text "(:annotate (:: a) '''abc''' )") (produces a::"abc"))
(then "symbol" (text "(:annotate (:: a) abc )") (produces a::abc))
(then "clob" (text "(:annotate (:: a) {{'''abc'''}} )") (produces a::{{"abc"}}))
// (then "blob" (text "(:annotate (:: a) {{+AB/}} )") (produces a::{{ +AB/ }}))
(then "list" (text "(:annotate (:: a) [0, 1, 2] )") (produces a::[0, 1, 2]))
(then "sexp" (text "(:annotate (:: a) (0 1 2) )") (produces a::(0 1 2)))
(then "struct" (text "(:annotate (:: a) {a:1} )") (produces a::{a:1}))
(then "the result of an e-expression"
(text " (:annotate (:: a) (:values 123))")
(produces a::123))
(then "the result of a tdl macro invocation"
(mactab (macro foo () (.annotate (.. "a") (.values "b"))))
(text "(:foo)")
(produces a::"b"))
(then "the value of a tdl variable"
(mactab (macro foo (x) (.annotate (.. "a") (%x))))
(text "(:foo 10)")
(produces a::10)))

(ion_1_1 "the annotations argument"
(then "may be"
(then "an empty expression group"
(text "(:annotate (::) 0)")
(produces 0))
(then "a symbol with unknown text"
(text "(:annotate (:: $0) true)")
// Could be (produces $0::true), but some implementations don't support $0 nicely.
(denotes (annot true 0)))
(each "a string"
(text "(:annotate (:: '''a''') 0)")
"a symbol"
(text "(:annotate (:: 'a') 0)")
"an expression that produces a text value"
(text "(:annotate (:: (:values a)) 0)")
(produces a::0))
(each "an expression group with multiple text values"
(text "(:annotate (:: a b) 0)")
"an expression that produces multiple text values"
(text "(:annotate (:: (:values a b)) 0)")
(produces a::b::0)))
(then "may not be"
(each "any null"
(text "(:annotate (:: null) 0)")
(text "(:annotate (:: null.string) 0)")
(text "(:annotate (:: null.symbol) 0)")
"a non-text value"
(text "(:annotate (:: 1) 0)")
(signals "invalid argument"))))

// the first argument must be zero or more non-null text values ($0 is allowed)
// the second argument must be a single expression that also expands to a single expression
64 changes: 59 additions & 5 deletions conformance/system_macros/flatten.ion
Original file line number Diff line number Diff line change
@@ -1,8 +1,62 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

// Test Cases:
// flatten can be invoked using any type of macro reference.
// the argument values may be zero or more non-null lists and s-expressions
// flatten expands to a stream whose elements are the concatenated elements of all its arguments
// annotations on the argument (list and sexp) values are silently dropped
(ion_1_1 "flatten can be invoked"
(each "in text with an unqualified macro name"
(text " (:flatten (::)) ")
"in text with an unqualified macro address"
(text " (:19 (::)) ")
"in text with a qualified macro name"
(text " (:$ion::flatten (::)) ")
"in text using qualified system macro address 19"
(text " (:$ion::19 (::)) ")
"in binary using system macro address 19"
(binary "EF 13 00")
"in binary with a user macro address"
(binary "13 00")
(produces /*nothing*/)))

(ion_1_1 "flatten creates a single, unannotated sexp from"
(then "0 values"
(text "(:flatten)")
(produces /*nothing*/))
(each "one list"
(text "(:flatten [1, 2, 3])")
"multiple lists"
(text "(:flatten [1, 2] [3])")
(text "(:flatten [1] [2] [3])")
(text "(:flatten [] [1, 2, 3] [])")
(text "(:flatten [] [1] [] [2] [] [3] [])")
"one sexp"
(text "(:flatten (1 2 3))")
"multiple sexps"
(text "(:flatten (1 2) (3))")
(text "(:flatten (1) (2) (3))")
(text "(:flatten () (1 2 3) ())")
(text "(:flatten () (1) () (2) () (3) ())")
"a mix of lists and sexps"
(text "(:flatten () [1] (2) [3])")
(text "(:flatten (1) [2, 3] ())")
"annotated sequence values"
// Argument annotations are silently dropped.
(text "(:flatten a::() b::[1] c::(2) d::[3])")
(produces 1 2 3)))

(ion_1_1 "the argument cannot be"
(each "null"
(text "(:flatten null)")
(text "(:flatten (1) null (2))")
"null.list"
(text "(:flatten null.list)")
(text "(:flatten (1) null.list (2))")
"null.sexp"
(text "(:flatten null.sexp)")
(text "(:flatten (1) null.sexp (2))")
"a non-sequence value"
(text "(:flatten {{ '''abc''' }})")
(text "(:flatten (1) {{ '''abc''' }} (2))")
(text "(:flatten 123)")
(text "(:flatten (1) 123 (2))")
(text "(:flatten { a: 1 })")
(text "(:flatten (1) { a: 1 } (2))")
(signals "invalid argument")))
87 changes: 83 additions & 4 deletions conformance/system_macros/parse_ion.ion
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,89 @@
(binary "10 94 74 72 75 65")
(produces true)))

// parse_ion can read Ion 1.0 and Ion 1.1, text and binary
(ion_1_1 "parse_ion can parse"
(each "Ion 1.0 text in a string literal"
(text '''(:parse_ion "$ion_1_0 0")''')
"Ion 1.0 text in a clob literal"
(text '''(:parse_ion {{ "$ion_1_0 0" }})''')
"Ion 1.0 text in a blob literal"
(text "(:parse_ion {{ JGlvbl8xXzAgMA== }})")
"Ion 1.0 binary in a blob literal"
(text "(:parse_ion {{ 4AEA6iA= }})")
"Ion 1.1 text in a string literal"
(text '''(:parse_ion "$ion_1_1 0")''')
"Ion 1.1 text in a clob literal"
(text '''(:parse_ion {{ "$ion_1_1 0" }})''')
"Ion 1.1 text in a blob literal"
(text "(:parse_ion {{ JGlvbl8xXzEgMA== }})")
"Ion 1.1 binary in a blob literal"
(text "(:parse_ion {{ 4AEB6mA= }})")
(produces 0)))

// parse_ion always produces user values. See https://github.com/amazon-ion/ion-docs/issues/365 for complications to be resolved.
(ion_1_1 "when invoked in TDL, parse_ion can parse"
(each "Ion 1.0 text in a string literal"
(mactab (macro foo () (.parse_ion "$ion_1_0 0")))
"Ion 1.0 text in a clob literal"
(mactab (macro foo () (.parse_ion {{ "$ion_1_0 0" }})))
"Ion 1.0 text in a blob literal"
(mactab (macro foo () (.parse_ion {{ JGlvbl8xXzAgMA== }})))
"Ion 1.0 binary in a blob literal"
(mactab (macro foo () (.parse_ion {{ 4AEA6iA= }} )))
"Ion 1.1 text in a string literal"
(mactab (macro foo () (.parse_ion "$ion_1_1 0")))
"Ion 1.1 text in a clob literal"
(mactab (macro foo () (.parse_ion {{ "$ion_1_1 0" }})))
"Ion 1.1 text in a blob literal"
(mactab (macro foo () (.parse_ion {{ JGlvbl8xXzEgMA== }})))
"Ion 1.1 binary in a blob literal"
(mactab (macro foo () (.parse_ion {{ 4AEB6mA= }} )))
(then (text "(:foo)")
(produces 0))))

// parse_ion requires exactly one argument
// parse_ion requires exactly one argument, which must be a string or lob literal
(ion_1_1 "parse_ion"
(each "must have exactly one argument"
(text '''(:parse_ion "$ion_1_0" "0")''')
(text "(:parse_ion)")
"argument must not be any null "
(text "(:parse_ion null)")
(text "(:parse_ion null.blob)")
(text "(:parse_ion null.clob)")
(text "(:parse_ion null.string)")
"argument may not be a symbol"
(text "(:parse_ion '$ion_1_0 0')")
"argument may not be an expression group"
(text '''(:parse_ion (:: "$ion_1_0 0"))''')
"argument may not be an e-expression"
(text '''(:parse_ion (:values "$ion_1_0 0"))''')
(signals "invalid argument"))
(each "argument may not be a tdl macro invocation"
(mactab (macro foo () (.parse_ion (.values "$ion_1_1 0"))))
"argument may not be a tdl variable"
(mactab (macro bar (x) (.parse_ion (%x))))
"argument may not be a special form"
(mactab (macro bar (x) (.parse_ion (.if_void (%x) "0" "1"))))
(signals "invalid macro definition")))

// parse_ion argument must be a string or lob literal
(ion_1_1 "when the enclosing document's encoding context has symbols and macros"
(toplevel $ion_symbol_table::{symbols: ["a", "b", "c", "d"]})
(mactab (macro pi () 3.14159))
(each "embedded Ion 1.0 does not inherit any outer symbols"
(text '''(:parse_ion "$ion_1_0 $10")''')
"embedded Ion 1.1 does not inherit any outer symbols"
(text '''(:parse_ion "$ion_1_1 $66")''')
"embedded Ion 1.1 does not inherit any outer macros"
(text '''(:parse_ion "$ion_1_1 (:pi)")''')
(signals "invalid argument")))

// This may need to be updated once https://github.com/amazon-ion/ion-docs/issues/365 is resolved
(ion_1_1 "parse_ion always produces user values"
(then "from values that look like system values"
(text '''(:parse_ion "$ion_1_1 $ion_literal::$ion_symbol_table::{symbols:[]}")''')
(produces $ion_symbol_table::{symbols:[]}))
(each "from values that look like IVMs"
(text '''(:parse_ion "$ion_1_1 $ion_literal::$ion_1_0")''')
// TODO: These might not be correct depending on the semantics around quoted IVMs.
(text '''(:parse_ion "$ion_1_1 '$ion_1_0')''')
(text '''(:parse_ion "$ion_1_0 '$ion_1_0')''')
(produces $ion_1_0)))

0 comments on commit 4d4720a

Please sign in to comment.