junoser 0.7.2 → 0.7.4

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +47 -7
  5. data/LICENSE.txt +1 -1
  6. data/README.md +1 -2
  7. data/Rakefile +6 -3
  8. data/bin/console +4 -3
  9. data/exe/junoser +6 -5
  10. data/exe/junoser-squash +9 -6
  11. data/junoser.gemspec +15 -17
  12. data/lib/junoser/cli.rb +2 -2
  13. data/lib/junoser/development.rb +2 -0
  14. data/lib/junoser/display/config_store.rb +12 -12
  15. data/lib/junoser/display/enumerable.rb +6 -4
  16. data/lib/junoser/display/set.rb +15 -17
  17. data/lib/junoser/display/structure.rb +7 -7
  18. data/lib/junoser/display.rb +2 -0
  19. data/lib/junoser/input.rb +11 -10
  20. data/lib/junoser/js_ruler.rb +74 -66
  21. data/lib/junoser/parser.rb +164 -8
  22. data/lib/junoser/rule_tree/node.rb +3 -1
  23. data/lib/junoser/rule_tree/parser.rb +11 -8
  24. data/lib/junoser/rule_tree.rb +2 -0
  25. data/lib/junoser/ruler.rb +193 -171
  26. data/lib/junoser/squash.rb +16 -14
  27. data/lib/junoser/transformer.rb +9 -8
  28. data/lib/junoser/version.rb +3 -1
  29. data/lib/junoser/xsd/base.rb +15 -14
  30. data/lib/junoser/xsd/choice.rb +10 -9
  31. data/lib/junoser/xsd/complex_type.rb +13 -11
  32. data/lib/junoser/xsd/element.rb +34 -36
  33. data/lib/junoser/xsd/enumeration.rb +9 -8
  34. data/lib/junoser/xsd/parsable.rb +2 -0
  35. data/lib/junoser/xsd/restriction.rb +15 -13
  36. data/lib/junoser/xsd/sequence.rb +11 -8
  37. data/lib/junoser/xsd/simple_content.rb +6 -4
  38. data/lib/junoser/xsd/simple_type.rb +7 -5
  39. data/lib/junoser/xsd/union.rb +6 -4
  40. data/lib/junoser.rb +2 -0
  41. data/lib/underscorable.rb +10 -5
  42. metadata +5 -65
  43. data/.github/dependabot.yml +0 -6
  44. data/.github/workflows/test-linux.yaml +0 -30
  45. data/.gitignore +0 -11
  46. data/.pre-commit-config.yaml +0 -7
  47. data/commitlint.config.js +0 -6
data/lib/junoser/ruler.rb CHANGED
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/PerlBackrefs -- to simplify
4
+
1
5
  module Junoser
2
6
  class Ruler
3
7
  OFFSET = ' '
@@ -7,20 +11,20 @@ module Junoser
7
11
  end
8
12
 
9
13
  def to_rule
10
- rule_header << rule << rule_footer
14
+ +rule_header << rule << rule_footer
11
15
  end
12
16
 
13
17
  def rule
14
18
  str = @rule.read
15
19
  str = remove_comments(str)
16
20
  str = process_reserved_element(str)
17
- str.split(/\n/).map { |l| format(process_line(l)) }.join("\n")
21
+ str.split("\n").map { |l| fmt(process_line(l)) }.join("\n")
18
22
  end
19
23
 
20
24
  private
21
25
 
22
26
  def remove_comments(str)
23
- str.gsub(%r(\s*/\*.*\*/), '')
27
+ str.gsub(%r{\s*/\*.*\*/}, '')
24
28
  end
25
29
 
26
30
  def process_line(str)
