pubid 2.0.1 → 2.0.3

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.
Files changed (216) hide show
  1. checksums.yaml +4 -4
  2. data/data/ieee/update_codes.yaml +2 -2
  3. data/data/parg/adobe.json +1 -1
  4. data/data/parg/aiee.json +1 -1
  5. data/data/parg/aiee.parg +99 -9
  6. data/data/parg/amca.json +1 -1
  7. data/data/parg/ansi.json +1 -1
  8. data/data/parg/api.json +1 -1
  9. data/data/parg/ashrae.json +1 -1
  10. data/data/parg/asme.json +1 -1
  11. data/data/parg/astm.json +1 -1
  12. data/data/parg/bipm.json +1 -1
  13. data/data/parg/bsi.json +1 -1
  14. data/data/parg/ccsds.json +1 -1
  15. data/data/parg/cen_cenelec.json +1 -1
  16. data/data/parg/cie.json +1 -1
  17. data/data/parg/csa.json +1 -1
  18. data/data/parg/ecma.json +1 -1
  19. data/data/parg/etsi.json +1 -1
  20. data/data/parg/evs.json +1 -1
  21. data/data/parg/gost.json +1 -1
  22. data/data/parg/iala.json +1 -1
  23. data/data/parg/iana.json +1 -1
  24. data/data/parg/idf.json +1 -1
  25. data/data/parg/iec.json +1 -1
  26. data/data/parg/ieee.json +1 -1
  27. data/data/parg/iho.json +1 -0
  28. data/data/parg/ire.json +1 -1
  29. data/data/parg/ire.parg +51 -12
  30. data/data/parg/isbn.json +1 -1
  31. data/data/parg/iso.json +1 -1
  32. data/data/parg/iso.parg +751 -0
  33. data/data/parg/itu.json +1 -1
  34. data/data/parg/jcgm.json +1 -1
  35. data/data/parg/jis.json +1 -1
  36. data/data/parg/nesc.json +1 -1
  37. data/data/parg/nesc.parg +97 -12
  38. data/data/parg/nist.json +1 -1
  39. data/data/parg/oiml.json +1 -1
  40. data/data/parg/omg.json +1 -1
  41. data/data/parg/parg.json +1 -1
  42. data/data/parg/plateau.json +1 -1
  43. data/data/parg/tgpp.json +1 -1
  44. data/data/parg/un.json +1 -1
  45. data/lib/pubid/adobe/identifier.rb +1 -1
  46. data/lib/pubid/adobe/parser.rb +2 -113
  47. data/lib/pubid/amca/identifiers/base.rb +1 -1
  48. data/lib/pubid/amca/parser.rb +1 -145
  49. data/lib/pubid/ansi/identifier.rb +1 -1
  50. data/lib/pubid/ansi/parser.rb +1 -82
  51. data/lib/pubid/api/builder.rb +32 -1
  52. data/lib/pubid/api/identifier.rb +1 -1
  53. data/lib/pubid/api/parser.rb +1 -131
  54. data/lib/pubid/ashrae/identifiers/base.rb +1 -1
  55. data/lib/pubid/ashrae/parser.rb +3 -710
  56. data/lib/pubid/asme/builder.rb +6 -4
  57. data/lib/pubid/asme/identifier.rb +1 -2
  58. data/lib/pubid/asme/parser.rb +1 -328
  59. data/lib/pubid/astm/builder.rb +12 -3
  60. data/lib/pubid/astm/identifier.rb +1 -2
  61. data/lib/pubid/astm/parser.rb +1 -227
  62. data/lib/pubid/bipm/identifier.rb +8 -1
  63. data/lib/pubid/bipm/parser.rb +2 -224
  64. data/lib/pubid/bsi/builder.rb +2 -4
  65. data/lib/pubid/bsi/parser.rb +1 -666
  66. data/lib/pubid/bsi/single_identifier.rb +38 -1
  67. data/lib/pubid/calconnect/parser.rb +2 -40
  68. data/lib/pubid/ccsds/identifier.rb +1 -1
  69. data/lib/pubid/ccsds/parser.rb +1 -66
  70. data/lib/pubid/cen_cenelec/identifier.rb +16 -1
  71. data/lib/pubid/cen_cenelec/parser.rb +3 -136
  72. data/lib/pubid/cie/identifier.rb +1 -1
  73. data/lib/pubid/cie/parser.rb +3 -378
  74. data/lib/pubid/conformance.rb +1 -1
  75. data/lib/pubid/csa/builder.rb +41 -18
  76. data/lib/pubid/csa/identifier.rb +12 -4
  77. data/lib/pubid/csa/identifiers/amendment.rb +28 -0
  78. data/lib/pubid/csa/identifiers.rb +1 -0
  79. data/lib/pubid/csa/parser.rb +16 -397
  80. data/lib/pubid/doi/parser.rb +2 -37
  81. data/lib/pubid/easc/builder.rb +1 -1
  82. data/lib/pubid/easc/parser.rb +2 -70
  83. data/lib/pubid/ecma/CLAUDE.md +8 -1
  84. data/lib/pubid/ecma/identifier.rb +13 -8
  85. data/lib/pubid/ecma/parser.rb +2 -54
  86. data/lib/pubid/ecma/renderer.rb +3 -7
  87. data/lib/pubid/errors.rb +18 -13
  88. data/lib/pubid/etsi/builder.rb +8 -5
  89. data/lib/pubid/etsi/identifiers/base.rb +1 -1
  90. data/lib/pubid/etsi/parser.rb +1 -132
  91. data/lib/pubid/evs/identifier.rb +7 -0
  92. data/lib/pubid/evs/parser.rb +1 -8
  93. data/lib/pubid/evs.rb +1 -1
  94. data/lib/pubid/gb/identifier.rb +6 -0
  95. data/lib/pubid/gb/parser.rb +2 -51
  96. data/lib/pubid/gost/identifier.rb +9 -1
  97. data/lib/pubid/gost/parser.rb +4 -81
  98. data/lib/pubid/iala/builder.rb +48 -39
  99. data/lib/pubid/iala/identifier.rb +1 -1
  100. data/lib/pubid/iala/parser.rb +2 -121
  101. data/lib/pubid/iana/identifier.rb +1 -1
  102. data/lib/pubid/iana/parser.rb +2 -24
  103. data/lib/pubid/identifier.rb +78 -1
  104. data/lib/pubid/idf/builder.rb +42 -0
  105. data/lib/pubid/idf/identifier.rb +1 -1
  106. data/lib/pubid/idf/parser.rb +1 -137
  107. data/lib/pubid/idf.rb +1 -2
  108. data/lib/pubid/iec/builder.rb +158 -50
  109. data/lib/pubid/iec/identifier.rb +1 -1
  110. data/lib/pubid/iec/parser.rb +3 -430
  111. data/lib/pubid/ieee/CLAUDE.md +2 -0
  112. data/lib/pubid/ieee/aiee/identifier.rb +2 -1
  113. data/lib/pubid/ieee/aiee.rb +0 -1
  114. data/lib/pubid/ieee/builder.rb +177 -30
  115. data/lib/pubid/ieee/components/code.rb +46 -7
  116. data/lib/pubid/ieee/identifiers/base.rb +5 -2
  117. data/lib/pubid/ieee/identifiers/joint_development.rb +7 -3
  118. data/lib/pubid/ieee/ire/identifier.rb +2 -1
  119. data/lib/pubid/ieee/ire.rb +0 -1
  120. data/lib/pubid/ieee/nesc/builder.rb +1 -1
  121. data/lib/pubid/ieee/nesc.rb +0 -1
  122. data/lib/pubid/ieee/parser.rb +1 -1257
  123. data/lib/pubid/ieee/project_renderer.rb +5 -1
  124. data/lib/pubid/ieee/renderer.rb +4 -2
  125. data/lib/pubid/ieee/urn_generator.rb +4 -1
  126. data/lib/pubid/ietf/identifiers/base.rb +17 -1
  127. data/lib/pubid/ietf/parser.rb +2 -47
  128. data/lib/pubid/ietf/renderer.rb +17 -2
  129. data/lib/pubid/iho/CLAUDE.md +98 -0
  130. data/lib/pubid/iho/builder.rb +4 -1
  131. data/lib/pubid/iho/identifiers/base.rb +28 -2
  132. data/lib/pubid/iho/renderer.rb +8 -0
  133. data/lib/pubid/iho/urn_generator.rb +14 -1
  134. data/lib/pubid/iho/urn_parser.rb +45 -32
  135. data/lib/pubid/iho.rb +0 -1
  136. data/lib/pubid/isbn/identifier.rb +1 -1
  137. data/lib/pubid/isbn/parser.rb +2 -31
  138. data/lib/pubid/iso/CLAUDE.md +66 -0
  139. data/lib/pubid/iso/builder.rb +229 -15
  140. data/lib/pubid/iso/identifier.rb +22 -12
  141. data/lib/pubid/iso/identifiers/directives.rb +6 -3
  142. data/lib/pubid/iso/identifiers/guide.rb +6 -3
  143. data/lib/pubid/iso/identifiers/international_workshop_agreement.rb +6 -3
  144. data/lib/pubid/iso/normalizer.rb +5 -4
  145. data/lib/pubid/iso/parser.rb +1 -539
  146. data/lib/pubid/iso/supplement_identifier.rb +6 -3
  147. data/lib/pubid/iso/urn_parser.rb +13 -8
  148. data/lib/pubid/iso/utilities.rb +4 -5
  149. data/lib/pubid/iso.rb +1 -19
  150. data/lib/pubid/itu/builder.rb +36 -20
  151. data/lib/pubid/itu/identifiers/base.rb +15 -2
  152. data/lib/pubid/itu/parser.rb +1 -775
  153. data/lib/pubid/jcgm/builder.rb +35 -0
  154. data/lib/pubid/jcgm/parser.rb +1 -151
  155. data/lib/pubid/jcgm.rb +1 -2
  156. data/lib/pubid/jis/CLAUDE.md +36 -0
  157. data/lib/pubid/jis/builder.rb +30 -41
  158. data/lib/pubid/jis/identifier.rb +12 -2
  159. data/lib/pubid/jis/identifiers/welding_engineering_standard.rb +35 -0
  160. data/lib/pubid/jis/identifiers.rb +2 -0
  161. data/lib/pubid/jis/parser.rb +1 -128
  162. data/lib/pubid/jis/renderer.rb +1 -1
  163. data/lib/pubid/jis/supplement_identifier.rb +5 -5
  164. data/lib/pubid/jis/urn_parser.rb +2 -1
  165. data/lib/pubid/jis.rb +3 -2
  166. data/lib/pubid/nist/builder.rb +5 -1
  167. data/lib/pubid/nist/caster.rb +13 -3
  168. data/lib/pubid/nist/identifiers/base.rb +37 -2
  169. data/lib/pubid/nist/identifiers/circular.rb +2 -2
  170. data/lib/pubid/nist/identifiers/circular_supplement.rb +2 -2
  171. data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +2 -2
  172. data/lib/pubid/nist/identifiers/crpl_report.rb +2 -2
  173. data/lib/pubid/nist/identifiers/handbook.rb +2 -2
  174. data/lib/pubid/nist/identifiers/internal_report.rb +2 -2
  175. data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +2 -2
  176. data/lib/pubid/nist/identifiers/monograph.rb +2 -2
  177. data/lib/pubid/nist/identifiers/report.rb +2 -2
  178. data/lib/pubid/nist/parser.rb +19 -775
  179. data/lib/pubid/nist/router.rb +4 -1
  180. data/lib/pubid/nist/supplement_identifier.rb +2 -2
  181. data/lib/pubid/oasis/parser.rb +2 -16
  182. data/lib/pubid/ogc/parser.rb +2 -26
  183. data/lib/pubid/oiml/identifiers/certification_system.rb +7 -0
  184. data/lib/pubid/oiml/identifiers/dual_published.rb +2 -2
  185. data/lib/pubid/oiml/parser.rb +1 -303
  186. data/lib/pubid/oiml.rb +1 -2
  187. data/lib/pubid/omg/identifier.rb +6 -1
  188. data/lib/pubid/omg/parser.rb +2 -76
  189. data/lib/pubid/parser.rb +5 -4
  190. data/lib/pubid/parsers/mr_string.rb +1 -1
  191. data/lib/pubid/plateau/parser.rb +1 -66
  192. data/lib/pubid/plateau.rb +1 -2
  193. data/lib/pubid/renderers/directives_renderer.rb +1 -1
  194. data/lib/pubid/renderers/iwa_renderer.rb +1 -1
  195. data/lib/pubid/renderers/publisher_stripper.rb +82 -0
  196. data/lib/pubid/renderers/supplement_renderer.rb +1 -1
  197. data/lib/pubid/renderers.rb +1 -0
  198. data/lib/pubid/sae/parser.rb +1 -48
  199. data/lib/pubid/tgpp/identifier.rb +1 -1
  200. data/lib/pubid/tgpp/parser.rb +2 -59
  201. data/lib/pubid/un/identifier.rb +1 -1
  202. data/lib/pubid/un/parser.rb +1 -18
  203. data/lib/pubid/version.rb +1 -1
  204. data/lib/pubid/w3c/parser.rb +2 -27
  205. data/lib/pubid/xsf/identifier.rb +10 -6
  206. data/lib/pubid/xsf/parser.rb +3 -31
  207. data/lib/pubid/xsf/renderer.rb +2 -4
  208. data/lib/pubid.rb +8 -9
  209. metadata +9 -23
  210. data/lib/pubid/ieee/aiee/parser.rb +0 -130
  211. data/lib/pubid/ieee/ire/parser.rb +0 -109
  212. data/lib/pubid/ieee/nesc/parser.rb +0 -155
  213. data/lib/pubid/iho/parser.rb +0 -68
  214. data/lib/pubid/parser/common_parse_methods.rb +0 -13
  215. data/lib/pubid/parser/common_parse_rules.rb +0 -56
  216. data/lib/pubid/parser/grammar.rb +0 -96
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Ecma
7
- # Parslet grammar for ECMA identifiers.
5
+ # Grammar for ECMA identifiers.
8
6
  #
