junoser 0.7.6 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ad52f9bcc08bafe72ec4b432c96d85dce85d1db353c29053ebad6bbc7918690
4
- data.tar.gz: 8a54ab81259886f5d52b59fa438dd00df6e60b1436221a810aa47c9120306fee
3
+ metadata.gz: 15ee47f89643f4f98a2382fd8be31c3a4758daf9ce84b0da6e1df20eaac1f90f
4
+ data.tar.gz: 1dbdef198298e901d75b43659427084dd6e6a3f701450d890fde32d51def2c65
5
5
  SHA512:
6
- metadata.gz: b7b885fa0bfdb1668bea5613e65888488a42b88be667d0812052e0b72a2c68cc49772d4070f4bc24ef72c2b3c20ba69f2b563bd00f289341420bc33a2d27083e
7
- data.tar.gz: 50994ee96cf22cb5079fe2ded3ca5b13e82de325d91c68abfb00899c2c27dbcbbb9e11052f0ebd114dfce0aea23c03b86eeb79f39f55354140407cad3d120868
6
+ metadata.gz: c8eff73a8e20c78062db8a30c8330d00ac919b7d16649139c92dbfee2e5c46d53638d697cafbcae8cfa9ba1a2bcac1be0aa5fa9a27cbd8cef1f7e6c51e2ada59
7
+ data.tar.gz: 59c3a183983e42497adb6534faf85582267adcbe5186739e802e43c8766c25cec32a34adf4647de4b05d87ad68352879bf170cd5b807d04054dff7f50285358e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ ## [0.8.0] - 2026-10-17
2
+
3
+ ### Added
4
+
5
+ * Recreate parser based on MX 23.4R2-S7.4 xsd
6
+ * Newly supported syntax
7
+ * "mld-snooping vlan all disable"
8
+ * "poe fpc xxx"
9
+ * "chassis alarm fru-absence ignore fpc xxx pem xxx"
10
+ * "ethernet-switching-options voip interface xxx"
11
+ * Positional arguments of a choice
12
+ * "protocols mpls label-switched-path xxx priority xxx xxx"
13
+ * "routing-options aggregate route xxx as-path aggregator xxx xxx"
14
+
15
+
1
16
  ## [0.7.6] - 2026-10-01
2
17
 
3
18
  ### Fixed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- junoser (0.7.6)
4
+ junoser (0.8.0)
5
5
  parslet
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Junoser
2
2
 
