junoser 0.3.3 → 0.3.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.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +6 -4
- data/CHANGELOG.md +169 -75
- data/Gemfile.lock +30 -0
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/Rakefile +11 -3
- data/example/vmx-17.2R1.13.rb +42432 -42429
- data/junoser.gemspec +6 -2
- data/lib/junoser/development.rb +1 -0
- data/lib/junoser/display/config_store.rb +15 -15
- data/lib/junoser/display/enumerable.rb +35 -0
- data/lib/junoser/display.rb +3 -0
- data/lib/junoser/js_ruler.rb +334 -0
- data/lib/junoser/parser.rb +315 -308
- data/lib/junoser/ruler.rb +12 -3
- data/lib/junoser/squash.rb +9 -1
- data/lib/junoser/transformer.rb +8 -0
- data/lib/junoser/version.rb +1 -1
- data/lib/junoser/xsd/element.rb +13 -0
- data/lib/junoser/xsd/restriction.rb +9 -1
- metadata +7 -4
data/junoser.gemspec
CHANGED
@@ -13,14 +13,18 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.description = %q{PEG parser to vefiry and translate into different formats for JUNOS configuration.}
|
14
14
|
spec.homepage = "https://github.com/codeout/junoser"
|
15
15
|
|
16
|
-
|
16
|
+
# Specify which files should be added to the gem when it is released.
|
17
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
|
+
end
|
17
21
|
spec.bindir = "exe"
|
18
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
23
|
spec.require_paths = ["lib"]
|
20
24
|
|
21
25
|
spec.add_dependency "parslet"
|
22
26
|
|
23
|
-
spec.add_development_dependency "bundler", "~>
|
27
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
24
28
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
29
|
spec.add_development_dependency "nokogiri"
|
26
30
|
spec.add_development_dependency "test-unit"
|
data/lib/junoser/development.rb
CHANGED
@@ -42,33 +42,33 @@ module Junoser
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
def each_with_inactive(&block)
|
45
|
+
def each_with_inactive(str='', &block)
|
46
46
|
each do |k, v|
|
47
47
|
k = "inactive: #{k}" if v.deactivated
|
48
|
-
yield k, v
|
48
|
+
yield k, v, str
|
49
49
|
end
|
50
|
+
|
51
|
+
str
|
50
52
|
end
|
51
53
|
|
52
54
|
def to_s
|
53
|
-
|
54
|
-
|
55
|
-
each_with_inactive do |k, v|
|
56
|
-
if v.empty?
|
57
|
-
str << OFFSET*@depth << "#{k};\n"
|
58
|
-
else
|
59
|
-
str << OFFSET*@depth << "#{k} {\n"
|
60
|
-
str << v.to_s.chop << "\n"
|
61
|
-
str << OFFSET*@depth << "}\n"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
str
|
55
|
+
each_with_inactive('', &method(:hash_item_to_s))
|
66
56
|
end
|
67
57
|
|
68
58
|
def_delegators :@hash, :[], :[]=, :each, :empty?
|
69
59
|
|
70
60
|
private
|
71
61
|
|
62
|
+
def hash_item_to_s(key, value, str)
|
63
|
+
if value.empty?
|
64
|
+
str << OFFSET*@depth << "#{key};\n"
|
65
|
+
else
|
66
|
+
str << OFFSET*@depth << "#{key} {\n"
|
67
|
+
str << value.to_s.chop << "\n"
|
68
|
+
str << OFFSET*@depth << "}\n"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
72
|
def join_arg(str)
|
73
73
|
str.gsub!(/\narg\((.*)\)$/) { " #$1" }
|
74
74
|
str.gsub!(/arg\((.*)\)/) { "#$1" }
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'junoser/display/config_store'
|
2
|
+
|
3
|
+
module Junoser
|
4
|
+
module Display
|
5
|
+
module Enumerable
|
6
|
+
attr_accessor :in_from, :in_then, :in_group
|
7
|
+
|
8
|
+
def to_enum
|
9
|
+
if @hash.size > 1
|
10
|
+
"[#{@hash.keys.join(' ')}]"
|
11
|
+
else
|
12
|
+
@hash.keys.first
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def hash_item_to_s(key, value, str)
|
19
|
+
value.in_from = true if key == 'from'
|
20
|
+
value.in_then = true if key == 'then'
|
21
|
+
value.in_group = true if key =~ /^group /
|
22
|
+
|
23
|
+
if in_from && ['next-header', 'port', 'protocol'].include?(key) ||
|
24
|
+
in_then && key == 'origin' ||
|
25
|
+
in_group && key == 'type'
|
26
|
+
str << Junoser::Display::ConfigStore::OFFSET * @depth << "#{key} #{value.to_enum};\n"
|
27
|
+
else
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Junoser::Display::ConfigStore.prepend(Junoser::Display::Enumerable)
|
data/lib/junoser/display.rb
CHANGED
@@ -0,0 +1,334 @@
|
|
1
|
+
module Junoser
|
2
|
+
class JsRuler
|
3
|
+
|
4
|
+
def initialize(input)
|
5
|
+
@rule = input
|
6
|
+
@sequence = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def sequence
|
10
|
+
@sequence += 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_rule
|
14
|
+
rule_header << rule.gsub(/^/, ' ') << rule_footer
|
15
|
+
end
|
16
|
+
|
17
|
+
def rule
|
18
|
+
str = @rule.read
|
19
|
+
str = str.split(/\n/).map {|l| format(process_line(l))}.join("\n")
|
20
|
+
finalize(str)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def process_line(str)
|
27
|
+
remove_undefined_variables(str)
|
28
|
+
process_common_syntax(str)
|
29
|
+
process_argument_syntax(str)
|
30
|
+
process_structural_syntax(str)
|
31
|
+
process_word(str)
|
32
|
+
process_reserved_word(str)
|
33
|
+
process_comment(str)
|
34
|
+
|
35
|
+
str
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_undefined_variables(str)
|
39
|
+
str.gsub! '.as(:oneline)', ''
|
40
|
+
|
41
|
+
# "$junos-interface-unit" |
|
42
|
+
str.gsub! /"\$[\w-]+" \| /, ''
|
43
|
+
|
44
|
+
str
|
45
|
+
end
|
46
|
+
|
47
|
+
def process_common_syntax(str)
|
48
|
+
# rule(:foo) do -> foo(...args) {
|
49
|
+
str.gsub!(/^rule\(:(\S+)\) do/) {"#$1(...args) { return_next_line"}
|
50
|
+
# end -> }
|
51
|
+
str.gsub!(/^(\s*)end$/) {"#$1}"}
|
52
|
+
|
53
|
+
# arg.as(:arg) ( -> {"arg":
|
54
|
+
str.gsub! /arg\.as\(:arg\) \(/, '{"arg":'
|
55
|
+
# arg.as(:arg) -> {"arg": null}
|
56
|
+
str.gsub! /arg.as\(:arg\)/, '{"arg": null}'
|
57
|
+
# ("foo" | "bar").as(:arg) ( -> {"(bar|baz)":
|
58
|
+
str.gsub!(/\(([^)]+)\)\.as\(:arg\) \(/) {"{\"(#{$1.gsub('"', '').split(' | ').join('|')})\":"}
|
59
|
+
# enum(("foo" | "bar")).as(:arg) ( -> {"(bar|baz)": # TODO: support "enum" in the middle of line
|
60
|
+
str.gsub!(/enum\(\(([^)]+)\)\)\.as\(:arg\) \(/) {"{\"(#{$1.gsub('"', '').split(' | ').join('|')})\":"}
|
61
|
+
|
62
|
+
str.gsub! '.as(:arg)', ''
|
63
|
+
|
64
|
+
# any -> "any"
|
65
|
+
|
66
|
+
# c( -> {
|
67
|
+
str.gsub!(/^(\s*)c\(/) {"#$1{"}
|
68
|
+
|
69
|
+
# foo /* doc */, -> foo() /* doc */,
|
70
|
+
str.gsub!(%r|^(\s*)(?!arg)(\w+)( /\* (.*) \*/,?)$|) {"#$1this.#$2()#$3"}
|
71
|
+
# foo, -> foo(),
|
72
|
+
str.gsub!(%r|^(\s*)(?!arg)(\w+)(,?)$|) {"#$1this.#$2()#$3"}
|
73
|
+
|
74
|
+
# ) -> }
|
75
|
+
str.gsub!(/^(\s+)\)/) {"#$1}"}
|
76
|
+
|
77
|
+
str
|
78
|
+
end
|
79
|
+
|
80
|
+
def process_argument_syntax(str)
|
81
|
+
# "foo" (("bar" | "baz")) ( -> "foo(bar|baz)": {
|
82
|
+
# "foo" enum(("bar" | "baz")) ( -> "foo(bar|baz)": { # TODO: support "enum" in the middle of line
|
83
|
+
str.gsub!(/^(\s*)"(\S+)" (?:enum)?\(\((.*)\)\) \(/) {"#$1\"#$2(#{$3.gsub('"', '').split(' | ').join('|')})\": {"}
|
84
|
+
# "foo" arg ( -> "foo(arg)": {
|
85
|
+
str.gsub!(/^(\s*)"(\S+)" arg \(/) {"#$1\"#$2(arg)\": {"}
|
86
|
+
|
87
|
+
# "foo" (... | arg) /* doc */ -> "foo": "arg"
|
88
|
+
str.gsub!(%r|^(\s*)"(\S+)" \(.* \| arg ?.*\) /\* (.*) \*/(,?)$|) {"#$1\"#$2 | #$3\": arg#$4"}
|
89
|
+
|
90
|
+
# "foo" (... | arg) ( /* doc */ -> "foo(arg)": {
|
91
|
+
str.gsub!(%r|^(\s*)"(\S+)" \(.* \| arg ?.*\) \( /\* (.*) \*/$|) {"#$1\"#$2(arg) | #$3\": {"}
|
92
|
+
# "foo" (... | arg) ( -> "foo(arg)": {
|
93
|
+
str.gsub!(%r|^(\s*)"(\S+)" \(.* \| arg ?.*\) \($|) {"#$1\"#$2(arg)\": {"}
|
94
|
+
|
95
|
+
# "foo" (arg | ...) /* doc */ -> "foo": "arg"
|
96
|
+
str.gsub!(%r|^(\s*)"(\S+)" \(arg ?.*\) /\* (.*) \*/(,?)$|) {"#$1\"#$2 | #$3\": arg#$4"}
|
97
|
+
|
98
|
+
# "foo" (arg | ...) ( /* doc */ -> "foo(arg)": {
|
99
|
+
str.gsub!(%r|^(\s*)"(\S+)" \(arg ?.*\) \( /\* (.*) \*/(,?)$|) {"#$1\"#$2(arg) | #$3\": {#$4"}
|
100
|
+
|
101
|
+
# "foo" ("bar" | "baz") ( /* doc */ -> "foo(bar|baz)": {
|
102
|
+
str.gsub!(%r|^(\s*)"(\S+)" \("([^)]+)"\) \( /\* (.*) \*/$|) {"#$1\"#$2(#{$3.split('" | "').join('|')}) | #$4\": {"}
|
103
|
+
# "foo" ("bar" | "baz") ( -> "foo(bar|baz)": {
|
104
|
+
str.gsub!(%r|^(\s*)"(\S+)" \("([^)]+)"\) \($|) {"#$1\"#$2(#{$3.split('" | "').join('|')})\": {"}
|
105
|
+
|
106
|
+
# "foo" ("bar" | "baz") /* doc */, -> "foo": ["bar", "baz"],
|
107
|
+
str.gsub!(%r|^(\s*)"(\S+)" \(([^)]+)\) /\* (.*) \*/(,?)$|) {"#$1\"#$2 | #$4\": [#{$3.split(' | ').join(', ')}]#$5"}
|
108
|
+
# "foo" ("bar" | "baz"), -> "foo": ["bar", "baz"],
|
109
|
+
str.gsub!(%r|^(\s*)"(\S+)" \(([^)]+)\)(,?)$|) {"#$1\"#$2\": [#{$3.split(' | ').join(', ')}]#$4"}
|
110
|
+
|
111
|
+
# "foo" enum(("bar" | "baz")) -> "foo": new Enumeration(["bar", "baz"])
|
112
|
+
str.gsub!(/^(\s*)("\S+") enum\(\((.*)\)\)/) {"#$1#$2: new Enumeration(#{$3.gsub('"', '').split(' | ')})"}
|
113
|
+
|
114
|
+
# "foo" arg /* doc */, -> "foo | doc": "arg",
|
115
|
+
str.gsub!(%r|^(\s*)"([^"]+)" arg /\* (.*) \*/(,?)$|) {"#$1\"#$2 | #$3\": \"arg\"#$4"}
|
116
|
+
# "foo" arg, -> "foo": "arg",
|
117
|
+
str.gsub!(%r|(\s*)"([^"]+)" arg(,?)$|) {"#$1\"#$2\": \"arg\"#$3"}
|
118
|
+
|
119
|
+
# "foo" ipaddr, -> "foo": this.ipaddr(),
|
120
|
+
str.gsub!(%r|(\s*)"([^"]+)" ipaddr(,?)$|) {"#$1\"#$2\": this.ipaddr()#$3"}
|
121
|
+
|
122
|
+
str
|
123
|
+
end
|
124
|
+
|
125
|
+
def process_structural_syntax(str)
|
126
|
+
# "foo" ( /* doc */ -> "foo | doc": (...args) => {
|
127
|
+
str.gsub!(%r|^(\s*)"(\S+)" \( /\* (.*) \*/|) {"#$1\"#$2 | #$3\": {"}
|
128
|
+
# "foo" ( -> "foo": (...args) => {
|
129
|
+
str.gsub!(%r|^(\s*)"(\S+)" \($|) {"#$1\"#$2\": {"}
|
130
|
+
|
131
|
+
# "foo" arg ( /* doc */ -> "foo | doc": (...args) => {
|
132
|
+
str.gsub!(%r|^(\s*)"(\S+)" arg \( /\* (.*) \*/|) {"#$1\"#$2 | #$3\": {"}
|
133
|
+
|
134
|
+
str
|
135
|
+
end
|
136
|
+
|
137
|
+
def process_word(str)
|
138
|
+
# arg -> "arg"
|
139
|
+
# (arg) -> "arg"
|
140
|
+
# enum(arg) -> "arg"
|
141
|
+
str.gsub!(/ ((?!arg\w)(arg)|(enum)?\(arg\))/, ' "arg"')
|
142
|
+
|
143
|
+
# "foo" /* doc */, -> "foo | doc": null,
|
144
|
+
str.gsub!(%r|^(\s*)"([^"]+)" /\* (.*) \*/(,?)$|) {"#$1\"#$2 | #$3\": null#$4"}
|
145
|
+
# "foo", -> "foo": null,
|
146
|
+
str.gsub!(%r|^(\s*)"([^"]+)"(,?)$|) {"#$1\"#$2\": null#$3"}
|
147
|
+
|
148
|
+
# ("foo" | "bar") -> ["foo", "bar"]
|
149
|
+
str.gsub!(/^(\s*) \(+("[^"]+(?:" \| "[^"]+)*")\)+(,?)$/) do
|
150
|
+
comma = $3
|
151
|
+
"#$1#{$2.gsub('"', '').split(' | ')}#{comma}"
|
152
|
+
end
|
153
|
+
|
154
|
+
# enum(("bar" | "baz")) -> "foo": new Enumeration(["bar", "baz"])
|
155
|
+
str.gsub!(/^(\s*)enum\(\((.*)\)\)/) {"#$1new Enumeration(#{$2.gsub('"', '').split(' | ')})"}
|
156
|
+
|
157
|
+
# (arg | "foo") -> ["arg", "foo"]
|
158
|
+
str.gsub!(/^(\s*) \(arg( \| "[^"]+")+\)/) {"#$1[\"arg\"#{$2.split(' | ').join(', ')}]"}
|
159
|
+
|
160
|
+
str
|
161
|
+
end
|
162
|
+
|
163
|
+
def process_reserved_word(str)
|
164
|
+
# ieee-802.3ad -> 802.3ad
|
165
|
+
str.gsub! 'ieee-802.3ad', '802.3ad'
|
166
|
+
fix_route_filter(str)
|
167
|
+
|
168
|
+
str
|
169
|
+
end
|
170
|
+
|
171
|
+
def fix_route_filter(str)
|
172
|
+
str.gsub!(/("exact \| [^"]*"): "arg"/) {"#$1: null"}
|
173
|
+
str.gsub!(/("longer \| [^"]*"): "arg"/) {"#$1: null"}
|
174
|
+
str.gsub!(/("orlonger \| [^"]*"): "arg"/) {"#$1: null"}
|
175
|
+
end
|
176
|
+
|
177
|
+
def process_comment(str)
|
178
|
+
# % -> %%
|
179
|
+
str.gsub! '%', '' % %''
|
180
|
+
|
181
|
+
# "foo": ... /* doc */, -> "foo | doc": ...,
|
182
|
+
str.gsub!(%r|^(\s*)"([^"]+)": (.*) /\* (.*) \*/(,?)$|) {"#$1\"#$2 | #$4\": #$3#$5"}
|
183
|
+
|
184
|
+
str
|
185
|
+
end
|
186
|
+
|
187
|
+
def finalize(lines)
|
188
|
+
lines = balance_parenthesis(lines)
|
189
|
+
objectize_arg_of_s(lines)
|
190
|
+
|
191
|
+
# return_next_line
|
192
|
+
# ...
|
193
|
+
#
|
194
|
+
# ->
|
195
|
+
#
|
196
|
+
# return ...
|
197
|
+
lines.gsub!(/return_next_line\n(\s*)/m) {"\n#$1return "}
|
198
|
+
|
199
|
+
# {
|
200
|
+
# {
|
201
|
+
#
|
202
|
+
# ->
|
203
|
+
#
|
204
|
+
# {
|
205
|
+
# "null_1": {
|
206
|
+
lines.gsub!(/([{,]\n\s*){/m) {"#$1\"null_#{sequence}\": {"}
|
207
|
+
|
208
|
+
# {
|
209
|
+
# foo()
|
210
|
+
#
|
211
|
+
# ->
|
212
|
+
#
|
213
|
+
# {
|
214
|
+
# "null_1": foo()
|
215
|
+
lines.gsub!(/([{,]\n\s*)([^ "(]+)/m) {"#$1\"null_#{sequence}\": #$2"}
|
216
|
+
|
217
|
+
# "arg" -> "arg_1"
|
218
|
+
lines.gsub('"arg":') {%["arg_#{sequence}":]}
|
219
|
+
end
|
220
|
+
|
221
|
+
def balance_parenthesis(lines)
|
222
|
+
# Fixes
|
223
|
+
lines.gsub! 'Timeslots (1..24;', 'Timeslots (1..24);'
|
224
|
+
|
225
|
+
count = 0
|
226
|
+
target = nil
|
227
|
+
buf = ''
|
228
|
+
stack = []
|
229
|
+
|
230
|
+
lines.each_char do |c|
|
231
|
+
case c
|
232
|
+
when '('
|
233
|
+
count += 1
|
234
|
+
stack << target
|
235
|
+
target = count
|
236
|
+
|
237
|
+
buf << c
|
238
|
+
when '{'
|
239
|
+
count += 1
|
240
|
+
|
241
|
+
buf << c
|
242
|
+
when ')'
|
243
|
+
count -= 1
|
244
|
+
target = stack.pop
|
245
|
+
|
246
|
+
buf << c
|
247
|
+
when '}'
|
248
|
+
count -= 1
|
249
|
+
|
250
|
+
if target && count == target - 1
|
251
|
+
target = stack.pop
|
252
|
+
|
253
|
+
buf << ')'
|
254
|
+
else
|
255
|
+
buf << c
|
256
|
+
end
|
257
|
+
else
|
258
|
+
buf << c
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
buf
|
263
|
+
end
|
264
|
+
|
265
|
+
def objectize_arg_of_s(lines)
|
266
|
+
# s( -> s({
|
267
|
+
lines.gsub!(/^( *(?:return|\S+:)? +)s\($/m) {"#$1this.s({"}
|
268
|
+
|
269
|
+
# ) -> })
|
270
|
+
lines.gsub!(/^( *)\)$/m) {"#$1})"}
|
271
|
+
|
272
|
+
lines
|
273
|
+
end
|
274
|
+
|
275
|
+
def rule_header
|
276
|
+
<<~EOS
|
277
|
+
class Enumeration {
|
278
|
+
constructor(list) {
|
279
|
+
this.list = list;
|
280
|
+
this.children = [];
|
281
|
+
}
|
282
|
+
|
283
|
+
map(callback) {
|
284
|
+
// NOTE: This is a bit hacky but assuming that it's called like "this.children.map((node) => node.name)"
|
285
|
+
return this.list;
|
286
|
+
}
|
287
|
+
|
288
|
+
find(callback) {
|
289
|
+
// NOTE: This is a bit hacky but assuming that it's called like "this.children.find((node) => node.name === string)"
|
290
|
+
return;
|
291
|
+
}
|
292
|
+
|
293
|
+
forEach(callback) {
|
294
|
+
return this.list.forEach(callback);
|
295
|
+
}
|
296
|
+
|
297
|
+
keywords() {
|
298
|
+
return this.list;
|
299
|
+
}
|
300
|
+
}
|
301
|
+
|
302
|
+
class Sequence {
|
303
|
+
constructor(list) {
|
304
|
+
this.list = list;
|
305
|
+
}
|
306
|
+
|
307
|
+
get(depth) {
|
308
|
+
return this.list[depth];
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
class JunosSchema {
|
313
|
+
any() {
|
314
|
+
return "any";
|
315
|
+
}
|
316
|
+
|
317
|
+
s(obj) {
|
318
|
+
const list = Object.entries(obj).map(([key, value]) => ({[key]: value}));
|
319
|
+
return new Sequence(list);
|
320
|
+
}
|
321
|
+
|
322
|
+
EOS
|
323
|
+
end
|
324
|
+
|
325
|
+
def rule_footer
|
326
|
+
<<~EOS
|
327
|
+
|
328
|
+
}
|
329
|
+
|
330
|
+
module.exports = {JunosSchema: JunosSchema, Enumeration: Enumeration, Sequence: Sequence};
|
331
|
+
EOS
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|