feat(expr): add 1-D array indexing to the binding evaluator (#142)#169
Merged
Conversation
Postfix subscripts A[i] now parse and evaluate. Grammar: a new postfix
production sits between unary and primary, so a subscript binds tighter
than the unary sign (-A[i] == -(A[i])) and chains (A[1][2]) parse as
nested Index nodes. Inside the brackets each subscript is a full range
expression grammatically, but one that parses to a range is array
slicing — rejected at parse with a typed error — and empty brackets are
malformed; the primary-position '[' matrix-constructor rejection is
byte-unchanged. Comma-separated subscripts parse into one Index node
and defer at evaluation with a typed DomainError naming the
multi-dimensional deferral.
Eval policy: the base must evaluate to an array (scalar base is a
TypeError naming the found type — which also covers chained indexing,
since A[1] is a scalar). Subscripts must be Integer scalars: Real
(including whole-valued 1.0), Boolean, String, and Enum subscripts are
the new NonIntegerIndex error — no integer()-style floor coercion, so a
0.999999 rounding artifact fails loudly instead of reading the wrong
element. Bounds are 1-based (CDL) and checked on the i64 subscript
against the length widened to i64 before any usize conversion, so
i64::MAX/i64::MIN subscripts report the new IndexOutOfBounds
{index, size} without wrap or saturation; every subscript of an empty
array is out of bounds with size 0. The element read is a clone of the
already-canonicalized stored value, so -0.0 and canonical-NaN bits pass
through bit-identical (golden-pinned).
Canary flip: tests.rs pinned a[1] as a parse rejection; it now parses
and fails as UnknownIdent at evaluation. The malformed-syntax list in
eval_array_tests drops its "a[1]" row for the same reason ("a[" stays).
The leading-'[' and comprehension parse rejections keep their pins.
Mutation self-checks (applied locally, confirmed failing, reverted):
an off-by-one bounds check (i < 0) fails
subscript_zero_negative_and_past_the_end_are_out_of_bounds and
every_subscript_of_an_empty_array_is_out_of_bounds; a floor-coercion
subscript fails non_integer_subscripts_are_rejected_without_coercion.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…essage tighten
Adds the Enumeration case to the subscript type-gate matrix: an enum
value in subscript position is NonIntegerIndex{found: "Enumeration"}
(the test scope now resolves enum references, mirroring tests.rs), and
the module rustdoc says "Enumeration" to match eval::type_name. Drops
the doubled "; oce-expr arrays are 1-D" from the multi-subscript
deferral message and updates its pinned golden.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourth PR of the issue-#142 array lane.
Scope
Postfix
A[i]indexing inoce-expr:ExprAst::Index,ExprError::{IndexOutOfBounds{index,size}, NonIntegerIndex{found}}(the sanctioned PR-2 deferral lands here). Grammar: postfix binds tighter than unary sign (-A[i] == -(A[i])); slicingA[a:b](even parenthesized) and empty brackets are typed parse rejections;A[i,j]parses then defers at eval (multi-dim); the leading-[matrix-constructor rejection is byte-unchanged.Safety policy
1-based bounds checked on the
i64subscript before any offset math (i64::MAX/MINsafe by construction); access via checked conversion +.get()with a deliberately distinct DomainError backstop so a bounds-check mutation cannot be masked;A[1.0]isNonIntegerIndex— no Real coercion (rounding-trap rationale in-test).Tests
20 new (oce-expr 115; workspace 1151), including −0.0/NaN bit pass-through goldens,
size(A)[1]composition, chained-index scalar-base error, extreme-subscript wrap traps, and two implementer-run mutation self-checks (off-by-one bounds; floor-coerced subscript) confirmed failing before revert.Gates (implementer + independent lead verification at ae18257)
fmt ✓ · clippy
-D warnings✓ · nextest 1151/1151 ✓ · doctests ✓ · file-size ✓ · determinism mirror 410/410 ✓ · diff scope = oce-expr only ✓🤖 Generated with Claude Code