3
- [![Test on Ubuntu](https://github.com/codeout/junoser/actions/workflows/test-linux.yaml/badge.svg)](https://github.com/codeout/junoser/actions/workflows/test-linux.yaml)
3
+ [![Test](https://github.com/codeout/junoser/actions/workflows/test.yaml/badge.svg)](https://github.com/codeout/junoser/actions/workflows/test.yaml)
4
+ [![Lint](https://github.com/codeout/junoser/actions/workflows/lint.yaml/badge.svg)](https://github.com/codeout/junoser/actions/workflows/lint.yaml)
4
5
  [![Inline docs](http://inch-ci.org/github/codeout/junoser.svg)](http://inch-ci.org/github/codeout/junoser)
5
6
 
6
7
  Junoser is a JUNOS configuration PEG parser which can be automatically generated from Juniper's netconf.xsd. (XML Schema Definition for NETCONF)
data/Rakefile CHANGED
@@ -3,22 +3,19 @@
3
3
  require 'bundler/gem_tasks'
4
4
  require 'junoser/development'
5
5
  require 'nokogiri'
6
- require 'pathname'
7
6
  require 'rake/testtask'
8
7
 
9
- xsd_path = File.join(__dir__, 'tmp/junos-system-22.4.xsd')
8
+ xsd_path = File.join(__dir__, 'tmp/junos-system-23.4.xsd')
10
9
  rule_path = File.join(__dir__, 'tmp/rule.rb')
11
10
  ruby_parser_path = File.join(__dir__, 'lib/junoser/parser.rb')
12
11
  js_parser_path = File.join(__dir__, 'tmp/junos.js')
13
12
 
14
13
  def open_files(input, output)
15
- i = File.open(input)
16
- o = File.open(output, 'w')
17
-
18
- yield i, o
19
-
20
- i.close
21
- o.close
14
+ File.open(input) do |i|
15
+ File.open(output, 'w') do |o|
16
+ yield i, o
17
+ end
18
+ end
22
19
  end
23
20
 
24
21
  def move_wildcards(element)
data/exe/junoser CHANGED
@@ -3,9 +3,8 @@
3
3
 
4
4
  require 'English'
5
5
  require 'optparse'
6
- require 'pathname'
7
6
 
8
- $LOAD_PATH << File.expand_path('../../lib', Pathname.new(__FILE__).realpath)
7
+ $LOAD_PATH << File.expand_path('../../lib', File.realpath(__FILE__))
9
8
 
10
9
  require 'junoser'
11
10
 
data/exe/junoser-squash CHANGED
@@ -3,9 +3,8 @@
3
3
 
4
4
  require 'English'
5
5
  require 'optparse'
6
- require 'pathname'
7
6
 
8
- $LOAD_PATH << File.expand_path('../../lib', Pathname.new(__FILE__).realpath)
7
+ $LOAD_PATH << File.expand_path('../../lib', File.realpath(__FILE__))
9
8
  require 'junoser/squash'
10
9
 
11
10
  command = :squash
@@ -77,6 +77,7 @@ module Junoser
77
77
 
78
78
  def process_line(str)
79
79
  str = remove_undefined_variables(str)
80
+ str = process_placeholder_syntax(str)
80
81
  str = process_common_syntax(str)
81
82
  str = process_argument_syntax(str)
82
83
  str = process_structural_syntax(str)
@@ -90,6 +91,20 @@ module Junoser
90
91
 
91
92
  # "$junos-interface-unit" |
92
93
  str.gsub!(/"\$[\w-]+" \| /, '')
94
+ # | "$junos-pon-id-interface-set-name" (this one trails the other members)
95
+ str.gsub!(/ \| "\$[\w-]+"/, '')
96
+
97
+ str
98
+ end
99
+
100
+ def process_placeholder_syntax(str)
101
+ # The .xsd puts a placeholder name where an argument belongs, so the parser would accept only that word.
102
+ # Ruler does the same.
103
+ str.gsub!(/\((.*) \| "name"\)/) { "(#{$1} | arg)" }
104
+ str.gsub!(/\("name" \| (.*?)\)/) { "(#{$1} | arg)" }
105
+ str.gsub! '"vlan" ("all" | "vlan-name")', '"vlan" ("all" | arg)'
106
+ str.gsub! '("all" | "fpc-slot-number")', '("all" | arg)'
107
+ str.gsub! '("all" | "psu-slot-number")', '("all" | arg)'
93
108
 
94
109
  str
95
110
  end
@@ -134,19 +149,27 @@ module Junoser
134
149
  # "foo" arg ( -> "foo(arg)": {
135
150
  str.gsub!(/^(\s*)"(\S+)" arg \(/) { "#{$1}\"#{$2}(arg)\": {" }
136
151
 
137
- # "foo" (... | arg) /* doc */ -> "foo": "arg"
138
- str.gsub!(%r{^(\s*)"(\S+)" \(.* \| arg ?.*\) /\* (.*) \*/(,?)$}) { "#{$1}\"#{$2} | #{$3}\": arg#{$4}" }
152
+ # "foo" ("bar" | arg) /* doc */ -> "foo | doc": ["bar", "arg"]
153
+ str.gsub!(%r{^(\s*)"(\S+)" \((.* \| arg ?.*)\) /\* (.*) \*/(,?)$}) do
154
+ "#{$1}\"#{$2} | #{$4}\": #{array_members($3)}#{$5}"
155
+ end
139
156
 
140
- # "foo" (... | arg) ( /* doc */ -> "foo(arg)": {
141
- str.gsub!(%r{^(\s*)"(\S+)" \(.* \| arg ?.*\) \( /\* (.*) \*/$}) { "#{$1}\"#{$2}(arg) | #{$3}\": {" }
142
- # "foo" (... | arg) ( -> "foo(arg)": {
143
- str.gsub!(/^(\s*)"(\S+)" \(.* \| arg ?.*\) \($/) { "#{$1}\"#{$2}(arg)\": {" }
157
+ # "foo" ("bar" | arg) ( /* doc */ -> "foo(bar|arg) | doc": {
158
+ str.gsub!(%r{^(\s*)"(\S+)" \((.* \| arg ?.*)\) \( /\* (.*) \*/$}) do
159
+ "#{$1}\"#{$2}(#{pipe_members($3)}) | #{$4}\": {"
160
+ end
161
+ # "foo" ("bar" | arg) ( -> "foo(bar|arg)": {
162
+ str.gsub!(/^(\s*)"(\S+)" \((.* \| arg ?.*)\) \($/) { "#{$1}\"#{$2}(#{pipe_members($3)})\": {" }
144
163
 
145
- # "foo" (arg | ...) /* doc */ -> "foo": "arg"
146
- str.gsub!(%r{^(\s*)"(\S+)" \(arg ?.*\) /\* (.*) \*/(,?)$}) { "#{$1}\"#{$2} | #{$3}\": arg#{$4}" }
164
+ # "foo" (arg | "bar") /* doc */ -> "foo | doc": ["arg", "bar"]
165
+ str.gsub!(%r{^(\s*)"(\S+)" \((arg ?.*)\) /\* (.*) \*/(,?)$}) do
166
+ "#{$1}\"#{$2} | #{$4}\": #{array_members($3)}#{$5}"
167
+ end
147
168
 
148
- # "foo" (arg | ...) ( /* doc */ -> "foo(arg)": {
149
- str.gsub!(%r{^(\s*)"(\S+)" \(arg ?.*\) \( /\* (.*) \*/(,?)$}) { "#{$1}\"#{$2}(arg) | #{$3}\": {#{$4}" }
169
+ # "foo" (arg | "bar") ( /* doc */ -> "foo(arg|bar) | doc": {
170
+ str.gsub!(%r{^(\s*)"(\S+)" \((arg ?.*)\) \( /\* (.*) \*/(,?)$}) do
171
+ "#{$1}\"#{$2}(#{pipe_members($3)}) | #{$4}\": {#{$5}"
172
+ end
150
173
 
151
174
  # "foo" ("bar" | "baz") ( /* doc */ -> "foo(bar|baz)": {
152
175
  str.gsub!(%r{^(\s*)"(\S+)" \("([^)]+)"\) \( /\* (.*) \*/$}) do
@@ -182,9 +205,6 @@ module Junoser
182
205
  # "foo" ( -> "foo": (opts) => {
183
206
  str.gsub!(/^(\s*)"(\S+)" \($/) { "#{$1}\"#{$2}\": {" }
184
207
 
185
- # "foo" arg ( /* doc */ -> "foo | doc": (opts) => {
186
- str.gsub!(%r{^(\s*)"(\S+)" arg \( /\* (.*) \*/}) { "#{$1}\"#{$2} | #{$3}\": {" }
187
-
188
208
  str
189
209
  end
190
210
 
@@ -208,8 +228,13 @@ module Junoser
208
228
  # enum(("bar" | "baz")) -> "foo": new Enumeration(["bar", "baz"])
209
229
  str.gsub!(/^(\s*)enum\(\((.*)\)\)/) { "#{$1}new Enumeration(#{$2.gsub('"', '').split(' | ')})" }
210
230
 
211
- # (arg | "foo") -> ["arg", "foo"]
212
- str.gsub!(/^(\s*) \(arg( \| "[^"]+")+\)/) { "#{$1}[\"arg\"#{$2.split(' | ').join(', ')}]" }
231
+ # (arg | "foo" | "bar") -> ["arg", "foo", "bar"]
232
+ # capture the tail as a whole, because a repeated group keeps only its last match
233
+ str.gsub!(/^(\s*) \(arg((?: \| "[^"]+")+)\)/) do
234
+ indent = $1
235
+ members = ['"arg"'] + $2.scan(/"[^"]+"/)
236
+ "#{indent}[#{members.uniq.join(', ')}]"
237
+ end
213
238
 
214
239
  str
215
240
  end
@@ -261,6 +286,21 @@ module Junoser
261
286
  str
262
287
  end
263
288
 
289
+ # Join the members of a choice with a pipe, for the parentheses of a key: "all" | arg -> all|arg.
290
+ # Keep the keywords next to arg, because vscode-junos offers every member as a completion candidate.
291
+ def pipe_members(choice)
292
+ choice.gsub('"', '').split(' | ').join('|')
293
+ end
294
+
295
+ # Put the members of a choice in an array, for a value. A lone arg stays a scalar, which is the shape
296
+ # loadString handles.
297
+ def array_members(choice)
298
+ list = choice.gsub('"', '').split(' | ')
299
+ return 'arg' if list == ['arg']
300
+
301
+ "[#{list.map { |member| %("#{member}") }.join(', ')}]"
302
+ end
303
+
264
304
  def finalize(lines)
265
305
  lines = balance_parenthesis(lines)
266
306
  objectize_arg_of_s(lines)
@@ -292,7 +332,7 @@ module Junoser
292
332
  lines.gsub!(/([{,]\n\s*)([^ "(]+)/m) { "#{$1}\"null_#{sequence}\": #{$2}" }
293
333
 
294
334
  # "arg" -> "arg_1"
295
- lines.gsub('"arg":') { %("arg_#{sequence}":) }
335
+ lines.gsub!('"arg":') { %("arg_#{sequence}":) }
296
336
 
297
337
  conditional_groups(lines)
298
338
  end