diff --git a/draft-marchan-kdl2.md b/draft-marchan-kdl2.md index 865d13a..924c96e 100644 --- a/draft-marchan-kdl2.md +++ b/draft-marchan-kdl2.md @@ -265,10 +265,66 @@ KDL does not specify any restrictions on what implementations might do with these annotations. They are free to ignore them, or use them to make decisions about how to interpret a value. -Additionally, the following type annotations MAY be recognized by KDL parsers -and, if used, SHOULD interpret these types as follows: +### Suffix Type Annotation -### Reserved Type Annotations for Numbers Without Decimals: +When a ({{value}}) is a ({{number}}), it's possible to attach the type +annotation as a "suffix", instead of prepending it between `(` and `)`. This +makes it possible to, for example, write `10px`, `10.5%`, `512GiB`, etc., which +are equivalent to `(px)10`, `(%)5`, and `(GiB)512`, respectively. + +An implementation that finds BOTH a parenthesized and a suffix +({{type-annotation}}) on the same ({{number}}) MUST yield a syntax error. + +Suffixes MUST BE plain ({{identifier-string}})s. No other ({{string}}) is +acceptable. + +There are two kinds of ({{suffix-type-annotation}}) available: +({{bare-suffix-type-annotation}})s and ({{explicit-suffix-type-annotation}}). + +#### Bare Suffix Type Annotation + +When a ({{value}}) is a decimal ({{number}}) WITHOUT exponential syntax (`1e+5` +etc) (and ONLY a decimal), it's possible to attach the type annotation as a +suffix directly to the number, without any additional syntax. + +They also come with some additional rules (like only being available for +decimals), in order to prevent potential ambiguity or footguns with the syntax. +This is generally acceptable, as type annotations in particular tend to be +application-defined and limited in scope, rather than arbitrary user data. In +designing this feature, it was determined that the value for various real-world +DSLs outweighed the complexity of the following rules. + +As such, to remove ambiguity, the suffix ({{identifier-string}}) MUST NOT start +with any of the following patterns, all of which MUST yield syntax errors: + +* `.`, `,`, or `_` +* `[a-zA-Z][0-9_]` (to disambiguate all non-decimals, with breathing room) +* `[eE][+-]?[0-9]` (to disambiguate exponentials) +* `[xX][a-fA-F]` (to disambiguate hexadecimals) + +All other ({{identifier-string}})s can be safely appended to decimal numbers, so +long as the decimal does not include an exponential component. + +If the desired suffix would violate any of the above rules, either regular +parenthetical ({{type-annotation}})s, or ({{explicit-suffix-type-annotation}})s +may be used. + +#### Explicit Suffix Type Annotation + +Any ({{number}}) may have a `#` attached to it, followed by any valid +({{identifier-string}}). This is an explicit ({{suffix-type-annotation}}) syntax +without any of the relatively complex requirements of +({{bare-suffix-type-annotation}}), which can be a useful escape hatch. For +example: `10.0#u8` is invalid syntax without the `#` prefix. + +Note again that, unlike ({{bare-suffix-type-annotation}})s, Explicit Suffixes +may be used with ALL ({{number}}) formats (hexadecimal, decimal, octal, and +binary). For example, `0x1234#u16` is valid. + +### Reserved Type Annotations for Numbers Without Decimals + +Additionally, the following type annotations MAY be recognized by KDL parsers +and, if used, SHOULD interpret these types as follows. Signed integers of various sizes (the number is the bit size): @@ -335,6 +391,7 @@ IEEE 754-2008 decimal floating point numbers ~~~kdl node (u8)123 +node 123#i64 node prop=(regex).* (published)date "1970-01-01" (contributor)person name="Foo McBar" @@ -1013,12 +1070,26 @@ multi-line-raw-string-body := // Numbers number := keyword-number | hex | octal | binary | decimal -decimal := sign? integer ('.' integer)? exponent? +decimal := sign? integer ('.' integer)? ( + // NOTE: This grammar does not explicitly guard against having both + // parenthesized and type suffixes. + bare-type-suffix | + explicit-type-suffix | + (exponent explicit-type-suffix?) + )? exponent := ('e' | 'E') sign? integer integer := digit (digit | '_')* digit := [0-9] sign := '+' | '-' +bare-type-suffix := bare-type-suffix-initial identifier-char* +bare-type-suffix-initial := identifier-char + - '.' - ',' - '_' + - ([a-zA-Z] [0-9_]) + - (('e' | 'E') sign? digit) + - (('x' | 'X') [a-fA-F]) +explicit-type-suffix := '#' identifier-string + hex := sign? '0x' hex-digit (hex-digit | '_')* octal := sign? '0o' [0-7] [0-7_]* binary := sign? '0b' ('0' | '1') ('0' | '1' | '_')* diff --git a/tests/test_cases/expected_kdl/suffix_type_bare.kdl b/tests/test_cases/expected_kdl/suffix_type_bare.kdl new file mode 100644 index 0000000..885fe20 --- /dev/null +++ b/tests/test_cases/expected_kdl/suffix_type_bare.kdl @@ -0,0 +1 @@ +node (abc)123 diff --git a/tests/test_cases/expected_kdl/suffix_type_explicit.kdl b/tests/test_cases/expected_kdl/suffix_type_explicit.kdl new file mode 100644 index 0000000..885fe20 --- /dev/null +++ b/tests/test_cases/expected_kdl/suffix_type_explicit.kdl @@ -0,0 +1 @@ +node (abc)123 diff --git a/tests/test_cases/expected_kdl/suffix_type_explicit_exponent.kdl b/tests/test_cases/expected_kdl/suffix_type_explicit_exponent.kdl new file mode 100644 index 0000000..c3f9ce0 --- /dev/null +++ b/tests/test_cases/expected_kdl/suffix_type_explicit_exponent.kdl @@ -0,0 +1 @@ +node (abc)12300000 diff --git a/tests/test_cases/expected_kdl/suffix_type_explicit_non_decimal.kdl b/tests/test_cases/expected_kdl/suffix_type_explicit_non_decimal.kdl new file mode 100644 index 0000000..81b59ff --- /dev/null +++ b/tests/test_cases/expected_kdl/suffix_type_explicit_non_decimal.kdl @@ -0,0 +1 @@ +node (abc)5 (def)83 (zzz)291 diff --git a/tests/test_cases/input/suffix_type_bare.kdl b/tests/test_cases/input/suffix_type_bare.kdl new file mode 100644 index 0000000..a032d6a --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare.kdl @@ -0,0 +1 @@ +node 123abc diff --git a/tests/test_cases/input/suffix_type_bare_comma_fail.kdl b/tests/test_cases/input/suffix_type_bare_comma_fail.kdl new file mode 100644 index 0000000..1ff2480 --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_comma_fail.kdl @@ -0,0 +1 @@ +node 123,abc diff --git a/tests/test_cases/input/suffix_type_bare_exponent_exponent_fail.kdl b/tests/test_cases/input/suffix_type_bare_exponent_exponent_fail.kdl new file mode 100644 index 0000000..235474e --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_exponent_exponent_fail.kdl @@ -0,0 +1 @@ +node 123e5e5 diff --git a/tests/test_cases/input/suffix_type_bare_exponentish_fail.kdl b/tests/test_cases/input/suffix_type_bare_exponentish_fail.kdl new file mode 100644 index 0000000..678175a --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_exponentish_fail.kdl @@ -0,0 +1 @@ +node 123e5abc diff --git a/tests/test_cases/input/suffix_type_bare_hexish_fail.kdl b/tests/test_cases/input/suffix_type_bare_hexish_fail.kdl new file mode 100644 index 0000000..fc4ae1e --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_hexish_fail.kdl @@ -0,0 +1 @@ +node 123xabc123 \ No newline at end of file diff --git a/tests/test_cases/input/suffix_type_bare_keyword_fail.kdl b/tests/test_cases/input/suffix_type_bare_keyword_fail.kdl new file mode 100644 index 0000000..ffdaff4 --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_keyword_fail.kdl @@ -0,0 +1 @@ +node #truefoo #false#bar #nullish #nan#no #inf123 #-inf#123 diff --git a/tests/test_cases/input/suffix_type_bare_letter_number_fail.kdl b/tests/test_cases/input/suffix_type_bare_letter_number_fail.kdl new file mode 100644 index 0000000..3310eca --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_letter_number_fail.kdl @@ -0,0 +1 @@ +node 123u8 diff --git a/tests/test_cases/input/suffix_type_bare_non_decimal_fail.kdl b/tests/test_cases/input/suffix_type_bare_non_decimal_fail.kdl new file mode 100644 index 0000000..bf09c80 --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_non_decimal_fail.kdl @@ -0,0 +1 @@ +node 0b0101abc 0o123def 0x123zzz diff --git a/tests/test_cases/input/suffix_type_bare_non_identifier_fail.kdl b/tests/test_cases/input/suffix_type_bare_non_identifier_fail.kdl new file mode 100644 index 0000000..0fee4ad --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_non_identifier_fail.kdl @@ -0,0 +1 @@ +node 123"astring" 456#"rawstring"# diff --git a/tests/test_cases/input/suffix_type_bare_period_fail.kdl b/tests/test_cases/input/suffix_type_bare_period_fail.kdl new file mode 100644 index 0000000..8f0aad5 --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_period_fail.kdl @@ -0,0 +1 @@ +node 123.abc diff --git a/tests/test_cases/input/suffix_type_bare_redundant_fail.kdl b/tests/test_cases/input/suffix_type_bare_redundant_fail.kdl new file mode 100644 index 0000000..8fc9127 --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_redundant_fail.kdl @@ -0,0 +1 @@ +node (abc)123def diff --git a/tests/test_cases/input/suffix_type_bare_string_fail.kdl b/tests/test_cases/input/suffix_type_bare_string_fail.kdl new file mode 100644 index 0000000..f8ace5f --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_string_fail.kdl @@ -0,0 +1 @@ +node "foo"bar diff --git a/tests/test_cases/input/suffix_type_bare_underscore_fail.kdl b/tests/test_cases/input/suffix_type_bare_underscore_fail.kdl new file mode 100644 index 0000000..e7ee020 --- /dev/null +++ b/tests/test_cases/input/suffix_type_bare_underscore_fail.kdl @@ -0,0 +1 @@ +node 123_abc diff --git a/tests/test_cases/input/suffix_type_explicit.kdl b/tests/test_cases/input/suffix_type_explicit.kdl new file mode 100644 index 0000000..47e027e --- /dev/null +++ b/tests/test_cases/input/suffix_type_explicit.kdl @@ -0,0 +1 @@ +node 123#abc diff --git a/tests/test_cases/input/suffix_type_explicit_exponent.kdl b/tests/test_cases/input/suffix_type_explicit_exponent.kdl new file mode 100644 index 0000000..87deb17 --- /dev/null +++ b/tests/test_cases/input/suffix_type_explicit_exponent.kdl @@ -0,0 +1 @@ +node 123e5#abc diff --git a/tests/test_cases/input/suffix_type_explicit_keyword_fail.kdl b/tests/test_cases/input/suffix_type_explicit_keyword_fail.kdl new file mode 100644 index 0000000..cd233ad --- /dev/null +++ b/tests/test_cases/input/suffix_type_explicit_keyword_fail.kdl @@ -0,0 +1 @@ +node 123#true 456#false 789#null 012#nan 345#inf 678#-inf diff --git a/tests/test_cases/input/suffix_type_explicit_non_decimal.kdl b/tests/test_cases/input/suffix_type_explicit_non_decimal.kdl new file mode 100644 index 0000000..2eb552a --- /dev/null +++ b/tests/test_cases/input/suffix_type_explicit_non_decimal.kdl @@ -0,0 +1 @@ +node 0b0101#abc 0o123#def 0x123#zzz diff --git a/tests/test_cases/input/suffix_type_explicit_non_identifier_fail.kdl b/tests/test_cases/input/suffix_type_explicit_non_identifier_fail.kdl new file mode 100644 index 0000000..0b0a11a --- /dev/null +++ b/tests/test_cases/input/suffix_type_explicit_non_identifier_fail.kdl @@ -0,0 +1 @@ +node 123#"string" 456##"rawstring"# diff --git a/tests/test_cases/input/suffix_type_explicit_number_fail.kdl b/tests/test_cases/input/suffix_type_explicit_number_fail.kdl new file mode 100644 index 0000000..ef43d25 --- /dev/null +++ b/tests/test_cases/input/suffix_type_explicit_number_fail.kdl @@ -0,0 +1 @@ +node 123#123 diff --git a/tests/test_cases/input/suffix_type_explicit_redundant_fail.kdl b/tests/test_cases/input/suffix_type_explicit_redundant_fail.kdl new file mode 100644 index 0000000..7bd2fe9 --- /dev/null +++ b/tests/test_cases/input/suffix_type_explicit_redundant_fail.kdl @@ -0,0 +1 @@ +node (abc)123#def