parsanol 1.3.130-aarch64-linux → 1.3.132-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 147fa1b4b09c7fbf0da0149a6d77dac14cda6bb6dda5c93cbdb1a91e01505346
4
- data.tar.gz: 8264f6f37afe9dbba6e1b4c5c43164eb24dc18b0c3220c67ee1942bf46ce8c33
3
+ metadata.gz: 858f8defa63f9c570ba144672710e3d152dcf8c9e4e2433ca63085f2c6573b1e
4
+ data.tar.gz: 2c45e86e29e2a55f4ef6c312bfcb42d5766b7e7f343ee74ee903f080f47fa839
5
5
  SHA512:
6
- metadata.gz: 4f4d24a7faf80254f9b927f7d16df15b4629e4c0f78ab6e41bf464dc20067b0b086978198717de66af954234db2d6340f84f496bc0fbde74518635d8a892a557
7
- data.tar.gz: e8faf9507ee432b2c5c0941239ec5122acd4c5c547978cc43d5990a473fd11bc055081c21c0d4ad83abd17858bbfdae2c6452ae00c0d1fe6898261b37cb124c3
6
+ metadata.gz: 837d88e5055370b3929b7626f4045a95112f8745fed10a90cfe79b8fe6df6acfc5337bbaf9d80fe6f2431912f83e26cc5665b7f1dd6a8295661c1d13e8c1317c
7
+ data.tar.gz: 12d330e16733a67a038b2cb2750f3a514375b1bbf5dcd778f20fb8128f33dd7fcfc5d07cb4d38dfca8842d7d1ade199f688facf3d5089fef47e53cdfe4e4a7d5
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/3.2/parsanol_native.so
3
- size: 47067800 bytes
4
- sha256: 652002b1d06d654635fcde5365367d688d81bc55691b59799c92be2051165b09
3
+ size: 47412264 bytes
4
+ sha256: ad5843845e6d583178abc630acb3bf6ea77f7944beb00d3f4312dc19a0e12f7e
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/3.3/parsanol_native.so
3
- size: 47302944 bytes
4
- sha256: 6cc3f000e39baf60b108cd6eb37beace605a6f75434ac651f16c91d49441da36
3
+ size: 47647304 bytes
4
+ sha256: ae55584ad3bb49b78d5f2a2f496cf47bcf6bb89070484f98ff936d45de6a1ad7
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/3.4/parsanol_native.so
3
- size: 47298544 bytes
4
- sha256: 178a1d42ff09fa4eaf837f7af48cd7ff3ae06922a4005565f1c6d788a14ec312
3
+ size: 47647080 bytes
4
+ sha256: a0a07b8bb12f97e21c7e4056df1c4971ed51578080306b4a4c7961610374ba1b
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/4.0/parsanol_native.so
3
- size: 47308536 bytes
4
- sha256: d644e6e76268084ea3520dfa5630fcd892eb6d9dfd7e6d7209bd88c836baae35
3
+ size: 47657088 bytes
4
+ sha256: bc004ef9e5f6794a52c01ec04536c34d32a9a129c6ee2076c545efec1b1bae7d
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/native/libparsanol.so
3
- size: 42777792 bytes
4
- sha256: 8e7cd8e9943ae0c50bcf181aafd345d1f31bb21a0fba713ee332c8bf76de8a63
3
+ size: 43125664 bytes
4
+ sha256: 63ef8e072548f50461d5624438deb51e41bb60f0b0d1a57c5bb76695367a645d
@@ -15,13 +15,15 @@ module Parsanol
15
15
  attr_reader :envelope, :path, :tables_dir, :imports_dir
16
16
 
17
17
  def self.load(path, tables_dir: nil, imports_dir: nil)
18
- envelope = JSON.parse(PARG.read_utf8(path))
18
+ text = PARG.read_utf8(path)
19
+ envelope = JSON.parse(text)
19
20
  new(envelope, path, tables_dir || File.dirname(path),
20
- imports_dir: imports_dir)
21
+ imports_dir: imports_dir, raw_text: text)
21
22
  end
22
23
 
23
24
  def self.from_json(text, tables_dir: nil, imports_dir: nil)
24
- new(JSON.parse(text), nil, tables_dir, imports_dir: imports_dir)
25
+ new(JSON.parse(text), nil, tables_dir, imports_dir: imports_dir,
26
+ raw_text: text)
25
27
  end
26
28
 
27
29
  SUPPORTED_SHAPE = "parsanol-tree/v2"
@@ -37,7 +39,7 @@ module Parsanol
37
39
  2 => 2, "2" => 2, "v2" => 2
38
40
  }.freeze
39
41
 
