From 0e57e2ab12fdcb5b1bbea72eaabc0a7e3ce01d16 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Wed, 30 Sep 2026 17:42:53 +0800 Subject: [PATCH] feat: author the ISC grammar as a portable .parg artifact MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isc.parg — the whole grammar (8 concern files, ~55 rules) in the parsanol grammar language: one source compiled to a checksummed artifact that both parsanol-ruby and parsanol npm register natively. Differential gate (spec/support/parg_diff.rb): the artifact tree must equal the Ruby-DSL parser's tree (post-Normalizer) per map. Current corpus standing: 278/285 byte-identical; the rest are 2 files the Ruby parser itself rejects, one parallel-comment capture shape, one parse failure under investigation. Grammar-port invariants pinned by the gate: repetitions are ABNF prefixes (1*x); captures on whole alternations need outer parens; captures on rule references nest inner captures exactly like parslet .as; the item continuation needs the cont_stop/item_atom_start guards or 'to'/'before' get eaten as concat atoms. --- isc.artifact.json | 1 + lib/interscript/isc/grammar/isc.parg | 194 +++++++++++++++++++++++++++ spec/support/parg_diff.rb | 43 ++++++ 3 files changed, 238 insertions(+) create mode 100644 isc.artifact.json create mode 100644 lib/interscript/isc/grammar/isc.parg create mode 100644 spec/support/parg_diff.rb diff --git a/isc.artifact.json b/isc.artifact.json new file mode 100644 index 00000000..712cb792 --- /dev/null +++ b/isc.artifact.json @@ -0,0 +1 @@ +{"version":"0.1.0","grammar":"InterscriptIsc","shape":"parsanol-tree/v2","binding_version":1,"entries":{"isc":{"root":"isc_source","grammar":{"atoms":[{"Entity":{"atom":25}},{"Entity":{"atom":8}},{"Re":{"pattern":"[ ]"}},{"Re":{"pattern":"[\\x09]"}},{"Re":{"pattern":"[\\x0a]"}},{"Re":{"pattern":"[\\x0b]"}},{"Re":{"pattern":"[\\x0c]"}},{"Re":{"pattern":"[\\x0d]"}},{"Alternative":{"atoms":[2,3,4,5,6,7]}},{"Entity":{"atom":23}},{"Str":{"pattern":"#"}},{"Entity":{"atom":17}},{"Re":{"pattern":"[\\x0d]"}},{"Re":{"pattern":"[\\x0a]"}},{"Sequence":{"atoms":[12,13]}},{"Re":{"pattern":"[\\x0a]"}},{"Re":{"pattern":"[\\x0d]"}},{"Alternative":{"atoms":[14,15,16]}},{"Lookahead":{"atom":11,"positive":false}},{"Entity":{"atom":20}},{"Re":{"pattern":"[\\x00-\\u{10ffff}]"}},{"Sequence":{"atoms":[18,19]}},{"Repetition":{"atom":21,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[10,22]}},{"Alternative":{"atoms":[1,9]}},{"Repetition":{"atom":24,"min":1,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":0,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":858}},{"Str":{"pattern":"system"}},{"Entity":{"atom":25}},{"Entity":{"atom":94}},{"Entity":{"atom":82}},{"Str":{"pattern":"\""}},{"Entity":{"atom":79}},{"Entity":{"atom":68}},{"Str":{"pattern":"\\"}},{"Entity":{"atom":38}},{"Str":{"pattern":"n"}},{"Named":{"name":"newline","atom":37}},{"Entity":{"atom":41}},{"Str":{"pattern":"r"}},{"Named":{"name":"carriage_return","atom":40}},{"Entity":{"atom":44}},{"Str":{"pattern":"t"}},{"Named":{"name":"tab","atom":43}},{"Entity":{"atom":47}},{"Str":{"pattern":"\""}},{"Named":{"name":"dquote","atom":46}},{"Entity":{"atom":50}},{"Str":{"pattern":"\\"}},{"Named":{"name":"backslash","atom":49}},{"Entity":{"atom":60}},{"Str":{"pattern":"u"}},{"Entity":{"atom":57}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Re":{"pattern":"[\\x41-\\x46]"}},{"Re":{"pattern":"[\\x61-\\x66]"}},{"Alternative":{"atoms":[54,55,56]}},{"Repetition":{"atom":53,"min":4,"max":4,"tag":"Repetition"}},{"Named":{"name":"unicode","atom":58}},{"Sequence":{"atoms":[52,59]}},{"Entity":{"atom":66}},{"Str":{"pattern":"U"}},{"Entity":{"atom":57}},{"Repetition":{"atom":63,"min":8,"max":8,"tag":"Repetition"}},{"Named":{"name":"unicode","atom":64}},{"Sequence":{"atoms":[62,65]}},{"Alternative":{"atoms":[36,39,42,45,48,51,61]}},{"Sequence":{"atoms":[35,67]}},{"Entity":{"atom":76}},{"Str":{"pattern":"\\"}},{"Str":{"pattern":"\""}},{"Alternative":{"atoms":[70,71]}},{"Lookahead":{"atom":72,"positive":false}},{"Entity":{"atom":20}},{"Sequence":{"atoms":[73,74]}},{"Repetition":{"atom":75,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"run","atom":69}},{"Alternative":{"atoms":[34,77]}},{"Repetition":{"atom":78,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"string","atom":33}},{"Str":{"pattern":"\""}},{"Sequence":{"atoms":[32,80,81]}},{"Entity":{"atom":93}},{"Str":{"pattern":"'"}},{"Entity":{"atom":90}},{"Str":{"pattern":"'"}},{"Lookahead":{"atom":86,"positive":false}},{"Entity":{"atom":20}},{"Sequence":{"atoms":[87,88]}},{"Repetition":{"atom":89,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"string","atom":85}},{"Str":{"pattern":"'"}},{"Sequence":{"atoms":[84,91,92]}},{"Alternative":{"atoms":[31,83]}},{"Named":{"name":"system_code","atom":30}},{"Entity":{"atom":25}},{"Repetition":{"atom":96,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Entity":{"atom":8}},{"Entity":{"atom":23}},{"Alternative":{"atoms":[100,101]}},{"Repetition":{"atom":102,"min":0,"max":null,"tag":"Repetition"}},{"Repetition":{"atom":99,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":850}},{"Entity":{"atom":25}},{"Repetition":{"atom":106,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":319}},{"Str":{"pattern":"metadata"}},{"Entity":{"atom":25}},{"Repetition":{"atom":110,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":113,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":314}},{"Entity":{"atom":313}},{"Entity":{"atom":25}},{"Repetition":{"atom":117,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":140}},{"Str":{"pattern":"description"}},{"Entity":{"atom":25}},{"Repetition":{"atom":121,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":124,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":135}},{"Str":{"pattern":"\\"}},{"Entity":{"atom":20}},{"Sequence":{"atoms":[127,128]}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":130,"positive":false}},{"Entity":{"atom":20}},{"Sequence":{"atoms":[131,132]}},{"Alternative":{"atoms":[129,133]}},{"Repetition":{"atom":134,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"description","atom":126}},{"Entity":{"atom":103}},{"Repetition":{"atom":137,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[120,122,123,125,136,138,139]}},{"Entity":{"atom":180}},{"Str":{"pattern":"relations"}},{"Entity":{"atom":25}},{"Repetition":{"atom":143,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":146,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":174}},{"Entity":{"atom":25}},{"Repetition":{"atom":149,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":159}},{"Str":{"pattern":"supersedes"}},{"Str":{"pattern":"superseded_by"}},{"Str":{"pattern":"based_on"}},{"Str":{"pattern":"basis_for"}},{"Str":{"pattern":"alias_of"}},{"Str":{"pattern":"adopted_from"}},{"Str":{"pattern":"related_to"}},{"Alternative":{"atoms":[152,153,154,155,156,157,158]}},{"Named":{"name":"type","atom":151}},{"Entity":{"atom":25}},{"Entity":{"atom":94}},{"Named":{"name":"system","atom":162}},{"Entity":{"atom":170}},{"Entity":{"atom":25}},{"Str":{"pattern":"note"}},{"Entity":{"atom":25}},{"Entity":{"atom":94}},{"Named":{"name":"note","atom":168}},{"Sequence":{"atoms":[165,166,167,169]}},{"Repetition":{"atom":164,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":25}},{"Repetition":{"atom":172,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[150,160,161,163,171,173]}},{"Repetition":{"atom":148,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"relations","atom":175}},{"Entity":{"atom":103}},{"Repetition":{"atom":177,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[142,144,145,147,176,178,179]}},{"Entity":{"atom":189}},{"Str":{"pattern":"system_status"}},{"Entity":{"atom":25}},{"Str":{"pattern":"current"}},{"Str":{"pattern":"former"}},{"Str":{"pattern":"inactive"}},{"Alternative":{"atoms":[184,185,186]}},{"Named":{"name":"system_status","atom":187}},{"Sequence":{"atoms":[182,183,188]}},{"Entity":{"atom":198}},{"Str":{"pattern":"code_status"}},{"Entity":{"atom":25}},{"Str":{"pattern":"preferred"}},{"Str":{"pattern":"proposed"}},{"Str":{"pattern":"deprecated"}},{"Alternative":{"atoms":[193,194,195]}},{"Named":{"name":"code_status","atom":196}},{"Sequence":{"atoms":[191,192,197]}},{"Entity":{"atom":215}},{"Str":{"pattern":"specification"}},{"Entity":{"atom":25}},{"Entity":{"atom":204}},{"Entity":{"atom":94}},{"Named":{"name":"specification","atom":203}},{"Entity":{"atom":209}},{"Str":{"pattern":","}},{"Entity":{"atom":25}},{"Repetition":{"atom":207,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[206,208]}},{"Entity":{"atom":204}},{"Sequence":{"atoms":[205,210]}},{"Repetition":{"atom":211,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[202,212]}},{"Named":{"name":"specification","atom":213}},{"Sequence":{"atoms":[200,201,214]}},{"Entity":{"atom":239}},{"Str":{"pattern":"notes"}},{"Entity":{"atom":25}},{"Repetition":{"atom":218,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":221,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":233}},{"Entity":{"atom":25}},{"Repetition":{"atom":224,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"note"}},{"Entity":{"atom":25}},{"Repetition":{"atom":227,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":94}},{"Named":{"name":"note","atom":229}},{"Entity":{"atom":25}},{"Repetition":{"atom":231,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[225,226,228,230,232]}},{"Repetition":{"atom":223,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"notes","atom":234}},{"Entity":{"atom":103}},{"Repetition":{"atom":236,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[217,219,220,222,235,237,238]}},{"Entity":{"atom":253}},{"Str":{"pattern":"provenance"}},{"Entity":{"atom":25}},{"Repetition":{"atom":242,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":246}},{"Entity":{"atom":94}},{"Named":{"name":"provenance","atom":245}},{"Entity":{"atom":209}},{"Entity":{"atom":246}},{"Sequence":{"atoms":[247,248]}},{"Repetition":{"atom":249,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[244,250]}},{"Named":{"name":"provenance","atom":251}},{"Sequence":{"atoms":[241,243,252]}},{"Entity":{"atom":309}},{"Entity":{"atom":269}},{"Entity":{"atom":260}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Re":{"pattern":"[_]"}},{"Alternative":{"atoms":[257,258,259]}},{"Entity":{"atom":266}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Re":{"pattern":"[\\x61-\\x7a]"}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Re":{"pattern":"[_]"}},{"Alternative":{"atoms":[262,263,264,265]}},{"Repetition":{"atom":261,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[256,267]}},{"Named":{"name":"identifier","atom":268}},{"Named":{"name":"field_name","atom":255}},{"Entity":{"atom":279}},{"Entity":{"atom":276}},{"Re":{"pattern":"[ ]"}},{"Re":{"pattern":"[\\x09]"}},{"Alternative":{"atoms":[273,274]}},{"Repetition":{"atom":275,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":23}},{"Alternative":{"atoms":[272,277]}},{"Repetition":{"atom":278,"min":0,"max":null,"tag":"Repetition"}},{"Entity":{"atom":293}},{"Entity":{"atom":94}},{"Entity":{"atom":291}},{"Re":{"pattern":"[\\x0a]"}},{"Lookahead":{"atom":283,"positive":false}},{"Str":{"pattern":"}"}},{"Lookahead":{"atom":285,"positive":false}},{"Str":{"pattern":"{"}},{"Lookahead":{"atom":287,"positive":false}},{"Entity":{"atom":20}},{"Sequence":{"atoms":[284,286,288,289]}},{"Repetition":{"atom":290,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"raw","atom":282}},{"Alternative":{"atoms":[281,292]}},{"Named":{"name":"field_value","atom":280}},{"Entity":{"atom":304}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":297,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":135}},{"Named":{"name":"field_block","atom":299}},{"Entity":{"atom":103}},{"Repetition":{"atom":301,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[296,298,300,302,303]}},{"Entity":{"atom":307}},{"Entity":{"atom":17}},{"Lookahead":{"atom":306,"positive":true}},{"Alternative":{"atoms":[294,295,305]}},{"Sequence":{"atoms":[270,271,308]}},{"Alternative":{"atoms":[119,141,181,190,199,216,240,254]}},{"Entity":{"atom":25}},{"Repetition":{"atom":311,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[118,310,312]}},{"Repetition":{"atom":116,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"metadata","atom":115}},{"Entity":{"atom":103}},{"Repetition":{"atom":316,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[109,111,112,114,315,317,318]}},{"Entity":{"atom":559}},{"Str":{"pattern":"aliases"}},{"Entity":{"atom":25}},{"Repetition":{"atom":322,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":325,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":554}},{"Entity":{"atom":553}},{"Entity":{"atom":25}},{"Repetition":{"atom":329,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":269}},{"Named":{"name":"name","atom":331}},{"Entity":{"atom":25}},{"Repetition":{"atom":333,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"="}},{"Entity":{"atom":25}},{"Repetition":{"atom":336,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":549}},{"Entity":{"atom":547}},{"Entity":{"atom":94}},{"Entity":{"atom":343}},{"Str":{"pattern":"none"}},{"Named":{"name":"none","atom":342}},{"Entity":{"atom":352}},{"Str":{"pattern":"boundary"}},{"Str":{"pattern":"line_start"}},{"Str":{"pattern":"line_end"}},{"Str":{"pattern":"word_boundary"}},{"Str":{"pattern":"space"}},{"Str":{"pattern":"non_boundary"}},{"Alternative":{"atoms":[345,346,347,348,349,350]}},{"Named":{"name":"primitive","atom":351}},{"Entity":{"atom":355}},{"Str":{"pattern":"any_character"}},{"Named":{"name":"any_char","atom":354}},{"Entity":{"atom":546}},{"Str":{"pattern":"any"}},{"Str":{"pattern":"("}},{"Entity":{"atom":103}},{"Repetition":{"atom":359,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":541}},{"Entity":{"atom":372}},{"Entity":{"atom":94}},{"Named":{"name":"lo","atom":363}},{"Entity":{"atom":103}},{"Repetition":{"atom":365,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":".."}},{"Entity":{"atom":103}},{"Repetition":{"atom":368,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":94}},{"Named":{"name":"hi","atom":370}},{"Sequence":{"atoms":[364,366,367,369,371]}},{"Entity":{"atom":532}},{"Entity":{"atom":94}},{"Named":{"name":"single","atom":374}},{"Str":{"pattern":"["}},{"Entity":{"atom":103}},{"Repetition":{"atom":377,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":380}},{"Entity":{"atom":519}},{"Entity":{"atom":389}},{"Entity":{"atom":103}},{"Repetition":{"atom":382,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"+"}},{"Entity":{"atom":103}},{"Repetition":{"atom":385,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[383,384,386]}},{"Entity":{"atom":25}},{"Alternative":{"atoms":[387,388]}},{"Entity":{"atom":397}},{"Str":{"pattern":"to"}},{"Str":{"pattern":"before"}},{"Str":{"pattern":"after"}},{"Str":{"pattern":"not_before"}},{"Str":{"pattern":"not_after"}},{"Str":{"pattern":"}"}},{"Alternative":{"atoms":[391,392,393,394,395,396]}},{"Lookahead":{"atom":390,"positive":false}},{"Entity":{"atom":403}},{"Str":{"pattern":"\""}},{"Str":{"pattern":"'"}},{"Entity":{"atom":260}},{"Alternative":{"atoms":[400,401,402]}},{"Lookahead":{"atom":399,"positive":true}},{"Entity":{"atom":515}},{"Entity":{"atom":420}},{"Str":{"pattern":"capture"}},{"Str":{"pattern":"("}},{"Entity":{"atom":103}},{"Repetition":{"atom":409,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":415}},{"Sequence":{"atoms":[381,398,404,405]}},{"Repetition":{"atom":412,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[339,413]}},{"Named":{"name":"concatenation","atom":414}},{"Named":{"name":"capture_inner","atom":411}},{"Entity":{"atom":103}},{"Repetition":{"atom":417,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[407,408,410,416,418,419]}},{"Entity":{"atom":431}},{"Str":{"pattern":"maybe"}},{"Str":{"pattern":"("}},{"Entity":{"atom":103}},{"Repetition":{"atom":424,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":415}},{"Named":{"name":"maybe_inner","atom":426}},{"Entity":{"atom":103}},{"Repetition":{"atom":428,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[422,423,425,427,429,430]}},{"Entity":{"atom":442}},{"Str":{"pattern":"some"}},{"Str":{"pattern":"("}},{"Entity":{"atom":103}},{"Repetition":{"atom":435,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":415}},{"Named":{"name":"some_inner","atom":437}},{"Entity":{"atom":103}},{"Repetition":{"atom":439,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[433,434,436,438,440,441]}},{"Entity":{"atom":451}},{"Str":{"pattern":"upcase"}},{"Str":{"pattern":"downcase"}},{"Str":{"pattern":"title_case"}},{"Str":{"pattern":"reverse"}},{"Str":{"pattern":"strip"}},{"Str":{"pattern":"swapcase"}},{"Alternative":{"atoms":[444,445,446,447,448,449]}},{"Named":{"name":"function","atom":450}},{"Entity":{"atom":466}},{"Entity":{"atom":465}},{"Str":{"pattern":"ref"}},{"Str":{"pattern":"("}},{"Entity":{"atom":103}},{"Repetition":{"atom":456,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":461}},{"Entity":{"atom":460}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Named":{"name":"digit","atom":459}},{"Entity":{"atom":103}},{"Repetition":{"atom":462,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[454,455,457,458,463,464]}},{"Named":{"name":"ref","atom":453}},{"Entity":{"atom":514}},{"Entity":{"atom":503}},{"Str":{"pattern":"parallel"}},{"Str":{"pattern":"sequence"}},{"Str":{"pattern":"stage"}},{"Str":{"pattern":"compose"}},{"Str":{"pattern":"separate"}},{"Str":{"pattern":"system"}},{"Str":{"pattern":"metadata"}},{"Str":{"pattern":"aliases"}},{"Str":{"pattern":"tests"}},{"Str":{"pattern":"notes"}},{"Str":{"pattern":"description"}},{"Str":{"pattern":"any_character"}},{"Str":{"pattern":"authority"}},{"Str":{"pattern":"dependency"}},{"Str":{"pattern":"run"}},{"Str":{"pattern":"sub"}},{"Str":{"pattern":"before"}},{"Str":{"pattern":"after"}},{"Str":{"pattern":"not_before"}},{"Str":{"pattern":"not_after"}},{"Str":{"pattern":"any"}},{"Str":{"pattern":"none"}},{"Str":{"pattern":"boundary"}},{"Str":{"pattern":"line_start"}},{"Str":{"pattern":"line_end"}},{"Str":{"pattern":"word_boundary"}},{"Str":{"pattern":"downcase"}},{"Str":{"pattern":"upcase"}},{"Str":{"pattern":"title_case"}},{"Str":{"pattern":"capture"}},{"Str":{"pattern":"maybe"}},{"Str":{"pattern":"some"}},{"Str":{"pattern":"ref"}},{"Str":{"pattern":"name"}},{"Alternative":{"atoms":[469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502]}},{"Lookahead":{"atom":468,"positive":false}},{"Entity":{"atom":269}},{"Str":{"pattern":"."}},{"Entity":{"atom":269}},{"Named":{"name":"qualified_name","atom":507}},{"Sequence":{"atoms":[506,508]}},{"Repetition":{"atom":509,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Lookahead":{"atom":511,"positive":false}},{"Sequence":{"atoms":[504,505,510,512]}},{"Named":{"name":"alias","atom":513}},{"Alternative":{"atoms":[340,341,344,353,356,406,421,432,443,452,467]}},{"Sequence":{"atoms":[381,398,404,405]}},{"Repetition":{"atom":516,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[339,517]}},{"Named":{"name":"concatenation","atom":518}},{"Entity":{"atom":209}},{"Entity":{"atom":25}},{"Alternative":{"atoms":[520,521]}},{"Entity":{"atom":380}},{"Sequence":{"atoms":[522,523]}},{"Repetition":{"atom":524,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[379,525]}},{"Named":{"name":"list","atom":526}},{"Entity":{"atom":103}},{"Repetition":{"atom":528,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"]"}},{"Sequence":{"atoms":[376,378,527,529,530]}},{"Alternative":{"atoms":[375,531]}},{"Entity":{"atom":540}},{"Entity":{"atom":352}},{"Lookahead":{"atom":534,"positive":false}},{"Entity":{"atom":503}},{"Lookahead":{"atom":536,"positive":false}},{"Entity":{"atom":269}},{"Sequence":{"atoms":[535,537,538]}},{"Named":{"name":"alias_ref","atom":539}},{"Alternative":{"atoms":[362,373,533]}},{"Named":{"name":"any","atom":361}},{"Entity":{"atom":103}},{"Repetition":{"atom":543,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[357,358,360,542,544,545]}},{"Alternative":{"atoms":[340,341,344,353,356,406,421,432,443,452,467]}},{"Sequence":{"atoms":[339,517]}},{"Named":{"name":"concatenation","atom":548}},{"Named":{"name":"value","atom":338}},{"Entity":{"atom":25}},{"Repetition":{"atom":551,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[330,332,334,335,337,550,552]}},{"Repetition":{"atom":328,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"aliases","atom":327}},{"Entity":{"atom":103}},{"Repetition":{"atom":556,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[321,323,324,326,555,557,558]}},{"Entity":{"atom":598}},{"Str":{"pattern":"tests"}},{"Entity":{"atom":25}},{"Repetition":{"atom":562,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":565,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":593}},{"Entity":{"atom":592}},{"Entity":{"atom":25}},{"Repetition":{"atom":569,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":94}},{"Named":{"name":"input","atom":571}},{"Entity":{"atom":579}},{"Entity":{"atom":25}},{"Repetition":{"atom":574,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"->"}},{"Entity":{"atom":25}},{"Repetition":{"atom":577,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[575,576,578]}},{"Entity":{"atom":94}},{"Named":{"name":"expected","atom":580}},{"Entity":{"atom":588}},{"Entity":{"atom":25}},{"Str":{"pattern":"note"}},{"Entity":{"atom":25}},{"Entity":{"atom":94}},{"Named":{"name":"note","atom":586}},{"Sequence":{"atoms":[583,584,585,587]}},{"Repetition":{"atom":582,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":25}},{"Repetition":{"atom":590,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[570,572,573,581,589,591]}},{"Repetition":{"atom":568,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"tests","atom":567}},{"Entity":{"atom":103}},{"Repetition":{"atom":595,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[561,563,564,566,594,596,597]}},{"Entity":{"atom":832}},{"Str":{"pattern":"stage"}},{"Entity":{"atom":606}},{"Str":{"pattern":"("}},{"Entity":{"atom":269}},{"Named":{"name":"stage_name","atom":603}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[602,604,605]}},{"Entity":{"atom":611}},{"Entity":{"atom":25}},{"Entity":{"atom":269}},{"Named":{"name":"stage_name","atom":609}},{"Sequence":{"atoms":[608,610]}},{"Alternative":{"atoms":[601,607]}},{"Entity":{"atom":103}},{"Repetition":{"atom":613,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":616,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":827}},{"Entity":{"atom":826}},{"Entity":{"atom":103}},{"Repetition":{"atom":620,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":724}},{"Str":{"pattern":"sequence"}},{"Entity":{"atom":103}},{"Repetition":{"atom":624,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":627,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":719}},{"Entity":{"atom":718}},{"Entity":{"atom":103}},{"Repetition":{"atom":631,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":699}},{"Entity":{"atom":684}},{"Str":{"pattern":"sub"}},{"Entity":{"atom":103}},{"Repetition":{"atom":636,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":639,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"from"}},{"Entity":{"atom":25}},{"Entity":{"atom":549}},{"Named":{"name":"from","atom":643}},{"Entity":{"atom":103}},{"Repetition":{"atom":645,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"to"}},{"Entity":{"atom":25}},{"Entity":{"atom":549}},{"Named":{"name":"to","atom":649}},{"Entity":{"atom":25}},{"Entity":{"atom":677}},{"Entity":{"atom":658}},{"Str":{"pattern":"after"}},{"Entity":{"atom":25}},{"Entity":{"atom":549}},{"Named":{"name":"after","atom":656}},{"Sequence":{"atoms":[654,655,657]}},{"Entity":{"atom":664}},{"Str":{"pattern":"before"}},{"Entity":{"atom":25}},{"Entity":{"atom":549}},{"Named":{"name":"before","atom":662}},{"Sequence":{"atoms":[660,661,663]}},{"Entity":{"atom":670}},{"Str":{"pattern":"not_before"}},{"Entity":{"atom":25}},{"Entity":{"atom":549}},{"Named":{"name":"not_before","atom":668}},{"Sequence":{"atoms":[666,667,669]}},{"Entity":{"atom":676}},{"Str":{"pattern":"not_after"}},{"Entity":{"atom":25}},{"Entity":{"atom":549}},{"Named":{"name":"not_after","atom":674}},{"Sequence":{"atoms":[672,673,675]}},{"Alternative":{"atoms":[653,659,665,671]}},{"Sequence":{"atoms":[651,652]}},{"Repetition":{"atom":678,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"constraints","atom":679}},{"Entity":{"atom":103}},{"Repetition":{"atom":681,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[635,637,638,640,641,642,644,646,647,648,650,680,682,683]}},{"Entity":{"atom":698}},{"Str":{"pattern":"sub"}},{"Entity":{"atom":25}},{"Entity":{"atom":547}},{"Named":{"name":"from","atom":688}},{"Entity":{"atom":25}},{"Entity":{"atom":547}},{"Named":{"name":"to","atom":691}},{"Entity":{"atom":25}},{"Entity":{"atom":677}},{"Sequence":{"atoms":[693,694]}},{"Repetition":{"atom":695,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"constraints","atom":696}},{"Sequence":{"atoms":[686,687,689,690,692,697]}},{"Alternative":{"atoms":[634,685]}},{"Entity":{"atom":714}},{"Str":{"pattern":"#"}},{"Re":{"pattern":"[\\x0a]"}},{"Lookahead":{"atom":702,"positive":false}},{"Entity":{"atom":20}},{"Sequence":{"atoms":[703,704]}},{"Repetition":{"atom":705,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[701,706]}},{"Named":{"name":"comment","atom":707}},{"Entity":{"atom":503}},{"Lookahead":{"atom":709,"positive":false}},{"Entity":{"atom":269}},{"Sequence":{"atoms":[710,711]}},{"Named":{"name":"noop","atom":712}},{"Alternative":{"atoms":[708,713]}},{"Alternative":{"atoms":[633,700]}},{"Entity":{"atom":103}},{"Repetition":{"atom":716,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[632,715,717]}},{"Repetition":{"atom":630,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"sequence","atom":629}},{"Entity":{"atom":103}},{"Repetition":{"atom":721,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[623,625,626,628,720,722,723]}},{"Entity":{"atom":737}},{"Str":{"pattern":"parallel"}},{"Entity":{"atom":103}},{"Repetition":{"atom":727,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"{"}},{"Entity":{"atom":103}},{"Repetition":{"atom":730,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":719}},{"Named":{"name":"parallel","atom":732}},{"Entity":{"atom":103}},{"Repetition":{"atom":734,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[726,728,729,731,733,735,736]}},{"Entity":{"atom":756}},{"Str":{"pattern":"run"}},{"Entity":{"atom":25}},{"Entity":{"atom":748}},{"Str":{"pattern":"map."}},{"Entity":{"atom":269}},{"Named":{"name":"dep","atom":743}},{"Str":{"pattern":".stage."}},{"Entity":{"atom":269}},{"Named":{"name":"stage","atom":746}},{"Sequence":{"atoms":[742,744,745,747]}},{"Entity":{"atom":754}},{"Str":{"pattern":"stage."}},{"Entity":{"atom":269}},{"Named":{"name":"stage","atom":751}},{"Sequence":{"atoms":[750,752]}},{"Named":{"name":"run_stage_only","atom":753}},{"Alternative":{"atoms":[741,749]}},{"Sequence":{"atoms":[739,740,755]}},{"Entity":{"atom":768}},{"Str":{"pattern":"separate"}},{"Named":{"name":"separate","atom":758}},{"Entity":{"atom":766}},{"Entity":{"atom":25}},{"Str":{"pattern":"separator"}},{"Entity":{"atom":25}},{"Entity":{"atom":547}},{"Named":{"name":"separator","atom":764}},{"Sequence":{"atoms":[761,762,763,765]}},{"Repetition":{"atom":760,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[759,767]}},{"Entity":{"atom":803}},{"Entity":{"atom":774}},{"Str":{"pattern":"downcase"}},{"Str":{"pattern":"upcase"}},{"Alternative":{"atoms":[771,772]}},{"Named":{"name":"case","atom":773}},{"Entity":{"atom":802}},{"Str":{"pattern":"title_case"}},{"Named":{"name":"case","atom":776}},{"Entity":{"atom":800}},{"Entity":{"atom":25}},{"Entity":{"atom":798}},{"Entity":{"atom":793}},{"Entity":{"atom":792}},{"Entity":{"atom":269}},{"Named":{"name":"kwarg_name","atom":783}},{"Entity":{"atom":103}},{"Repetition":{"atom":785,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":":"}},{"Entity":{"atom":103}},{"Repetition":{"atom":788,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":94}},{"Named":{"name":"kwarg_value","atom":790}},{"Sequence":{"atoms":[784,786,787,789,791]}},{"Named":{"name":"kwarg","atom":782}},{"Entity":{"atom":209}},{"Entity":{"atom":793}},{"Sequence":{"atoms":[794,795]}},{"Repetition":{"atom":796,"min":0,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[781,797]}},{"Named":{"name":"case_kwargs","atom":780}},{"Sequence":{"atoms":[779,799]}},{"Repetition":{"atom":778,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[777,801]}},{"Alternative":{"atoms":[770,775]}},{"Entity":{"atom":808}},{"Str":{"pattern":"compose"}},{"Str":{"pattern":"decompose"}},{"Alternative":{"atoms":[805,806]}},{"Named":{"name":"compose","atom":807}},{"Entity":{"atom":818}},{"Str":{"pattern":"rababa"}},{"Named":{"name":"funcall_name","atom":810}},{"Entity":{"atom":816}},{"Entity":{"atom":25}},{"Entity":{"atom":798}},{"Named":{"name":"funcall_kwargs","atom":814}},{"Sequence":{"atoms":[813,815]}},{"Repetition":{"atom":812,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[811,817]}},{"Entity":{"atom":821}},{"Entity":{"atom":699}},{"Named":{"name":"bare_rule","atom":820}},{"Entity":{"atom":714}},{"Alternative":{"atoms":[622,725,738,757,769,804,809,819,822]}},{"Entity":{"atom":103}},{"Repetition":{"atom":824,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[621,823,825]}},{"Repetition":{"atom":619,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"stage","atom":618}},{"Entity":{"atom":103}},{"Repetition":{"atom":829,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Sequence":{"atoms":[600,612,614,615,617,828,830,831]}},{"Entity":{"atom":846}},{"Str":{"pattern":"dependency"}},{"Entity":{"atom":25}},{"Entity":{"atom":94}},{"Named":{"name":"target","atom":836}},{"Entity":{"atom":844}},{"Entity":{"atom":25}},{"Str":{"pattern":"as"}},{"Entity":{"atom":25}},{"Entity":{"atom":269}},{"Named":{"name":"alias","atom":842}},{"Sequence":{"atoms":[839,840,841,843]}},{"Repetition":{"atom":838,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[834,835,837,845]}},{"Alternative":{"atoms":[108,320,560,599,833]}},{"Entity":{"atom":25}},{"Repetition":{"atom":848,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[107,847,849]}},{"Repetition":{"atom":105,"min":0,"max":null,"tag":"Repetition"}},{"Named":{"name":"body","atom":851}},{"Entity":{"atom":103}},{"Repetition":{"atom":853,"min":0,"max":1,"tag":"Maybe"}},{"Str":{"pattern":"}"}},{"Entity":{"atom":25}},{"Repetition":{"atom":856,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[28,29,95,97,98,104,852,854,855,857]}},{"Named":{"name":"system","atom":27}},{"Entity":{"atom":25}},{"Repetition":{"atom":860,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[26,859,861]}}],"root":862},"bindings":[]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule newline: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 2 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 2 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 2 and 16 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 2 and 32 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 3 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 3 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 3 and 16 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 3 and 32 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 4 and 30 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 5 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 5 and 16 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 5 and 32 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 6 and 16 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 6 and 32 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 7 and 31 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 8 and 12 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 8 and 13 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 8 and 18 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 8 and 21 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 9 and 29 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 10 and 19 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 10 and 20 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 10 and 22 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 10 and 34 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 11 and 14 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 11 and 27 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 12 and 13 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 12 and 18 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 12 and 21 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 13 and 18 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 13 and 21 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 14 and 27 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 15 and 33 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 16 and 32 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 17 and 23 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 18 and 21 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 19 and 20 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 19 and 22 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 19 and 34 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 20 and 22 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 20 and 34 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 22 and 34 are order-dependent (shared first bytes; ordered choice is decisive)","rule keyword: branches 24 and 25 are order-dependent (shared first bytes; ordered choice is decisive)","rule relation_type: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule relation_type: branches 3 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule relation_type: branches 5 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule md_field_value: branches 1 and 2 are order-dependent (first set not statically known)","rule item_sep: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule any_arg: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule any_arg: branches 1 and 3 are order-dependent (first set not statically known)","rule any_arg: branches 2 and 3 are order-dependent (first set not statically known)","rule cont_stop: branches 4 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule item_atom: branches 1 and 11 are order-dependent (first set not statically known)","rule item_atom: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule item_atom: branches 2 and 11 are order-dependent (first set not statically known)","rule item_atom: branches 3 and 8 are order-dependent (shared first bytes; ordered choice is decisive)","rule item_atom: branches 3 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule item_atom: branches 3 and 11 are order-dependent (first set not statically known)","rule item_atom: branches 4 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule item_atom: branches 4 and 11 are order-dependent (first set not statically known)","rule item_atom: branches 5 and 11 are order-dependent (first set not statically known)","rule item_atom: branches 6 and 11 are order-dependent (first set not statically known)","rule item_atom: branches 7 and 11 are order-dependent (first set not statically known)","rule item_atom: branches 8 and 9 are order-dependent (shared first bytes; ordered choice is decisive)","rule item_atom: branches 8 and 11 are order-dependent (first set not statically known)","rule item_atom: branches 9 and 10 are order-dependent (shared first bytes; ordered choice is decisive)","rule item_atom: branches 9 and 11 are order-dependent (first set not statically known)","rule item_atom: branches 10 and 11 are order-dependent (first set not statically known)","rule constraint: branches 3 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule rule: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule comment_item: branches 1 and 2 are order-dependent (first set not statically known)"]},"default_entry":"isc","render":{},"derive":{},"tests":[],"docs":{},"source":"# Interscript ISC grammar — parsanol grammar language (PG).\n#\n# Line-oriented port of lib/interscript/isc/grammar/concerns/*.rb.\n# The capture tree must reproduce the Ruby-DSL parser's tree exactly:\n# - repetitions are ABNF prefixes (1*x, *x)\n# - negation is !x, positive lookahead is &x\n# - captures at use sites (rule_ref as name) nest inner captures\n# the same way parslet .as does\n#\n# Consumed by the interscript gem (artifact parse) and parsanol npm.\n\ngrammar InterscriptIsc version \"0.1.0\" {\n\n # -- lexical primitives -------------------------------------------------\n\n chr = %x00-10FFFF\n space_char = %x20 / %x09 / %x0A / %x0B / %x0C / %x0D\n newline = %x0D.0A / %x0A / %x0D\n line_comment = \"#\" *(!(newline) chr)\n whitespace = 1*(space_char / line_comment)\n ws = *(space_char / line_comment)\n inline_space = 1*(%x20 / %x09)\n inline_ws = *(inline_space / line_comment)\n hexd = %x30-39 / %x41-46 / %x61-66\n digit = %x30-39\n\n ident_first = %x41-5A / %x61-7A / %x5F\n ident_rest = %x41-5A / %x61-7A / %x30-39 / %x5F\n identifier = (ident_first 0*ident_rest) as identifier\n\n keyword = \"parallel\" / \"sequence\" / \"stage\" / \"compose\" / \"separate\" / \"system\" / \"metadata\" / \"aliases\" / \"tests\" / \"notes\" / \"description\" / \"any_character\" / \"authority\" / \"dependency\" / \"run\" / \"sub\" / \"before\" / \"after\" / \"not_before\" / \"not_after\" / \"any\" / \"none\" / \"boundary\" / \"line_start\" / \"line_end\" / \"word_boundary\" / \"downcase\" / \"upcase\" / \"title_case\" / \"capture\" / \"maybe\" / \"some\" / \"ref\" / \"name\"\n\n # -- string literals ----------------------------------------------------\n\n esc_nl = (\"n\") as newline\n esc_cr = (\"r\") as carriage_return\n esc_tab = (\"t\") as tab\n esc_dq = \"\\\"\" as dquote\n esc_bs = \"\\\\\" as backslash\n esc_uni4 = \"u\" (4hexd) as unicode\n esc_uni8 = \"U\" (8hexd) as unicode\n escape_sequence = \"\\\\\" (esc_nl / esc_cr / esc_tab / esc_dq / esc_bs / esc_uni4 / esc_uni8)\n\n raw_run = 1*(!(\"\\\\\" / \"\\\"\") chr)\n dq_parts = *(escape_sequence / (raw_run as run))\n double_quoted = \"\\\"\" (dq_parts as string) \"\\\"\"\n sq_body = *(!\"'\" chr)\n single_quoted = \"'\" (sq_body as string) \"'\"\n quoted_string = double_quoted / single_quoted\n\n arrow = [whitespace] \"->\" [whitespace]\n comma_ws = \",\" [whitespace]\n\n # -- metadata -----------------------------------------------------------\n\n md_authority = \"authority\" whitespace ((quoted_string) as authority)\n md_source_spelling = \"source_spelling\" whitespace ((quoted_string) as source_spelling)\n md_target_spelling = \"target_spelling\" whitespace ((quoted_string) as target_spelling)\n md_identifying = \"identifying\" whitespace ((quoted_string) as identifying)\n md_name = \"name\" whitespace ((quoted_string) as name)\n md_spec_value = quoted_string as specification\n md_specification = \"specification\" whitespace ((md_spec_value *(comma_ws md_spec_value)) as specification)\n md_description = \"description\" [whitespace] \"{\" [ws] (raw_text as description) [ws] \"}\"\n md_system_status = \"system_status\" whitespace ((\"current\" / \"former\" / \"inactive\") as system_status)\n md_code_status = \"code_status\" whitespace ((\"preferred\" / \"proposed\" / \"deprecated\") as code_status)\n\n relation_type = \"supersedes\" / \"superseded_by\" / \"based_on\" / \"basis_for\" / \"alias_of\" / \"adopted_from\" / \"related_to\"\n relation_note = whitespace \"note\" whitespace (quoted_string as note)\n relation_item = [whitespace] (relation_type as type) whitespace (quoted_string as system) [(relation_note)] [whitespace]\n md_relations = \"relations\" [whitespace] \"{\" [ws] ((*(relation_item)) as relations) [ws] \"}\"\n\n note_item = [whitespace] \"note\" [whitespace] (quoted_string as note) [whitespace]\n md_notes = \"notes\" [whitespace] \"{\" [ws] ((*(note_item)) as notes) [ws] \"}\"\n\n md_provenance_value = quoted_string as provenance\n md_provenance = \"provenance\" [whitespace] ((md_provenance_value *(comma_ws md_provenance_value)) as provenance)\n\n md_empty_field = &(newline)\n md_raw_field_value = 1*(!%x0A !\"}\" !\"{\" chr)\n md_field_value = quoted_string / (md_raw_field_value as raw)\n md_field_block = \"{\" [ws] (raw_text as field_block) [ws] \"}\"\n md_generic_field = (identifier as field_name) inline_ws ((md_field_value as field_value) / md_field_block / md_empty_field)\n\n metadata_field = [whitespace] (md_description / md_relations / md_system_status / md_code_status / md_specification / md_notes / md_provenance / md_generic_field) [whitespace]\n md_items = *(metadata_field)\n metadata_block = \"metadata\" [whitespace] \"{\" [ws] (md_items as metadata) [ws] \"}\"\n\n raw_text = *(\"\\\\\" chr / (!\"}\" chr))\n\n # -- aliases ------------------------------------------------------------\n\n alias_decl = [whitespace] (identifier as name) [whitespace] \"=\" [whitespace] (item as value) [whitespace]\n alias_items = *(alias_decl)\n aliases_block = \"aliases\" [whitespace] \"{\" [ws] (alias_items as aliases) [ws] \"}\"\n\n # -- dependencies -------------------------------------------------------\n\n dep_alias = whitespace \"as\" whitespace (identifier as alias)\n dependency_decl = \"dependency\" whitespace (quoted_string as target) [(dep_alias)]\n\n # -- item expressions ---------------------------------------------------\n\n zw_primitive = ((\"boundary\") / (\"line_start\") / (\"line_end\") / (\"word_boundary\") / (\"space\") / (\"non_boundary\")) as primitive\n fn_lit = ((\"upcase\") / (\"downcase\") / (\"title_case\") / (\"reverse\") / (\"strip\") / (\"swapcase\")) as function\n any_char_lit = (\"any_character\") as any_char\n none_lit = (\"none\") as none\n\n digit_cap = digit as digit\n capture_reference_body = \"ref\" \"(\" [ws] digit_cap [ws] \")\"\n capture_reference = capture_reference_body as ref\n\n item_ref = identifier \".\" (identifier as qualified_name)\n alias_reference = (!(keyword) identifier [(\".\" (identifier as qualified_name))] !\"{\" ) as alias\n\n item_seq = item_atom\n item_sep = ([ws] \"+\" [ws]) / whitespace\n item_cont = &item_atom\n\n range_arg = (quoted_string as lo) [ws] \"..\" [ws] (quoted_string as hi)\n list_item = item\n list_items = (list_item *((comma_ws / whitespace) list_item))\n set_arg = ((quoted_string as single)) / (\"[\" [ws] ((list_item *((comma_ws / whitespace) list_item)) as list) [ws] \"]\")\n alias_arg = (!zw_primitive !(keyword) identifier) as alias_ref\n any_arg = range_arg / set_arg / alias_arg\n any_ctor = \"any\" \"(\" [ws] (any_arg as any) [ws] \")\"\n capture_ctor = \"capture\" \"(\" [ws] (item as capture_inner) [ws] \")\"\n maybe_ctor = \"maybe\" \"(\" [ws] (item as maybe_inner) [ws] \")\"\n some_ctor = \"some\" \"(\" [ws] (item as some_inner) [ws] \")\"\n\n item_atom_start = \"\\\"\" / \"'\" / ident_first\n cont_stop = \"to\" / \"before\" / \"after\" / \"not_before\" / \"not_after\" / \"}\"\n item_atom = quoted_string / none_lit / zw_primitive / any_char_lit / any_ctor / capture_ctor / maybe_ctor / some_ctor / fn_lit / capture_reference / alias_reference\n item = (item_atom 0*(item_sep !cont_stop &item_atom_start item_atom)) as concatenation\n\n # -- rules and stages ---------------------------------------------------\n\n constraint_after = \"after\" whitespace (item as after)\n constraint_before = \"before\" whitespace (item as before)\n constraint_not_before = \"not_before\" whitespace (item as not_before)\n constraint_not_after = \"not_after\" whitespace (item as not_after)\n constraint = constraint_after / constraint_before / constraint_not_before / constraint_not_after\n constraints = *(whitespace constraint)\n\n compact_rule = \"sub\" whitespace (item_atom as from) whitespace (item_atom as to) ((*(whitespace constraint)) as constraints)\n\n block_rule = \"sub\" [ws] \"{\" [ws] \"from\" whitespace (item as from) [ws] \"to\" whitespace (item as to) ((*(whitespace constraint)) as constraints) [ws] \"}\"\n\n rule = block_rule / compact_rule\n\n comment_item = ((\"#\" *(!%x0A chr)) as comment) / ((!(keyword) identifier) as noop)\n rule_line = [ws] (rule / comment_item) [ws]\n rule_items = *(rule_line)\n sequence_block = \"sequence\" [ws] \"{\" [ws] (rule_items as sequence) [ws] \"}\"\n parallel_block = \"parallel\" [ws] \"{\" [ws] (rule_items as parallel) [ws] \"}\"\n\n separator_part = whitespace \"separator\" whitespace (item_atom as separator)\n separate_directive = (\"separate\") as separate [(separator_part)]\n case_directive = ((\"downcase\") / (\"upcase\")) as case\n case_kwargs_part = whitespace (kwarg_list as case_kwargs)\n title_case_directive = (\"title_case\") as case [(case_kwargs_part)]\n string_case_directive = case_directive / title_case_directive\n compose_directive = ((\"compose\") / (\"decompose\")) as compose\n kwarg = (identifier as kwarg_name) [ws] \":\" [ws] ((quoted_string) as kwarg_value)\n kwarg_item = (kwarg) as kwarg\n kwarg_list = (kwarg_item *(comma_ws kwarg_item))\n funcall_kwargs_part = whitespace (kwarg_list as funcall_kwargs)\n rababa_directive = (\"rababa\") as funcall_name [(funcall_kwargs_part)]\n run_dep = \"map.\" (identifier as dep) \".stage.\" (identifier as stage)\n run_stage_only = (\"stage.\" (identifier as stage)) as run_stage_only\n run_rule = \"run\" whitespace ((run_dep) / run_stage_only)\n\n bare_rule = (rule) as bare_rule\n stage_item = [ws] (sequence_block / parallel_block / run_rule / separate_directive / string_case_directive / compose_directive / rababa_directive / bare_rule / comment_item) [ws]\n stage_items = *(stage_item)\n stage_name_paren = \"(\" (identifier as stage_name) \")\"\n stage_name_ws = whitespace (identifier as stage_name)\n stage_block = \"stage\" (stage_name_paren / stage_name_ws) [ws] \"{\" [ws] (stage_items as stage) [ws] \"}\"\n\n # -- tests ---------------------------------------------------------------\n\n test_note = whitespace \"note\" whitespace (quoted_string as note)\n test_line = [whitespace] (quoted_string as input) arrow (quoted_string as expected) [(test_note)] [whitespace]\n test_items = *(test_line)\n tests_block = \"tests\" [whitespace] \"{\" [ws] (test_items as tests) [ws] \"}\"\n\n # -- system --------------------------------------------------------------\n\n block_item = [whitespace] (metadata_block / aliases_block / tests_block / stage_block / dependency_decl) [whitespace]\n body_items = *(block_item)\n system_block = \"system\" whitespace ((quoted_string) as system_code) [whitespace] \"{\" [ws] ((*(block_item)) as body) [ws] \"}\" [whitespace]\n isc_source = [whitespace] ((system_block) as system) [whitespace]\n\n entry isc: isc_source\n}\n","checksum":"sha256:5191a1c12a17071f604b0f09506439c7c52f0d85484591da0a4f02654349b8e1"} diff --git a/lib/interscript/isc/grammar/isc.parg b/lib/interscript/isc/grammar/isc.parg new file mode 100644 index 00000000..fd8e710b --- /dev/null +++ b/lib/interscript/isc/grammar/isc.parg @@ -0,0 +1,194 @@ +# Interscript ISC grammar — parsanol grammar language (PG). +# +# Line-oriented port of lib/interscript/isc/grammar/concerns/*.rb. +# The capture tree must reproduce the Ruby-DSL parser's tree exactly: +# - repetitions are ABNF prefixes (1*x, *x) +# - negation is !x, positive lookahead is &x +# - captures at use sites (rule_ref as name) nest inner captures +# the same way parslet .as does +# +# Consumed by the interscript gem (artifact parse) and parsanol npm. + +grammar InterscriptIsc version "0.1.0" { + + # -- lexical primitives ------------------------------------------------- + + chr = %x00-10FFFF + space_char = %x20 / %x09 / %x0A / %x0B / %x0C / %x0D + newline = %x0D.0A / %x0A / %x0D + line_comment = "#" *(!(newline) chr) + whitespace = 1*(space_char / line_comment) + ws = *(space_char / line_comment) + inline_space = 1*(%x20 / %x09) + inline_ws = *(inline_space / line_comment) + hexd = %x30-39 / %x41-46 / %x61-66 + digit = %x30-39 + + ident_first = %x41-5A / %x61-7A / %x5F + ident_rest = %x41-5A / %x61-7A / %x30-39 / %x5F + identifier = (ident_first 0*ident_rest) as identifier + + keyword = "parallel" / "sequence" / "stage" / "compose" / "separate" / "system" / "metadata" / "aliases" / "tests" / "notes" / "description" / "any_character" / "authority" / "dependency" / "run" / "sub" / "before" / "after" / "not_before" / "not_after" / "any" / "none" / "boundary" / "line_start" / "line_end" / "word_boundary" / "downcase" / "upcase" / "title_case" / "capture" / "maybe" / "some" / "ref" / "name" + + # -- string literals ---------------------------------------------------- + + esc_nl = ("n") as newline + esc_cr = ("r") as carriage_return + esc_tab = ("t") as tab + esc_dq = "\"" as dquote + esc_bs = "\\" as backslash + esc_uni4 = "u" (4hexd) as unicode + esc_uni8 = "U" (8hexd) as unicode + escape_sequence = "\\" (esc_nl / esc_cr / esc_tab / esc_dq / esc_bs / esc_uni4 / esc_uni8) + + raw_run = 1*(!("\\" / "\"") chr) + dq_parts = *(escape_sequence / (raw_run as run)) + double_quoted = "\"" (dq_parts as string) "\"" + sq_body = *(!"'" chr) + single_quoted = "'" (sq_body as string) "'" + quoted_string = double_quoted / single_quoted + + arrow = [whitespace] "->" [whitespace] + comma_ws = "," [whitespace] + + # -- metadata ----------------------------------------------------------- + + md_authority = "authority" whitespace ((quoted_string) as authority) + md_source_spelling = "source_spelling" whitespace ((quoted_string) as source_spelling) + md_target_spelling = "target_spelling" whitespace ((quoted_string) as target_spelling) + md_identifying = "identifying" whitespace ((quoted_string) as identifying) + md_name = "name" whitespace ((quoted_string) as name) + md_spec_value = quoted_string as specification + md_specification = "specification" whitespace ((md_spec_value *(comma_ws md_spec_value)) as specification) + md_description = "description" [whitespace] "{" [ws] (raw_text as description) [ws] "}" + md_system_status = "system_status" whitespace (("current" / "former" / "inactive") as system_status) + md_code_status = "code_status" whitespace (("preferred" / "proposed" / "deprecated") as code_status) + + relation_type = "supersedes" / "superseded_by" / "based_on" / "basis_for" / "alias_of" / "adopted_from" / "related_to" + relation_note = whitespace "note" whitespace (quoted_string as note) + relation_item = [whitespace] (relation_type as type) whitespace (quoted_string as system) [(relation_note)] [whitespace] + md_relations = "relations" [whitespace] "{" [ws] ((*(relation_item)) as relations) [ws] "}" + + note_item = [whitespace] "note" [whitespace] (quoted_string as note) [whitespace] + md_notes = "notes" [whitespace] "{" [ws] ((*(note_item)) as notes) [ws] "}" + + md_provenance_value = quoted_string as provenance + md_provenance = "provenance" [whitespace] ((md_provenance_value *(comma_ws md_provenance_value)) as provenance) + + md_empty_field = &(newline) + md_raw_field_value = 1*(!%x0A !"}" !"{" chr) + md_field_value = quoted_string / (md_raw_field_value as raw) + md_field_block = "{" [ws] (raw_text as field_block) [ws] "}" + md_generic_field = (identifier as field_name) inline_ws ((md_field_value as field_value) / md_field_block / md_empty_field) + + metadata_field = [whitespace] (md_description / md_relations / md_system_status / md_code_status / md_specification / md_notes / md_provenance / md_generic_field) [whitespace] + md_items = *(metadata_field) + metadata_block = "metadata" [whitespace] "{" [ws] (md_items as metadata) [ws] "}" + + raw_text = *("\\" chr / (!"}" chr)) + + # -- aliases ------------------------------------------------------------ + + alias_decl = [whitespace] (identifier as name) [whitespace] "=" [whitespace] (item as value) [whitespace] + alias_items = *(alias_decl) + aliases_block = "aliases" [whitespace] "{" [ws] (alias_items as aliases) [ws] "}" + + # -- dependencies ------------------------------------------------------- + + dep_alias = whitespace "as" whitespace (identifier as alias) + dependency_decl = "dependency" whitespace (quoted_string as target) [(dep_alias)] + + # -- item expressions --------------------------------------------------- + + zw_primitive = (("boundary") / ("line_start") / ("line_end") / ("word_boundary") / ("space") / ("non_boundary")) as primitive + fn_lit = (("upcase") / ("downcase") / ("title_case") / ("reverse") / ("strip") / ("swapcase")) as function + any_char_lit = ("any_character") as any_char + none_lit = ("none") as none + + digit_cap = digit as digit + capture_reference_body = "ref" "(" [ws] digit_cap [ws] ")" + capture_reference = capture_reference_body as ref + + item_ref = identifier "." (identifier as qualified_name) + alias_reference = (!(keyword) identifier [("." (identifier as qualified_name))] !"{" ) as alias + + item_seq = item_atom + item_sep = ([ws] "+" [ws]) / whitespace + item_cont = &item_atom + + range_arg = (quoted_string as lo) [ws] ".." [ws] (quoted_string as hi) + list_item = item + list_items = (list_item *((comma_ws / whitespace) list_item)) + set_arg = ((quoted_string as single)) / ("[" [ws] ((list_item *((comma_ws / whitespace) list_item)) as list) [ws] "]") + alias_arg = (!zw_primitive !(keyword) identifier) as alias_ref + any_arg = range_arg / set_arg / alias_arg + any_ctor = "any" "(" [ws] (any_arg as any) [ws] ")" + capture_ctor = "capture" "(" [ws] (item as capture_inner) [ws] ")" + maybe_ctor = "maybe" "(" [ws] (item as maybe_inner) [ws] ")" + some_ctor = "some" "(" [ws] (item as some_inner) [ws] ")" + + item_atom_start = "\"" / "'" / ident_first + cont_stop = "to" / "before" / "after" / "not_before" / "not_after" / "}" + item_atom = quoted_string / none_lit / zw_primitive / any_char_lit / any_ctor / capture_ctor / maybe_ctor / some_ctor / fn_lit / capture_reference / alias_reference + item = (item_atom 0*(item_sep !cont_stop &item_atom_start item_atom)) as concatenation + + # -- rules and stages --------------------------------------------------- + + constraint_after = "after" whitespace (item as after) + constraint_before = "before" whitespace (item as before) + constraint_not_before = "not_before" whitespace (item as not_before) + constraint_not_after = "not_after" whitespace (item as not_after) + constraint = constraint_after / constraint_before / constraint_not_before / constraint_not_after + constraints = *(whitespace constraint) + + compact_rule = "sub" whitespace (item_atom as from) whitespace (item_atom as to) ((*(whitespace constraint)) as constraints) + + block_rule = "sub" [ws] "{" [ws] "from" whitespace (item as from) [ws] "to" whitespace (item as to) ((*(whitespace constraint)) as constraints) [ws] "}" + + rule = block_rule / compact_rule + + comment_item = (("#" *(!%x0A chr)) as comment) / ((!(keyword) identifier) as noop) + rule_line = [ws] (rule / comment_item) [ws] + rule_items = *(rule_line) + sequence_block = "sequence" [ws] "{" [ws] (rule_items as sequence) [ws] "}" + parallel_block = "parallel" [ws] "{" [ws] (rule_items as parallel) [ws] "}" + + separator_part = whitespace "separator" whitespace (item_atom as separator) + separate_directive = ("separate") as separate [(separator_part)] + case_directive = (("downcase") / ("upcase")) as case + case_kwargs_part = whitespace (kwarg_list as case_kwargs) + title_case_directive = ("title_case") as case [(case_kwargs_part)] + string_case_directive = case_directive / title_case_directive + compose_directive = (("compose") / ("decompose")) as compose + kwarg = (identifier as kwarg_name) [ws] ":" [ws] ((quoted_string) as kwarg_value) + kwarg_item = (kwarg) as kwarg + kwarg_list = (kwarg_item *(comma_ws kwarg_item)) + funcall_kwargs_part = whitespace (kwarg_list as funcall_kwargs) + rababa_directive = ("rababa") as funcall_name [(funcall_kwargs_part)] + run_dep = "map." (identifier as dep) ".stage." (identifier as stage) + run_stage_only = ("stage." (identifier as stage)) as run_stage_only + run_rule = "run" whitespace ((run_dep) / run_stage_only) + + bare_rule = (rule) as bare_rule + stage_item = [ws] (sequence_block / parallel_block / run_rule / separate_directive / string_case_directive / compose_directive / rababa_directive / bare_rule / comment_item) [ws] + stage_items = *(stage_item) + stage_name_paren = "(" (identifier as stage_name) ")" + stage_name_ws = whitespace (identifier as stage_name) + stage_block = "stage" (stage_name_paren / stage_name_ws) [ws] "{" [ws] (stage_items as stage) [ws] "}" + + # -- tests --------------------------------------------------------------- + + test_note = whitespace "note" whitespace (quoted_string as note) + test_line = [whitespace] (quoted_string as input) arrow (quoted_string as expected) [(test_note)] [whitespace] + test_items = *(test_line) + tests_block = "tests" [whitespace] "{" [ws] (test_items as tests) [ws] "}" + + # -- system -------------------------------------------------------------- + + block_item = [whitespace] (metadata_block / aliases_block / tests_block / stage_block / dependency_decl) [whitespace] + body_items = *(block_item) + system_block = "system" whitespace ((quoted_string) as system_code) [whitespace] "{" [ws] ((*(block_item)) as body) [ws] "}" [whitespace] + isc_source = [whitespace] ((system_block) as system) [whitespace] + + entry isc: isc_source +} diff --git a/spec/support/parg_diff.rb b/spec/support/parg_diff.rb new file mode 100644 index 00000000..6c378928 --- /dev/null +++ b/spec/support/parg_diff.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +# Differential gate: parse .isc sources under the Ruby-DSL parser and the +# compiled .parg artifact; the capture trees must agree modulo slice +# stringification. +module PargDiff + module_function + + def normalize(node) + case node + when Hash + node.to_h { |k, v| [k, normalize(v)] } + when Array + node.map { |v| normalize(v) } + when String + node + else + node.respond_to?(:to_s) ? node.to_s : node + end + end + + def diff(a, b, path = "") + return [] if a == b + + if a.is_a?(Hash) && b.is_a?(Hash) + (a.keys | b.keys).flat_map do |k| + if !a.key?(k) + ["#{path}.#{k}: missing in ruby"] + elsif !b.key?(k) + ["#{path}.#{k}: missing in parg"] + else + diff(a[k], b[k], "#{path}.#{k}") + end + end + elsif a.is_a?(Array) && b.is_a?(Array) + return ["#{path}: len #{a.size} != #{b.size}"] if a.size != b.size + + a.each_with_index.flat_map { |v, i| diff(v, b[i], "#{path}[#{i}]") } + else + ["#{path}: #{a.inspect[0, 60]} != #{b.inspect[0, 60]}"] + end + end +end