junoser 0.7.1 → 0.7.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +36 -6
  5. data/Rakefile +6 -3
  6. data/bin/console +4 -3
  7. data/exe/junoser +6 -5
  8. data/exe/junoser-squash +9 -6
  9. data/junoser.gemspec +15 -17
  10. data/lib/junoser/cli.rb +2 -2
  11. data/lib/junoser/development.rb +2 -0
  12. data/lib/junoser/display/config_store.rb +12 -12
  13. data/lib/junoser/display/enumerable.rb +6 -4
  14. data/lib/junoser/display/set.rb +15 -17
  15. data/lib/junoser/display/structure.rb +7 -7
  16. data/lib/junoser/display.rb +2 -0
  17. data/lib/junoser/input.rb +11 -10
  18. data/lib/junoser/js_ruler.rb +74 -66
  19. data/lib/junoser/parser.rb +160 -12
  20. data/lib/junoser/rule_tree/node.rb +3 -1
  21. data/lib/junoser/rule_tree/parser.rb +11 -8
  22. data/lib/junoser/rule_tree.rb +2 -0
  23. data/lib/junoser/ruler.rb +193 -170
  24. data/lib/junoser/squash.rb +16 -14
  25. data/lib/junoser/transformer.rb +9 -8
  26. data/lib/junoser/version.rb +3 -1
  27. data/lib/junoser/xsd/base.rb +15 -14
  28. data/lib/junoser/xsd/choice.rb +10 -9
  29. data/lib/junoser/xsd/complex_type.rb +13 -11
  30. data/lib/junoser/xsd/element.rb +34 -36
  31. data/lib/junoser/xsd/enumeration.rb +9 -8
  32. data/lib/junoser/xsd/parsable.rb +2 -0
  33. data/lib/junoser/xsd/restriction.rb +15 -13
  34. data/lib/junoser/xsd/sequence.rb +11 -8
  35. data/lib/junoser/xsd/simple_content.rb +6 -4
  36. data/lib/junoser/xsd/simple_type.rb +7 -5
  37. data/lib/junoser/xsd/union.rb +6 -4
  38. data/lib/junoser.rb +2 -0
  39. data/lib/underscorable.rb +10 -5
  40. metadata +5 -65
  41. data/.github/dependabot.yml +0 -6
  42. data/.github/workflows/test-linux.yaml +0 -30
  43. data/.gitignore +0 -11
  44. data/.pre-commit-config.yaml +0 -7
  45. 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,15 @@ 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
167
  # Fix overkill