9
7
  # Four printed forms:
10
8
  # ECMA-411 standard
@@ -20,57 +18,7 @@ module Pubid
20
18
  # Each branch captures its number under a distinct key so the builder can
21
19
  # pick the identifier class without a separate type token. Numbers are kept
22
20
  # as strings to preserve any leading zeros.
23
- class Parser < ::Pubid::Parser::Grammar
24
- rule(:digits) { match["0-9"].repeat(1) }
25
-
26
- rule(:prefix) { str("ECMA") }
27
-
28
- rule(:tr) { str(" TR/") >> digits.as(:tr_number) }
29
-
30
- rule(:mem) { str(" MEM/") >> digits.as(:mem_number) }
31
-
32
- # Standards print with a hyphen ("ECMA-6"), but references are commonly
33
- # written with a space ("ECMA 6"). Both parse; the renderer normalises to
34
- # the hyphen, so the space form round-trips as "ECMA-6" by design and must
35
- # never enter the byte-exact `pass` fixtures.
36
- rule(:standard_separator) { str("-") | str(" ") }
37
-
38
- rule(:standard) do
39
- standard_separator >> digits.as(:number) >>
40
- (str("-") >> digits.as(:part)).maybe
41
- end
42
-
43
- # Editions are not all integers: ECMA-402 ed5.1 is a real document. The
44
- # value is stored verbatim so it renders and serializes unchanged.
45
- rule(:edition_number) { digits >> (str(".") >> digits).repeat(0) }
46
-
47
- rule(:edition) { str(" ed") >> edition_number.as(:edition) }
48
-
49
- rule(:volume) { str(" vol") >> digits.as(:volume) }
50
-
51
- # The suffixes attach AFTER the type alternation, so a technical report
52
- # and a memento carry them exactly as a standard does.
53
- #
54
- # Two PEG facts this arrangement depends on:
55
- #
56
- # 1. `standard` cannot shadow " TR/" or " MEM/", because after the space
57
- # it demands a digit and "T"/"M" are not digits. Keeping tr|mem first
58
- # is belt-and-braces; the DISJOINT FIRST CHARACTER is the load-bearing
59
- # invariant.
60
- # 2. Appending a sequence moves the alternation out of tail position, so
61
- # Parslet no longer threads consume_all into it and will not backtrack
62
- # into an alternative that already succeeded. A future branch that
63
- # could match a proper prefix of another branch's input would now fail
64
- # the whole parse instead of being rescued by the next alternative.
65
- rule(:identifier) do
66
- prefix >> (tr | mem | standard) >> edition.maybe >> volume.maybe
67
- end
68
-
69
- rule(:root) { identifier }
70
-
71
- def self.parse(input)
72
- new.parse(input)
73
- end
21
+ class Parser
74
22
  end