40
- def initialize(envelope, path, tables_dir, imports_dir: nil)
42
+ def initialize(envelope, path, tables_dir, imports_dir: nil, raw_text: nil)
41
43
  unless envelope["shape"] == SUPPORTED_SHAPE
42
44
  raise ArtifactError,
43
45
  "unsupported artifact shape #{envelope['shape'].inspect} " \
@@ -63,6 +65,7 @@ module Parsanol
63
65
  # sources when the pure-Ruby path recompiles the embedded source;
64
66
  # in-memory artifacts (path nil) pass the directory explicitly.
65
67
  @imports_dir = imports_dir
68
+ @raw_text = raw_text
66
69
  verify_checksum!
67
70
  @compiler = nil
68
71
  end
@@ -655,7 +658,16 @@ module Parsanol
655
658
  @compiled[rule] ||= compiler.atom_for(rule)
656
659
  end
657
660
 
661
+ # Two tiers. FAST: when the bake sealed the file, a zero-hole
662
+ # sha256 over the raw bytes proves byte integrity without parsing
663
+ # semantics — O(bytes), no canonicalization. DEEP: the canonical
664
+ # checksum (cross-engine content equality), which also covers
665
+ # artifacts baked before byte_checksum existed. A sealed artifact
666
+ # loaded with its raw text verifies BOTH; a bare envelope (direct
667
+ # construction) verifies canonical only.
658
668
  def verify_checksum!
669
+ verify_byte_checksum! if envelope["byte_checksum"]
670
+
659
671
  stored = envelope["checksum"]
660
672
  computed = Compiler.checksum(envelope)
661
673
  return if stored == computed
@@ -664,6 +676,26 @@ module Parsanol
664
676
  "artifact checksum mismatch: stored #{stored.inspect}, " \
665
677
  "computed #{computed.inspect}"
666
678
  end
679
+
680
+ # The bake computed the digest over the file bytes with the
681
+ # digest's own 64-hex region zeroed (Compiler.seal_json). Any
682
+ # flipped bit anywhere else in the file mismatches — corrupted
683
+ # distribution, wrong file, hand-edited payload: all rejected
684
+ # before any parse work.
685
+ def verify_byte_checksum!
686
+ stored = envelope["byte_checksum"]
687
+ return if stored.nil? || @raw_text.nil?
688
+
689
+ placeholder = Compiler::BYTE_CHECKSUM_PLACEHOLDER
690
+ zeroed = @raw_text.sub(stored.to_s, placeholder)
691
+ computed = "sha256:#{Digest::SHA256.hexdigest(zeroed)}"
692
+ return if stored == computed
693
+
694
+ raise ArtifactError,
695
+ "artifact byte checksum mismatch: stored #{stored.inspect}, " \
696
+ "computed #{computed.inspect} — the file bytes are not the " \
697
+ "sealed bake output"
698
+ end
667
699
  end
668
700
  end
669
701
  end
@@ -18,7 +18,11 @@ module Parsanol
18
18
  usage: parsanol parg <command> <grammar> [arguments]
19
19
 
20
20
  commands:
