mallinna 0.0.2
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 +7 -0
- data/Gemfile +2 -0
- data/README.md +73 -0
- data/Rakefile +48 -0
- data/lib/malline.rb +14 -0
- data/lib/malline/compiler.rb +95 -0
- data/lib/malline/handler.rb +17 -0
- data/lib/malline/lexer.rb +36 -0
- data/lib/malline/parser.rb +152 -0
- data/lib/malline/parser.y +40 -0
- data/mallinna.gemspec +17 -0
- data/spec/compiler_spec.rb +44 -0
- data/spec/handler_spec.rb +22 -0
- data/spec/lexer_spec.rb +29 -0
- data/spec/spec_helper.rb +82 -0
- data/tst.rb +23 -0
- metadata +74 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 05a38f928da3bea1a216060832b7a371efcae6e7
|
4
|
+
data.tar.gz: 7262a8d7a83b458fe5c1943bb101013e402b546a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 565b2a393437c24dcebe207cb1efa4e828b819d6bb980dd7a17dc13f28124f8f2eb15aebe2410a91f589c320e8690e92322069bf2d4fe0503eb1af8f54a8bc2c
|
7
|
+
data.tar.gz: ac44b2df593e894b243eaac63e41892d3c2ee74b5920f2230c0fe49273528f077215a213a4aa21a7870c11a908a55ebca57f0b8cda6419b332856a4b9e425732
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# Malline
|
2
|
+
|
3
|
+
Malline is template engine targeting IOS/JunOS or generally configurations (or
|
4
|
+
more accurately what ever text stuff I'm doing).
|
5
|
+
|
6
|
+
It surfaced from the need to do something like this in ERB:
|
7
|
+
```
|
8
|
+
interfaces {
|
9
|
+
<%= name %> {
|
10
|
+
description "<%if description %>";
|
11
|
+
}
|
12
|
+
}
|
13
|
+
```
|
14
|
+
|
15
|
+
where description "description"; is only output if the code evaluates true,
|
16
|
+
keeping the template clean in common use-case
|
17
|
+
|
18
|
+
```
|
19
|
+
Consider this code
|
20
|
+
require_relative './lib/malline'
|
21
|
+
require 'pry'
|
22
|
+
class Int
|
23
|
+
attr_accessor :name, :mtu, :description, :poop
|
24
|
+
def initialize
|
25
|
+
@name = 'xe-1/2/3.42'
|
26
|
+
@mtu = 1500
|
27
|
+
@description = 'K: S-123455 - poop customer'
|
28
|
+
@poop = true
|
29
|
+
end
|
30
|
+
def get_binding
|
31
|
+
binding
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
template = <<EOF
|
36
|
+
interfaces {
|
37
|
+
<% name %> {
|
38
|
+
mtu <%if mtu %>;
|
39
|
+
description "<%if description %>";
|
40
|
+
}
|
41
|
+
}
|
42
|
+
EOF
|
43
|
+
|
44
|
+
int = Int.new
|
45
|
+
cfg = Malline.run template, int.get_binding
|
46
|
+
binding.pry
|
47
|
+
```
|
48
|
+
|
49
|
+
Real use might look something like this:
|
50
|
+
```
|
51
|
+
[1] pry(main)> puts cfg
|
52
|
+
interfaces {
|
53
|
+
xe-1/2/3.42 {
|
54
|
+
mtu 1500;
|
55
|
+
description "K: S-123455 - poop customer";
|
56
|
+
}
|
57
|
+
}
|
58
|
+
=> nil
|
59
|
+
[2] pry(main)> int.mtu = 900
|
60
|
+
=> 900
|
61
|
+
[3] pry(main)> int.description = nil
|
62
|
+
=> nil
|
63
|
+
[4] pry(main)> puts Malline.run template, int.get_binding
|
64
|
+
interfaces {
|
65
|
+
xe-1/2/3.42 {
|
66
|
+
mtu 900;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
=> nil
|
70
|
+
```
|
71
|
+
|
72
|
+
|
73
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
rule '.rb' => '.y' do |t|
|
4
|
+
sh "racc -l -o #{t.name} #{t.source}"
|
5
|
+
end
|
6
|
+
gemspec = eval(File.read(Dir['*.gemspec'].first))
|
7
|
+
file = [gemspec.name, gemspec.version].join('-') + '.gem'
|
8
|
+
|
9
|
+
desc 'Validate gemspec'
|
10
|
+
task :gemspec do
|
11
|
+
gemspec.validate
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Run minitest'
|
15
|
+
task :test => :compile do
|
16
|
+
Rake::TestTask.new do |t|
|
17
|
+
t.libs.push "lib"
|
18
|
+
all = FileList['spec/*_spec.rb']
|
19
|
+
first = %w(spec/lexer_spec.rb spec/handler_spec.rb)
|
20
|
+
t.test_files = [first, all-first].flatten
|
21
|
+
t.verbose = true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'Compile RACC parser'
|
26
|
+
task :compile => 'lib/malline/parser.rb'
|
27
|
+
|
28
|
+
desc 'Build gem'
|
29
|
+
task :build => [:gemspec, :test] do
|
30
|
+
system "gem build #{gemspec.name}.gemspec"
|
31
|
+
FileUtils.mkdir_p 'gems'
|
32
|
+
FileUtils.mv file, 'gems'
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'Install gem'
|
36
|
+
task :install => :build do
|
37
|
+
system "sudo sh -c \'umask 022; gem install gems/#{file}\'"
|
38
|
+
end
|
39
|
+
|
40
|
+
desc 'Remove gems'
|
41
|
+
task :clean do
|
42
|
+
FileUtils.rm_rf 'gems'
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'Push to rubygems'
|
46
|
+
task :push do
|
47
|
+
system "gem push gems/#{file}"
|
48
|
+
end
|
data/lib/malline.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Malline
|
2
|
+
require_relative './malline/lexer'
|
3
|
+
require_relative './malline/parser'
|
4
|
+
require_relative './malline/compiler.rb'
|
5
|
+
|
6
|
+
def self.run template, binding
|
7
|
+
lex = Lexer.new template
|
8
|
+
par = Parser.new lex
|
9
|
+
han = par.parse
|
10
|
+
com = Compiler.new han.parsed
|
11
|
+
template = com.compile
|
12
|
+
eval(template, binding)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Malline
|
2
|
+
class Compiler
|
3
|
+
VAR = '_malline_'
|
4
|
+
OUT = VAR + '_out'
|
5
|
+
class InvalidKind < NameError; end
|
6
|
+
attr_reader :out
|
7
|
+
def initialize parsed
|
8
|
+
@parsed = parsed
|
9
|
+
@out = nil
|
10
|
+
@cmd = {
|
11
|
+
:if => {
|
12
|
+
:now => 0,
|
13
|
+
:line => [],
|
14
|
+
:id => [],
|
15
|
+
},
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def compile
|
20
|
+
@out = resolve(combine)
|
21
|
+
end
|
22
|
+
|
23
|
+
def combine
|
24
|
+
out = []
|
25
|
+
@parsed.each do |e|
|
26
|
+
case kind=e.shift
|
27
|
+
when :data then data(out, *e)
|
28
|
+
when :code then code(out, *e)
|
29
|
+
else
|
30
|
+
raise InvalidKind, "'#{kind}' was not recognized"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
out
|
34
|
+
end
|
35
|
+
|
36
|
+
def data out, str, lines
|
37
|
+
lines = (lines.first .. lines.last).to_a
|
38
|
+
str.lines.each_with_index do |line, index|
|
39
|
+
linenr = lines[index]
|
40
|
+
line = '%s.concat \'%s\'' % [OUT, line.gsub(/'/, %q(\\\'))]
|
41
|
+
out << [line, linenr]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def code out, cmd, str, lines
|
46
|
+
lines = (lines.first .. lines.last).to_a
|
47
|
+
str.lines.each_with_index do |line, index|
|
48
|
+
linenr = lines[index]
|
49
|
+
line = cmd_if(line, lines, @cmd[:if][:now]+=1) if cmd == :if
|
50
|
+
line = '%s.concat %s' % [OUT, line] unless cmd == :s
|
51
|
+
out << ["#{line}.to_s", linenr]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def cmd_if str, lines, id
|
57
|
+
name = VAR+'if_'+id.to_s
|
58
|
+
(lines.first .. lines.last).each do |line|
|
59
|
+
@cmd[:if][:line][line] ||= []
|
60
|
+
@cmd[:if][:line][line] << id
|
61
|
+
@cmd[:if][:id][id] = [name, str]
|
62
|
+
end
|
63
|
+
name
|
64
|
+
end
|
65
|
+
|
66
|
+
def resolve out_org
|
67
|
+
out = ["#{OUT}='';"]
|
68
|
+
cmp = []
|
69
|
+
seen = []
|
70
|
+
out_org.each do |line, line_nr|
|
71
|
+
resolve_cmd_if(line_nr, cmp, out) unless seen.include? line_nr
|
72
|
+
seen << line_nr
|
73
|
+
if not cmp.empty?
|
74
|
+
line = line + ' if ' + cmp.join(' and ')
|
75
|
+
end
|
76
|
+
out << line + ';'
|
77
|
+
end
|
78
|
+
out << "#{OUT}"
|
79
|
+
out.join "\n"
|
80
|
+
end
|
81
|
+
|
82
|
+
def resolve_cmd_if line_nr, cmp, out
|
83
|
+
cmp.clear
|
84
|
+
set = []
|
85
|
+
[@cmd[:if][:line][line_nr]].flatten.compact.each do |cmd_id|
|
86
|
+
varname, varvalue = @cmd[:if][:id][cmd_id]
|
87
|
+
set << [varname,varvalue]
|
88
|
+
cmp << varname
|
89
|
+
end
|
90
|
+
set.each do |name, value|
|
91
|
+
out << name + '=' + value + ';'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Malline
|
2
|
+
class Handler
|
3
|
+
attr_reader :parsed
|
4
|
+
attr_accessor :token_line
|
5
|
+
def initialize
|
6
|
+
@parsed = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def code string, cmd=nil
|
10
|
+
@parsed << [:code, cmd, string, token_line]
|
11
|
+
end
|
12
|
+
|
13
|
+
def data string
|
14
|
+
@parsed << [:data, string, token_line]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module Malline
|
5
|
+
class Lexer
|
6
|
+
TOKEN = {
|
7
|
+
:START_CODE => /<%(if|s)?/,
|
8
|
+
:END_CODE => /%>/,
|
9
|
+
}
|
10
|
+
|
11
|
+
def initialize io
|
12
|
+
io = StringIO.new io if io.class == String
|
13
|
+
@ss = StringScanner.new io.read
|
14
|
+
@token, @state = [], [:CODE, :DATA]
|
15
|
+
end
|
16
|
+
|
17
|
+
def next_token
|
18
|
+
return @token.shift unless @token.empty?
|
19
|
+
return if @ss.eos?
|
20
|
+
|
21
|
+
start = line
|
22
|
+
name = @state.rotate!.first == :DATA ? :START_CODE : :END_CODE
|
23
|
+
if data = @ss.scan_until(TOKEN[name])
|
24
|
+
@token << [name, @ss.matched, start, line]
|
25
|
+
@token << [:CMD, @ss.matched[2..-1], start, line] if @ss.matched.size > 2
|
26
|
+
[:DATA, data[0..-(@ss.matched.size+1)], start, line]
|
27
|
+
else #gobble gobble gobble
|
28
|
+
[:DATA, @ss.scan(/.*/m), start, line]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def line
|
33
|
+
@ss.string[0..@ss.pos].lines.count
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
#
|
2
|
+
# DO NOT MODIFY!!!!
|
3
|
+
# This file is automatically generated by Racc 1.4.11
|
4
|
+
# from Racc grammer file "".
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'racc/parser.rb'
|
8
|
+
module Malline
|
9
|
+
class Parser < Racc::Parser
|
10
|
+
|
11
|
+
|
12
|
+
require_relative 'handler'
|
13
|
+
|
14
|
+
def initialize lexer, handler=Handler.new
|
15
|
+
@lexer = lexer
|
16
|
+
@handler = handler
|
17
|
+
super()
|
18
|
+
end
|
19
|
+
|
20
|
+
def next_token
|
21
|
+
token, string, start_line, end_line = @lexer.next_token
|
22
|
+
@handler.token_line = [start_line, end_line]
|
23
|
+
[token, string] if token
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse
|
27
|
+
do_parse
|
28
|
+
@handler
|
29
|
+
end
|
30
|
+
##### State transition tables begin ###
|
31
|
+
|
32
|
+
racc_action_table = [
|
33
|
+
7, 5, 5, 6, 6, 9, 11, 10, 12, 13,
|
34
|
+
14, 15 ]
|
35
|
+
|
36
|
+
racc_action_check = [
|
37
|
+
1, 0, 1, 0, 1, 5, 5, 5, 7, 10,
|
38
|
+
11, 13 ]
|
39
|
+
|
40
|
+
racc_action_pointer = [
|
41
|
+
-1, 0, nil, nil, nil, 2, nil, 8, nil, nil,
|
42
|
+
5, 7, nil, 8, nil, nil ]
|
43
|
+
|
44
|
+
racc_action_default = [
|
45
|
+
-9, -9, -2, -3, -4, -9, -8, -9, -1, -5,
|
46
|
+
-9, -9, 16, -9, -7, -6 ]
|
47
|
+
|
48
|
+
racc_goto_table = [
|
49
|
+
2, 8, 1 ]
|
50
|
+
|
51
|
+
racc_goto_check = [
|
52
|
+
2, 2, 1 ]
|
53
|
+
|
54
|
+
racc_goto_pointer = [
|
55
|
+
nil, 2, 0, nil, nil ]
|
56
|
+
|
57
|
+
racc_goto_default = [
|
58
|
+
nil, nil, nil, 3, 4 ]
|
59
|
+
|
60
|
+
racc_reduce_table = [
|
61
|
+
0, 0, :racc_error,
|
62
|
+
2, 7, :_reduce_none,
|
63
|
+
1, 7, :_reduce_none,
|
64
|
+
1, 8, :_reduce_none,
|
65
|
+
1, 8, :_reduce_none,
|
66
|
+
2, 9, :_reduce_none,
|
67
|
+
4, 9, :_reduce_6,
|
68
|
+
3, 9, :_reduce_7,
|
69
|
+
1, 10, :_reduce_8 ]
|
70
|
+
|
71
|
+
racc_reduce_n = 9
|
72
|
+
|
73
|
+
racc_shift_n = 16
|
74
|
+
|
75
|
+
racc_token_table = {
|
76
|
+
false => 0,
|
77
|
+
:error => 1,
|
78
|
+
:START_CODE => 2,
|
79
|
+
:END_CODE => 3,
|
80
|
+
:DATA => 4,
|
81
|
+
:CMD => 5 }
|
82
|
+
|
83
|
+
racc_nt_base = 6
|
84
|
+
|
85
|
+
racc_use_result_var = true
|
86
|
+
|
87
|
+
Racc_arg = [
|
88
|
+
racc_action_table,
|
89
|
+
racc_action_check,
|
90
|
+
racc_action_default,
|
91
|
+
racc_action_pointer,
|
92
|
+
racc_goto_table,
|
93
|
+
racc_goto_check,
|
94
|
+
racc_goto_default,
|
95
|
+
racc_goto_pointer,
|
96
|
+
racc_nt_base,
|
97
|
+
racc_reduce_table,
|
98
|
+
racc_token_table,
|
99
|
+
racc_shift_n,
|
100
|
+
racc_reduce_n,
|
101
|
+
racc_use_result_var ]
|
102
|
+
|
103
|
+
Racc_token_to_s_table = [
|
104
|
+
"$end",
|
105
|
+
"error",
|
106
|
+
"START_CODE",
|
107
|
+
"END_CODE",
|
108
|
+
"DATA",
|
109
|
+
"CMD",
|
110
|
+
"$start",
|
111
|
+
"values",
|
112
|
+
"value",
|
113
|
+
"code_block",
|
114
|
+
"data" ]
|
115
|
+
|
116
|
+
Racc_debug_parser = false
|
117
|
+
|
118
|
+
##### State transition tables end #####
|
119
|
+
|
120
|
+
# reduce 0 omitted
|
121
|
+
|
122
|
+
# reduce 1 omitted
|
123
|
+
|
124
|
+
# reduce 2 omitted
|
125
|
+
|
126
|
+
# reduce 3 omitted
|
127
|
+
|
128
|
+
# reduce 4 omitted
|
129
|
+
|
130
|
+
# reduce 5 omitted
|
131
|
+
|
132
|
+
def _reduce_6(val, _values, result)
|
133
|
+
@handler.code(val[2], val[1].to_sym)
|
134
|
+
result
|
135
|
+
end
|
136
|
+
|
137
|
+
def _reduce_7(val, _values, result)
|
138
|
+
@handler.code val[1]
|
139
|
+
result
|
140
|
+
end
|
141
|
+
|
142
|
+
def _reduce_8(val, _values, result)
|
143
|
+
@handler.data val.first
|
144
|
+
result
|
145
|
+
end
|
146
|
+
|
147
|
+
def _reduce_none(val, _values, result)
|
148
|
+
val[0]
|
149
|
+
end
|
150
|
+
|
151
|
+
end # class Parser
|
152
|
+
end # module Malline
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class Malline::Parser
|
2
|
+
token START_CODE END_CODE DATA CMD
|
3
|
+
rule
|
4
|
+
values
|
5
|
+
: values value
|
6
|
+
| value
|
7
|
+
;
|
8
|
+
value
|
9
|
+
: code_block
|
10
|
+
| data
|
11
|
+
;
|
12
|
+
code_block
|
13
|
+
: START_CODE END_CODE
|
14
|
+
| START_CODE CMD DATA END_CODE { @handler.code(val[2], val[1].to_sym) }
|
15
|
+
| START_CODE DATA END_CODE { @handler.code val[1] }
|
16
|
+
;
|
17
|
+
data : DATA { @handler.data val.first }
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
---- inner
|
22
|
+
|
23
|
+
require_relative 'handler'
|
24
|
+
|
25
|
+
def initialize lexer, handler=Handler.new
|
26
|
+
@lexer = lexer
|
27
|
+
@handler = handler
|
28
|
+
super()
|
29
|
+
end
|
30
|
+
|
31
|
+
def next_token
|
32
|
+
token, string, start_line, end_line = @lexer.next_token
|
33
|
+
@handler.token_line = [start_line, end_line]
|
34
|
+
[token, string] if token
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse
|
38
|
+
do_parse
|
39
|
+
@handler
|
40
|
+
end
|
data/mallinna.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'mallinna'
|
3
|
+
s.version = '0.0.2'
|
4
|
+
s.platform = Gem::Platform::RUBY
|
5
|
+
s.authors = [ 'Saku Ytti' ]
|
6
|
+
s.email = %w( saku@ytti.fi )
|
7
|
+
s.homepage = 'https://github.com/ytti/malline'
|
8
|
+
s.summary = 'Templating language targeting IOS/JunOS configs'
|
9
|
+
s.description = 'Mostly like ERB, but <%if foo%> conditional will omit whole line unless code evaluates true'
|
10
|
+
s.rubyforge_project = s.name
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.require_path = 'lib'
|
13
|
+
|
14
|
+
# s.add_dependency 'bundler'
|
15
|
+
|
16
|
+
s.add_development_dependency 'racc'
|
17
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'spec_helper.rb'
|
2
|
+
|
3
|
+
class Int
|
4
|
+
attr_accessor :name, :mtu, :description, :vrf
|
5
|
+
def initialize
|
6
|
+
@name = 'xe-1/2/3'
|
7
|
+
@mtu = 1500
|
8
|
+
@description = 'K: S-12345 foobar \'quote\' xyzzy'
|
9
|
+
@vrf = 'VRF0001'
|
10
|
+
end
|
11
|
+
def get_binding; binding; end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe Compiler do
|
15
|
+
#it "can be created with string argument" do
|
16
|
+
# Lexer.new('k').must_be_instance_of Lexer
|
17
|
+
#end
|
18
|
+
|
19
|
+
#it "can't be created without an argument" do
|
20
|
+
# ->{Lexer.new}.must_raise ArgumentError
|
21
|
+
#end
|
22
|
+
|
23
|
+
it "must pass junos example" do
|
24
|
+
int = Int.new
|
25
|
+
str = Malline.run TemplateJunOS, int.get_binding
|
26
|
+
str.must_equal ResultJunOS[0]
|
27
|
+
int.description = nil
|
28
|
+
int.mtu = 42
|
29
|
+
str = Malline.run TemplateJunOS, int.get_binding
|
30
|
+
str.must_equal ResultJunOS[1]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "must pass ios example" do
|
34
|
+
int = Int.new
|
35
|
+
int.name = 'GigabitEthernet0/42'
|
36
|
+
str = Malline.run TemplateIOS, int.get_binding
|
37
|
+
str.must_equal ResultIOS[0]
|
38
|
+
int.description = 'f00fc7c8'
|
39
|
+
int.mtu = nil
|
40
|
+
int.vrf = 'xyzzy'
|
41
|
+
str = Malline.run TemplateIOS, int.get_binding
|
42
|
+
str.must_equal ResultIOS[1]
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'spec_helper.rb'
|
2
|
+
|
3
|
+
def get_handler string
|
4
|
+
lex = Lexer.new string
|
5
|
+
par = Parser.new lex
|
6
|
+
par.parse
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Handler do
|
10
|
+
|
11
|
+
it "can be created" do
|
12
|
+
Handler.new.must_be_instance_of Handler
|
13
|
+
end
|
14
|
+
|
15
|
+
it "must get correctly parsed data" do
|
16
|
+
# yeah bit of a cope out :)
|
17
|
+
TEST.each do |tst|
|
18
|
+
han = get_handler tst[:string]
|
19
|
+
han.parsed.must_equal tst[:parsed]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/spec/lexer_spec.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'spec_helper.rb'
|
2
|
+
describe Lexer do
|
3
|
+
it "can be created with string argument" do
|
4
|
+
Lexer.new('k').must_be_instance_of Lexer
|
5
|
+
end
|
6
|
+
|
7
|
+
it "can't be created without an argument" do
|
8
|
+
->{Lexer.new}.must_raise ArgumentError
|
9
|
+
end
|
10
|
+
|
11
|
+
it "must lex correctly" do
|
12
|
+
# yeah bit of a cope out :)
|
13
|
+
TEST.each do |tst|
|
14
|
+
lex = Lexer.new tst[:string]
|
15
|
+
ary, token = [], nil
|
16
|
+
ary << token while token = lex.next_token
|
17
|
+
ary.must_equal tst[:lex]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#line' do
|
22
|
+
it 'must not consider trailing \n as new line' do
|
23
|
+
lex = Lexer.new "foo\n"
|
24
|
+
ss = lex.instance_variable_get :@ss
|
25
|
+
ss.scan_until(/.*/m)
|
26
|
+
lex.line.must_equal 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'minitest/pride'
|
3
|
+
require_relative '../lib/malline.rb'
|
4
|
+
include Malline
|
5
|
+
|
6
|
+
class Int
|
7
|
+
attr_accessor :name, :mtu, :description, :vrf
|
8
|
+
def initialize
|
9
|
+
@name = 'xe-1/2/3'
|
10
|
+
@mtu = 1500
|
11
|
+
@description = 'K: S-12345 foobar \'quote\' xyzzy'
|
12
|
+
@vrf = 'VRF0001'
|
13
|
+
end
|
14
|
+
def get_binding; binding; end
|
15
|
+
end
|
16
|
+
|
17
|
+
TemplateJunOS = <<EOF
|
18
|
+
interfaces {
|
19
|
+
<% name %> {
|
20
|
+
mtu <%if mtu %>;
|
21
|
+
description "<%if description %>";
|
22
|
+
}
|
23
|
+
}
|
24
|
+
EOF
|
25
|
+
TemplateIOS = <<EOF
|
26
|
+
interface <% name %>
|
27
|
+
description <%if description %>
|
28
|
+
mtu <%if mtu %>
|
29
|
+
vrf forwarding <%if vrf %>
|
30
|
+
EOF
|
31
|
+
|
32
|
+
ResultJunOS = []
|
33
|
+
ResultJunOS << <<EOF
|
34
|
+
interfaces {
|
35
|
+
xe-1/2/3 {
|
36
|
+
mtu 1500;
|
37
|
+
description "K: S-12345 foobar 'quote' xyzzy";
|
38
|
+
}
|
39
|
+
}
|
40
|
+
EOF
|
41
|
+
ResultJunOS << <<EOF
|
42
|
+
interfaces {
|
43
|
+
xe-1/2/3 {
|
44
|
+
mtu 42;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
EOF
|
48
|
+
|
49
|
+
ResultIOS = []
|
50
|
+
ResultIOS << <<EOF
|
51
|
+
interface GigabitEthernet0/42
|
52
|
+
description K: S-12345 foobar 'quote' xyzzy
|
53
|
+
mtu 1500
|
54
|
+
vrf forwarding VRF0001
|
55
|
+
EOF
|
56
|
+
ResultIOS << <<EOF
|
57
|
+
interface GigabitEthernet0/42
|
58
|
+
description f00fc7c8
|
59
|
+
vrf forwarding xyzzy
|
60
|
+
EOF
|
61
|
+
|
62
|
+
|
63
|
+
TEST = [
|
64
|
+
{
|
65
|
+
:string => "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
66
|
+
:lex => [[:DATA, "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 1, 1]],
|
67
|
+
:parsed => [[:data, "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", [1, 1]]],
|
68
|
+
},
|
69
|
+
|
70
|
+
{
|
71
|
+
:string => "Lorem ipsum <%dolor sit amet%>, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
72
|
+
:lex => [[:DATA, "Lorem ipsum ", 1, 1], [:START_CODE, "<%", 1, 1], [:DATA, "dolor sit amet", 1, 1], [:END_CODE, "%>", 1, 1], [:DATA, ", consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 1, 1]],
|
73
|
+
:parsed => [[:data, "Lorem ipsum ", [1, 1]], [:code, nil, "dolor sit amet", [1, 1]], [:data, ", consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", [1, 1]]],
|
74
|
+
},
|
75
|
+
|
76
|
+
{
|
77
|
+
:string => "Lorem ipsum <%dolor\nsit amet%>, con\nsectetur <%if testing\n bar\n fooi %> adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
78
|
+
:lex => [[:DATA, "Lorem ipsum ", 1, 1], [:START_CODE, "<%", 1, 1], [:DATA, "dolor\nsit amet", 1, 2], [:END_CODE, "%>", 1, 2], [:DATA, ", con\nsectetur ", 2, 3], [:START_CODE, "<%if", 2, 3], [:CMD, "if", 2, 3], [:DATA, " testing\n bar\n fooi ", 3, 5], [:END_CODE, "%>", 3, 5], [:DATA, " adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 5, 5]],
|
79
|
+
:parsed => [[:data, "Lorem ipsum ", [1, 1]], [:code, nil, "dolor\nsit amet", [1, 2]], [:data, ", con\nsectetur ", [2, 3]], [:code, :if, " testing\n bar\n fooi ", [3, 5]], [:data, " adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", [5, 5]]],
|
80
|
+
},
|
81
|
+
|
82
|
+
]
|
data/tst.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative './lib/malline'
|
4
|
+
class Int
|
5
|
+
attr_accessor :name, :mtu, :description, :poop
|
6
|
+
def initialize
|
7
|
+
@name = 'xe-1/2/3.42'
|
8
|
+
@mtu = 1500
|
9
|
+
@description = 'K: S-123455 - poop customer'
|
10
|
+
end
|
11
|
+
def get_binding; binding; end
|
12
|
+
end
|
13
|
+
|
14
|
+
template = <<EOF
|
15
|
+
interfaces {
|
16
|
+
<% name %> {
|
17
|
+
mtu <%if mtu %>;
|
18
|
+
description "<%if description %>";
|
19
|
+
}
|
20
|
+
}
|
21
|
+
EOF
|
22
|
+
|
23
|
+
puts Malline.run template, Int.new.get_binding
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mallinna
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Saku Ytti
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: racc
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Mostly like ERB, but <%if foo%> conditional will omit whole line unless
|
28
|
+
code evaluates true
|
29
|
+
email:
|
30
|
+
- saku@ytti.fi
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- Gemfile
|
36
|
+
- README.md
|
37
|
+
- Rakefile
|
38
|
+
- lib/malline.rb
|
39
|
+
- lib/malline/compiler.rb
|
40
|
+
- lib/malline/handler.rb
|
41
|
+
- lib/malline/lexer.rb
|
42
|
+
- lib/malline/parser.rb
|
43
|
+
- lib/malline/parser.y
|
44
|
+
- mallinna.gemspec
|
45
|
+
- spec/compiler_spec.rb
|
46
|
+
- spec/handler_spec.rb
|
47
|
+
- spec/lexer_spec.rb
|
48
|
+
- spec/spec_helper.rb
|
49
|
+
- tst.rb
|
50
|
+
homepage: https://github.com/ytti/malline
|
51
|
+
licenses: []
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project: mallinna
|
69
|
+
rubygems_version: 2.0.14
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: Templating language targeting IOS/JunOS configs
|
73
|
+
test_files: []
|
74
|
+
has_rdoc:
|