@@ -29,21 +33,31 @@ module Junoser
29
33
  str.gsub!(/("[^"]+")/) { "str(#{$1})" } # "foo" -> str("foo")
30
34
 
31
35
  str.gsub!(/^(\s*)arg(\.as\(:\S+\))? \($/) { "#{$1}b(arg#{$2}," } # arg ( -> b(arg,
32
- str.gsub!(/^(\s*)(str\(\S+\)) ([^ \t\n\r\f(|,]+)(\.as\(:\S+\))?(,?)$/) { "#{$1}a(#{$2}, #{$3})#{$4}#{$5}" } # str("foo") bar -> a(str("foo"), bar)
33
- str.gsub!(/^(\s*)(str\(\S+\)) (enum)?\((.*)\)(,?)$/) { "#{$1}a(#{$2}, #{$3}#{$4})#{$5}" } # str("foo") (a | b) -> a(str("foo"), a | b)
34
-
36
+ # str("foo") bar -> a(str("foo"), bar)
37
+ str.gsub!(/^(\s*)(str\(\S+\)) ([^ \t\n\r\f(|,]+)(\.as\(:\S+\))?(,?)$/) do
38
+ "#{$1}a(#{$2}, #{$3})#{$4}#{$5}"
39
+ end
40
+ # str("foo") (a | b) -> a(str("foo"), a | b)
41
+ str.gsub!(/^(\s*)(str\(\S+\)) (enum)?\((.*)\)(,?)$/) do
42
+ "#{$1}a(#{$2}, #{$3}#{$4})#{$5}"
43
+ end
35
44
  str.gsub!(/^(\s*)(str\(\S+\)) \($/) { "#{$1}b(#{$2}," } # str("foo") ( -> b(str("foo"),
36
45
  str.gsub!(/^(\s*)(enum)?(\(.*\))(\.as\(:\S\))? \($/) { "#{$1}b(#{$2}#{$3}#{$4}," } # (a | b) ( -> b((a | b),
37
- str.gsub!(/^(\s*)(str\(\S+\)) ([^ \t\n\r\f(|,]+) \($/) { "#{$1}b(a(#{$2}, #{$3})," } # str("foo") bar ( -> b(a(str("foo"), bar),
38
- str.gsub!(/^(\s*)(str\(\S+\)) (enum)?\((.*)\) \($/) { "#{$1}a(#{$2}, #{$3}#{$4}," } # str("foo") (a | b) ( -> a(str("foo"), a | b,
39
-
46
+ # str("foo") bar ( -> b(a(str("foo"), bar),
47
+ str.gsub!(/^(\s*)(str\(\S+\)) ([^ \t\n\r\f(|,]+) \($/) do
48
+ "#{$1}b(a(#{$2}, #{$3}),"
49
+ end
50
+ # str("foo") (a | b) ( -> a(str("foo"), a | b,
51
+ str.gsub!(/^(\s*)(str\(\S+\)) (enum)?\((.*)\) \($/) do
52
+ "#{$1}a(#{$2}, #{$3}#{$4},"
53
+ end
40
54
  str
41
55
  end
42
56
 
43
57
  def process_reserved_element(str)
44
- str.gsub! /"\$\S+"/, 'arg'
58
+ str.gsub!(/"\$\S+"/, 'arg')
45
59
 
46
- str.gsub! /"groups" \(\s*s\(\s*any\s*\)\s*\)/, <<-EOS.strip
60
+ str.gsub!(/"groups" \(\s*s\(\s*any\s*\)\s*\)/, <<-REPLACE.strip)
47
61
  b(a("groups", arg),
48
62
  c(
49
63
  configuration,
@@ -65,7 +79,7 @@ module Junoser
65
79
  )
66
80
  )
67
81
  )
68
- EOS
82
+ REPLACE
69
83
 
70
84
  str.gsub! '"equal-literal"', '"="'
71
85
  str.gsub! '"plus-literal"', '"+"'
@@ -81,55 +95,55 @@ module Junoser
81
95
  str.gsub! '"tcp-flags" arg', '"tcp-flags" (quote | arg)'
82
96
 
83
97
  str.gsub!(/^(\s*)"as-path" arg \(\s*c\(\s*arg/) do
84
- format(['"as-path" arg (',
85
- ' c(',
86
- ' quote | arg'], $1)
98
+ fmt(['"as-path" arg (',
99
+ ' c(',
100
+ ' quote | arg'], $1)
87
101
  end
88
102
 
89
103
  str.gsub!(/^rule\(:regular_expression\) do\s.*?\s*end/) do
90
- <<~EOS
104
+ <<~REPLACE
91
105
  rule(:regular_expression) do
92
106
  (quote | arg).as(:arg)
93
107
  end
94
- EOS
108
+ REPLACE
95
109
  end
96
110
 
97
111
  str.gsub!(/^rule\(:login_user_object\) do\s*arg\.as\(:arg\) \(\s*c\(\s*"full-name" arg,/) do
98
- <<~EOS
112
+ <<~REPLACE
99
113
  rule(:login_user_object) do
100
114
  arg.as(:arg) (
101
115
  sc(
102
116
  "full-name" (quote | arg),
103
- EOS
117
+ REPLACE
104
118
  end
105
119
 
106
120
  str.gsub!(/^(\s*)"location" arg,\s*"contact" arg,/) do
107
- format(['"location" (quote | arg),',
108
- '"contact" (quote | arg),'], $1)
121
+ fmt(['"location" (quote | arg),',
122
+ '"contact" (quote | arg),'], $1)
109
123
  end
110
124
 
111
125
  str.gsub!(/^(\s*)"as-path" \(\s*c\(\s*"path" arg/) do
112
- format(['"as-path" (',
113
- ' c(',
114
- ' "path" (quote | arg)'], $1)
126
+ fmt(['"as-path" (',
127
+ ' c(',
128
+ ' "path" (quote | arg)'], $1)
115
129
  end
116
130
 
117
131
  str.gsub!(/^(\s*)prefix_list_items,\s*"apply-path" arg/) do
118
- format(['"apply-path" (quote | arg),',
119
- 'prefix_list_items'], $1)
132
+ fmt(['"apply-path" (quote | arg),',
133
+ 'prefix_list_items'], $1)
120
134
  end
121
135
 
122
136
  str.gsub!(/^(\s*)"drop-profile-map" \(\s*s\(\s*"loss-priority" \(\s*(.*\s*\),)\s*"protocol" \(\s*(.*\s*\),)\s*c\(\s*"drop-profile" (.*)/) do
123
- format([
124
- '"drop-profile-map" (',
125
- ' s(',
126
- ' s("loss-priority",',
127
- " #{$2}",
128
- ' s("protocol",',
129
- " #{$3}",
130
- ' s("drop-profile",',
131
- " #{$4}"
132
- ], $1)
137
+ fmt([
138
+ '"drop-profile-map" (',
139
+ ' s(',
140
+ ' s("loss-priority",',
141
+ " #{$2}",
142
+ ' s("protocol",',
143
+ " #{$3}",
144
+ ' s("drop-profile",',
145
+ " #{$4}"
146
+ ], $1)
133
147
  end
134
148
 
135
149
  #
@@ -145,15 +159,17 @@ module Junoser
145
159
  str.gsub!(/^(rule\(:server_group_type\) do\s*)c\(\s*c\(\s*arg\s*\)\s*\)/) { "#{$1}s(arg, arg)" }
146
160
 
147
161
  str.gsub!(/^(rule\(:rib_group_inet_type\) do)\s*c\(\s*arg/) do
148
- format([$1,
149
- ' ca(',
150
- ' a(arg, arg)'], '')
162
+ fmt([$1,
163
+ ' ca(',
164
+ ' a(arg, arg)'], '')
151
165
  end
152
166
 
153
- # Fix overkill
167
+ str.gsub! '"any" | "any-ipv4" | "any-ipv6"', '"any-ipv4" | "any-ipv6" | "any"'
168
+
169
+ # Fix overkill
154
170
  str.gsub!(/^(\s*)"priority" \(\s*ca\(\s*arg\s*\)/) do
155
- format(['"priority" (',
156
- ' a(arg, arg)', $1])
171
+ fmt(['"priority" (',
172
+ ' a(arg, arg)', $1])
157
173
  end
158
174
 
159
175
  #
@@ -166,37 +182,37 @@ module Junoser
166
182
  #
167
183
  # "inet",
168
184
  # "inet6"
169
- str.gsub!(/"cspf"(.*\s*.*)"cspf-link"/) { %["cspf-link"#{$1}"cspf"] }
170
- str.gsub!(/"http"(.*\s*.*)"https"/) { %["https"#{$1}"http"] }
171
- str.gsub!(/"inet"(.*\s*.*)"inet6"/) { %["inet6"#{$1}"inet"] }
172
- str.gsub!(/"icmp"(.*\s*.*)"icmp6"/) { %["icmp6"#{$1}"icmp"] }
173
- str.gsub!(/"icmp"(.*\s*.*)"icmpv6"/) { %["icmpv6"#{$1}"icmp"] }
174
- str.gsub!(/"snmp"(.*\s*.*)"snmptrap"/) { %["snmptrap"#{$1}"snmp"] }
175
- str.gsub!(/"ospf"(.*\s*.*)"ospf3"/) { %["ospf3"#{$1}"ospf"] }
176
- str.gsub!(/"deny"(.*\s*.*)"deny-password"/) { %["deny-password"#{$1}"deny"] }
177
- str.gsub!(/"no-redirects"(.*\s*.*)"no-redirects-ipv6"/) { %["no-redirects-ipv6"#{$1}"no-redirects"] }
178
- str.gsub!(/"chassis"([^()]*)"chassis-ha-reswatch"/m) { %["chassis-ha-reswatch"#{$1}"chassis"] }
185
+ str.gsub!(/"cspf"(.*\s*.*)"cspf-link"/) { %("cspf-link"#{$1}"cspf") }
186
+ str.gsub!(/"http"(.*\s*.*)"https"/) { %("https"#{$1}"http") }
187
+ str.gsub!(/"inet"(.*\s*.*)"inet6"/) { %("inet6"#{$1}"inet") }
188
+ str.gsub!(/"icmp"(.*\s*.*)"icmp6"/) { %("icmp6"#{$1}"icmp") }
189
+ str.gsub!(/"icmp"(.*\s*.*)"icmpv6"/) { %("icmpv6"#{$1}"icmp") }
190
+ str.gsub!(/"snmp"(.*\s*.*)"snmptrap"/) { %("snmptrap"#{$1}"snmp") }
191
+ str.gsub!(/"ospf"(.*\s*.*)"ospf3"/) { %("ospf3"#{$1}"ospf") }
192
+ str.gsub!(/"deny"(.*\s*.*)"deny-password"/) { %("deny-password"#{$1}"deny") }
193
+ str.gsub!(/"no-redirects"(.*\s*.*)"no-redirects-ipv6"/) { %("no-redirects-ipv6"#{$1}"no-redirects") }
194
+ str.gsub!(/"chassis"([^()]*)"chassis-ha-reswatch"/m) { %("chassis-ha-reswatch"#{$1}"chassis") }
179
195
  str.gsub! '"tls1" | "tls11" | "tls12"', '"tls11" | "tls12" | "tls1"'
180
196
  str.gsub!(/("group1" \| "group2" \| "group5") \| ([^)]+)/) { "#{$2} | #{$1}" }
181
197
 
182
198
  %w[ccc ethernet-over-atm tcc vpls bridge].each do |encap|
183
- str.gsub!(/"ethernet"(.*)"ethernet-#{encap}"/) { %["ethernet-#{encap}"#{$1}"ethernet"] }
199
+ str.gsub!(/"ethernet"(.*)"ethernet-#{encap}"/) { %("ethernet-#{encap}"#{$1}"ethernet") }
184
200
  end
185
201
 
186
202
  str.gsub!(/^(\s*)"path" arg \(\s*c\(\s*sc\(\s*"abstract",\s*c\(\s*"loose",\s*"loose-link",\s*"strict"\s*\)\s*\)\.as\(:oneline\)/) do
187
- format(['"path" arg (',
188
- ' c(',
189
- ' b(',
190
- ' ipaddr,',
191
- ' c(',
192
- ' "abstract",',
193
- ' c(',
194
- ' "loose-link",',
195
- ' "loose",',
196
- ' "strict"',
197
- ' )',
198
- ' ).as(:oneline)',
199
- ' )', $1])
203
+ fmt(['"path" arg (',
204
+ ' c(',
205
+ ' b(',
206
+ ' ipaddr,',
207
+ ' c(',
208
+ ' "abstract",',
209
+ ' c(',
210
+ ' "loose-link",',
211
+ ' "loose",',
212
+ ' "strict"',
213
+ ' )',
214
+ ' ).as(:oneline)',
215
+ ' )', $1])
200
216
  end
201
217
 
202
218
  #
@@ -207,7 +223,7 @@ module Junoser
207
223
  str.gsub!(/\((.*) \| "vlan-id"\)/) { "(#{$1} | arg)" }
208
224
 
209
225
  %w[filename].each do |key|
210
- str.gsub! %["#{key}" arg], 'arg'
226
+ str.gsub! %("#{key}" arg), 'arg'
211
227
  end
212
228
 
213
229
  # "filename" fix above leaves "arg". Move to the end
@@ -226,14 +242,16 @@ module Junoser
226
242
  # Fix .xsd: "arg" is missing
227
243
  #
228
244
  str.gsub!(/"route-filter" (\(\s*control_route_filter_type\s*\))/) { %["route-filter" arg #{$1}.as(:oneline)] }
229
- str.gsub!(/"source-address-filter" (\(\s*control_source_address_filter_type\s*\))/) { %["source-adress-filter" arg #{$1}.as(:oneline)] }
245
+ str.gsub!(/"source-address-filter" (\(\s*control_source_address_filter_type\s*\))/) do
246
+ %["source-adress-filter" arg #{$1}.as(:oneline)]
247
+ end
230
248
  %w[file].each do |key|
231
249
  str.gsub!(/^(\s*"#{key}" \(\s*)c\(\s*arg,/) { "#{$1}sca(" }
232
250
  end
233
251
 
234
252
  # Fix .xsd: Unnecessary "arg" is added
235
253
  %w[exact longer orlonger].each do |key|
236
- str.gsub!(/^(\s*"#{key}") arg/) { "#{$1}" }
254
+ str.gsub!(/^(\s*"#{key}") arg/) { $1.to_s }
237
255
  end
238
256
 
239
257
  # Fix .xsd: "ieee-802.3ad" is invalid
@@ -247,7 +265,7 @@ module Junoser
247
265
 
248
266
  # Fix .xsd: "from-zone" arg is also required
249
267
  str.gsub!(/^ "policy" \(\s*s\(\s*arg,\s*"to-zone-name" arg,\s*(.*?)\s*\)\s*^ \),/m) do
250
- format(['b(', ' s("from-zone", arg, "to-zone", arg),', " #$1", '),'], ' ')
268
+ fmt(['b(', ' s("from-zone", arg, "to-zone", arg),', " #{$1}", '),'], ' ')
251
269
  end
252
270
 
253
271
  # Fix .xsd: "members" accepts [ foo bar ]
@@ -282,18 +300,20 @@ module Junoser
282
300
 
283
301
  # Fix .xsd: support "set protocols iccp peer xxx liveness-detection single-hop"
284
302
  str.gsub!(/(^rule\(:peer_group\) do.*?\n(\s*)"detection-time" \(\s*c\(\s*"threshold" arg\s*\)\s*\))/m) do
285
- "#{$1},\n#{format('"single-hop"', $2)}"
303
+ "#{$1},\n#{fmt('"single-hop"', $2)}"
286
304
  end
287
305
 
288
306
  # Fix .xsd: support "set interfaces xxx ether-options speed"
289
307
  str.gsub! '"ethernet-1', '"1'
290
308
 
291
309
  # Fix .xsd: support "set policy-options policy-statement xxx from policy [expression]"
292
- str.gsub!(/^rule\(:policy_algebra\) do(\s*)arg\.as\(:arg\)\send/) { "rule(:policy_algebra) do#{$1}any.as(:arg)\nend" }
310
+ str.gsub!(/^rule\(:policy_algebra\) do(\s*)arg\.as\(:arg\)\send/) do
311
+ "rule(:policy_algebra) do#{$1}any.as(:arg)\nend"
312
+ end
293
313
 
294
314
  # Fix .xsd: support "set interfaces xxx enable"
295
315
  str.gsub!(/^(rule\(:interfaces_type\) do\s*[^\n]*\s*c\()(\s*)/m) do
296
- %[#{$1}#{$2}"enable",#{$2}]
316
+ %(#{$1}#{$2}"enable",#{$2})
297
317
  end
298
318
 
299
319
  # Fix .xsd: arg should be regular_expression
@@ -302,7 +322,7 @@ module Junoser
302
322
  str
303
323
  end
304
324
 
305
- def format(str, offset = OFFSET)
325
+ def fmt(str, offset = OFFSET)
306
326
  case str
307
327
  when String
308
328
  str.empty? ? '' : offset + str
@@ -314,107 +334,109 @@ module Junoser
314
334
  end
315
335
 
316
336
  def rule_header
317
- <<-EOS
318
- require 'parslet'
319
-
320
- module Junoser
321
- class Parser < Parslet::Parser
322
- def parse_lines(config)
323
- lines = config.split("\\n").map(&:strip)
324
- lines_without_deactivate = lines.reject {|l| l =~ /^deactivate/ }
325
-
326
- lines.inject(true) do |passed, line|
327
- passed & parse_line(line, lines_without_deactivate)
328
- end
329
- end
330
-
331
- def parse_line(line, lines_without_deactivate)
332
- if line =~ /^deactivate/
333
- if lines_without_deactivate.grep(/^\#{line.sub(/^deactivate/, 'set')}/).empty?
334
- $stderr.puts %(Corresponding "set" statement is not found: \#{line})
335
- return false
336
- else
337
- return true
338
- end
339
- end
340
-
341
- begin
342
- # .xsd doesn't include "apply-groups"
343
- if line =~ /(.*)\\s+apply-groups(-except)?\\s+(\\S+|\\[.*\\])$/
344
- return \$1 == 'set' ? true : parse(\$1)
345
- end
346
-
347
- parse line
348
- true
349
- rescue Parslet::ParseFailed
350
- $stderr.puts "Invalid syntax: \#{line}"
351
- false
352
- end
353
- end
354
-
355
- # block with children maybe
356
- def b(object, *children)
357
- children.inject(object) {|rule, child| rule.as(:label) >> (space >> child.as(:child) | eos) }
358
- end
359
-
360
- # with an argument, and children maybe
361
- def a(object, arg, *children)
362
- b(object.as(:statement) >> space >> arg.as(:argument), *children)
363
- end
364
-
365
- # choice
366
- def c(*objects)
367
- objects.inject {|rule, object| rule | object }
368
- end
369
-
370
- def ca(*objects)
371
- objects.inject {|rule, object| rule | object } | arg
372
- end
373
-
374
- # sequence
375
- def s(*objects)
376
- # TODO: eval "minOccurs" attribute of choice element
377
- objects.inject {|rule, object| rule >> (space >> object).maybe }
378
- end
379
-
380
- # sequential choice
381
- def sc(*objects)
382
- (c(*objects) >> space.maybe).repeat(0)
383
- end
384
-
385
- def sca(*objects)
386
- (c(*objects, arg) >> space.maybe).repeat(0)
387
- end
388
-
389
- def enum(object)
390
- (object.as(:enum))
391
- end
392
-
393
- rule(:arg) { match('\\S').repeat(1) }
394
- rule(:space) { match('\\s').repeat(1) }
395
- rule(:any) { match('.').repeat(1) }
396
- rule(:eos) { match('$') }
397
- rule(:dotted) { match('[^. \\t\\n\\r\\f]').repeat(1) >> str('.') >> match('[^. \\t\\n\\r\\f]').repeat(1) }
398
- rule(:quote) { str('"') >> match('[^"]').repeat(1) >> str('"') }
399
- rule(:address) { match('[0-9a-fA-F:\.]').repeat(1) }
400
- rule(:prefix ) { address >> (str('/') >> match('[0-9]').repeat(1)).maybe }
401
-
402
- root(:set)
403
- rule(:set) { str('set') >> space >> configuration.as(:config) >> comment.maybe }
404
-
405
- rule(:comment) { space.maybe >> (hash_comment | slash_asterisk) }
406
- rule(:hash_comment) { str('#') >> any.maybe }
407
- rule(:slash_asterisk) { str('/*') >> match('(?!\\*\\/).').repeat(0) >> str('*/') }
408
-
409
- EOS
337
+ <<~HEADER
338
+ require 'parslet'
339
+
340
+ module Junoser
341
+ class Parser < Parslet::Parser
342
+ def parse_lines(config)
343
+ lines = config.split("\\n").map(&:strip)
344
+ lines_without_deactivate = lines.reject {|l| l =~ /^deactivate/ }
345
+
346
+ lines.inject(true) do |passed, line|
347
+ passed & parse_line(line, lines_without_deactivate)
348
+ end
349
+ end
350
+
351
+ def parse_line(line, lines_without_deactivate)
352
+ if line =~ /^deactivate/
353
+ if lines_without_deactivate.grep(/^\#{line.sub(/^deactivate/, 'set')}/).empty?
354
+ $stderr.puts %(Corresponding "set" statement is not found: \#{line})
355
+ return false
356
+ else
357
+ return true
358
+ end
359
+ end
360
+
361
+ begin
362
+ # .xsd doesn't include "apply-groups"
363
+ if line =~ /(.*)\\s+apply-groups(-except)?\\s+(\\S+|\\[.*\\])$/
364
+ return $1 == 'set' ? true : parse($1)
365
+ end
366
+
367
+ parse line
368
+ true
369
+ rescue Parslet::ParseFailed
370
+ $stderr.puts "Invalid syntax: \#{line}"
371
+ false
372
+ end
373
+ end
374
+
375
+ # block with children maybe
376
+ def b(object, *children)
377
+ children.inject(object) {|rule, child| rule.as(:label) >> (space >> child.as(:child) | eos) }
378
+ end
379
+
380
+ # with an argument, and children maybe
381
+ def a(object, arg, *children)
382
+ b(object.as(:statement) >> space >> arg.as(:argument), *children)
383
+ end
384
+
385
+ # choice
386
+ def c(*objects)
387
+ objects.inject {|rule, object| rule | object }
388
+ end
389
+
390
+ def ca(*objects)
391
+ objects.inject {|rule, object| rule | object } | arg
392
+ end
393
+
394
+ # sequence
395
+ def s(*objects)
396
+ # TODO: eval "minOccurs" attribute of choice element
397
+ objects.inject {|rule, object| rule >> (space >> object).maybe }
398
+ end
399
+
400
+ # sequential choice
401
+ def sc(*objects)
402
+ (c(*objects) >> space.maybe).repeat(0)
403
+ end
404
+
405
+ def sca(*objects)
406
+ (c(*objects, arg) >> space.maybe).repeat(0)
407
+ end
408
+
409
+ def enum(object)
410
+ (object.as(:enum))
411
+ end
412
+
413
+ rule(:arg) { match('\\S').repeat(1) }
414
+ rule(:space) { match('\\s').repeat(1) }
415
+ rule(:any) { match('.').repeat(1) }
416
+ rule(:eos) { match('$') }
417
+ rule(:dotted) { match('[^. \\t\\n\\r\\f]').repeat(1) >> str('.') >> match('[^. \\t\\n\\r\\f]').repeat(1) }
418
+ rule(:quote) { str('"') >> match('[^"]').repeat(1) >> str('"') }
419
+ rule(:address) { match('[0-9a-fA-F:.]').repeat(1) }
420
+ rule(:prefix ) { address >> (str('/') >> match('[0-9]').repeat(1)).maybe }
421
+
422
+ root(:set)
423
+ rule(:set) { str('set') >> space >> configuration.as(:config) >> comment.maybe }
424
+
425
+ rule(:comment) { space.maybe >> (hash_comment | slash_asterisk) }
426
+ rule(:hash_comment) { str('#') >> any.maybe }
427
+ rule(:slash_asterisk) { str('/*') >> match('(?!\\*\\/).').repeat(0) >> str('*/') }
428
+
429
+ HEADER
410
430
  end
411
431
 
412
432
  def rule_footer
413
- <<-EOS
433
+ <<~FOOTER
414
434
 
415
- end
416
- end
417
- EOS
435
+ end
436
+ end
437
+ FOOTER
418
438
  end
419
439
  end
420
440
  end
441
+
442
+ # rubocop:enable Style/PerlBackrefs
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'junoser'
2
4
  require 'parslet'
3
5
 
@@ -22,9 +24,9 @@ module Junoser
22
24
  when /^activate /
23
25
  delete_lines l.gsub(/^activate /, 'deactivate ')
24
26
  when /^insert (.*) before (.*)/
25
- insert_before "set #{$1}", $2
27
+ insert_before "set #{::Regexp.last_match(1)}", ::Regexp.last_match(2)
26
28
  when /^insert (.*) after (.*)/
27
- insert_after "set #{$1}", $2
29
+ insert_after "set #{::Regexp.last_match(1)}", ::Regexp.last_match(2)
28
30
  end
29
31
  end
30
32
 
@@ -36,11 +38,11 @@ module Junoser
36
38
 
37
39
  def remove_command_context(lines)
38
40
  lines.each_with_index do |l, i|
39
- lines[i..-1].each do |l2|
40
- if l2[-1] == " " # l2 is a command context
41
- lines.delete(l2) if l.include?(l2) and l != l2
42
- else # l2 has argument
43
- lines.delete(l2) if l.include?("#{l2} ")
41
+ lines[i..].each do |l2|
42
+ if l2[-1] == ' ' # l2 is a command context
43
+ lines.delete(l2) if l.include?(l2) && (l != l2)
44
+ elsif l.include?("#{l2} ") # l2 has argument
45
+ lines.delete(l2)
44
46
  end
45
47
  end
46
48
  end
@@ -48,24 +50,24 @@ module Junoser
48
50
 
49
51
  def delete_lines(pattern)
50
52
  @lines.each do |l|
51
- l.sub!(/#{pattern}/) { $1 }
53
+ l.sub!(/#{pattern}/) { ::Regexp.last_match(1) }
52
54
  end
53
55
  end
54
56
 
55
57
  def split_last_token(line)
56
58
  tokens = join_arg(@transformer.apply(@parser.parse(line))).split("\n")
57
- tokens.map! { |t|
58
- t.gsub!(/arg\((.*)\)/) { "#$1" } # Strip arg
59
+ tokens.map! do |t|
60
+ t.gsub!(/arg\((.*)\)/) { ::Regexp.last_match(1).to_s } # Strip arg
59
61
  Regexp.escape(t.strip)
60
- }
62
+ end
61
63
 
62
64
  [tokens[0..-2].join(' '), tokens.last]
63
65
  end
64
66
 
65
67
  # Ported from lib/junoser/display/config_store.rb
66
68
  def join_arg(str)
67
- str.gsub!(/\narg\((.*)\)$/) { " #$1" }
68
- str.gsub!(/arg\((.*)\)/) { "#$1" }
69
+ str.gsub!(/\narg\((.*)\)$/) { " #{::Regexp.last_match(1)}" }
70
+ str.gsub!(/arg\((.*)\)/) { ::Regexp.last_match(1).to_s }
69
71
  str
70
72
  end
71
73
 
@@ -76,7 +78,7 @@ module Junoser
76
78
 
77
79
  def insert_before(statement_to_insert, key_statement)
78
80
  key_tokens = key_statement.strip.split
79
- key_statement = (statement_to_insert.strip.split[0..-(key_tokens.size+1)] + key_tokens).join(' ')
81
+ key_statement = (statement_to_insert.strip.split[0..-(key_tokens.size + 1)] + key_tokens).join(' ')
80
82
 
81
83
  lines_to_insert = @lines.select { |l| l.include?(statement_to_insert) }
82
84
  @lines.reject! { |l| l.include?(statement_to_insert) }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'parslet'
2
4
 
3
5
  module Junoser
@@ -24,7 +26,7 @@ module Junoser
24
26
 
25
27
  rule(label: simple(:label), child: sequence(:children)) do
26
28
  Junoser::Transformer.remove_slash_asterisk children
27
- %[#{label}\n#{children.join("\n")}]
29
+ %(#{label}\n#{children.join("\n")})
28
30
  end
29
31
 
30
32
  rule(statement: simple(:statement), argument: simple(:argument)) do
@@ -33,7 +35,7 @@ module Junoser
33
35
 
34
36
  rule(statement: simple(:statement), argument: sequence(:arguments)) do
35
37
  Junoser::Transformer.remove_slash_asterisk arguments
36
- %[#{statement}\n#{arguments.join("\n")}]
38
+ %(#{statement}\n#{arguments.join("\n")})
37
39
  end
38
40
 
39
41
  rule(oneline: simple(:str)) do
@@ -52,15 +54,14 @@ module Junoser
52
54
  strs.join(' ')
53
55
  end
54
56
 
55
-
56
57
  def self.remove_slash_asterisk(array)
57
58
  open = array.index("arg(/*)\n")
58
- close = array.index("arg(*/)")
59
+ close = array.index('arg(*/)')
60
+
61
+ return unless open && close
59
62
 
60
- if open && close
61
- (open..close).reverse_each do |i|
62
- array.delete_at i
63
- end
63
+ (open..close).reverse_each do |i|
64
+ array.delete_at i
64
65
  end
65
66
  end
66
67
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Junoser
2
- VERSION = "0.7.2"
4
+ VERSION = '0.7.4'
3
5
  end