75
23
  end
76
24
  end
@@ -11,8 +11,8 @@ module Pubid
11
11
  # "ECMA MEM/1970" memento
12
12
  # "ECMA-269 ed3 vol2" with the edition and volume suffixes
13
13
  #
14
- # With `with_publisher: false` the leading ECMA token is dropped, yielding
15
- # "-411" / "TR/101" / "MEM/1970".
14
+ # The ECMA token is part of the designation and is always rendered; see
15
+ # `Pubid::Ecma::Identifier#publisher_prefixes`.
16
16
  class Renderer < ::Pubid::Renderers::Base
17
17
  PUBLISHER = "ECMA"
18
18
 
@@ -32,7 +32,7 @@ module Pubid
32
32
  # with a space ("ECMA TR/101"). type_prefix is nil only for standards.
33
33
  sep = id.type_prefix.nil? ? "" : " "
34
34
  core = id.type_prefix.nil? ? standard_core(id) : typed_core(id)
35
- result = with_publisher?(id) ? "#{PUBLISHER}#{sep}#{core}" : core
35
+ result = "#{PUBLISHER}#{sep}#{core}"
36
36
  result + suffixes(id, with_edition, with_volume)
37
37
  end
38
38
 
@@ -58,10 +58,6 @@ module Pubid
58
58
  def typed_core(id)
