parsanol 1.3.129-aarch64-linux → 1.3.131-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: '00494d5c6457d13e4ea19433c89617230dc038ded5597b3974e34383e1328d45'
4
- data.tar.gz: 55446231250ce4163b4311d4db99e2e0df93c8ae09207d197a4cf8dc11e65b57
3
+ metadata.gz: cab7e7f96b08e9558a216f4658540a8ad88572a1ca3a0adb2c77602ee2ed3b61
4
+ data.tar.gz: ffb527fbf0a3bbd5a99da93148b52f1264a5bf9c9055e1c56e712223aaca9efc
5
5
  SHA512:
6
- metadata.gz: 412c084efeb1ffd89c8f943a18d8003d0f755cdf7dcb1b26bae49d8ee49ea8fc8686096fe4f593805fcefae17517cbca5ab7f2e60d16de5cb2a1f1b142019d47
7
- data.tar.gz: ef1e0943944fb3ccab2181d12c23f01654b80ad89068ab3914f6ac14db57b24c9d5da29960d133c20fcc2192dd729eb0f945d27786ae5d8b728ea63ed82bb52f
6
+ metadata.gz: c7e577f8fe5d6c850d6d03950462b41d8f38ef82f0f9b8b64bc066abf321b436a21a5805402276b02238049f0de97d932b109da152855e0f19e826ea6ed6fa38
7
+ data.tar.gz: 440e7adc83c768447f378b3b1cebccd73fd1e25e1bd1905eef09ea938ee8d6c2d2c980296b783507453da935ef373c9ac1c41eb7bce519f10cbba903e88442a3
@@ -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: 46909480 bytes
4
- sha256: b5917afaec0bf088952709bbc9d466d9523d43d2f69e61fbb93ae7cf34b7a8d4
3
+ size: 47415752 bytes
4
+ sha256: b5df655f1f7e38d27146c84678d78ebf1ac7fe0979ee0318932b95e7ccaf61f4
@@ -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: 47140352 bytes
4
- sha256: 82f28bcb6c308d92666a8db53df981bda6989a5f5d69a3eb74dc6cfb53a0edd9
3
+ size: 47650784 bytes
4
+ sha256: 729b12a5631399a9c51ca0c6e5379a8f7845988b1a39c5f7eff37f48c37bed4c
@@ -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: 47136024 bytes
4
- sha256: 6b3c06517eb782506624ab7f9126df5bbc144943bafc64ade94e5b5be46e8dbf
3
+ size: 47646368 bytes
4
+ sha256: f2b4449cac1d28ef08ab661c81c6c63ef43efc34dc47983361df95a32443b9b0
@@ -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: 47146000 bytes
4
- sha256: bc1b98e9825576ef8b9519ced50fd0bce2c0f3a41d0c37d36a2fdc6485117f57
3
+ size: 47656376 bytes
4
+ sha256: c058a8a40c01d04c483705050364261dffec34a911fdcd284f2bafc49dd2ce52
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: data/lib/parsanol/native/libparsanol.so
3
- size: 42614960 bytes
4
- sha256: 585cf127dcd5f801311d35755fc7a4373e1b7f24734ee69383c804751141b4f2
3
+ size: 43124920 bytes
4
+ sha256: c1ba12fc7ff6c821f87cb4e4d0bb533abb6891ce5457e44b223f1535e4e98542
@@ -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
@@ -95,6 +98,18 @@ module Parsanol
95
98
  source_hash == Digest::SHA256.hexdigest(source_text.to_s)
96
99
  end
97
100
 
101
+ # The declared per-target binding artifact for this parse artifact
102
+ # (parsanol-ruby#432): the entry binding records extracted into
103
+ # their own checksummed artifact, pinned to this grammar by source
104
+ # hash. Targets: ruby, rs, ts, python.
105
+ def bindings_artifact(target)
106
+ Bindings::Artifact.emit(envelope, target)
107
+ end
108
+
109
+ def bindings_artifact_json(target)
110
+ Bindings::Artifact.emit_json(envelope, target)
111
+ end
112
+
98
113
  def entries = envelope["entries"].keys
99
114
 
100
115
  def entry(name)
@@ -643,7 +658,16 @@ module Parsanol
643
658
  @compiled[rule] ||= compiler.atom_for(rule)
644
659
  end
645
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.
646
668
  def verify_checksum!
669
+ verify_byte_checksum! if envelope["byte_checksum"]
670
+
647
671
  stored = envelope["checksum"]
648
672
  computed = Compiler.checksum(envelope)
649
673
  return if stored == computed
@@ -652,6 +676,26 @@ module Parsanol
652
676
  "artifact checksum mismatch: stored #{stored.inspect}, " \
653
677
  "computed #{computed.inspect}"
654
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
655
699
  end
656
700
  end
657
701
  end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Parsanol