154
168
  str.gsub!(/^(\s*)"priority" \(\s*ca\(\s*arg\s*\)/) do
155
- format(['"priority" (',
156
- ' a(arg, arg)', $1])
169
+ fmt(['"priority" (',
170
+ ' a(arg, arg)', $1])
157
171
  end
158
172
 
159
173
  #
@@ -166,37 +180,37 @@ module Junoser
166
180
  #
167
181
  # "inet",
168
182
  # "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"] }
183
+ str.gsub!(/"cspf"(.*\s*.*)"cspf-link"/) { %("cspf-link"#{$1}"cspf") }
184
+ str.gsub!(/"http"(.*\s*.*)"https"/) { %("https"#{$1}"http") }
185
+ str.gsub!(/"inet"(.*\s*.*)"inet6"/) { %("inet6"#{$1}"inet") }
186
+ str.gsub!(/"icmp"(.*\s*.*)"icmp6"/) { %("icmp6"#{$1}"icmp") }
187
+ str.gsub!(/"icmp"(.*\s*.*)"icmpv6"/) { %("icmpv6"#{$1}"icmp") }
188
+ str.gsub!(/"snmp"(.*\s*.*)"snmptrap"/) { %("snmptrap"#{$1}"snmp") }
189
+ str.gsub!(/"ospf"(.*\s*.*)"ospf3"/) { %("ospf3"#{$1}"ospf") }
190
+ str.gsub!(/"deny"(.*\s*.*)"deny-password"/) { %("deny-password"#{$1}"deny") }
191
+ str.gsub!(/"no-redirects"(.*\s*.*)"no-redirects-ipv6"/) { %("no-redirects-ipv6"#{$1}"no-redirects") }
192
+ str.gsub!(/"chassis"([^()]*)"chassis-ha-reswatch"/m) { %("chassis-ha-reswatch"#{$1}"chassis") }
179
193
  str.gsub! '"tls1" | "tls11" | "tls12"', '"tls11" | "tls12" | "tls1"'
180
194
  str.gsub!(/("group1" \| "group2" \| "group5") \| ([^)]+)/) { "#{$2} | #{$1}" }
181
195
 
182
196
  %w[ccc ethernet-over-atm tcc vpls bridge].each do |encap|
183
- str.gsub!(/"ethernet"(.*)"ethernet-#{encap}"/) { %["ethernet-#{encap}"#{$1}"ethernet"] }
197
+ str.gsub!(/"ethernet"(.*)"ethernet-#{encap}"/) { %("ethernet-#{encap}"#{$1}"ethernet") }
184
198
  end
185
199
 
186
200
  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])
201
+ fmt(['"path" arg (',
202
+ ' c(',
203
+ ' b(',
204
+ ' ipaddr,',
205
+ ' c(',
206
+ ' "abstract",',
207
+ ' c(',
208
+ ' "loose-link",',
209
+ ' "loose",',
210
+ ' "strict"',
211
+ ' )',
212
+ ' ).as(:oneline)',
213
+ ' )', $1])
200
214
  end
201
215
 
202
216
  #
@@ -207,7 +221,7 @@ module Junoser
207
221
  str.gsub!(/\((.*) \| "vlan-id"\)/) { "(#{$1} | arg)" }
208
222
 
209
223
  %w[filename].each do |key|
210
- str.gsub! %["#{key}" arg], 'arg'
224
+ str.gsub! %("#{key}" arg), 'arg'
211
225
  end
212
226
 
213
227
  # "filename" fix above leaves "arg". Move to the end
@@ -226,14 +240,16 @@ module Junoser
226
240
  # Fix .xsd: "arg" is missing
227
241
  #
228
242
  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)] }
243
+ str.gsub!(/"source-address-filter" (\(\s*control_source_address_filter_type\s*\))/) do
244
+ %["source-adress-filter" arg #{$1}.as(:oneline)]
245
+ end
230
246
  %w[file].each do |key|
231
247
  str.gsub!(/^(\s*"#{key}" \(\s*)c\(\s*arg,/) { "#{$1}sca(" }
232
248
  end
233
249
 
234
250
  # Fix .xsd: Unnecessary "arg" is added
235
251
  %w[exact longer orlonger].each do |key|
236
- str.gsub!(/^(\s*"#{key}") arg/) { "#{$1}" }
252
+ str.gsub!(/^(\s*"#{key}") arg/) { $1.to_s }
237
253
  end
238
254
 
239
255
  # Fix .xsd: "ieee-802.3ad" is invalid
@@ -247,7 +263,7 @@ module Junoser
247
263
 
248
264
  # Fix .xsd: "from-zone" arg is also required
249
265
  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", '),'], ' ')
266
+ fmt(['b(', ' s("from-zone", arg, "to-zone", arg),', " #{$1}", '),'], ' ')
251
267
  end
252
268
 
253
269
  # Fix .xsd: "members" accepts [ foo bar ]
@@ -282,24 +298,29 @@ module Junoser
282
298
 
283
299
  # Fix .xsd: support "set protocols iccp peer xxx liveness-detection single-hop"
284
300
  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)}"
301
+ "#{$1},\n#{fmt('"single-hop"', $2)}"
286
302
  end
287
303
 
288
304
  # Fix .xsd: support "set interfaces xxx ether-options speed"
289
305
  str.gsub! '"ethernet-1', '"1'
290
306
 
291
307
  # 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" }
308
+ str.gsub!(/^rule\(:policy_algebra\) do(\s*)arg\.as\(:arg\)\send/) do
309
+ "rule(:policy_algebra) do#{$1}any.as(:arg)\nend"
310
+ end
293
311
 
294
312
  # Fix .xsd: support "set interfaces xxx enable"
295
313
  str.gsub!(/^(rule\(:interfaces_type\) do\s*[^\n]*\s*c\()(\s*)/m) do
296
- %[#{$1}#{$2}"enable",#{$2}]
314
+ %(#{$1}#{$2}"enable",#{$2})
297
315
  end
298
316
 
317
+ # Fix .xsd: arg should be regular_expression
318
+ str.gsub!(/^(\s*"(allow|deny)-(commands|configuration)-regexps") arg/) { "#{$1} regular_expression" }
319
+
299
320
  str
300
321
  end
301
322
 
302
- def format(str, offset = OFFSET)
323
+ def fmt(str, offset = OFFSET)
303
324
  case str
304
325
  when String
305
326
  str.empty? ? '' : offset + str
@@ -311,107 +332,109 @@ module Junoser
311
332
  end
312
333
 
313
334
  def rule_header
314
- <<-EOS
315
- require 'parslet'
316
-
317
- module Junoser
318
- class Parser < Parslet::Parser
319
- def parse_lines(config)
320
- lines = config.split("\\n").map(&:strip)
321
- lines_without_deactivate = lines.reject {|l| l =~ /^deactivate/ }
322
-
323
- lines.inject(true) do |passed, line|
324
- passed & parse_line(line, lines_without_deactivate)
325
- end
326
- end
327
-
328
- def parse_line(line, lines_without_deactivate)
329
- if line =~ /^deactivate/
330
- if lines_without_deactivate.grep(/^\#{line.sub(/^deactivate/, 'set')}/).empty?
331
- $stderr.puts %(Corresponding "set" statement is not found: \#{line})
332
- return false
333
- else
334
- return true
335
- end
336
- end
337
-
338
- begin
339
- # .xsd doesn't include "apply-groups"
340
- if line =~ /(.*)\\s+apply-groups(-except)?\\s+(\\S+|\\[.*\\])$/
341
- return \$1 == 'set' ? true : parse(\$1)
342
- end
343
-
344
- parse line
345
- true
346
- rescue Parslet::ParseFailed
347
- $stderr.puts "Invalid syntax: \#{line}"
348
- false
349
- end
350
- end
351
-
352
- # block with children maybe
353
- def b(object, *children)
354
- children.inject(object) {|rule, child| rule.as(:label) >> (space >> child.as(:child) | eos) }
355
- end
356
-
357
- # with an argument, and children maybe
358
- def a(object, arg, *children)
359
- b(object.as(:statement) >> space >> arg.as(:argument), *children)
360
- end
361
-
362
- # choice
363
- def c(*objects)
364
- objects.inject {|rule, object| rule | object }
365
- end
366
-
367
- def ca(*objects)
368
- objects.inject {|rule, object| rule | object } | arg
369
- end
370
-
371
- # sequence
372
- def s(*objects)
373
- # TODO: eval "minOccurs" attribute of choice element
374
- objects.inject {|rule, object| rule >> (space >> object).maybe }
375
- end
376
-
377
- # sequential choice
378
- def sc(*objects)
379
- (c(*objects) >> space.maybe).repeat(0)
380
- end
381
-
382
- def sca(*objects)
383
- (c(*objects, arg) >> space.maybe).repeat(0)
384
- end
385
-
386
- def enum(object)
387
- (object.as(:enum))
388
- end
389
-
390
- rule(:arg) { match('\\S').repeat(1) }
391
- rule(:space) { match('\\s').repeat(1) }
392
- rule(:any) { match('.').repeat(1) }
393
- rule(:eos) { match('$') }
394
- rule(:dotted) { match('[^. \\t\\n\\r\\f]').repeat(1) >> str('.') >> match('[^. \\t\\n\\r\\f]').repeat(1) }
395
- rule(:quote) { str('"') >> match('[^"]').repeat(1) >> str('"') }
396
- rule(:address) { match('[0-9a-fA-F:\.]').repeat(1) }
397
- rule(:prefix ) { address >> (str('/') >> match('[0-9]').repeat(1)).maybe }
398
-
399
- root(:set)
400
- rule(:set) { str('set') >> space >> configuration.as(:config) >> comment.maybe }
401
-
402
- rule(:comment) { space.maybe >> (hash_comment | slash_asterisk) }
403
- rule(:hash_comment) { str('#') >> any.maybe }
404
- rule(:slash_asterisk) { str('/*') >> match('(?!\\*\\/).').repeat(0) >> str('*/') }
405
-
406
- EOS
335
+ <<~HEADER
336
+ require 'parslet'
337
+
338
+ module Junoser
339
+ class Parser < Parslet::Parser
340
+ def parse_lines(config)
341
+ lines = config.split("\\n").map(&:strip)
342
+ lines_without_deactivate = lines.reject {|l| l =~ /^deactivate/ }
343
+
344
+ lines.inject(true) do |passed, line|
345
+ passed & parse_line(line, lines_without_deactivate)
346
+ end
347
+ end
348
+
349
+ def parse_line(line, lines_without_deactivate)
350
+ if line =~ /^deactivate/
351
+ if lines_without_deactivate.grep(/^\#{line.sub(/^deactivate/, 'set')}/).empty?
352
+ $stderr.puts %(Corresponding "set" statement is not found: \#{line})
353
+ return false
354
+ else
355
+ return true
356
+ end
357
+ end
358
+
359
+ begin
360
+ # .xsd doesn't include "apply-groups"
361
+ if line =~ /(.*)\\s+apply-groups(-except)?\\s+(\\S+|\\[.*\\])$/
362
+ return $1 == 'set' ? true : parse($1)
363
+ end
364
+
365
+ parse line
366
+ true
367
+ rescue Parslet::ParseFailed
368
+ $stderr.puts "Invalid syntax: \#{line}"
369
+ false
370
+ end
371
+ end
372
+
373
+ # block with children maybe
374
+ def b(object, *children)
375
+ children.inject(object) {|rule, child| rule.as(:label) >> (space >> child.as(:child) | eos) }
376
+ end
377
+
378
+ # with an argument, and children maybe
379
+ def a(object, arg, *children)
380
+ b(object.as(:statement) >> space >> arg.as(:argument), *children)
381
+ end
382
+
383
+ # choice
384
+ def c(*objects)
385
+ objects.inject {|rule, object| rule | object }
386
+ end
387
+
388
+ def ca(*objects)
389
+ objects.inject {|rule, object| rule | object } | arg
390
+ end
391
+
392
+ # sequence
393
+ def s(*objects)
394
+ # TODO: eval "minOccurs" attribute of choice element
395
+ objects.inject {|rule, object| rule >> (space >> object).maybe }
396
+ end
397
+
398
+ # sequential choice
399
+ def sc(*objects)
400
+ (c(*objects) >> space.maybe).repeat(0)
401
+ end
402
+
403
+ def sca(*objects)
404
+ (c(*objects, arg) >> space.maybe).repeat(0)
405
+ end
406
+
407
+ def enum(object)
408
+ (object.as(:enum))
409
+ end
410
+
411
+ rule(:arg) { match('\\S').repeat(1) }
412
+ rule(:space) { match('\\s').repeat(1) }
413
+ rule(:any) { match('.').repeat(1) }
414
+ rule(:eos) { match('$') }
415
+ rule(:dotted) { match('[^. \\t\\n\\r\\f]').repeat(1) >> str('.') >> match('[^. \\t\\n\\r\\f]').repeat(1) }
416
+ rule(:quote) { str('"') >> match('[^"]').repeat(1) >> str('"') }
417
+ rule(:address) { match('[0-9a-fA-F:.]').repeat(1) }
418
+ rule(:prefix ) { address >> (str('/') >> match('[0-9]').repeat(1)).maybe }
419
+
420
+ root(:set)
421
+ rule(:set) { str('set') >> space >> configuration.as(:config) >> comment.maybe }
422
+
423
+ rule(:comment) { space.maybe >> (hash_comment | slash_asterisk) }
424
+ rule(:hash_comment) { str('#') >> any.maybe }
425
+ rule(:slash_asterisk) { str('/*') >> match('(?!\\*\\/).').repeat(0) >> str('*/') }
426
+
427
+ HEADER
407
428
  end
408
429
 
409
430
  def rule_footer
410
- <<-EOS
431
+ <<~FOOTER
411
432
 
412
- end
413
- end
414
- EOS
433
+ end
434
+ end
435
+ FOOTER
415
436
  end
416
437
  end
417
438
  end
439
+
440
+ # 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.1"
4
+ VERSION = '0.7.3'
3
5
  end