59
59
  "#{id.type_prefix}/#{id.number}"
60
60
  end
61
-
62
- def with_publisher?(id)
63
- id.with_publisher != false
64
- end
65
61
  end
66
62
  end
67
63
  end
data/lib/pubid/errors.rb CHANGED
@@ -3,14 +3,12 @@
3
3
  # no `require "pubid"` here: errors.rb loads from pubid.rb itself, and
4
4
  # the umbrella self-require is a circular load whose failure order is
5
5
  # platform-dependent (windows raises mid-cycle where linux tolerates it)
6
- require "parslet"
7
6
 
8
7
  module Pubid
9
8
  # Every failure pubid raises.
10
9
  #
11
- # The three classes below have three different superclasses, chosen so that
12
- # code written against the old contract keeps working: relaton-cli rescues
13
- # `Parslet::ParseFailed`, and the input guards have always raised
10
+ # The three classes below have three different superclasses: a parse
11
+ # failure is a `StandardError`, and the input guards have always raised
14
12
  # `ArgumentError`. What unifies them is the marker module
15
13
  # {Pubid::Errors::Error}, which every one of them includes, so a caller that
16
14
  # wants "pubid could not do this" writes one rescue:
@@ -21,8 +19,8 @@ module Pubid
21
19
  # warn e.message