6
+ module PARG
7
+ module Bindings
8
+ # The declared per-target binding artifact (parsanol-ruby#432): from
9
+ # one compiled grammar the toolchain emits one checksummed bindings
10
+ # artifact per target, each carrying the entry binding records and
11
+ # pinned to its grammar by source hash. Declared and diffable, not
12
+ # baked: consumers pin and verify what they load.
13
+ #
14
+ # Boundary: the records carry AST construction only — the
15
+ # production-to-binding mapping. Semantics live in the model layer
16
+ # (LML), never here. Execution resources (preprocess steps and their
17
+ # tables) resolve from the PAIRED parse artifact, which is why
18
+ # #pair_with! is mandatory before #apply.
19
+ class Artifact
20
+ KIND = "parg-bindings"
21
+ TARGETS = %w[ruby rs ts python].freeze
22
+ SUPPORTED_BINDING_VERSION = 1
23
+
24
+ attr_reader :target, :binding_version, :grammar, :grammar_version,
25
+ :grammar_source_hash, :entries, :payload
26
+
27
+ class << self
28
+ # Emit the per-target bindings artifact from a compiled parse
29
+ # artifact envelope. Returns the verified artifact; the payload
30
+ # is checksummed with the artifact checksum scheme (canonical
31
+ # JSON sha256 over everything except the checksum itself), so
32
+ # every engine re-derives it identically.
33
+ def emit(envelope, target)
34
+ target = target.to_s
35
+ unless TARGETS.include?(target)
36
+ raise ArtifactError,
37
+ "unknown binding target #{target.inspect}; known: #{TARGETS.join(', ')}"
38
+ end
39
+
40
+ artifact = new(build(envelope, target))
41
+ artifact.verify
42
+ end
43
+
44
+ # The emitted artifact as JSON (the bake/CLI surface).
45
+ def emit_json(envelope, target)
46
+ JSON.generate(emit(envelope, target).payload)
47
+ end
48
+
49
+ # Parse and fully verify an emitted bindings artifact. `target:`
50
+ # pins the expected target; omit it in multi-target tooling that
51
+ # accepts any declared target.
52
+ def from_json(json, target: nil)
53
+ artifact = new(JSON.parse(json))
54
+ artifact.verify
55
+ artifact.verify_target!(target) unless target.nil?
56
+ artifact
57
+ end
58
+
59
+ private
60
+
61
+ def build(envelope, target)
62
+ payload = {
63
+ "artifact_kind" => KIND,
64
+ "target" => target,
65
+ "binding_version" => SUPPORTED_BINDING_VERSION,
66
+ "grammar" => envelope["grammar"],
67
+ "grammar_version" => envelope["version"],
68
+ "grammar_source_hash" => envelope["source_hash"],
69
+ "shape" => envelope["shape"],
70
+ "entries" => envelope["entries"].to_h do |name, entry|
71
+ [name, { "bindings" => entry["bindings"].to_a }]
72
+ end,
73
+ }
74
+ payload.merge("checksum" => Compiler.checksum(payload))
75
+ end
76
+ end
77
+
78
+ def initialize(payload)
79
+ raise ArtifactError, "bindings artifact payload must be an object" unless payload.is_a?(Hash)
80
+
81
+ @payload = payload
82
+ @target = payload["target"]
83
+ @binding_version = payload["binding_version"]
84
+ @grammar = payload["grammar"]
85
+ @grammar_version = payload["grammar_version"]
86
+ @grammar_source_hash = payload["grammar_source_hash"]
87
+ @entries = payload["entries"]
88
+ @parg_artifact = nil
89
+ end
90
+
91
+ # Structural verification: kind, checksum, binding version,
92
+ # target, entries map. Every loader path goes through here.
93
+ def verify
94
+ unless @payload["artifact_kind"] == KIND
95
+ raise ArtifactError,
96
+ "not a #{KIND} artifact (artifact_kind #{@payload['artifact_kind'].inspect})"
97
+ end
98
+
99
+ expected = Compiler.checksum(@payload)
100
+ actual = @payload["checksum"]
101
+ unless actual.is_a?(String) && actual == expected
102
+ raise ArtifactError,
103
+ "bindings artifact checksum mismatch: expected #{expected}, got #{actual.inspect}"
104
+ end
105
+
106
+ unless TARGETS.include?(@target)
107
+ raise ArtifactError,
108
+ "unknown binding target #{@target.inspect}; known: #{TARGETS.join(', ')}"
109
+ end
110
+ unless @binding_version == SUPPORTED_BINDING_VERSION
111
+ raise ArtifactError,
112
+ "unsupported binding_version #{@binding_version.inspect} " \
113
+ "(supported: #{SUPPORTED_BINDING_VERSION})"
114
+ end
115
+ unless @entries.is_a?(Hash)
116
+ raise ArtifactError, "bindings artifact carries no entries map"
117
+ end
118
+
119
+ self
120
+ end
121
+
122
+ def verify_target!(target)
123
+ target = target.to_s
124
+ return if @target == target
125
+
126
+ raise ArtifactError,
127
+ "bindings artifact targets #{@target.inspect}, not #{target.inspect}"
128
+ end
129
+
130
+ # Pair with the parse artifact this layer was compiled from: same
131
+ # grammar, same version, same source. The pairing is what lets
132
+ # apply resolve preprocess references — they live in the parse
133
+ # artifact's checksummed envelope.
134
+ def pair_with!(parg_artifact)
135
+ envelope = parg_artifact.envelope
136
+ mismatches = []
137
+ mismatches << "grammar (#{@grammar.inspect} vs #{envelope['grammar'].inspect})" if @grammar != envelope["grammar"]
138
+ if @grammar_version != envelope["version"]
139
+ mismatches << "version (#{@grammar_version.inspect} vs #{envelope['version'].inspect})"
140
+ end
141
+ if @grammar_source_hash != envelope["source_hash"]
142
+ mismatches << "source_hash (#{@grammar_source_hash.inspect} vs #{envelope['source_hash'].inspect})"
143
+ end
144
+ unless mismatches.empty?
145
+ raise ArtifactError,
146
+ "bindings artifact does not pair with the parse artifact: #{mismatches.join(', ')}"
147
+ end
148
+
149
+ @parg_artifact = parg_artifact
150
+ self
151
+ end
152
+
153
+ def entry(name)
154
+ @entries.fetch(name) { raise ArtifactError, "unknown entry #{name.inspect}" }
155
+ end
156
+
157
+ def bindings(name)
158
+ entry(name)["bindings"]
159
+ end
160
+
161
+ def entry_names
162
+ @entries.keys
163
+ end
164
+
165
+ # Materialize the bound map from a parse shape using the DECLARED
166
+ # records. The paired parse artifact supplies entry context and
167
+ # preprocess resolution; the same Bindings.apply runs as for the
168
+ # embedded records, so a standalone artifact and its parse
169
+ # artifact's baked records produce identical bound maps.
170
+ def apply(entry_name, shape)
171
+ parg = @parg_artifact
172
+ if parg.nil?
173
+ raise ArtifactError,
174
+ "pair_with! the parse artifact before applying bindings"
175
+ end
176
+
177
+ crafted = { "name" => entry_name, "bindings" => bindings(entry_name) }
178
+ Bindings.apply(parg, crafted, shape)
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -8,6 +8,9 @@ module Parsanol
8
8
  # producing the attribute hash that fills a lutaml-model class:
