junoser 0.3.3 → 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|