22
20
  # end
23
21
  #
24
- # A URN failure deliberately does not inherit `Parslet::ParseFailed`: no
25
- # grammar runs, so there is no parse failure to report.
22
+ # A URN failure deliberately does not share the parse failure's
23
+ # superclass: no grammar runs, so there is no parse failure to report.
26
24
  module Errors
27
25
  # Marker module included by every pubid error. Rescue this to catch all
28
26
  # of them, whatever their superclass.
@@ -32,13 +30,14 @@ module Pubid
32
30
  #
33
31
  # Raised for a grammar rejection, and also for the few flavors that reject
34
32
  # an input before the grammar runs (a CSA comment line, a bad ISBN check
35
- # digit). Those synthetic failures carry no parslet cause, so
36
- # {#parse_failure_cause} is nillable — check it before calling
37
- # `ascii_tree` on it.
33
+ # digit). Those synthetic failures carry no cause, so
34
+ # {#parse_failure_cause} is nillable — check it before using it.
38
35
  #
39
36
  # v1's `Pubid::Core::Errors::ParseError` flattened the cause into the
40
- # message and threw the object away. This one keeps it.
41
- class ParseError < ::Parslet::ParseFailed
37
+ # message and threw the object away. This one keeps it. The superclass
38
+ # was `Parslet::ParseFailed` while the grammars ran on parslet; the parg
39
+ # artifacts retired that engine, and the class stands on its own now.
40
+ class ParseError < ::StandardError
42
41
  include Error
43
42
 
44
43
  # The string that could not be parsed, when it is known.
@@ -54,16 +53,22 @@ module Pubid
54
53
  attr_reader :flavor
55
54
 
56
55
  # @param message [String]
57
- # @param parse_failure_cause [Parslet::Cause, nil] parslet's structured
56
+ # @param parse_failure_cause [Object, nil] the engine's structured
58
57
  # cause, nil for a failure raised without running a grammar
59
58
  # @param input [String, nil]
60
59
  # @param flavor [String, nil]
61
60
  def initialize(message, parse_failure_cause = nil, input: nil,
62
61
  flavor: nil)
63
- super(message, parse_failure_cause)
62
+ super(message)
63
+ @parse_failure_cause = parse_failure_cause
64
64
  @input = input
65
65
  @flavor = flavor
66
66
  end
67
+
68
+ # @return [Object, nil]
69
+ def parse_failure_cause
70
+ @parse_failure_cause
71
+ end
67
72
  end
68
73
 
69
74
  # A URN could not be turned into an identifier object.
@@ -28,17 +28,20 @@ module Pubid
28
28
  supplements_array = data[:supplements]
29
29
  supplements_array = [supplements_array] unless supplements_array.is_a?(Array)
30
30
 
31
- # Build supplements recursively - each wraps the previous
31
+ # Build supplements recursively - each wraps the previous. The
32
+ # bindings pair each supplement's kind ("/A", "/C") with its number
33
+ # by occurrence index.
32
34
  supplements_array.reduce(base) do |current_base, supp|
33
- if supp[:amendment]
35
+ case supp[:supplement_kind]
36
+ when "/A"
34
37
  Identifiers::Amendment.new(
35
38
  base: current_base,
36
- number: supp[:amendment][:number].to_i,
39
+ number: supp[:supplement_number].to_i,
37
40
  )
38
- elsif supp[:corrigendum]
41
+ when "/C"
39
42
  Identifiers::Corrigendum.new(
40
43
  base: current_base,
41
- number: supp[:corrigendum][:number].to_i,
44
+ number: supp[:supplement_number].to_i,
42
45
  )
43
46
  else
44
47
  current_base # Skip unknown supplement types
@@ -18,7 +18,7 @@ module Pubid
18
18
  raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
19
19
  end
20
20
 
21
- parsed = Pubid::Parg::Backend.parse(:etsi, identifier)
21
+ parsed = Pubid::Parg::Model.bound(:etsi, identifier)
22
22
  Builder.build(parsed)
23
23
  end
24
24
 