9
9
  # capture collection -> preprocessing -> type cast -> path assignment.
10
10
  module Bindings
11
+ # The declared per-target binding artifact (parsanol-ruby#432).
12
+ autoload :Artifact, "parsanol/parg/bindings/artifact"
13
+
11
14
  module_function
12
15
 
13
16
  def apply(artifact, entry, shape)
@@ -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
 
@@ -46,6 +46,12 @@ module Parsanol
46
46
 
47
47
  document.rules[qualified] = copy(node, prefix)
48
48
  end
49
+ # The `atomic` exemption rides the rule name: a component's
50
+ # atomic rules qualify with everything else, or the exemption
51
+ # never matches post-merge.
52
+ used.atomic_rules.each do |name|
53
+ document.atomic_rules << "#{prefix}#{name}"
54
+ end
49
55
  used.entries.each do |entry, rule|
50
56
  document.entries["#{prefix}#{entry}"] = "#{prefix}#{rule}"
51
57
  end
@@ -79,7 +85,17 @@ module Parsanol
79
85
  when :pred
80
86
  Node.new(:pred, node.a, copy(node.b, prefix), node.c)
81
87
  when :cap then Node.new(:cap, node.a, copy(node.b, prefix))
82
- when :ref then Node.new(:ref, "#{prefix}#{node.a}")
88
+ # A qualified reference (`other.rule`) already names its own
89
+ # imported grammar — prefixing would double-qualify it
90
+ # (cross-import references compose: the other grammar's rules
91
+ # are prefixed by their own merge).
92
+ when :ref
93
+ name = node.a.to_s
94
+ if name.include?(".")
95
+ Node.new(:ref, name)
96
+ else
97
+ Node.new(:ref, "#{prefix}#{name}")
98
+ end
83
99
  else node
84
100
  end
85
101
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = "1.3.129"
4
+ VERSION = "1.3.131"
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.129
4
+ version: 1.3.131
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.
@@ -101,6 +101,7 @@ files:
101
101
  - lib/parsanol/parg/artifact.rb
102
102
  - lib/parsanol/parg/authoring.rb
103
103
  - lib/parsanol/parg/bindings.rb
104
+ - lib/parsanol/parg/bindings/artifact.rb
104
105
  - lib/parsanol/parg/cli.rb
105
106
  - lib/parsanol/parg/compiler.rb
106
107
  - lib/parsanol/parg/derive.rb