21
- compile <file.parg> [-o out.json] compile to an artifact envelope
21
+ compile <file.parg> [-o out.json] [--bindings TARGET|all]
22
+ compile to an artifact envelope;
23
+ --bindings also writes one
24
+ checksummed per-target binding
25
+ artifact beside it (#432)
22
26
  test <file.parg|artifact.json> run the grammar's test section
23
27
  parse <file> [--entry E] INPUT parse one input; prints the tree
24
28
  and the bound captures
@@ -141,11 +145,33 @@ module Parsanol
141
145
  source = @argv.shift
142
146
  out = flag_value("-o") || default_artifact_name(source)
143
147
  envelope = compile_source(source).envelope
144
- File.write(out, "#{JSON.generate(envelope)}\n", encoding: Encoding::UTF_8)
148
+ # Seal: the exact text written is the exact text the byte-level
149
+ # checksum covers (Compiler.seal_json includes the trailing
150
+ # newline).
151
+ File.write(out, Compiler.seal_json(envelope), encoding: Encoding::UTF_8)
145
152
  puts "#{out} #{envelope['checksum']} warnings=#{envelope['lint']['order_warnings'].size}"
153
+ bindings_targets.each do |target|
154
+ # The declared per-target binding artifacts (#432): one
155
+ # checksummed bindings payload per target, written beside the
156
+ # artifact. `--bindings all` expands to the full target set.
157
+ artifact = Artifact.from_json(JSON.generate(envelope))
158
+ path = out.sub(/\.json\z/i, ".bindings.#{target}.json")
159
+ File.write(path, "#{artifact.bindings_artifact_json(target)}\n", encoding: Encoding::UTF_8)
160
+ puts "#{path} #{artifact.bindings_artifact(target).payload['checksum']}"
161
+ end
146
162
  0
147
163
  end
148
164
 
165
+ # Every --bindings flag value, `all` expanded to the target set.
166
+ # Empty when the flag is absent (compile emits no binding artifacts).
167
+ def bindings_targets
168
+ values = []
169
+ while (value = flag_value("--bindings"))
170
+ values.concat(value == "all" ? Bindings::Artifact::TARGETS.dup : [value])
171
+ end
172
+ values.uniq
173
+ end
174
+
149
175
  # Freshness (parsanol-ruby#209): verify an artifact was compiled
150
176
  # from the given source. The stale-shipped-artifact incident class
151
177
  # (an old artifact silently taking precedence over the corrected
@@ -25,19 +25,43 @@ module Parsanol
25
25
  new(document, tables_dir).compile
26
26
  end
27
27
 
28
- # Canonical checksum over an envelope (everything except the checksum
29
- # itself). Used both when building an artifact and when verifying one.
28
+ # Canonical checksum over an envelope (everything except the
29
+ # checksum itself and the byte-level checksum — the byte digest
30
+ # attests the FILE serialization, which is producer-specific and
31
+ # must never enter the cross-engine canonical payload). Used both
32
+ # when building an artifact and when verifying one.
30
33
  def self.checksum(envelope)
31
- payload = envelope.except("checksum")
34
+ payload = envelope.except("checksum", "byte_checksum")
32
35
  "sha256:#{Digest::SHA256.hexdigest(JSON.generate(canonicalize(payload)))}"
33
36
  end
34
37
 
38
+ # The byte-level checksum's placeholder: a fixed-width zero region,
39
+ # so the sealed bytes and the verification bytes differ only inside
40
+ # that region (sha256 hex is 64 chars — same width).
41
+ BYTE_CHECKSUM_PLACEHOLDER = ("0" * 64).freeze
42
+ BYTE_CHECKSUM_TRAILING = "\n"
43
+
44
+ # Seal an envelope for writing: stamp the byte-level checksum over
45
+ # the exact file bytes (the final serialization with a trailing
46
+ # newline and the digest's own region zeroed) and return the exact
47
+ # text to write. No sidecar — the integrity travels inside the
48
+ # artifact. The canonical `checksum` is untouched: it attests
49
+ # content, `byte_checksum` attests bytes.
50
+ def self.seal_json(envelope)
51
+ sealed = envelope.merge("byte_checksum" => BYTE_CHECKSUM_PLACEHOLDER)
52
+ text = JSON.generate(sealed) + BYTE_CHECKSUM_TRAILING
53
+ digest = Digest::SHA256.hexdigest(text)
54
+ sealed["byte_checksum"] = "sha256:#{digest}"
55
+ JSON.generate(sealed) + BYTE_CHECKSUM_TRAILING
56
+ end
57
+
35
58
  # Producer metadata: the checksum pins the exact producer, but the
36
59
  # semantics hash (ruby#377) skips these fields so a re-bake by a
37
60
  # newer compiler — or a different Ruby patch stamping
38
61
  # unicode_tables — is byte-stable when the grammar's behavior is
39
62
  # unchanged. Mirrors the cross-engine parity view.
40
- SEMANTICS_EXCLUDED = %w[checksum compiler_version unicode_tables semantics_hash].freeze
63
+ SEMANTICS_EXCLUDED = %w[checksum byte_checksum compiler_version
64
+ unicode_tables semantics_hash validated].freeze
41
65
 
42
66
  def self.semantics_hash(envelope)
43
67
  payload = envelope.except(*SEMANTICS_EXCLUDED)
@@ -129,6 +153,18 @@ module Parsanol
129
153
  failures = self.class.self_test(envelope, tables_dir: @tables_dir)
130
154
  raise CompileError, failures.join("\n") unless failures.empty?
131
155
 
156
+ # The validation receipt: what this bake actually checked, inside
157
+ # the envelope the checksums cover. "valid/validated" is then an
158
+ # inspectable field of the artifact, not an out-of-band claim.
159
+ envelope["validated"] = {
160
+ "lint_errors" => 0, # compile raises on lint errors before this
161
+ "lint_warnings" => @envelope_warnings.size,
162
+ "tests_total" => @document.tests.size,
163
+ "tests_failures" => 0, # run_tests raises on failures before this
164
+ "self_test" => Native.available?,
165
+ }
166
+ envelope["checksum"] = Compiler.checksum(envelope)
167
+
132
168
  Result.new(@atom_cache, @warnings, envelope)
133
169
  end
134
170
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = "1.3.130"
4
+ VERSION = "1.3.132"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parsanol
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.130
4
+ version: 1.3.132
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.