interscript 3.2.1 → 3.4.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 +4 -4
- data/README.md +26 -0
- data/interscript.gemspec +1 -1
- data/lib/interscript/interpreter.rb +61 -6
- data/lib/interscript/isc/document_builder.rb +47 -2
- data/lib/interscript/isc/grammar/concerns/items.rb +8 -1
- data/lib/interscript/isc/grammar/concerns/stages.rb +41 -5
- data/lib/interscript/isc/grammar/concerns/system.rb +13 -2
- data/lib/interscript/isc/grammar/isc.parg +20 -5
- data/lib/interscript/isc/model/document.rb +5 -0
- data/lib/interscript/isc/model/rule.rb +2 -0
- data/lib/interscript/isc/model/ruleset.rb +21 -0
- data/lib/interscript/isc/model/stage_item.rb +9 -1
- data/lib/interscript/isc/model.rb +1 -0
- data/lib/interscript/isc/node_adapter.rb +192 -29
- data/lib/interscript/isc/normalizer.rb +21 -0
- data/lib/interscript/isc/serializer.rb +34 -0
- data/lib/interscript/isc/transform.rb +15 -0
- data/lib/interscript/isc/yaml_bridge.rb +37 -3
- data/lib/interscript/node/document.rb +18 -6
- data/lib/interscript/node/group/parallel.rb +4 -0
- data/lib/interscript/node/languages_decl.rb +26 -0
- data/lib/interscript/node/rule/sub.rb +11 -3
- data/lib/interscript/node/rule/use.rb +31 -0
- data/lib/interscript/node/rule.rb +1 -0
- data/lib/interscript/node/ruleset.rb +6 -0
- data/lib/interscript/node.rb +2 -0
- data/lib/interscript/version.rb +1 -1
- data/lib/interscript.rb +36 -3
- metadata +8 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4eb31ebccf84ad17a8c5ec38431c2368e8e96bf925aa4b80b2014ceb7f5a5b4c
|
|
4
|
+
data.tar.gz: 7922c008dc6d3b4eea5243dcd12b79ed377a80e91096282d1c7cf13cd48a6069
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c0bd6f3b49ba673e7fdc181e815c634464d7bebb6522f0b6ce57dfa87845653495b6a8f5c18ff20890fbed841295688cdd848ecfda7f978962d7f2b1acb0821
|
|
7
|
+
data.tar.gz: 54e5bdf832a5fe40c2cde82e7f5b3a915da598f75bae1db8f688e37df6c1c508dfc7ad6909a72dd3b298219521b23e65084387bad7db9afd7ae5a4e94c77aeb2
|
data/README.md
CHANGED
|
@@ -33,6 +33,32 @@ Interscript.transliterate("odni-rus-Cyrl-Latn-2015", "привет")
|
|
|
33
33
|
# => "privet"
|
|
34
34
|
----
|
|
35
35
|
|
|
36
|
+
== Workflow extension (ISC-WF-1)
|
|
37
|
+
|
|
38
|
+
Maps can reuse rule blocks and condition rows on a language. A
|
|
39
|
+
`ruleset` is spliced at a `use` position (recursively, across
|
|
40
|
+
declared dependencies); `use ... override { ... }` replaces rows on
|
|
41
|
+
byte-exact from-equality; a `languages` declaration plus `when`
|
|
42
|
+
guards let the caller select a language per call — guards are inert
|
|
43
|
+
without a selection:
|
|
44
|
+
|
|
45
|
+
[source,ruby]
|
|
46
|
+
----
|
|
47
|
+
# alalc-tha and bgnpcgn-tha share their leading-vowel machinery
|
|
48
|
+
# through libs/thai-latn.isc — one ruleset, per-map overrides
|
|
49
|
+
Interscript.transliterate("bgnpcgn-tha-Thai-Latn-2002", "กรุงเทพ")
|
|
50
|
+
|
|
51
|
+
# language conditioning: select one of the map's declared languages;
|
|
52
|
+
# guards are inert when no (or an undeclared) language is passed
|
|
53
|
+
Interscript.transliterate("some-map-id", "...", {}, language: "kur")
|
|
54
|
+
----
|
|
55
|
+
|
|
56
|
+
The constructs are additive: maps that don't use them transliterate
|
|
57
|
+
identically. The corpus adoption so far is the Thai leading-vowel
|
|
58
|
+
shared ruleset (W1/W2); W3 conditioning is exercised by the
|
|
59
|
+
conformance fixtures. Normative text: interscript/maps
|
|
60
|
+
`docs/isc-wf-1.md`.
|
|
61
|
+
|
|
36
62
|
== Neural models (Interscript::ML)
|
|
37
63
|
|
|
38
64
|
The gem embeds the IMF v1 model runtime — resolve a model id through
|
data/interscript.gemspec
CHANGED
|
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
|
|
|
33
33
|
spec.require_paths = ["lib"]
|
|
34
34
|
|
|
35
35
|
spec.add_dependency "thor"
|
|
36
|
-
spec.add_dependency "interscript-maps", "~>
|
|
36
|
+
spec.add_dependency "interscript-maps", "~> 3.0"
|
|
37
37
|
spec.add_dependency "text"
|
|
38
38
|
spec.add_dependency "parsanol", ">= 1.3.61", "< 2"
|
|
39
39
|
end
|
|
@@ -5,7 +5,7 @@ class Interscript::Interpreter < Interscript::Compiler
|
|
|
5
5
|
self
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def call(str, stage = :main, each: false, &block)
|
|
8
|
+
def call(str, stage = :main, each: false, language: nil, &block)
|
|
9
9
|
stage = @map.stages[stage]
|
|
10
10
|
if each
|
|
11
11
|
e = Enumerator.new do |yielder|
|
|
@@ -22,7 +22,7 @@ class Interscript::Interpreter < Interscript::Compiler
|
|
|
22
22
|
# standard:disable Style/GlobalVars (deliberate $DEBUG / -d-flag debug idiom)
|
|
23
23
|
$select_nth_string = true
|
|
24
24
|
# standard:enable Style/GlobalVars
|
|
25
|
-
result = Stage.new(@map, str).execute_rule(stage)
|
|
25
|
+
result = Stage.new(@map, str, language: language).execute_rule(stage)
|
|
26
26
|
# standard:disable Style/GlobalVars (deliberate $DEBUG / -d-flag debug idiom)
|
|
27
27
|
$select_nth_string = false
|
|
28
28
|
# standard:enable Style/GlobalVars
|
|
@@ -68,20 +68,72 @@ class Interscript::Interpreter < Interscript::Compiler
|
|
|
68
68
|
e
|
|
69
69
|
end
|
|
70
70
|
else
|
|
71
|
-
Stage.new(@map, str).execute_rule(stage)
|
|
71
|
+
Stage.new(@map, str, language: language).execute_rule(stage)
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
+
# ISC-WF-1 W3: effective language for a run. A selection outside the
|
|
76
|
+
# map's `languages` declaration (or any selection on a map without a
|
|
77
|
+
# declaration) means every `when` guard is inert (spec §5.1).
|
|
78
|
+
def self.select_language(map, language)
|
|
79
|
+
return nil if language.nil?
|
|
80
|
+
|
|
81
|
+
declared = map.languages
|
|
82
|
+
return nil unless declared.is_a?(Interscript::Node::LanguagesDecl) &&
|
|
83
|
+
declared.include?(language.to_sym)
|
|
84
|
+
|
|
85
|
+
language.to_sym
|
|
86
|
+
end
|
|
87
|
+
|
|
75
88
|
class Stage
|
|
76
|
-
|
|
89
|
+
attr_reader :language
|
|
90
|
+
|
|
91
|
+
def initialize(map, str, language: nil)
|
|
77
92
|
@str = str
|
|
78
93
|
@map = map
|
|
94
|
+
@language = Interscript::Interpreter.select_language(map, language)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# ISC-WF-1 W3 guard applicability. Unguarded rules always apply; a
|
|
98
|
+
# guarded rule applies only under a selected language that its `when`
|
|
99
|
+
# list contains. Inert rules are absent from the pass (spec §5.1).
|
|
100
|
+
def guard_applies? rule
|
|
101
|
+
return true unless Interscript::Node::Rule::Sub === rule && rule.when_languages
|
|
102
|
+
return false unless @language
|
|
103
|
+
|
|
104
|
+
rule.when_languages.include?(@language)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# ISC-WF-1 W3: the effective rule set of a parallel pass = unguarded
|
|
108
|
+
# rules ∪ guard-applicable rules; where an unguarded rule and a
|
|
109
|
+
# guard-applicable rule share the same from-string, the
|
|
110
|
+
# guard-applicable rule wins (spec §5.2). Passes without any guard
|
|
111
|
+
# return the group untouched — zero change for pre-WF1 maps. Filtered
|
|
112
|
+
# variants are memoized per selected language on the group so the
|
|
113
|
+
# compiled-tree cache below stays per-language.
|
|
114
|
+
def resolve_parallel_guards r
|
|
115
|
+
return r unless r.children.any? { |c| Interscript::Node::Rule::Sub === c && c.when_languages }
|
|
116
|
+
|
|
117
|
+
r.wf1_variants ||= {}
|
|
118
|
+
r.wf1_variants[@language] ||= begin
|
|
119
|
+
applicable = r.children.select { |c| guard_applies?(c) }
|
|
120
|
+
guarded_froms = applicable
|
|
121
|
+
.select { |c| Interscript::Node::Rule::Sub === c && c.when_languages }
|
|
122
|
+
.map { |c| c.from.inspect }
|
|
123
|
+
filtered = Interscript::Node::Group::Parallel.new
|
|
124
|
+
filtered.children = applicable.reject do |c|
|
|
125
|
+
Interscript::Node::Rule::Sub === c && !c.when_languages &&
|
|
126
|
+
guarded_froms.include?(c.from.inspect)
|
|
127
|
+
end
|
|
128
|
+
filtered
|
|
129
|
+
end
|
|
79
130
|
end
|
|
80
131
|
|
|
81
132
|
def execute_rule r
|
|
82
133
|
return if r.reverse_run == true
|
|
83
134
|
case r
|
|
84
135
|
when Interscript::Node::Group::Parallel
|
|
136
|
+
r = resolve_parallel_guards(r)
|
|
85
137
|
if r.cached_tree
|
|
86
138
|
@str = Interscript::Stdlib.parallel_replace_tree(@str, r.cached_tree)
|
|
87
139
|
elsif r.subs_regexp && r.subs_replacements
|
|
@@ -135,9 +187,12 @@ class Interscript::Interpreter < Interscript::Compiler
|
|
|
135
187
|
end
|
|
136
188
|
when Interscript::Node::Group
|
|
137
189
|
r.children.each do |t|
|
|
190
|
+
# ISC-WF-1 W3: in sequence, an inert guarded rule is skipped.
|
|
191
|
+
next unless guard_applies?(t)
|
|
138
192
|
execute_rule(t)
|
|
139
193
|
end
|
|
140
194
|
when Interscript::Node::Rule::Sub
|
|
195
|
+
return @str unless guard_applies?(r)
|
|
141
196
|
@str = if r.to == :upcase
|
|
142
197
|
@str.gsub(Regexp.new(build_regexp(r)), &:upcase)
|
|
143
198
|
elsif r.to == :downcase
|
|
@@ -151,10 +206,10 @@ class Interscript::Interpreter < Interscript::Compiler
|
|
|
151
206
|
if r.stage.map
|
|
152
207
|
doc = @map.dep_aliases[r.stage.map].document
|
|
153
208
|
stage = doc.imported_stages[r.stage.name]
|
|
154
|
-
@str = Stage.new(doc, @str).execute_rule(stage)
|
|
209
|
+
@str = Stage.new(doc, @str, language: @language).execute_rule(stage)
|
|
155
210
|
else
|
|
156
211
|
stage = @map.imported_stages[r.stage.name]
|
|
157
|
-
@str = Stage.new(@map, @str).execute_rule(stage)
|
|
212
|
+
@str = Stage.new(@map, @str, language: @language).execute_rule(stage)
|
|
158
213
|
end
|
|
159
214
|
end
|
|
160
215
|
|
|
@@ -37,6 +37,8 @@ module Interscript
|
|
|
37
37
|
tests_arr = []
|
|
38
38
|
stages_arr = []
|
|
39
39
|
dependencies_arr = []
|
|
40
|
+
rulesets_arr = []
|
|
41
|
+
languages_arr = []
|
|
40
42
|
|
|
41
43
|
body.each do |item|
|
|
42
44
|
if item[:metadata]
|
|
@@ -45,6 +47,12 @@ module Interscript
|
|
|
45
47
|
aliases_arr.concat(extract_aliases(item[:aliases]))
|
|
46
48
|
elsif item[:tests]
|
|
47
49
|
tests_arr.concat(extract_tests(item[:tests]))
|
|
50
|
+
elsif item[:languages]
|
|
51
|
+
# ISC-WF-1 W3: map-declared languages.
|
|
52
|
+
languages_arr.concat(Array(item[:languages]).map { |l| ident(l) })
|
|
53
|
+
elsif item[:ruleset]
|
|
54
|
+
# ISC-WF-1 W1: named ruleset declaration.
|
|
55
|
+
rulesets_arr << extract_ruleset(item)
|
|
48
56
|
elsif item[:stage]
|
|
49
57
|
stages_arr << extract_stage(item)
|
|
50
58
|
elsif item[:target]
|
|
@@ -52,7 +60,7 @@ module Interscript
|
|
|
52
60
|
end
|
|
53
61
|
end
|
|
54
62
|
|
|
55
|
-
{
|
|
63
|
+
doc = {
|
|
56
64
|
schemaVersion: SCHEMA_VERSION,
|
|
57
65
|
filename: @filename,
|
|
58
66
|
systemCode: code,
|
|
@@ -63,6 +71,11 @@ module Interscript
|
|
|
63
71
|
stages: stages_arr,
|
|
64
72
|
dependencies: dependencies_arr
|
|
65
73
|
}
|
|
74
|
+
# ISC-WF-1 keys are present only when the map uses the constructs —
|
|
75
|
+
# documents of maps that don't keep their exact pre-WF1 shape.
|
|
76
|
+
doc[:languages] = languages_arr if languages_arr.any?
|
|
77
|
+
doc[:rulesets] = rulesets_arr if rulesets_arr.any?
|
|
78
|
+
doc
|
|
66
79
|
end
|
|
67
80
|
|
|
68
81
|
private
|
|
@@ -254,6 +267,15 @@ module Interscript
|
|
|
254
267
|
{name: name, body: body}
|
|
255
268
|
end
|
|
256
269
|
|
|
270
|
+
# ISC-WF-1 W1: a named ruleset declaration — same body shape as a
|
|
271
|
+
# stage, addressed by name through `use` expansion.
|
|
272
|
+
def extract_ruleset(item)
|
|
273
|
+
node = item[:ruleset]
|
|
274
|
+
name = ident(item[:ruleset_name])
|
|
275
|
+
body = Array(node).flat_map { |n| extract_stage_items(n) }
|
|
276
|
+
{name: name, body: body}
|
|
277
|
+
end
|
|
278
|
+
|
|
257
279
|
def extract_stage_items(n)
|
|
258
280
|
return [] unless n.is_a?(Hash)
|
|
259
281
|
if n[:sequence]
|
|
@@ -279,6 +301,10 @@ module Interscript
|
|
|
279
301
|
[{kind: :run, dependency: nil, stage: ident(n[:run_stage_only][:stage])}]
|
|
280
302
|
elsif n[:bare_rule]
|
|
281
303
|
[{kind: :bare_rule, rule: extract_rule(n[:bare_rule])}]
|
|
304
|
+
elsif n[:use]
|
|
305
|
+
# ISC-WF-1 W1/W2: `use <ident>` or `use <alias>.<ident>` with an
|
|
306
|
+
# optional override block.
|
|
307
|
+
[extract_use(n[:use])]
|
|
282
308
|
elsif n[:comment]
|
|
283
309
|
[]
|
|
284
310
|
elsif n[:noop]
|
|
@@ -288,6 +314,19 @@ module Interscript
|
|
|
288
314
|
end
|
|
289
315
|
end
|
|
290
316
|
|
|
317
|
+
# `use <name>` (same-map ruleset) or `use <dep>.<name>` (dependency-
|
|
318
|
+
# declared ruleset), plus the optional override rows (W2).
|
|
319
|
+
def extract_use(u)
|
|
320
|
+
dep = u[:qualified_name] ? ident(u[:use_alias]) : nil
|
|
321
|
+
name = ident(u[:qualified_name] || u[:use_alias])
|
|
322
|
+
item = {kind: :use, ruleset: name, dependency: dep}
|
|
323
|
+
overrides = Array(u[:use_overrides])
|
|
324
|
+
.select { |r| r.is_a?(Hash) && r.key?(:from) }
|
|
325
|
+
.map { |r| extract_rule(r) }
|
|
326
|
+
item[:overrides] = overrides if overrides.any?
|
|
327
|
+
item
|
|
328
|
+
end
|
|
329
|
+
|
|
291
330
|
# Filter out noop rules (from: None, to: None) created by empty
|
|
292
331
|
# parallel/sequence blocks that contain only comments or whitespace.
|
|
293
332
|
def filter_noop(rules)
|
|
@@ -302,11 +341,17 @@ module Interscript
|
|
|
302
341
|
from_val = r.is_a?(Hash) ? r[:from] : nil
|
|
303
342
|
to_val = r.is_a?(Hash) ? r[:to] : nil
|
|
304
343
|
constraints_val = r.is_a?(Hash) ? r[:constraints] : nil
|
|
305
|
-
{
|
|
344
|
+
rule = {
|
|
306
345
|
from: from_val ? materialize(from_val) : Items::None.new,
|
|
307
346
|
to: to_val ? materialize(to_val) : Items::None.new,
|
|
308
347
|
constraints: extract_constraints(constraints_val)
|
|
309
348
|
}
|
|
349
|
+
# ISC-WF-1 W3: a `when` guard folds to the list of language names.
|
|
350
|
+
# The rule hash never goes through Transform as a whole (from/to/
|
|
351
|
+
# constraints are materialized individually), so the guard fragment
|
|
352
|
+
# gets its own apply.
|
|
353
|
+
rule[:when_languages] = @transform.apply(when: r[:when]) if r.is_a?(Hash) && r[:when]
|
|
354
|
+
rule
|
|
310
355
|
end
|
|
311
356
|
|
|
312
357
|
def extract_constraints(arr)
|
|
@@ -125,7 +125,14 @@ module Interscript
|
|
|
125
125
|
str("line_end") | str("word_boundary") |
|
|
126
126
|
str("downcase") | str("upcase") | str("title_case") |
|
|
127
127
|
str("capture") | str("maybe") | str("some") | str("ref") |
|
|
128
|
-
str("any_character")
|
|
128
|
+
str("any_character") |
|
|
129
|
+
# ISC-WF-1: ruleset / use / override / languages / when join
|
|
130
|
+
# the keyword table (spec §2). Prefix matching follows the
|
|
131
|
+
# existing table behavior; quoted-string forms remain the
|
|
132
|
+
# pass-through escape hatch. Verified against the 404-map
|
|
133
|
+
# corpus: no bare identifier starts with any of these words.
|
|
134
|
+
str("ruleset") | str("use") | str("override") |
|
|
135
|
+
str("languages") | str("when")
|
|
129
136
|
end
|
|
130
137
|
|
|
131
138
|
# Concatenation: one or more atoms. The continuation pattern
|
|
@@ -17,18 +17,52 @@ module Interscript
|
|
|
17
17
|
braced(stage_item.repeat(0)).as(:stage)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
# ISC-WF-1 W1: a named, reusable block of stage items. Declared at
|
|
21
|
+
# map top level anywhere a stage may appear; never executes on its
|
|
22
|
+
# own (only `use` expansion reaches the runtime).
|
|
23
|
+
rule(:ruleset_block) do
|
|
24
|
+
str("ruleset") >> whitespace >>
|
|
25
|
+
identifier.as(:ruleset_name) >> whitespace? >>
|
|
26
|
+
braced(stage_item.repeat(0)).as(:ruleset)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# ISC-WF-1 W1/W2: `use <ident>` (same-map ruleset) or
|
|
30
|
+
# `use <alias>.<ident>` (dependency-declared ruleset), with an
|
|
31
|
+
# optional override block of full replacement rows (child-wins on
|
|
32
|
+
# exact from-equality).
|
|
33
|
+
rule(:use_directive) do
|
|
34
|
+
(str("use") >> whitespace >>
|
|
35
|
+
identifier.as(:use_alias) >>
|
|
36
|
+
(str(".") >> identifier.as(:qualified_name)).maybe >>
|
|
37
|
+
(whitespace >> str("override") >> whitespace? >>
|
|
38
|
+
braced(rule_line.repeat(0)).as(:use_overrides)).maybe
|
|
39
|
+
).as(:use)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# ISC-WF-1 W3: `when <ident> (","|ws <ident>)*` — a language guard
|
|
43
|
+
# on a sub rule. Inert unless the caller selects a language the
|
|
44
|
+
# map declares (spec §5.1). Identifiers are keyword-guarded so the
|
|
45
|
+
# repeat stops at the next rule instead of swallowing it.
|
|
46
|
+
rule(:when_guard) do
|
|
47
|
+
(whitespace >> str("when") >> whitespace >>
|
|
48
|
+
keyword.absent? >> identifier.as(:when_lang) >>
|
|
49
|
+
((comma | whitespace) >> (keyword.absent? >> identifier.as(:when_lang))).repeat
|
|
50
|
+
).as(:when)
|
|
51
|
+
end
|
|
52
|
+
|
|
20
53
|
rule(:stage_item) do
|
|
21
54
|
whitespace? >>
|
|
22
55
|
(sequence_block | parallel_block | run_rule | separate_directive |
|
|
23
56
|
string_case_directive | compose_directive | rababa_directive |
|
|
24
|
-
bare_rule | comment_item) >>
|
|
57
|
+
use_directive | bare_rule | comment_item) >>
|
|
25
58
|
whitespace?
|
|
26
59
|
end
|
|
27
60
|
|
|
28
|
-
# Comments
|
|
61
|
+
# Comments are consumed; stray identifiers are a parse error
|
|
62
|
+
# (WO87/A4 strict-parse ruling: the corpus is 295/295 strict-clean,
|
|
63
|
+
# so a typo'd map can no longer silently no-op).
|
|
29
64
|
rule(:comment_item) do
|
|
30
|
-
(str("#") >> (str("\n").absent? >> any).repeat).as(:comment)
|
|
31
|
-
identifier.as(:noop)
|
|
65
|
+
(str("#") >> (str("\n").absent? >> any).repeat).as(:comment)
|
|
32
66
|
end
|
|
33
67
|
|
|
34
68
|
# Bare rule directly in a stage body (not wrapped in sequence/parallel).
|
|
@@ -104,7 +138,8 @@ module Interscript
|
|
|
104
138
|
str("sub") >> whitespace >>
|
|
105
139
|
item_atom.as(:from) >> whitespace >>
|
|
106
140
|
item_atom.as(:to) >>
|
|
107
|
-
constraints
|
|
141
|
+
constraints >>
|
|
142
|
+
when_guard.maybe
|
|
108
143
|
end
|
|
109
144
|
|
|
110
145
|
rule(:block_rule) do
|
|
@@ -113,6 +148,7 @@ module Interscript
|
|
|
113
148
|
str("from") >> whitespace >> item.as(:from) >> whitespace? >>
|
|
114
149
|
str("to") >> whitespace >> item.as(:to) >>
|
|
115
150
|
(whitespace >> constraint).repeat.as(:constraints) >>
|
|
151
|
+
when_guard.maybe >>
|
|
116
152
|
whitespace? >> str("}")
|
|
117
153
|
end
|
|
118
154
|
|
|
@@ -10,12 +10,23 @@ module Interscript
|
|
|
10
10
|
module System
|
|
11
11
|
include Parsanol
|
|
12
12
|
|
|
13
|
+
# ISC-WF-1 W3: the languages a map serves, declared once per map
|
|
14
|
+
# inside the system block alongside metadata (spec §5.1). List
|
|
15
|
+
# identifiers are keyword-guarded so the repeat stops at the next
|
|
16
|
+
# block item (`stage`, `ruleset`, …) instead of swallowing it.
|
|
17
|
+
rule(:languages_decl) do
|
|
18
|
+
str("languages") >> whitespace >>
|
|
19
|
+
(keyword.absent? >> identifier.as(:language) >>
|
|
20
|
+
((comma | whitespace) >> (keyword.absent? >> identifier.as(:language))).repeat
|
|
21
|
+
).as(:languages)
|
|
22
|
+
end
|
|
23
|
+
|
|
13
24
|
# A block-item is any of the top-level constructs that may appear
|
|
14
25
|
# inside `system "<code>" { ... }`.
|
|
15
26
|
rule(:block_item) do
|
|
16
27
|
whitespace? >>
|
|
17
|
-
(metadata_block | aliases_block | tests_block |
|
|
18
|
-
stage_block | dependency_decl) >>
|
|
28
|
+
(metadata_block | aliases_block | tests_block | languages_decl |
|
|
29
|
+
ruleset_block | stage_block | dependency_decl) >>
|
|
19
30
|
whitespace?
|
|
20
31
|
end
|
|
21
32
|
|
|
@@ -28,7 +28,7 @@ grammar InterscriptIsc version "0.1.0" {
|
|
|
28
28
|
ident_rest = %x41-5A / %x61-7A / %x30-39 / %x5F
|
|
29
29
|
identifier = (ident_first 0*ident_rest) as identifier
|
|
30
30
|
|
|
31
|
-
keyword = "parallel" / "sequence" / "stage" / "compose" / "separate" / "system" / "metadata" / "aliases" / "tests" / "notes" / "description" / "any_character" / "authority" / "dependency" / "run" / "sub" / "before" / "after" / "not_before" / "not_after" / "any" / "none" / "boundary" / "line_start" / "line_end" / "word_boundary" / "downcase" / "upcase" / "title_case" / "capture" / "maybe" / "some" / "ref" / "name"
|
|
31
|
+
keyword = "parallel" / "sequence" / "stage" / "compose" / "separate" / "system" / "metadata" / "aliases" / "tests" / "notes" / "description" / "any_character" / "authority" / "dependency" / "run" / "sub" / "before" / "after" / "not_before" / "not_after" / "any" / "none" / "boundary" / "line_start" / "line_end" / "word_boundary" / "downcase" / "upcase" / "title_case" / "capture" / "maybe" / "some" / "ref" / "name" / "ruleset" / "use" / "override" / "languages" / "when"
|
|
32
32
|
|
|
33
33
|
# -- string literals ----------------------------------------------------
|
|
34
34
|
|
|
@@ -143,9 +143,12 @@ grammar InterscriptIsc version "0.1.0" {
|
|
|
143
143
|
constraint = constraint_after / constraint_before / constraint_not_before / constraint_not_after
|
|
144
144
|
constraints = *(whitespace constraint)
|
|
145
145
|
|
|
146
|
-
compact_rule = "sub" whitespace (item_atom as from) whitespace (item_atom as to) ((*(whitespace constraint)) as constraints)
|
|
146
|
+
compact_rule = "sub" whitespace (item_atom as from) whitespace (item_atom as to) ((*(whitespace constraint)) as constraints) [(when_guard)]
|
|
147
147
|
|
|
148
|
-
block_rule = "sub" [ws] "{" [ws] "from" whitespace (item as from) [ws] "to" whitespace (item as to) ((*(whitespace constraint)) as constraints) [ws] "}"
|
|
148
|
+
block_rule = "sub" [ws] "{" [ws] "from" whitespace (item as from) [ws] "to" whitespace (item as to) ((*(whitespace constraint)) as constraints) [(when_guard)] [ws] "}"
|
|
149
|
+
|
|
150
|
+
when_lang = (!(keyword) identifier) as when_lang
|
|
151
|
+
when_guard = (whitespace "when" whitespace (when_lang) *(((comma_ws / whitespace) (when_lang)))) as when
|
|
149
152
|
|
|
150
153
|
rule = block_rule / compact_rule
|
|
151
154
|
|
|
@@ -172,12 +175,21 @@ grammar InterscriptIsc version "0.1.0" {
|
|
|
172
175
|
run_rule = "run" whitespace ((run_dep) / run_stage_only)
|
|
173
176
|
|
|
174
177
|
bare_rule = (rule) as bare_rule
|
|
175
|
-
|
|
178
|
+
|
|
179
|
+
use_alias = (identifier) as use_alias
|
|
180
|
+
use_qualified = "." (identifier as qualified_name)
|
|
181
|
+
use_overrides_part = whitespace "override" [ws] "{" [ws] ((*(rule_line)) as use_overrides) [ws] "}"
|
|
182
|
+
use_directive = (("use" whitespace (use_alias) [(use_qualified)] [(use_overrides_part)]) as use)
|
|
183
|
+
|
|
184
|
+
stage_item = [ws] (sequence_block / parallel_block / run_rule / separate_directive / string_case_directive / compose_directive / rababa_directive / use_directive / bare_rule / comment_item) [ws]
|
|
176
185
|
stage_items = *(stage_item)
|
|
177
186
|
stage_name_paren = "(" (identifier as stage_name) ")"
|
|
178
187
|
stage_name_ws = whitespace (identifier as stage_name)
|
|
179
188
|
stage_block = "stage" (stage_name_paren / stage_name_ws) [ws] "{" [ws] (stage_items as stage) [ws] "}"
|
|
180
189
|
|
|
190
|
+
ruleset_name_ws = whitespace (identifier as ruleset_name)
|
|
191
|
+
ruleset_block = "ruleset" (ruleset_name_ws) [ws] "{" [ws] (stage_items as ruleset) [ws] "}"
|
|
192
|
+
|
|
181
193
|
# -- tests ---------------------------------------------------------------
|
|
182
194
|
|
|
183
195
|
test_note = whitespace "note" whitespace (quoted_string as note)
|
|
@@ -187,7 +199,10 @@ grammar InterscriptIsc version "0.1.0" {
|
|
|
187
199
|
|
|
188
200
|
# -- system --------------------------------------------------------------
|
|
189
201
|
|
|
190
|
-
|
|
202
|
+
language_ident = (!(keyword) identifier) as language
|
|
203
|
+
languages_decl = ("languages" whitespace ((language_ident *(((comma_ws / whitespace) (language_ident))))) as languages)
|
|
204
|
+
|
|
205
|
+
block_item = [whitespace] (metadata_block / aliases_block / tests_block / languages_decl / ruleset_block / stage_block / dependency_decl) [whitespace]
|
|
191
206
|
body_items = *(block_item)
|
|
192
207
|
system_block = "system" whitespace ((quoted_string) as system_code) [whitespace] "{" [ws] ((*(block_item)) as body) [ws] "}" [whitespace]
|
|
193
208
|
isc_source = [whitespace] ((system_block) as system) [whitespace]
|
|
@@ -4,6 +4,7 @@ require "lutaml/model"
|
|
|
4
4
|
require "interscript/isc/model/test"
|
|
5
5
|
require "interscript/isc/model/alias"
|
|
6
6
|
require "interscript/isc/model/stage"
|
|
7
|
+
require "interscript/isc/model/ruleset"
|
|
7
8
|
require "interscript/isc/model/dependency"
|
|
8
9
|
|
|
9
10
|
module Interscript
|
|
@@ -15,6 +16,8 @@ module Interscript
|
|
|
15
16
|
attribute :tests, Test, collection: true
|
|
16
17
|
attribute :aliases, Alias, collection: true
|
|
17
18
|
attribute :stages, Stage, collection: true
|
|
19
|
+
attribute :rulesets, Ruleset, collection: true
|
|
20
|
+
attribute :languages, :string, collection: true
|
|
18
21
|
attribute :dependencies, Dependency, collection: true
|
|
19
22
|
|
|
20
23
|
yaml do
|
|
@@ -23,6 +26,8 @@ module Interscript
|
|
|
23
26
|
map "tests", to: :tests
|
|
24
27
|
map "aliases", to: :aliases
|
|
25
28
|
map "stages", to: :stages
|
|
29
|
+
map "rulesets", to: :rulesets
|
|
30
|
+
map "languages", to: :languages
|
|
26
31
|
map "dependencies", to: :dependencies
|
|
27
32
|
end
|
|
28
33
|
end
|
|
@@ -11,11 +11,13 @@ module Interscript
|
|
|
11
11
|
attribute :from, Item
|
|
12
12
|
attribute :to, Item
|
|
13
13
|
attribute :constraints, Constraint, collection: true
|
|
14
|
+
attribute :when_languages, :string, collection: true
|
|
14
15
|
|
|
15
16
|
yaml do
|
|
16
17
|
map "from", to: :from
|
|
17
18
|
map "to", to: :to
|
|
18
19
|
map "constraints", to: :constraints
|
|
20
|
+
map "when_languages", to: :when_languages
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "lutaml/model"
|
|
4
|
+
require "interscript/isc/model/stage_item"
|
|
5
|
+
|
|
6
|
+
module Interscript
|
|
7
|
+
module Isc
|
|
8
|
+
module Model
|
|
9
|
+
# ISC-WF-1 W1: a named, reusable block of stage items (spec §3).
|
|
10
|
+
class Ruleset < Lutaml::Model::Serializable
|
|
11
|
+
attribute :name, :string
|
|
12
|
+
attribute :body, StageItem, collection: true
|
|
13
|
+
|
|
14
|
+
yaml do
|
|
15
|
+
map "name", to: :name
|
|
16
|
+
map "body", to: :body
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -8,7 +8,8 @@ module Interscript
|
|
|
8
8
|
module Isc
|
|
9
9
|
module Model
|
|
10
10
|
# A single item in a stage body. Discriminated by `kind`:
|
|
11
|
-
# parallel, sequence, bare_rule, run, separate, compose,
|
|
11
|
+
# parallel, sequence, bare_rule, run, use, separate, compose,
|
|
12
|
+
# string_case
|
|
12
13
|
class StageItem < Lutaml::Model::Serializable
|
|
13
14
|
attribute :kind, :string
|
|
14
15
|
attribute :rules, Rule, collection: true
|
|
@@ -17,6 +18,11 @@ module Interscript
|
|
|
17
18
|
attribute :stage, :string
|
|
18
19
|
attribute :separator, Item
|
|
19
20
|
attribute :op, :string
|
|
21
|
+
# ISC-WF-1 W1/W2: the referenced ruleset and the per-site
|
|
22
|
+
# override rows; `dependency` carries the alias for qualified
|
|
23
|
+
# references (`use alias.<ruleset>`).
|
|
24
|
+
attribute :ruleset, :string
|
|
25
|
+
attribute :overrides, Rule, collection: true
|
|
20
26
|
|
|
21
27
|
yaml do
|
|
22
28
|
map "kind", to: :kind
|
|
@@ -26,6 +32,8 @@ module Interscript
|
|
|
26
32
|
map "stage", to: :stage
|
|
27
33
|
map "separator", to: :separator
|
|
28
34
|
map "op", to: :op
|
|
35
|
+
map "ruleset", to: :ruleset
|
|
36
|
+
map "overrides", to: :overrides
|
|
29
37
|
end
|
|
30
38
|
end
|
|
31
39
|
end
|
|
@@ -10,6 +10,7 @@ module Interscript
|
|
|
10
10
|
autoload :Rule, "interscript/isc/model/rule"
|
|
11
11
|
autoload :StageItem, "interscript/isc/model/stage_item"
|
|
12
12
|
autoload :Stage, "interscript/isc/model/stage"
|
|
13
|
+
autoload :Ruleset, "interscript/isc/model/ruleset"
|
|
13
14
|
autoload :Alias, "interscript/isc/model/alias"
|
|
14
15
|
autoload :Test, "interscript/isc/model/test"
|
|
15
16
|
autoload :Dependency, "interscript/isc/model/dependency"
|