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.
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
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
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", "~> 1.9"
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"
@@ -1,3 +1,4 @@
1
1
  require 'underscorable'
2
2
  require 'junoser/xsd/parsable'
3
3
  require 'junoser/ruler'
4
+ require 'junoser/js_ruler'
@@ -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
- str = ''
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)
@@ -14,3 +14,6 @@ module Junoser
14
14
  end
15
15
  end
16
16
  end
17
+
18
+ # NOTE: Apply a patch
19
+ require 'junoser/display/enumerable'
@@ -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