diff --git a/lib/interscript/isc/grammar/concerns/stages.rb b/lib/interscript/isc/grammar/concerns/stages.rb index 70844407..32a90a3f 100644 --- a/lib/interscript/isc/grammar/concerns/stages.rb +++ b/lib/interscript/isc/grammar/concerns/stages.rb @@ -25,10 +25,11 @@ module Stages whitespace? end - # Comments and stray identifiers are silently consumed. + # Comments are consumed; stray identifiers are a parse error + # (WO87/A4 strict-parse ruling: the corpus is 295/295 strict-clean, + # so a typo'd map can no longer silently no-op). rule(:comment_item) do - (str("#") >> (str("\n").absent? >> any).repeat).as(:comment) | - identifier.as(:noop) + (str("#") >> (str("\n").absent? >> any).repeat).as(:comment) end # Bare rule directly in a stage body (not wrapped in sequence/parallel). diff --git a/spec/isc/semantics_spec.rb b/spec/isc/semantics_spec.rb index 613e8e5c..2c1bc4fa 100644 --- a/spec/isc/semantics_spec.rb +++ b/spec/isc/semantics_spec.rb @@ -228,10 +228,17 @@ def system_map(id, body) ISC end - it "rules: run dispatches the stdlib function" do - with_map("tst-fn-1.isc", system_map(51, "stage main { run downcase }")) do + it "rules: stdlib string functions are stage DIRECTIVES (downcase), not run targets" do + # The strict parse revealed `run downcase` only ever worked by + # accident (noop + case-directive). The sanctioned spelling is the + # bare directive. + with_map("tst-fn-1.isc", system_map(51, "stage main { downcase }")) do expect(Interscript.transliterate("tst-fn-1", "AbC")).to eq("abc") end + with_map("tst-fn-1b.isc", system_map(51, "stage main { run downcase }")) do + expect { Interscript.transliterate("tst-fn-1b", "AbC") } + .to raise_error(Interscript::Isc::ParseError) + end end it "rules: stage-level case directives run at their position, on the whole string" do @@ -249,15 +256,14 @@ def system_map(id, body) end end - it "rules: stray identifiers are consumed as no-ops — pinned as a hazard" do - # HAZARD (A4 follow-up): `run nosuchfunction` drops BOTH identifiers - # at parse time and the stage becomes empty — a typo'd map is - # silently wrong. Ruled normative-for-now (legacy .imp corpus - # tolerance, grammar/concerns/stages.rb comment_item); the stricter - # parse is an open item tracked in the IS-1 v2 text, gated on a - # clean parse of the full 293-map corpus across all three compilers. + it "rules: stray identifiers are a PARSE ERROR (the strict-parse ruling, A4 adopted)" do + # The batch-4 hazard (silent no-op) is CLOSED: the corpus gate + # passed (295/295 strict-clean after the odni-prs stray-'s' fix, + # maps#207), so the grammar rejects stray identifiers — a typo'd + # map fails loudly instead of being silently wrong. with_map("tst-fn-4.isc", system_map(54, "stage main { run nosuchfunction parallel { sub { from \"a\" to \"b\" } } }")) do - expect(Interscript.transliterate("tst-fn-4", "a")).to eq("b") + expect { Interscript.transliterate("tst-fn-4", "a") } + .to raise_error(Interscript::Isc::ParseError) end end end