junoser 0.3.3 → 0.3.4

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