asciichem 0.25.0 → 0.26.0

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: 7748aa7fb152b3dad47ff42c2debe7335af35a2a36c21d2248ff4f5a5231c575
4
- data.tar.gz: 4352b4fe40fc6998f9860d155e791d7cf2c4f3cd48f0184696346ba934adaa57
3
+ metadata.gz: 54b5e13e60ca4236628c8b8a145add5991c347747b7d7e2aa24f6acda6d89ad1
4
+ data.tar.gz: 7b8b2380ea9e5030065958cf35148da3addf2c6dcfb3dd1428066fee088dc410
5
5
  SHA512:
6
- metadata.gz: 6a22a8e911ec6a04498285f949f79c1ea5e7e6870d464e3229c3e783e744d151f0506b5200dba58e4e06f23177983dd80b24d172c6d5528e960907da821db64a
7
- data.tar.gz: 9d57bab57705518c6f2b40d28f40b0b62c20237d26b9daf20ae4d65bd9b60ad8497ea304c60c163878c412cc6cf61a3c99a6a9179b027bef527e524758b87085
6
+ metadata.gz: 0f3f2ae54757260f1ad644d77415a557b16ccdcb15d80829775362fbd2194de3351327babf11ed662676fcfefcd6898a3e9d942928e4a831ac770bb8339846fe
7
+ data.tar.gz: 360208b6be6fc9666505610618e7a029711595c9ff353e3ef391e09f80619e47cafbe1304c15e2dffac7c15d436f016094e039ce6a02480393f967bbc9ab1edc
data/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
3
3
  All notable changes to AsciiChem are documented here.
4
4
  This project follows [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [0.26.0] - 2026-09-14
7
+
8
+ ### Added
9
+ - The cite syntax (TODO.impl 45): `@cite("pubchem")` on a molecule
10
+ declares which source to cite it from; `Citation.for_molecule`
11
+ resolves the molecule's identifiers (registry keys preferred over
12
+ names) and emits one bibitem per cited source. Zero grammar
13
+ changes — `@cite` rides the property-annotation form by design.
14
+
15
+ ### Fixed
16
+ - Molecule annotations now canonicalise with spaces between them
17
+ (`@name("Water") @cas("...")`), matching the TypeScript and Python
18
+ canonicalisers; Ruby was the outlier joining them without a
19
+ separator.
20
+
6
21
  ## [0.25.0] - 2026-09-14
7
22
 
8
23
  ### Added
@@ -435,7 +450,8 @@ This project follows [Semantic Versioning](https://semver.org/).
435
450
  `version`.
436
451
  - Comprehensive RSpec suite with round-trip conformance.
437
452
 
438
- [Unreleased]: https://github.com/asciichem/asciichem-ruby/compare/v0.25.0...HEAD
453
+ [Unreleased]: https://github.com/asciichem/asciichem-ruby/compare/v0.26.0...HEAD
454
+ [0.26.0]: https://github.com/asciichem/asciichem-ruby/compare/v0.25.0...v0.26.0
439
455
  [0.25.0]: https://github.com/asciichem/asciichem-ruby/compare/v0.24.0...v0.25.0
440
456
  [0.24.0]: https://github.com/asciichem/asciichem-ruby/compare/v0.23.0...v0.24.0
441
457
  [0.23.0]: https://github.com/asciichem/asciichem-ruby/compare/v0.22.0...v0.23.0
data/benchmarks/README.md CHANGED
@@ -41,6 +41,16 @@ as a drop-in speedup for the reference grammar:
41
41
  revalidation against the whole corpus with no measured win to
42
42
  justify it yet.
43
43
 
44
- **Verdict: not adopted.** Revisit when Parsanol's native backend can
45
- be engaged for full grammars and shows measured wins on this
46
- workload; the shim path is a documented no.
44
+ **Verdict: not adopted yet.** Two corrections to the spike (tracked
45
+ upstream in parsanol-ruby#25):
46
+
47
+ 1. The native-DSL micro-benchmark ran in Parsanol's default `:ruby`
48
+ mode — the Rust core (`:native`) was never engaged, so the native
49
+ path is unmeasured, not disproven.
50
+ 2. The `SO_4^2-` failure is a candidate upstream bug (`repeat` of a
51
+ `maybe`-prefixed sequence fails at end-of-input; minimal repro in
52
+ the issue).
53
+
54
+ Revisit trigger unchanged: engage the native backend for full
55
+ grammars, fix the repetition-termination bug, and beat parslet on
56
+ this workload — then re-run the corpus against the port.
@@ -81,6 +81,57 @@ module AsciiChem
81
81
  bibitem(substance).to_xml
82
82
  end
83
83
 
84
+ # The cite syntax (TODO.impl 45): a molecule annotated
85
+ # `@cite("pubchem")` (a property annotation — the grammar needs
86
+ # no extension) declares *which source to cite it from*. This
87
+ # resolves the molecule's identifiers and emits one bibitem per
88
+ # cited source. Returns [[source, bibitem]] pairs; empty when the
89
+ # molecule has no @cite annotations.
90
+ #
91
+ # AsciiChem.parse('H_2O @name("water") @cite("pubchem")')
92
+ # AsciiChem::Citation.for_molecule(formula.nodes.first).map(&:last)
93
+ def for_molecule(molecule, cache: nil, fetch: nil)
94
+ sources = citation_sources(molecule)
95
+ return [] if sources.empty?
96
+
97
+ convention, value = lookup_key(molecule)
98
+ unless value
99
+ raise Error,
100
+ "molecule carries no resolvable identifier for citation " \
101
+ "(annotate @cas/@inchikey/@smiles or @name)"
102
+ end
103
+
104
+ sources.filter_map do |source|
105
+ substance = AsciiChem::Resolver[source].new.resolve(
106
+ value: value, convention: convention, cache: cache, fetch: fetch)
107
+ next unless substance
108
+
109
+ [source, bibitem(substance)]
110
+ end
111
+ end
112
+
113
+ private
114
+
115
+ # The property annotation whose title is "cite": values are the
116
+ # source names to cite from.
117
+ def citation_sources(molecule)
118
+ molecule.properties
119
+ .select { |p| p.title == "cite" && p.value }
120
+ .map(&:value)
121
+ end
122
+
123
+ # First identifier the resolver can look up by, in preference
124
+ # order: unambiguous registry keys before names.
125
+ def lookup_key(molecule)
126
+ identifier = molecule.identifiers.find { |i| %w[cas inchikey pubchem-cid].include?(i.convention) }
127
+ return [identifier.convention, identifier.value] if identifier
128
+
129
+ name = molecule.names.first
130
+ return ["name", name.content] if name
131
+
132
+ nil
133
+ end
134
+
84
135
  private
85
136
 
86
137
  def title_base(substance)
@@ -38,7 +38,7 @@ module AsciiChem
38
38
  molecule.labels.each { |l| parts << %(@label("#{l.value}")) if l.value }
39
39
  molecule.properties.each { |p| parts << %(@#{p.title}("#{p.value}")) if p.title && p.value }
40
40
  molecule.metadata.each { |m| parts << %(@meta("#{m.name}","#{m.content}")) }
41
- parts.empty? ? "" : " #{parts.join}"
41
+ parts.empty? ? "" : " #{parts.join(" ")}"
42
42
  end
43
43
 
44
44
  def visit_atom(atom)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AsciiChem
4
- VERSION = "0.25.0"
4
+ VERSION = "0.26.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asciichem
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.25.0
4
+ version: 0.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.