@@ -1,139 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Etsi
7
- class Parser < ::Pubid::Parser::Grammar
8
- # Basic elements
9
- rule(:digit) { match["0-9"] }
10
- rule(:digits) { digit.repeat(1) }
11
- rule(:letter) { match["A-Za-z"] }
12
- rule(:alnum) { match["A-Za-z0-9"] }
13
- rule(:letters) { letter.repeat(1) }
14
- rule(:alnums) { alnum.repeat(1) }
15
- rule(:space) { str(" ") }
16
- rule(:dash) { str("-") }
17
- rule(:dot) { str(".") }
18
-
19
- # ETSI prefix
20
- rule(:etsi_prefix) { str("ETSI") >> space }
21
-
22
- # Type prefixes - all 15 types
23
- rule(:type) do
24
- (str("I-ETS") | str("TCRTR") | str("GTS") | str("ETR") |
25
- str("ETS") | str("TBR") | str("NET") | str("EN") |
26
- str("ES") | str("EG") | str("TS") | str("GR") |
27
- str("GS") | str("SR") | str("TR")).as(:type)
28
- end
29
-
30
- # Number variants
31
-
32
- # GSM with dotted number: "GSM 11.14", "GSM 02.01"
33
- rule(:gsm_with_space_number) do
34
- str("GSM").capture(:gsm_prefix) >> space >>
35
- digits.capture(:main) >> dot >> digits.capture(:sub)
36
- end
37
-
38
- # Number with dot: 11.40, 02.01
39
- rule(:dotted_number) do
40
- digits.capture(:main) >> dot >> digits.capture(:sub)
41
- end
42
-
43
- # Complex format: ABC 123 or ABC-DEF 123 or IP6 031
44
- rule(:complex_number) do
45
- alnums.capture(:prefix1) >>
46
- (dash >> alnums.capture(:prefix2)).maybe >>
47
- space >>
48
- digits.capture(:num)
49
- end
50
-
51
- # Simple format: just digits
52
- rule(:simple_number) do
53
- digits.capture(:num)
54
- end
55
-
56
- # Main number
57
- rule(:number) do
58
- (gsm_with_space_number | dotted_number | complex_number | simple_number).as(:number)
59
- end
60
-
61
- # Optional minor part
62
- rule(:minor) do
63
- space >> digits.as(:minor)
64
- end
65
-
66
- # Parts
67
- rule(:part) do
68
- dash >> alnums.as(:part)
69
- end
70
-
71
- rule(:parts) { part.repeat(0).as(:parts) }
72
-
73
- # Version
74
- rule(:version) do
75
- str("V") >> match["0-9."].repeat(1).as(:version)
76
- end
77
-
78
- # Edition
79
- rule(:edition) do
80
- str("ed.") >> digits.as(:edition)
81
- end
82
-
83
- # Version or edition
84
- rule(:version_or_edition) do
85
- version | edition
86
- end
87
-
88
- # Published date
89
- rule(:date_part) do
90
- str("(") >>
91
- digit.repeat(4, 4).as(:year) >>
92
- dash >>
93
- digit.repeat(2, 2).as(:month) >>
94
- str(")")
95
- end
96
-
97
- # Individual supplements
98
- rule(:amendment) do
99
- str("/A") >> digits.as(:number)
100
- end
101
-
102
- rule(:corrigendum) do
103
- str("/C") >> digits.as(:number)
104
- end
105
-
106
- # Single supplement (AMD or COR)
107
- rule(:single_supplement) do
108
- amendment.as(:amendment) | corrigendum.as(:corrigendum)
109
- end
110
-
111
- # Multiple supplements
112
- rule(:supplements) do
113
- single_supplement.repeat(1).as(:supplements)
114
- end
115
-
116
- # Main identifier
117
- #
118
- # The mandatory core is `etsi_prefix type number` (+ optional
119
- # minor/parts/supplements), mirroring ISO. The version/edition and date
120
- # suffixes are optional so a partial reference (e.g. "ETSI GS ZSM 012")
121
- # parses with version/date left nil. Each `.maybe` guards its own leading
122
- # space so no trailing space is demanded when the suffix is absent.
123
- rule(:identifier) do
124
- etsi_prefix >>
125
- type >> space >>
126
- number >> minor.maybe >> parts >>
127
- supplements.maybe >>
128
- (space >> version_or_edition).maybe >>
129
- (space >> date_part).maybe
130
- end
131
-
132
- rule(:root) { identifier }
133
-
134
- def self.parse(input)
135
- new.parse(input)
136
- end
5
+ class Parser
137
6
  end
138
7
  end
139
8
  end
@@ -42,6 +42,13 @@ module Pubid
42
42
  render(format: :human, **opts)
43
43
  end
44
44
 
45
+ # Every EVS form starts with the `EVS` publisher, welded to an adopted
46
+ # designation by a dash (`EVS-EN 18216:2026`); `with_publisher: false`
47
+ # leaves the adopted designation (`EN 18216:2026`).
48
+ def publisher_prefixes
49
+ ["EVS"]
50
+ end
51
+
45
52
  def self.parse(string)
46
53
  unless string.is_a?(String)
47
54
  raise Pubid::Errors::InvalidInputError,
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Evs
7
5
  # Grammar for the printed EVS national-adoption reference, composed with
@@ -16,12 +14,7 @@ module Pubid
16
14
  #
17
15
  # "EVS-EN 18216:2026" → {evs_separator, adopted: {publisher, number, year}}
18
16
  # "EVS-EN ISO 9001:2015/A1:2024" → {evs_separator, adopted: {publisher, adopted_string, supplements}}
