pubid 2.0.0 → 2.0.2
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 +4 -4
- data/data/parg/adobe.json +1 -1
- data/data/parg/aiee.json +1 -1
- data/data/parg/amca.json +1 -1
- data/data/parg/ansi.json +1 -1
- data/data/parg/api.json +1 -1
- data/data/parg/ashrae.json +1 -1
- data/data/parg/asme.json +1 -1
- data/data/parg/astm.json +1 -1
- data/data/parg/bipm.json +1 -1
- data/data/parg/bsi.json +1 -1
- data/data/parg/calconnect.json +1 -1
- data/data/parg/ccsds.json +1 -1
- data/data/parg/cen_cenelec.json +1 -1
- data/data/parg/cie.json +1 -1
- data/data/parg/csa.json +1 -1
- data/data/parg/doi.json +1 -1
- data/data/parg/easc.json +1 -1
- data/data/parg/ecma.json +1 -1
- data/data/parg/etsi.json +1 -1
- data/data/parg/evs.json +1 -1
- data/data/parg/gb.json +1 -1
- data/data/parg/gost.json +1 -1
- data/data/parg/iala.json +1 -1
- data/data/parg/iana.json +1 -1
- data/data/parg/idf.json +1 -1
- data/data/parg/iec.json +1 -1
- data/data/parg/ieee.json +1 -29571
- data/data/parg/ietf.json +1 -1
- data/data/parg/iho.json +1 -0
- data/data/parg/ire.json +1 -1
- data/data/parg/isbn.json +1 -1
- data/data/parg/iso.json +1 -8510
- data/data/parg/itu.json +1 -1
- data/data/parg/jcgm.json +1 -1
- data/data/parg/jis.json +1 -1
- data/data/parg/nesc.json +1 -1
- data/data/parg/nist.json +1 -9588
- data/data/parg/oasis.json +1 -1
- data/data/parg/ogc.json +1 -1
- data/data/parg/oiml.json +1 -1
- data/data/parg/omg.json +1 -1
- data/data/parg/parg.json +1 -1
- data/data/parg/plateau.json +1 -1
- data/data/parg/sae.json +1 -1
- data/data/parg/tables/typed_stages.yaml +1 -0
- data/data/parg/tgpp.json +1 -1
- data/data/parg/un.json +1 -1
- data/data/parg/w3c.json +1 -1
- data/data/parg/xsf.json +1 -1
- data/lib/pubid/adobe/identifier.rb +1 -1
- data/lib/pubid/adobe/parser.rb +2 -113
- data/lib/pubid/amca/identifiers/base.rb +1 -1
- data/lib/pubid/amca/parser.rb +1 -145
- data/lib/pubid/ansi/identifier.rb +1 -1
- data/lib/pubid/ansi/parser.rb +1 -82
- data/lib/pubid/api/builder.rb +32 -1
- data/lib/pubid/api/identifier.rb +1 -1
- data/lib/pubid/api/parser.rb +1 -131
- data/lib/pubid/ashrae/identifiers/base.rb +1 -1
- data/lib/pubid/ashrae/parser.rb +3 -710
- data/lib/pubid/asme/builder.rb +6 -4
- data/lib/pubid/asme/identifier.rb +1 -2
- data/lib/pubid/asme/parser.rb +1 -328
- data/lib/pubid/astm/builder.rb +12 -3
- data/lib/pubid/astm/identifier.rb +1 -2
- data/lib/pubid/astm/parser.rb +1 -227
- data/lib/pubid/bipm/identifier.rb +8 -1
- data/lib/pubid/bipm/parser.rb +2 -224
- data/lib/pubid/bsi/builder.rb +2 -4
- data/lib/pubid/bsi/parser.rb +1 -666
- data/lib/pubid/bsi/single_identifier.rb +38 -1
- data/lib/pubid/calconnect/identifier.rb +1 -1
- data/lib/pubid/calconnect/parser.rb +2 -40
- data/lib/pubid/ccsds/identifier.rb +1 -1
- data/lib/pubid/ccsds/parser.rb +1 -66
- data/lib/pubid/cen_cenelec/identifier.rb +16 -1
- data/lib/pubid/cen_cenelec/parser.rb +3 -136
- data/lib/pubid/cie/identifier.rb +1 -1
- data/lib/pubid/cie/parser.rb +3 -378
- data/lib/pubid/conformance.rb +1 -1
- data/lib/pubid/csa/builder.rb +41 -18
- data/lib/pubid/csa/identifier.rb +12 -4
- data/lib/pubid/csa/identifiers/amendment.rb +28 -0
- data/lib/pubid/csa/identifiers.rb +1 -0
- data/lib/pubid/csa/parser.rb +16 -397
- data/lib/pubid/doi/identifier.rb +1 -1
- data/lib/pubid/doi/parser.rb +2 -37
- data/lib/pubid/easc/builder.rb +1 -1
- data/lib/pubid/easc/identifier.rb +1 -1
- data/lib/pubid/easc/parser.rb +2 -70
- data/lib/pubid/ecma/CLAUDE.md +8 -1
- data/lib/pubid/ecma/identifier.rb +13 -8
- data/lib/pubid/ecma/parser.rb +2 -54
- data/lib/pubid/ecma/renderer.rb +3 -7
- data/lib/pubid/errors.rb +18 -13
- data/lib/pubid/etsi/builder.rb +8 -5
- data/lib/pubid/etsi/identifiers/base.rb +1 -1
- data/lib/pubid/etsi/parser.rb +1 -132
- data/lib/pubid/evs/identifier.rb +7 -0
- data/lib/pubid/evs/parser.rb +1 -8
- data/lib/pubid/evs.rb +1 -1
- data/lib/pubid/gb/identifier.rb +7 -1
- data/lib/pubid/gb/parser.rb +2 -51
- data/lib/pubid/gost/identifier.rb +9 -1
- data/lib/pubid/gost/parser.rb +4 -81
- data/lib/pubid/iala/parser.rb +2 -121
- data/lib/pubid/iana/identifier.rb +1 -1
- data/lib/pubid/iana/parser.rb +2 -24
- data/lib/pubid/identifier.rb +78 -1
- data/lib/pubid/idf/builder.rb +42 -0
- data/lib/pubid/idf/identifier.rb +1 -1
- data/lib/pubid/idf/parser.rb +1 -137
- data/lib/pubid/idf.rb +1 -2
- data/lib/pubid/iec/builder.rb +158 -50
- data/lib/pubid/iec/identifier.rb +1 -1
- data/lib/pubid/iec/parser.rb +3 -430
- data/lib/pubid/ieee/aiee/identifier.rb +2 -1
- data/lib/pubid/ieee/aiee.rb +0 -1
- data/lib/pubid/ieee/builder.rb +2 -2
- data/lib/pubid/ieee/identifiers/base.rb +1 -1
- data/lib/pubid/ieee/ire/identifier.rb +2 -1
- data/lib/pubid/ieee/ire.rb +0 -1
- data/lib/pubid/ieee/nesc.rb +0 -1
- data/lib/pubid/ieee/parser.rb +1 -1257
- data/lib/pubid/ietf/identifiers/base.rb +17 -1
- data/lib/pubid/ietf/parser.rb +2 -47
- data/lib/pubid/ietf/renderer.rb +17 -2
- data/lib/pubid/iho/CLAUDE.md +89 -0
- data/lib/pubid/iho/builder.rb +4 -1
- data/lib/pubid/iho/identifiers/base.rb +28 -2
- data/lib/pubid/iho/renderer.rb +8 -0
- data/lib/pubid/iho/urn_generator.rb +11 -1
- data/lib/pubid/iho/urn_parser.rb +21 -11
- data/lib/pubid/iho.rb +0 -1
- data/lib/pubid/isbn/identifier.rb +1 -1
- data/lib/pubid/isbn/parser.rb +2 -31
- data/lib/pubid/iso/builder.rb +1 -1
- data/lib/pubid/iso/identifiers/directives.rb +6 -3
- data/lib/pubid/iso/identifiers/guide.rb +6 -3
- data/lib/pubid/iso/identifiers/international_standard.rb +3 -1
- data/lib/pubid/iso/identifiers/international_workshop_agreement.rb +6 -3
- data/lib/pubid/iso/normalizer.rb +1 -1
- data/lib/pubid/iso/parser.rb +1 -539
- data/lib/pubid/iso/supplement_identifier.rb +6 -3
- data/lib/pubid/iso/utilities.rb +4 -5
- data/lib/pubid/iso.rb +1 -19
- data/lib/pubid/itu/builder.rb +36 -20
- data/lib/pubid/itu/identifiers/base.rb +15 -2
- data/lib/pubid/itu/parser.rb +1 -775
- data/lib/pubid/jcgm/builder.rb +35 -0
- data/lib/pubid/jcgm/parser.rb +1 -151
- data/lib/pubid/jcgm.rb +1 -2
- data/lib/pubid/jis/builder.rb +18 -29
- data/lib/pubid/jis/identifier.rb +1 -1
- data/lib/pubid/jis/parser.rb +1 -128
- data/lib/pubid/nist/builder.rb +5 -1
- data/lib/pubid/nist/caster.rb +13 -3
- data/lib/pubid/nist/identifiers/base.rb +37 -2
- data/lib/pubid/nist/identifiers/circular.rb +2 -2
- data/lib/pubid/nist/identifiers/circular_supplement.rb +2 -2
- data/lib/pubid/nist/identifiers/commercial_standards_monthly.rb +2 -2
- data/lib/pubid/nist/identifiers/crpl_report.rb +2 -2
- data/lib/pubid/nist/identifiers/handbook.rb +2 -2
- data/lib/pubid/nist/identifiers/internal_report.rb +2 -2
- data/lib/pubid/nist/identifiers/miscellaneous_publication.rb +2 -2
- data/lib/pubid/nist/identifiers/monograph.rb +2 -2
- data/lib/pubid/nist/identifiers/report.rb +2 -2
- data/lib/pubid/nist/parser.rb +19 -775
- data/lib/pubid/nist/router.rb +4 -1
- data/lib/pubid/nist/supplement_identifier.rb +2 -2
- data/lib/pubid/oasis/identifier.rb +1 -1
- data/lib/pubid/oasis/parser.rb +2 -16
- data/lib/pubid/ogc/identifier.rb +1 -1
- data/lib/pubid/ogc/parser.rb +2 -26
- data/lib/pubid/oiml/identifiers/certification_system.rb +7 -0
- data/lib/pubid/oiml/identifiers/dual_published.rb +2 -2
- data/lib/pubid/oiml/parser.rb +1 -303
- data/lib/pubid/oiml.rb +1 -2
- data/lib/pubid/omg/identifier.rb +6 -1
- data/lib/pubid/omg/parser.rb +2 -76
- data/lib/pubid/parg/artifact.rb +22 -3
- data/lib/pubid/parg/model.rb +98 -0
- data/lib/pubid/parg.rb +1 -0
- data/lib/pubid/parser.rb +5 -4
- data/lib/pubid/parsers/mr_string.rb +1 -1
- data/lib/pubid/plateau/parser.rb +1 -66
- data/lib/pubid/plateau.rb +1 -2
- data/lib/pubid/renderers/directives_renderer.rb +1 -1
- data/lib/pubid/renderers/iwa_renderer.rb +1 -1
- data/lib/pubid/renderers/publisher_stripper.rb +82 -0
- data/lib/pubid/renderers/supplement_renderer.rb +1 -1
- data/lib/pubid/renderers.rb +1 -0
- data/lib/pubid/sae/identifiers/base.rb +1 -1
- data/lib/pubid/sae/parser.rb +1 -48
- data/lib/pubid/tgpp/identifier.rb +1 -1
- data/lib/pubid/tgpp/parser.rb +2 -59
- data/lib/pubid/un/identifier.rb +1 -1
- data/lib/pubid/un/parser.rb +1 -18
- data/lib/pubid/version.rb +1 -1
- data/lib/pubid/w3c/identifier.rb +1 -1
- data/lib/pubid/w3c/parser.rb +2 -27
- data/lib/pubid/xsf/identifier.rb +11 -7
- data/lib/pubid/xsf/parser.rb +3 -31
- data/lib/pubid/xsf/renderer.rb +2 -4
- data/lib/pubid.rb +8 -9
- metadata +15 -25
- data/lib/pubid/ieee/aiee/parser.rb +0 -130
- data/lib/pubid/ieee/ire/parser.rb +0 -109
- data/lib/pubid/ieee/nesc/parser.rb +0 -155
- data/lib/pubid/iho/parser.rb +0 -68
- data/lib/pubid/parser/common_parse_methods.rb +0 -13
- data/lib/pubid/parser/common_parse_rules.rb +0 -56
- data/lib/pubid/parser/grammar.rb +0 -96
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Parg
|
|
5
|
+
# Typed model binding for one artifact entry point. The grammar declares
|
|
6
|
+
# which captures bind to which attributes (its `bindings` block); this
|
|
7
|
+
# module parses through those bindings — either straight into the
|
|
8
|
+
# flavor's model class (`parse`) or into the builder-ready hash its
|
|
9
|
+
# Builder consumes (`bound`) — so consumers never see the wire tree.
|
|
10
|
+
module Model
|
|
11
|
+
ALL_PARTS_SUFFIX = "(all parts)"
|
|
12
|
+
ALL_PARTS_PATTERN = /\s*\(all parts\)\s*\z/
|
|
13
|
+
|
|
14
|
+
@registered = {}
|
|
15
|
+
@mutex = Mutex.new
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
# Parse +input+ through the artifact's declared bindings into the
|
|
19
|
+
# builder-ready attribute hash, without materializing a model. For
|
|
20
|
+
# flavors whose Builder still owns type dispatch and component
|
|
21
|
+
# construction: the bindings replace tree normalization, not the
|
|
22
|
+
# builder. The "(all parts)" suffix keeps its contract — stripped
|
|
23
|
+
# before parsing, marked on the hash for the builder's AllPartsWrap.
|
|
24
|
+
def bound(flavor, input, entry: "identifier")
|
|
25
|
+
ensure_bindings!(flavor, entry)
|
|
26
|
+
all_parts = input.end_with?(ALL_PARTS_SUFFIX)
|
|
27
|
+
source = all_parts ? input.sub(ALL_PARTS_PATTERN, "") : input
|
|
28
|
+
hash = Artifact.for(flavor).parse_and_bind(entry, source)
|
|
29
|
+
all_parts ? Backend.mark_all_parts(hash) : hash
|
|
30
|
+
rescue Parsanol::ParseFailed => e
|
|
31
|
+
parse_error(e, input, flavor)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Parse +input+ into +model_class+ through the artifact's declared
|
|
35
|
+
# bindings. The "(all parts)" suffix is stripped before parsing and
|
|
36
|
+
# re-applied to the model, exactly as the tree backend does.
|
|
37
|
+
def parse(flavor, model_class, input, entry: "identifier")
|
|
38
|
+
register(flavor, model_class, entry)
|
|
39
|
+
model, all_parts = materialize(flavor, model_class, input)
|
|
40
|
+
all_parts ? model.to_all_parts : model
|
|
41
|
+
rescue Parsanol::ParseFailed => e
|
|
42
|
+
parse_error(e, input, flavor)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def parse_error(error, input, flavor)
|
|
48
|
+
raise Pubid::Errors::ParseError.new(
|
|
49
|
+
error.message,
|
|
50
|
+
nil,
|
|
51
|
+
input: input,
|
|
52
|
+
flavor: Backend.registered_name(flavor),
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def materialize(flavor, model_class, input)
|
|
57
|
+
all_parts = input.end_with?(ALL_PARTS_SUFFIX)
|
|
58
|
+
source = all_parts ? input.sub(ALL_PARTS_PATTERN, "") : input
|
|
59
|
+
[model_class.public_send(:"from_#{format_for(flavor)}", source),
|
|
60
|
+
all_parts]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def register(flavor, model_class, entry)
|
|
64
|
+
format = format_for(flavor)
|
|
65
|
+
@mutex.synchronize do
|
|
66
|
+
return if @registered[format] == model_class
|
|
67
|
+
|
|
68
|
+
ensure_bindings!(flavor, entry)
|
|
69
|
+
bind!(flavor, model_class, format, entry)
|
|
70
|
+
@registered[format] = model_class
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def bind!(flavor, model_class, format, entry)
|
|
75
|
+
Parsanol::PARG::Lutaml.register(
|
|
76
|
+
model_class,
|
|
77
|
+
format_name: format,
|
|
78
|
+
artifact: Artifact.path_for(flavor),
|
|
79
|
+
entry: entry,
|
|
80
|
+
tables_dir: Artifact::TABLES_DIR,
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def ensure_bindings!(flavor, entry)
|
|
85
|
+
return unless Artifact.for(flavor).bindings(entry).empty?
|
|
86
|
+
|
|
87
|
+
raise Pubid::Errors::ParseError,
|
|
88
|
+
"PG artifact for #{flavor} declares no bindings " \
|
|
89
|
+
"for entry #{entry}"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def format_for(flavor)
|
|
93
|
+
:"pg_#{flavor}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
data/lib/pubid/parg.rb
CHANGED
data/lib/pubid/parser.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "pubid"
|
|
4
3
|
module Pubid
|
|
5
4
|
module Parser
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
# A trailing "(all parts)" marks the reference as the whole document.
|
|
6
|
+
# NIST's parse path strips it before preprocessing and carries the
|
|
7
|
+
# marker into the tree (:all_parts) for the builder to wrap; the
|
|
8
|
+
# shared Builder::Base reads the same marker for every other flavor.
|
|
9
|
+
ALL_PARTS_SUFFIX = "(all parts)".freeze
|
|
9
10
|
end
|
|
10
11
|
end
|
|
@@ -116,7 +116,7 @@ module Pubid
|
|
|
116
116
|
# failure — the class the cross-flavor contract requires, and the one
|
|
117
117
|
# `Pubid.parse` treats as "try the next flavor".
|
|
118
118
|
if identifier_string == mr_string && flavor_module == Pubid::Iso
|
|
119
|
-
raise
|
|
119
|
+
raise Pubid::Errors::ParseError,
|
|
120
120
|
"#{mr_string.inspect} is not an MR string"
|
|
121
121
|
end
|
|
122
122
|
|
data/lib/pubid/plateau/parser.rb
CHANGED
|
@@ -2,72 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Plateau
|
|
5
|
-
class Parser
|
|
6
|
-
rule(:space) { str(" ") }
|
|
7
|
-
rule(:dash) { str("-") }
|
|
8
|
-
rule(:hash) { str("#") }
|
|
9
|
-
|
|
10
|
-
rule(:digit) { match["0-9"] }
|
|
11
|
-
rule(:digits) { digit.repeat(1) }
|
|
12
|
-
|
|
13
|
-
# PLATEAU
|
|
14
|
-
rule(:publisher) { str("PLATEAU") }
|
|
15
|
-
|
|
16
|
-
# Handbook or Technical Report
|
|
17
|
-
rule(:doc_type) do
|
|
18
|
-
(str("Handbook") | str("Technical Report")).as(:type)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Number: #00, #01, #46, etc.
|
|
22
|
-
rule(:number) { hash >> digits.as(:number) }
|
|
23
|
-
|
|
24
|
-
# Annex: -1, -2, etc. Legacy Latin references use "_" as the sub-number
|
|
25
|
-
# separator (e.g. "#46_1"); both render as the canonical dash.
|
|
26
|
-
rule(:annex) { (dash | str("_")) >> digits.as(:annex) }
|
|
27
|
-
|
|
28
|
-
# Edition (Handbook only). Canonical form is 第X.Y版; legacy Latin
|
|
29
|
-
# references write the bare X.Y (e.g. "PLATEAU Handbook #00 1.0"). Both
|
|
30
|
-
# capture the same numeric :edition slice, so they build the same object
|
|
31
|
-
# and render to the canonical 第X.Y版.
|
|
32
|
-
rule(:edition_part) do
|
|
33
|
-
str("第") >>
|
|
34
|
-
(digits >> str(".") >> digits).as(:edition) >>
|
|
35
|
-
str("版")
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
rule(:edition_latin) { (digits >> str(".") >> digits).as(:edition) }
|
|
39
|
-
|
|
40
|
-
rule(:edition) { space >> (edition_part | edition_latin) }
|
|
41
|
-
|
|
42
|
-
# Annex supplement: "Annex A", "Annex B", etc.
|
|
43
|
-
rule(:annex_letter) { match["A-Z"].as(:annex_letter) }
|
|
44
|
-
rule(:annex_supplement) do
|
|
45
|
-
space >> str("Annex") >> space >> annex_letter
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Full identifier patterns
|
|
49
|
-
rule(:handbook) do
|
|
50
|
-
publisher >> space >> doc_type >> space >>
|
|
51
|
-
number >> annex.maybe >> edition.maybe
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Technical Report has no edition. A legacy Latin TR reference carrying a
|
|
55
|
-
# trailing edition (e.g. "#00 1.0") is matched by the `handbook` rule
|
|
56
|
-
# (doc_type matches either keyword, tried first); the builder dispatches
|
|
57
|
-
# on :type and drops the captured edition for Technical Report.
|
|
58
|
-
rule(:technical_report) do
|
|
59
|
-
publisher >> space >> doc_type >> space >>
|
|
60
|
-
number >> annex.maybe
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Annex identifier (supplement)
|
|
64
|
-
rule(:annex_identifier) do
|
|
65
|
-
(handbook | technical_report).as(:base) >> annex_supplement
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
rule(:identifier) { annex_identifier | handbook | technical_report }
|
|
69
|
-
|
|
70
|
-
rule(:root) { identifier }
|
|
5
|
+
class Parser
|
|
71
6
|
end
|
|
72
7
|
end
|
|
73
8
|
end
|
data/lib/pubid/plateau.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "pubid"
|
|
4
|
-
require "parslet"
|
|
5
4
|
|
|
6
5
|
module Pubid
|
|
7
6
|
module Plateau
|
|
@@ -31,7 +30,7 @@ module Pubid
|
|
|
31
30
|
|
|
32
31
|
# Apply legacy update_codes normalization first
|
|
33
32
|
normalized = Core::UpdateCodes.apply(input, :plateau)
|
|
34
|
-
parsed = Pubid::Parg::
|
|
33
|
+
parsed = Pubid::Parg::Model.bound(:plateau, normalized)
|
|
35
34
|
Builder.build(parsed)
|
|
36
35
|
end
|
|
37
36
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Renderers
|
|
5
5
|
class DirectivesRenderer < HumanReadable
|
|
6
|
-
def render(context:, with_edition: false)
|
|
6
|
+
def render(context:, with_edition: false, **_opts)
|
|
7
7
|
parts = []
|
|
8
8
|
parts << render_publisher_portion(context)
|
|
9
9
|
num_port = render_number_portion(context)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pubid
|
|
4
|
+
module Renderers
|
|
5
|
+
# Removes the leading publisher token from an already-rendered identifier,
|
|
6
|
+
# for `to_s(with_publisher: false)`.
|
|
7
|
+
#
|
|
8
|
+
# relaton#49 asks for an internal form of every identifier: the form an SDO
|
|
9
|
+
# uses when it cites its own documents, with the publisher prefix dropped
|
|
10
|
+
# (`NIST SP 800-113` -> `SP 800-113`). Teaching 40-odd flavor renderers to
|
|
11
|
+
# omit the publisher would mean 40 chances to drift, and a flavor added
|
|
12
|
+
# tomorrow would get nothing. This class works from the OUTSIDE instead,
|
|
13
|
+
# the way {Annotator} does: the identifier names the prefixes its rendering
|
|
14
|
+
# can start with (`Pubid::Identifier#publisher_prefixes`), and the prefix
|
|
15
|
+
# is cut off together with the one separator that follows it.
|
|
16
|
+
#
|
|
17
|
+
# Deliberate limits:
|
|
18
|
+
#
|
|
19
|
+
# * A prefix must be followed by a separator (space, `-`, `/` or `.`), so
|
|
20
|
+
# `ISO` never cuts into `ISOFIX`.
|
|
21
|
+
# * When no prefix matches, or cutting it would leave nothing, the input
|
|
22
|
+
# comes back unchanged: the option is ignored rather than producing a
|
|
23
|
+
# broken identifier.
|
|
24
|
+
class PublisherStripper
|
|
25
|
+
SEPARATOR = %r{\A(?:\s+|[-/.]\s*)}
|
|
26
|
+
|
|
27
|
+
def initialize(identifier)
|
|
28
|
+
@id = identifier
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# A co-published group is not always joined by `/`: CIE prints
|
|
32
|
+
# `CIE ISO 10916:2024`. The cut therefore repeats while a publisher
|
|
33
|
+
# token still leads, so the group drops whole whatever its joiner.
|
|
34
|
+
# The cap only guards against a cycle; no corpus id has more than three.
|
|
35
|
+
MAX_CUTS = 4
|
|
36
|
+
|
|
37
|
+
# @param rendered [String] the default human rendering of the identifier
|
|
38
|
+
# @return [String] the rendering without its leading publisher token
|
|
39
|
+
def strip(rendered)
|
|
40
|
+
return rendered unless rendered.is_a?(String)
|
|
41
|
+
|
|
42
|
+
candidates = prefixes
|
|
43
|
+
MAX_CUTS.times do
|
|
44
|
+
stripped = cut_once(rendered, candidates)
|
|
45
|
+
break if stripped.equal?(rendered)
|
|
46
|
+
|
|
47
|
+
rendered = stripped
|
|
48
|
+
end
|
|
49
|
+
rendered
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def cut_once(rendered, candidates)
|
|
55
|
+
candidates.each do |prefix|
|
|
56
|
+
next unless rendered.start_with?(prefix)
|
|
57
|
+
|
|
58
|
+
rest = rendered[prefix.length..]
|
|
59
|
+
next unless rest.match?(SEPARATOR)
|
|
60
|
+
|
|
61
|
+
stripped = rest.sub(SEPARATOR, "")
|
|
62
|
+
return stripped unless stripped.empty?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
rendered
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Longest first, so `ISO/IEC` wins over `ISO`. Each prefix also comes in
|
|
69
|
+
# its annotated spelling: ISO annotates inside its own renderer, so its
|
|
70
|
+
# string already reads `<span class="publisher">ISO</span> …` when it
|
|
71
|
+
# reaches this class.
|
|
72
|
+
def prefixes
|
|
73
|
+
Array(@id.publisher_prefixes)
|
|
74
|
+
.map { |p| p.to_s.strip }
|
|
75
|
+
.reject(&:empty?)
|
|
76
|
+
.flat_map { |p| [%(<span class="publisher">#{p}</span>), p] }
|
|
77
|
+
.uniq
|
|
78
|
+
.sort_by { |p| -p.length }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
data/lib/pubid/renderers.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module Pubid
|
|
4
4
|
module Renderers
|
|
5
5
|
autoload :Annotator, "pubid/renderers/annotator"
|
|
6
|
+
autoload :PublisherStripper, "pubid/renderers/publisher_stripper"
|
|
6
7
|
autoload :Base, "pubid/renderers/base"
|
|
7
8
|
autoload :DirectivesRenderer, "pubid/renderers/directives_renderer"
|
|
8
9
|
autoload :GuideRenderer, "pubid/renderers/guide_renderer"
|
data/lib/pubid/sae/parser.rb
CHANGED
|
@@ -1,55 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "parslet"
|
|
4
|
-
|
|
5
3
|
module Pubid
|
|
6
4
|
module Sae
|
|
7
|
-
class Parser
|
|
8
|
-
rule(:space) { str(" ") }
|
|
9
|
-
rule(:digit) { match["0-9"] }
|
|
10
|
-
rule(:digits) { digit.repeat(1) }
|
|
11
|
-
rule(:letter) { match["A-Z"] }
|
|
12
|
-
rule(:colon) { str(":") }
|
|
13
|
-
|
|
14
|
-
# Publisher
|
|
15
|
-
rule(:publisher) { str("SAE").as(:publisher) }
|
|
16
|
-
|
|
17
|
-
# Document types with optional number suffix (e.g., J300, J308A)
|
|
18
|
-
rule(:doc_type) do
|
|
19
|
-
(
|
|
20
|
-
str("AMS") | # Aerospace Material Specification
|
|
21
|
-
str("AIR") | # Aerospace Information Report
|
|
22
|
-
str("ARP") | # Aerospace Recommended Practice
|
|
23
|
-
str("AS") | # Aerospace Standard
|
|
24
|
-
str("J") | # SAE Standard (e.g., J300)
|
|
25
|
-
str("MA") # Material Advisory
|
|
26
|
-
).as(:type)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Number with optional letter prefix or suffix
|
|
30
|
-
rule(:number) do
|
|
31
|
-
((letter.repeat(1,
|
|
32
|
-
1) >> digits) | (digits >> letter.repeat(0,
|
|
33
|
-
1))).as(:number)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Year
|
|
37
|
-
rule(:year) { digit.repeat(4, 4).as(:year) }
|
|
38
|
-
|
|
39
|
-
# Date
|
|
40
|
-
rule(:date) { colon >> year }
|
|
41
|
-
|
|
42
|
-
# Main identifier
|
|
43
|
-
rule(:identifier) do
|
|
44
|
-
publisher >> space >> doc_type >> space.maybe >>
|
|
45
|
-
number >> date.maybe
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
root(:identifier)
|
|
49
|
-
|
|
50
|
-
def self.parse(input)
|
|
51
|
-
new.parse(input)
|
|
52
|
-
end
|
|
5
|
+
class Parser
|
|
53
6
|
end
|
|
54
7
|
end
|
|
55
8
|
end
|
|
@@ -113,7 +113,7 @@ module Pubid
|
|
|
113
113
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
-
parsed = Pubid::Parg::
|
|
116
|
+
parsed = Pubid::Parg::Model.bound(:tgpp, identifier)
|
|
117
117
|
Builder.build(parsed)
|
|
118
118
|
end
|
|
119
119
|
end
|
data/lib/pubid/tgpp/parser.rb
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "parslet"
|
|
4
|
-
|
|
5
3
|
module Pubid
|
|
6
4
|
module Tgpp
|
|
7
|
-
#
|
|
5
|
+
# Grammar for 3GPP identifiers.
|
|
8
6
|
#
|
|
9
7
|
# Form: `[3GPP ]<TR|TS> <NN.NNN>[suffix][-part…][:<release>][/<version>]`
|
|
10
8
|
# TS 23.207:REL-4/2.0.0
|
|
@@ -17,62 +15,7 @@ module Pubid
|
|
|
17
15
|
# document alone ("3GPP TS 23.207"), and relaton parses that bare form to
|
|
18
16
|
# search the index. Only the type and the dotted number core are required,
|
|
19
17
|
# so "TS" and "TS foo" are still rejected.
|
|
20
|
-
class Parser
|
|
21
|
-
rule(:digit) { match["0-9"] }
|
|
22
|
-
rule(:digits) { digit.repeat(1) }
|
|
23
|
-
rule(:space) { str(" ") }
|
|
24
|
-
|
|
25
|
-
# Optional leading publisher token.
|
|
26
|
-
rule(:publisher_prefix) { str("3GPP") >> space }
|
|
27
|
-
|
|
28
|
-
# Document type.
|
|
29
|
-
rule(:type) { (str("TR") | str("TS")).as(:type) }
|
|
30
|
-
|
|
31
|
-
# Dotted number core, kept as a string (preserves leading zeros).
|
|
32
|
-
rule(:number_core) { (digits >> str(".") >> digits).as(:number) }
|
|
33
|
-
|
|
34
|
-
# Letter suffix directly after the number ("U"/"dcs"/"ext"…). Generic so
|
|
35
|
-
# future controlled-vocabulary values keep round-tripping.
|
|
36
|
-
rule(:suffix) { match["A-Za-z"].repeat(1).as(:suffix) }
|
|
37
|
-
|
|
38
|
-
# Hyphen parts.
|
|
39
|
-
rule(:part) { str("-") >> digits.as(:part) }
|
|
40
|
-
rule(:parts) { part.repeat(0).as(:parts) }
|
|
41
|
-
|
|
42
|
-
# Release token, stored verbatim: everything up to the version separator.
|
|
43
|
-
# Excludes ":" (as well as "/") so the ":"-delimited URN scheme can always
|
|
44
|
-
# encode it — every real release ("REL-4", "Ph1", "UMTS", "Release 2000")
|
|
45
|
-
# is colon-free.
|
|
46
|
-
#
|
|
47
|
-
# A trailing segment shaped exactly like a version is a mistyped "/"
|
|
48
|
-
# separator ("TS 23.207:2.0.0"), not a release: no release in the 88,464
|
|
49
|
-
# published index rows contains a dot at all. Reject it rather than
|
|
50
|
-
# silently misfiling the version as the release — which only became
|
|
51
|
-
# reachable once the version segment was made optional.
|
|
52
|
-
rule(:release) do
|
|
53
|
-
(version_core >> any.absent?).absent? >>
|
|
54
|
-
match["^/:"].repeat(1).as(:release)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Three-part version, kept as a string.
|
|
58
|
-
rule(:version_core) do
|
|
59
|
-
digits >> str(".") >> digits >> str(".") >> digits
|
|
60
|
-
end
|
|
61
|
-
rule(:version) { version_core.as(:version) }
|
|
62
|
-
|
|
63
|
-
rule(:identifier) do
|
|
64
|
-
publisher_prefix.maybe >>
|
|
65
|
-
type >> space >>
|
|
66
|
-
number_core >> suffix.maybe >> parts >>
|
|
67
|
-
(str(":") >> release).maybe >>
|
|
68
|
-
(str("/") >> version).maybe
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
rule(:root) { identifier }
|
|
72
|
-
|
|
73
|
-
def self.parse(input)
|
|
74
|
-
new.parse(input)
|
|
75
|
-
end
|
|
18
|
+
class Parser
|
|
76
19
|
end
|
|
77
20
|
end
|
|
78
21
|
end
|
data/lib/pubid/un/identifier.rb
CHANGED
|
@@ -32,7 +32,7 @@ module Pubid
|
|
|
32
32
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
parsed = Pubid::Parg::
|
|
35
|
+
parsed = Pubid::Parg::Model.bound(:un, identifier)
|
|
36
36
|
Builder.build(parsed)
|
|
37
37
|
end
|
|
38
38
|
end
|
data/lib/pubid/un/parser.rb
CHANGED
|
@@ -1,25 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "parslet"
|
|
4
|
-
|
|
5
3
|
module Pubid
|
|
6
4
|
module Un
|
|
7
|
-
class Parser
|
|
8
|
-
rule(:slash) { str("/") }
|
|
9
|
-
rule(:un_prefix) { (str("UN") >> str(" ")).maybe }
|
|
10
|
-
rule(:token) { match("[A-Z0-9.]").repeat(1) }
|
|
11
|
-
|
|
12
|
-
rule(:identifier) do
|
|
13
|
-
un_prefix >>
|
|
14
|
-
token.as(:token) >>
|
|
15
|
-
(slash >> token.as(:token)).repeat(1)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
rule(:root) { identifier }
|
|
19
|
-
|
|
20
|
-
def self.parse(input)
|
|
21
|
-
new.parse(input)
|
|
22
|
-
end
|
|
5
|
+
class Parser
|
|
23
6
|
end
|
|
24
7
|
end
|
|
25
8
|
end
|
data/lib/pubid/version.rb
CHANGED
data/lib/pubid/w3c/identifier.rb
CHANGED
|
@@ -122,7 +122,7 @@ module Pubid
|
|
|
122
122
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
123
123
|
end
|
|
124
124
|
|
|
125
|
-
parsed = Pubid::Parg::
|
|
125
|
+
parsed = Pubid::Parg::Model.bound(:w3c, identifier)
|
|
126
126
|
Builder.build(parsed)
|
|
127
127
|
end
|
|
128
128
|
end
|
data/lib/pubid/w3c/parser.rb
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "parslet"
|
|
4
|
-
|
|
5
3
|
module Pubid
|
|
6
4
|
module W3c
|
|
7
|
-
#
|
|
5
|
+
# Grammar for printed W3C identifiers, e.g.
|
|
8
6
|
# "W3C WD-charmod-19991129" (type + code + date)
|
|
9
7
|
# "W3C NOTE-xml-names" (type + code)
|
|
10
8
|
# "W3C 2dcontext" (bare code)
|
|
@@ -14,30 +12,7 @@ module Pubid
|
|
|
14
12
|
# greedily consume the trailing date and make a `.maybe` date rule
|
|
15
13
|
# unreliable. This is safe: no real W3C code ends in a same-width (4/6/8)
|
|
16
14
|
# digit run, so the Builder's fixed-width trailing-date test never collides.
|
|
17
|
-
class Parser
|
|
18
|
-
rule(:prefix) { str("W3C") >> str(" ") }
|
|
19
|
-
|
|
20
|
-
# Known W3C maturity tokens. Order matters for shared prefixes: CRD before
|
|
21
|
-
# CR, PER before PR, DNOTE before NOTE.
|
|
22
|
-
rule(:type_token) do
|
|
23
|
-
str("DNOTE") | str("NOTE") | str("WD") | str("CRD") | str("CR") |
|
|
24
|
-
str("REC") | str("PER") | str("PR") | str("SPSD") | str("OBSL")
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# A leading token is only a type when immediately followed by "-".
|
|
28
|
-
rule(:type_part) { type_token.as(:type) >> str("-") }
|
|
29
|
-
|
|
30
|
-
# The remainder: code plus an optional trailing date. Captured whole and
|
|
31
|
-
# split in the Builder.
|
|
32
|
-
rule(:rest) { any.repeat(1).as(:rest) }
|
|
33
|
-
|
|
34
|
-
rule(:identifier) { prefix >> type_part.maybe >> rest }
|
|
35
|
-
|
|
36
|
-
rule(:root) { identifier }
|
|
37
|
-
|
|
38
|
-
def self.parse(input)
|
|
39
|
-
new.parse(input)
|
|
40
|
-
end
|
|
15
|
+
class Parser
|
|
41
16
|
end
|
|
42
17
|
end
|
|
43
18
|
end
|
data/lib/pubid/xsf/identifier.rb
CHANGED
|
@@ -30,15 +30,19 @@ module Pubid
|
|
|
30
30
|
# publisher name in an identifier — the leading token is "XEP".
|
|
31
31
|
PUBLISHER = "XSF"
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
# Basic string representation. Delegates to renderer.
|
|
34
|
+
#
|
|
35
|
+
# `with_publisher:` is accepted and has no effect: `XEP` names the
|
|
36
|
+
# series, and XSF itself cites `XEP 0001`, so the internal form is the
|
|
37
|
+
# external one (see {#publisher_prefixes}).
|
|
38
|
+
def to_s(**opts)
|
|
39
39
|
render(format: :human, **opts)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
def publisher_prefixes
|
|
43
|
+
[]
|
|
44
|
+
end
|
|
45
|
+
|
|
42
46
|
# from_hash is the shared polymorphic dispatch on Pubid::Identifier;
|
|
43
47
|
# XSF_TYPE_MAP remains as the key_value polymorphic_map.
|
|
44
48
|
|
|
@@ -58,7 +62,7 @@ module Pubid
|
|
|
58
62
|
raise Pubid::Errors::InvalidInputError, Pubid::INPUT_TOO_LONG_MESSAGE
|
|
59
63
|
end
|
|
60
64
|
|
|
61
|
-
parsed = Pubid::Parg::
|
|
65
|
+
parsed = Pubid::Parg::Model.bound(:xsf, identifier)
|
|
62
66
|
Builder.build(parsed)
|
|
63
67
|
end
|
|
64
68
|
end
|
data/lib/pubid/xsf/parser.rb
CHANGED
|
@@ -1,38 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "parslet"
|
|
4
|
-
|
|
5
3
|
module Pubid
|
|
6
4
|
module Xsf
|
|
7
|
-
#
|
|
8
|
-
# named documents
|
|
9
|
-
class Parser
|
|
10
|
-
# The XMPP extensions repository publishes two documents whose name is
|
|
11
|
-
# not a number: the editor README and the `xep-xxxx` template. Both reach
|
|
12
|
-
# pubid as real primary docids ("XEP README", "XEP xxxx"), so the number
|
|
13
|
-
# rule admits these two literals beside the digits. Exact case, and no
|
|
14
|
-
# other word: the flavor still rejects "XEP foo".
|
|
15
|
-
SPECIAL_NUMBERS = %w[README xxxx].freeze
|
|
16
|
-
|
|
17
|
-
rule(:digit) { match["0-9"] }
|
|
18
|
-
rule(:digits) { digit.repeat(1) }
|
|
19
|
-
|
|
20
|
-
rule(:special_number) do
|
|
21
|
-
SPECIAL_NUMBERS.map { |name| str(name) }.reduce(:|)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# "XEP 0001" — publisher token, a single space, then the number kept as
|
|
25
|
-
# a string of digits (leading zeros preserved by the builder), or the
|
|
26
|
-
# name of one of the two named documents.
|
|
27
|
-
rule(:identifier) do
|
|
28
|
-
str("XEP") >> str(" ") >> (digits | special_number).as(:number)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
rule(:root) { identifier }
|
|
32
|
-
|
|
33
|
-
def self.parse(input)
|
|
34
|
-
new.parse(input)
|
|
35
|
-
end
|
|
5
|
+
# Grammar for XSF identifiers. Two shapes: "XEP NNNN", and the two
|
|
6
|
+
# named documents (README and the four-x placeholder).
|
|
7
|
+
class Parser
|
|
36
8
|
end
|
|
37
9
|
end
|
|
38
10
|
end
|