Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quorum verdict: GOOD (5/5) — spec: EL §2.2.1 bool() string forms (1/true/on/yes, 0/false/off/no, case-insensitive). All 8 forms match the spec note exactly; lowercase output matches suite convention.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Expression Language §2.2.1: bool() string conversion is case-insensitive.
# The spec lists "1"/"true"/"on"/"yes" -> true and "0"/"false"/"off"/"no" -> false,
# matched case-insensitively. The existing bool-conversion fixture covers only
# lowercase word forms; this pins the numeric forms and mixed/upper case.
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'ONE:{{ bool("1") }}')
print(r'ZERO:{{ bool("0") }}')
print(r'TRUE_UC:{{ bool("TRUE") }}')
print(r'FALSE_UC:{{ bool("FALSE") }}')
print(r'YES_MIXED:{{ bool("YeS") }}')
print(r'NO_MIXED:{{ bool("No") }}')
print(r'ON_MIXED:{{ bool("On") }}')
print(r'OFF_UC:{{ bool("OFF") }}')
expected:
output:
- ONE:true
- ZERO:false
- TRUE_UC:true
- FALSE_UC:false
- YES_MIXED:true
- NO_MIXED:false
- ON_MIXED:true
- OFF_UC:false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quorum verdict: GOOD (5/5) — spec: EL §2.2.1 int(float) exactness + §1.2.1 int64. Reviewer-verified: 9e18 is exactly representable (mantissa < 2^53) and int(9e18)=9000000000000000000 < int64 max. Header math checks out.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Expression Language §2.2.1: int(value: float) converts to integer, error if not exact.
# 9e18 is exactly representable as an IEEE-754 double (9e18 = 8789062500000000 * 2^10,
# and 8789062500000000 < 2^53), its value is an exact integer, and it lies inside the
# 64-bit signed range (int64 max is ~9.22e18). So int(9e18) must succeed and produce
# exactly 9000000000000000000. Same for the negative counterpart.
# This pins the near-boundary success case; overflow rejection above int64 max is
# covered by the §2.2.2 int64 overflow fixtures.
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'POS:{{ int(9e18) }}')
print(r'NEG:{{ int(-9e18) }}')
expected:
output:
- POS:9000000000000000000
- NEG:-9000000000000000000

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quorum verdict: GOOD (5/5) — spec: EL §2.2.1 len = unicode codepoints; §1.1.5 escapes. All five expectations computed and confirmed (日本語=3, 👍=1, e+combining=2, \U0001D54F=1).

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Expression Language §2.2.1: len(s: string) returns the number of unicode CODEPOINTS.
# This is the first fixture with non-ASCII data in the EXPR suite. It distinguishes
# codepoint counting from byte counting (UTF-8) and from UTF-16 code-unit counting:
# - "日本語" is 3 codepoints (9 UTF-8 bytes)
# - "👍" (U+1F44D) is 1 codepoint (4 UTF-8 bytes, 2 UTF-16 code units / a surrogate
# pair — a UTF-16-based implementation would report 2)
# - "e" + U+0301 combining acute is 2 codepoints (1 grapheme cluster — a
# grapheme-based implementation would report 1)
# - U+1D54F (𝕏) is 1 codepoint (astral plane)
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'CJK:{{ len("日本語") }}')
print(r'EMOJI:{{ len("👍") }}')
print(r'COMBINING:{{ len("e\u0301") }}')
print(r'ASTRAL:{{ len("\U0001D54F") }}')
print(r'MIXED:{{ len("a👍日e\u0301") }}')
expected:
output:
- CJK:3
- EMOJI:1
- COMBINING:2
- ASTRAL:1
# a(1) + emoji(1) + CJK(1) + e(1) + combining mark(1) = 5 codepoints
- MIXED:5

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quorum verdict: GOOD (5/5) — spec: EL §2.2.1 note, verbatim: range_expr of a whitespace-only string is an error. Uses the spec's own example input.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Expression Language §2.2.1: range_expr(" ") (whitespace-only string) is an error.
# The spec note says: range_expr("") is an error, range_expr(" ") is an error, and
# range_expr([]) is an error — range expressions must contain at least one value.
# Empty-string and empty-list rejection are covered elsewhere; this pins the
# whitespace-only case.
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'R:{{ range_expr(" ") }}')

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quorum verdict: GOOD-WITH-NITS (3 reviewers, 2 distinct nits) — spec: EL §2.2.2 abs + §1.2.1 int64 (abs(-2^63)=2^63 unrepresentable). Nit 1 (adversarial): a parser treating -9223372036854775808 as unary-minus applied to a 2^63 literal rejects before abs ever runs — the test then passes without any abs overflow check existing; abs(0 - 9223372036854775807 - 1) would isolate the named rule. Nit 2 (literalist): the explicit overflow-is-error text was dropped from the published spec (it lives in RFC 0005); the mandate is inferred from the type's range — worth restoring in the spec.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Expression Language §2.2.2 (abs) + §2.1.1 (64-bit signed integer arithmetic):
# the absolute value of int64 min (−2^63) is 2^63, which is outside the
# 64-bit signed range, so the expression must be rejected. The operand is
# built as (0 - 9223372036854775807 - 1) rather than written as the literal
# -9223372036854775808: a parser that lexes the latter as unary-minus
# applied to a 2^63 literal would reject it BEFORE abs ever runs, letting
# the fixture pass with no abs overflow check at all. The arithmetic form
# is a valid int64 computation (== int64 min), so the only possible error
# is abs's own overflow.
# The operator-level overflow family (expr2.1.1--int64-bounds) covers + - * ;
# this is the function-level counterpart.
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'ABS:{{ abs(0 - 9223372036854775807 - 1) }}')
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Expression Language §2.2.2 (ceil) + §2.1.1 (64-bit signed integer range):
# ceil(x: float) -> int must produce an int64 value. 1e300 is a finite
# float far outside the 64-bit signed range (~9.22e18), so the conversion
# must be rejected rather than saturating or wrapping. floor has its own
# fixture (expr2.2.2--floor-exceeds-int64); split so each function's
# overflow check is independently pinned.
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
let:
- 'c = ceil(1e300)'
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'C:{{ c }}')
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Expression Language §2.2.2 (floor) + §2.1.1 (64-bit signed integer range):
# floor(x: float) -> int must produce an int64 value. 1e300 is a finite
# float far outside the 64-bit signed range (~9.22e18), so the conversion
# must be rejected rather than saturating or wrapping. ceil has its own
# fixture (expr2.2.2--ceil-exceeds-int64): bundling both into one
# abort-on-first-error template would leave the second function unpinned.
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
let:
- 'f = floor(1e300)'
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'F:{{ f }}')

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quorum verdict: GOOD-WITH-NITS — spec: EL §2.2.2 sum + §1.2.1 (max + 1 overflows; both elements individually valid, isolating the overflow to sum). Same caveat: overflow-is-error rests on the type table since the RFC 0005 normative text isn't in the published spec.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Expression Language §2.2.2 (sum) + §2.1.1 (64-bit signed integer arithmetic):
# sum(values: list[int]) accumulating past int64 max (2^63 - 1) must be rejected.
# 9223372036854775807 + 1 = 2^63 overflows the 64-bit signed range.
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'SUM:{{ sum([9223372036854775807, 1]) }}')

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quorum verdict: GOOD (5/5) — spec: EL §2.2.4 upper/lower. All six unicode expectations computed and confirmed; the deliberate exclusion of ß (full vs simple case mapping) and Turkish i (locale) is exactly right — the spec picks neither.

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Expression Language §2.2.4: upper()/lower() on non-ASCII input.
# Only unambiguous 1:1 Unicode simple case mappings are asserted here — these are
# locale-independent and identical under both simple and full case mapping.
#
# Deliberately NOT asserted (spec does not currently specify):
# - upper("ß"): "SS" under Unicode full case mapping, "ß" under simple mapping.
# The spec table says only "Convert to uppercase" and does not pick a mapping.
# - Turkish dotted/dotless i (upper("i") -> "İ" in tr locale): locale-dependent
# and the spec does not fix a locale.
# Both belong in the spec before they can be conformance-tested.
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'UP_EACUTE:{{ upper("é") }}')
print(r'LOW_EACUTE:{{ lower("É") }}')
print(r'UP_AUML:{{ upper("ä") }}')
print(r'LOW_AUML:{{ lower("Ä") }}')
print(r'UP_NTILDE:{{ upper("señor") }}')
print(r'UP_CJK_PASSTHROUGH:{{ upper("日本") }}')
expected:
output:
- UP_EACUTE:É
- LOW_EACUTE:é
- UP_AUML:Ä
- LOW_AUML:ä
- UP_NTILDE:SEÑOR
# CJK has no case; must pass through unchanged
- UP_CJK_PASSTHROUGH:日本

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quorum verdict: GOOD (4/5) — spec: EL §2.2.4 ljust/rjust/center/zfill + len-in-codepoints. Expectations verified; odd-split center correctly deferred as spec-unspecified (even split only). Nit: width-measured-in-codepoints is an inference from len's definition — the spec never states it for padding; a spec sentence would close it.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Expression Language §2.2.4: padding functions with non-ASCII (CJK wide) input.
# len() is specified in unicode codepoints, so padding width must also count
# codepoints — NOT display columns (CJK characters render two columns wide) and
# NOT bytes ("日本" is 6 UTF-8 bytes, which would already exceed width 5).
# (Codepoint counting for PADDING is an inference from len's definition —
# §2.2.4 never states the width unit for ljust/rjust/center/zfill; a spec
# sentence would make this normative rather than inferred.)
# ljust("日本", 5) -> "日本" + 3 spaces (2 codepoints + 3 pad = 5)
# rjust("日本", 5) -> 3 spaces + "日本"
# center("日本", 4) -> 1 space each side (even split; the odd-split side is
# spec-unspecified, see proposed/expr2.2.4--center-odd-padding)
# zfill("日", 3) -> "00日"
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'LJUST:[{{ ljust("日本", 5) }}]')
print(r'RJUST:[{{ rjust("日本", 5) }}]')
print(r'CENTER:[{{ center("日本", 4) }}]')
print(r'ZFILL:[{{ zfill("日", 3) }}]')
print(r'LEN_LJUST:{{ len(ljust("日本", 5)) }}')
expected:
output:
- "LJUST:[日本 ]"
- "RJUST:[ 日本]"
- "CENTER:[ 日本 ]"
- "ZFILL:[00日]"
- LEN_LJUST:5

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quorum verdict: GOOD (5/5) — spec: EL §2.2.4 predicate empty/uncased rules (isupper/islower need a cased char; isdigit/isalpha/isalnum/isspace need non-empty; isascii true on empty). Each of the 9 expectations follows verbatim from a table clause.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Expression Language §2.2.4: is* predicate clauses that are easy to get wrong.
# - isupper/islower require "at least one cased character", so a digits-only
# string is false for BOTH (not true for either).
# - isdigit/isalpha/isalnum/isspace require "string is non-empty", so "" is
# false for all four; isupper/islower on "" have no cased character -> false.
# - isascii is the exception: specified as true when "all characters are ASCII
# (U+0000–U+007F), or string is empty".
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'UPPER_DIGITS:{{ isupper("123") }}')
print(r'LOWER_DIGITS:{{ islower("123") }}')
print(r'E_ISDIGIT:{{ isdigit("") }}')
print(r'E_ISALPHA:{{ isalpha("") }}')
print(r'E_ISALNUM:{{ isalnum("") }}')
print(r'E_ISSPACE:{{ isspace("") }}')
print(r'E_ISUPPER:{{ isupper("") }}')
print(r'E_ISLOWER:{{ islower("") }}')
print(r'E_ISASCII:{{ isascii("") }}')
expected:
output:
- UPPER_DIGITS:false
- LOWER_DIGITS:false
- E_ISDIGIT:false
- E_ISALPHA:false
- E_ISALNUM:false
- E_ISSPACE:false
- E_ISUPPER:false
- E_ISLOWER:false
- E_ISASCII:true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quorum verdict: GOOD-WITH-NITS (4/5; 1 dissent NOT-GOOD) — spec: EL §2.2.4 isascii (explicit U+0000–U+007F — solid) and isalpha. Dissent: ALPHA_CJK:true / ALPHA_ACCENT:true pin Unicode-alphabetic semantics while the spec wording ("all characters are alphabetic") is exactly parallel to isdigit's, which this same PR rules ambiguous and routes to proposed/. Both implementations happen to agree here (Python str.isalpha and Rust char::is_alphabetic are both Unicode), which is why the majority keeps it — but consider a spec sentence defining "alphabetic", or move the two ALPHA lines alongside the isdigit question for consistency.

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Expression Language §2.2.4: classification predicates on non-ASCII data.
# - isascii is explicitly specified as U+0000–U+007F, so any non-ASCII
# character must make it false.
# - isalpha says "all characters are alphabetic"; CJK ideographs and accented
# Latin letters are Unicode-alphabetic. This is consistent with §2.2.5's
# explicit Unicode posture for \w and \d. CAVEAT: the spec wording is
# exactly parallel to isdigit's, which is treated as ambiguous (see
# below) — the ALPHA assertions are kept only because both reference
# implementations agree (Python str.isalpha and Rust char::is_alphabetic
# are both Unicode); a spec sentence defining "alphabetic" should ratify
# this. If the spec instead rules ASCII-only, these lines flip.
#
# NOT asserted here: isdigit on non-ASCII digits (e.g. Arabic-Indic "٣").
# Python's str.isdigit("٣") is True while openjd-rs returns false — a live
# implementation divergence on spec-ambiguous text ("all characters are digits").
# See proposed/expr2.2.4--isdigit-unicode.test.yaml.
template:
specificationVersion: jobtemplate-2023-09
extensions:
- EXPR
name: TestJob
steps:
- name: Step1
script:
actions:
onRun:
command: python
args:
- -c
- |
print(r'ASCII_PLAIN:{{ isascii("abc123") }}')
print(r'ASCII_ARABIC_DIGIT:{{ isascii("٣") }}')
print(r'ASCII_CAFE:{{ isascii("café") }}')
print(r'ASCII_CJK:{{ isascii("日本") }}')
print(r'ASCII_MIXED:{{ isascii("abc👍") }}')
print(r'ALPHA_CJK:{{ isalpha("日本") }}')
print(r'ALPHA_ACCENT:{{ isalpha("café") }}')
expected:
output:
- ASCII_PLAIN:true
- ASCII_ARABIC_DIGIT:false
- ASCII_CAFE:false
- ASCII_CJK:false
- ASCII_MIXED:false
- ALPHA_CJK:true
- ALPHA_ACCENT:true
Loading
Loading