19
- class Parser < ::Pubid::Parser::Grammar
20
- rule(:root) do
21
- str("EVS") >>
22
- (str("-") | str(" ")).as(:evs_separator) >>
23
- ::Pubid::CenCenelec::Parser.new.identifier.as(:adopted)
24
- end
17
+ class Parser
25
18
  end
26
19
  end
27
20
  end
data/lib/pubid/evs.rb CHANGED
@@ -39,7 +39,7 @@ module Pubid
39
39
  language = identifier[/ (et-en|et|en)\z/, 1]
40
40
  body = language ? identifier.sub(/ #{Regexp.escape(language)}\z/, "") : identifier
41
41
 
42
- parsed = Pubid::Parg::Backend.parse(:evs, body)
42
+ parsed = Pubid::Parg::Model.bound(:evs, body)
43
43
  built = Builder.new.build(parsed)
44
44
  # An "(all parts)" parse wraps the identifier — the language belongs
45
45
  # to the inner EVS identifier, not the flavor-less wrapper.
@@ -67,6 +67,12 @@ module Pubid
67
67
  render(format: :human, **opts)
68
68
  end
69
69
 
70
+ # `GB` is the standard code, welded to the mandate (`GB/T`), not a
71
+ # publisher prefix: `with_publisher: false` keeps it, as BSI keeps `BS`.
72
+ def publisher_prefixes
73
+ []
74
+ end
75
+
70
76
  # Parse a Chinese Standard identifier string.
71
77
  # @param identifier [String]
72
78
  # @return [Pubid::Gb::Identifier]
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Gb
7
- # Parslet grammar for Chinese Standard identifiers.
5
+ # Grammar for Chinese Standard identifiers.
8
6
  #
9
7
  # Accepts the canonical printed form
10
8
  #
@@ -14,54 +12,7 @@ module Pubid
14
12
  # T/GZAEPI, ...). TYPE is T (recommended), Z (guideline), or omitted.
15
13
  # YEAR is 4 digits, separated by either ASCII dash or em-dash (Chinese
16
14
  # typography uses the em-dash form).
17
- class Parser < ::Pubid::Parser::Grammar
18
- rule(:space) { str(" ") }
19
- rule(:dot) { str(".") }
20
- rule(:dash) { str("-") | str("—") }
21
- rule(:digits) { match("[0-9]").repeat(1) }
22
-
23
- # Publisher token: national/industry codes (1-3 letters, optionally
24
- # followed by /T or /Z) OR social-group form "T/{ACRONYM}".
25
- rule(:publisher_code) do
26
- # Social-group form: "T/" then 2+ uppercase letters/digits.
27
- (str("T/") >> match("[A-Z0-9]").repeat(1)).as(:publisher_code) |
28
- # Confidential national series. The lowercase "n" is part of the
29
- # code, so it comes before the uppercase-only rule below: a PEG
30
- # takes the first branch that matches and does not backtrack.
31
- (str("GBn") >> (str("/T") | str("/Z")).maybe).as(:publisher_code) |
32
- # Standard form: 1-3 letters, optional slash-T-or-Z.
33
- (match("[A-Z]").repeat(1, 3) >>
34
- (str("/T") | str("/Z")).maybe).as(:publisher_code)
35
- end
36
-
37
- # Mandate category captured separately when the publisher token didn't
38
- # already include it. Allows "GB/T" (inline) and "GB" + " T" (separate)
39
- # to both parse.
40
- rule(:mandate_suffix) do
41
- (str("/") >> (str("T") | str("Z")).as(:mandate)).maybe
42
- end
43
-
44
- rule(:number) { digits.as(:number) }
45
- rule(:part) { dot >> digits.as(:part) }
46
- rule(:year) { (dash >> match("[0-9]").repeat(4, 4).as(:year)).maybe }
47
- rule(:all_parts_flag) do
48
- (space >> str("(") >> str("all parts") >> str(")")).as(:all_parts)
49
- end
50
-
51
- rule(:identifier) do
52
- publisher_code >>
53
- space >>
54
- number >>
55
- part.maybe >>
56
- year >>
57
- all_parts_flag.maybe
58
- end
59
-
60
- rule(:root) { identifier }
61
-
62
- def self.parse(input)
63
- new.parse(input)
64
- end
15
+ class Parser
65
16
  end
66
17
  end
67
18
  end
@@ -23,7 +23,7 @@ module Pubid
23
23
  ::Pubid::INPUT_TOO_LONG_MESSAGE
24
24
  end
25
25
 
26
- parsed = Pubid::Parg::Backend.parse(:gost, Parser.normalize_input(identifier))
26
+ parsed = Pubid::Parg::Model.bound(:gost, Parser.normalize_input(identifier))
27
27
  Builder.build(parsed)
28
28
  end
29
29
 
@@ -38,6 +38,14 @@ module Pubid
38
38
  # document.
39
39
  subset_strict :copublisher
40
40
 
41
+ # `GOST` (and `GOST R`) is the national standard designation, as `GB`
42
+ # and `BS` are, not a publisher prefix: `GOST 26951-2025` without it is
43
+ # no designation, and `GOST IEC 62868` would read as the IEC document.
44
+ # `with_publisher: false` therefore keeps it.
45
+ def publisher_prefixes
46
+ []
47
+ end
48
+
41
49
  GOST_TYPE_MAP = {
42
50
  "pubid:gost:interstate-standard" => "Pubid::Gost::Identifiers::InterstateStandard",
43
51
  "pubid:gost:national-standard" => "Pubid::Gost::Identifiers::NationalStandard",
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "parslet"
4
-
5
3
  module Pubid
6
4
  module Gost
7
- # Parslet grammar for GOST identifiers.
5
+ # Grammar for GOST identifiers.
8
6
  #
9
7
  # Accepts the Latin and Cyrillic surface forms observed in the wild:
10
8
  #
@@ -26,87 +24,12 @@ module Pubid
26
24
  #
27
25
  # The builder interprets prefix_text (splits copublisher vs subtype,
28
26
  # normalizes Cyrillic to Latin) and raw (splits number vs year).
29
- class Parser < ::Pubid::Parser::Grammar
30
- include ::Pubid::Parser::CommonParseRules
31
-
32
- root :identifier
33
-
34
- rule(:space) { str(" ") }
35
- rule(:space?) { space.maybe }
36
- rule(:dot) { str(".") }
37
-
38
- rule(:year_sep) do
39
- (str("-") | str("—") | str("–")) >> space?.maybe
40
- end
41
-
42
- rule(:gost_word) do
43
- (str("GOST") | str("ГОСТ")).as(:gost_word) >> space
44
- end
45
-
46
- rule(:scope_r) do
47
- (match("R") | match("Р")).as(:scope_r) >> space
48
- end
49
-
50
- # Everything between scope_r and the first digit: copublisher,
51
- # subtype, or both. Captured as a raw string — the builder splits
52
- # it into copublisher + subtype. Matches uppercase letters (Latin
53
- # and Cyrillic), forward slashes, and spaces between words.
54
- rule(:prefix_text) do
55
- (match("[A-ZА-Яa-zа-я/]").repeat(1) >>
56
- (space >> match("[A-ZА-Яa-zа-я/]").repeat(1)).repeat).as(:prefix_text) >> space
57
- end
58
-
59
- rule(:digits) do
60
- match("[0-9]").repeat(1)
61
- end
62
-
63
- rule(:raw_body) do
64
- (digits >> ((dot | year_sep) >> digits).repeat).as(:raw)
65
- end
66
-
67
- # IDT adoption suffix: "/<foreign-identifier>". The slash is
68
- # consumed but NOT captured — only the foreign identifier text
69
- # goes into :adopted_raw. Captures everything after the first
70
- # slash, including subsequent slashes (the adopted identifier
71
- # may itself contain slashes, e.g. "ISO/TR 25901-1:2016").
72
- rule(:adopted_part) do
73
- space?.maybe >> str("/") >> match(".").repeat(1).as(:adopted_raw)
74
- end
75
-
76
- # Adoption reference in parens: "(<foreign-id>)". The parens
77
- # themselves are consumed but NOT captured. The content goes
78
- # into :adopted_reference_raw. This form appears for MOD/NEQ
79
- # adoptions where the foreign standard is referenced but not
80
- # identical (IDT would use the slash form instead).
81
- rule(:adopted_reference_part) do
82
- space?.maybe >> str("(") >> match("[^)]").repeat(1).as(:adopted_reference_raw) >> str(")")
83
- end
84
-
85
- rule(:identifier) do
86
- gost_word >> scope_r.maybe >> prefix_text.maybe >> raw_body >>
87
- adopted_part.maybe >> adopted_reference_part.maybe
88
- end
89
-
90
- # Pre-parse ingestion normalizations (R2): every parse path —
91
- # parslet and PG artifact alike — feeds the grammar the same
92
- # normalized string.
27
+ class Parser
28
+ # Pre-parse ingestion normalizations (R2): every parse path feeds
29
+ # the grammar the same normalized string.
93
30
  def self.normalize_input(string)
94
31
  string.strip
95
32
  end
96
-
97
- def self.parse(string)
98
- unless string.is_a?(String)
99
- raise ::Pubid::Errors::InvalidInputError,
100
- ::Pubid::INPUT_NOT_A_STRING_MESSAGE
101
- end
102
-
103
- if string.length > ::Pubid::MAX_INPUT_LENGTH
104
- raise ::Pubid::Errors::InvalidInputError,
105
- ::Pubid::INPUT_TOO_LONG_MESSAGE
106
- end
107
-
108
- new.parse(normalize_input(string))
109
- end
110
33
  end
111
34
  end
112
35
  end