rhcl 0.0.1

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.
@@ -0,0 +1,182 @@
1
+ class Rhcl::Parse
2
+ options no_result_var
3
+ rule
4
+ objectlist : objectitem
5
+ {
6
+ val[0]
7
+ }
8
+ | objectlist objectitem
9
+ {
10
+ val[0].deep_merge(val[1])
11
+ }
12
+
13
+ object: LEFTBRACE objectlist RIGHTBRACE
14
+ {
15
+ val[1]
16
+ }
17
+ | LEFTBRACE RIGHTBRACE
18
+ {
19
+ {}
20
+ }
21
+
22
+ objectitem: IDENTIFIER EQUAL number
23
+ {
24
+ {val[0] => val[2]}
25
+ }
26
+ | IDENTIFIER EQUAL BOOL
27
+ {
28
+ {val[0] => val[2]}
29
+ }
30
+ | IDENTIFIER EQUAL STRING
31
+ {
32
+ {val[0] => val[2]}
33
+ }
34
+ | IDENTIFIER EQUAL object
35
+ {
36
+ {val[0] => val[2]}
37
+ }
38
+ | IDENTIFIER EQUAL list
39
+ {
40
+ {val[0] => val[2]}
41
+ }
42
+ | block
43
+ {
44
+ val[0]
45
+ }
46
+
47
+ block: blockId object
48
+ {
49
+ {val[0] => val[1]}
50
+ }
51
+ | blockId block
52
+ {
53
+ {val[0] => val[1]}
54
+ }
55
+
56
+
57
+ blockId: IDENTIFIER
58
+ {
59
+ val[0]
60
+ }
61
+ | STRING
62
+ {
63
+ val[0]
64
+ }
65
+
66
+ list: LEFTBRACKET listitems RIGHTBRACKET
67
+ {
68
+ val[1]
69
+ }
70
+ | LEFTBRACKET RIGHTBRACKET
71
+ {
72
+ []
73
+ }
74
+
75
+ listitems: listitem
76
+ {
77
+ [val[0]]
78
+ }
79
+ | listitems COMMA listitem
80
+ {
81
+ val[0] + [val[2]]
82
+ }
83
+
84
+ listitem: number
85
+ {
86
+ val[0]
87
+ }
88
+ | STRING
89
+ {
90
+ val[0]
91
+ }
92
+
93
+ number: INTEGER
94
+ {
95
+ val[0]
96
+ }
97
+ | FLOAT
98
+ {
99
+ val[0]
100
+ }
101
+
102
+ ---- header
103
+
104
+ require 'strscan'
105
+
106
+ ---- inner
107
+
108
+ def initialize(obj)
109
+ src = obj.is_a?(IO) ? obj.read : obj.to_s
110
+ @ss = StringScanner.new(src)
111
+ end
112
+
113
+ def scan
114
+ tok = nil
115
+
116
+ until @ss.eos?
117
+ if (tok = @ss.scan /\s+/)
118
+ # nothing to do
119
+ elsif (tok = @ss.scan(/#/))
120
+ @ss.scan_until(/\n/)
121
+ elsif (tok = @ss.scan(%r|/|))
122
+ case @ss.getch
123
+ when '/'
124
+ @ss.scan_until(/(\n|\z)/)
125
+ when '*'
126
+ nested = 1
127
+
128
+ until nested.zero?
129
+ case @ss.scan_until(%r{(/\*|\*/|\z)})
130
+ when %r|/\*\z|
131
+ nested += 1
132
+ when %r|\*/\z|
133
+ nested -= 1
134
+ else
135
+ break
136
+ end
137
+ end
138
+ else
139
+ raise "comment expected, got '#{tok}'"
140
+ end
141
+ elsif (tok = @ss.scan(/-?\d+\.\d+/))
142
+ yield [:FLOAT, tok.to_f]
143
+ elsif (tok = @ss.scan(/-?\d+/))
144
+ yield [:INTEGER, tok.to_i]
145
+ elsif (tok = @ss.scan(/,/))
146
+ yield [:COMMA, tok]
147
+ elsif (tok = @ss.scan(/\=/))
148
+ yield [:EQUAL, tok]
149
+ elsif (tok = @ss.scan(/\[/))
150
+ yield [:LEFTBRACKET, tok]
151
+ elsif (tok = @ss.scan(/\]/))
152
+ yield [:RIGHTBRACKET, tok]
153
+ elsif (tok = @ss.scan(/\{/))
154
+ yield [:LEFTBRACE, tok]
155
+ elsif (tok = @ss.scan(/\}/))
156
+ yield [:RIGHTBRACE, tok]
157
+ elsif (tok = @ss.scan(/"/))
158
+ yield [:STRING, (@ss.scan_until(/("|\z)/) || '').sub(/"\z/, '')]
159
+ else
160
+ identifier = (@ss.scan_until(/(\s|\z)/) || '').sub(/\s\z/, '')
161
+ token_type = :IDENTIFIER
162
+
163
+ if ['true', 'false'].include?(identifier)
164
+ identifier = !!(identifier =~ /true/)
165
+ token_type = :BOOL
166
+ end
167
+
168
+ yield [token_type, identifier]
169
+ end
170
+ end
171
+
172
+ yield [false, '$end']
173
+ end
174
+ private :scan
175
+
176
+ def parse
177
+ yyparse self, :scan
178
+ end
179
+
180
+ def self.parse(obj)
181
+ self.new(obj).parse
182
+ end
@@ -0,0 +1,3 @@
1
+ module Rhcl
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rhcl/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rhcl'
8
+ spec.version = Rhcl::VERSION
9
+ spec.authors = ['Genki Sugawara']
10
+ spec.email = ['sgwr_dts@yahoo.co.jp']
11
+ spec.summary = %q{Pure Ruby HCL parser}
12
+ spec.description = %q{Pure Ruby HCL parser}
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'deep_merge'
22
+ spec.add_development_dependency 'bundler', '~> 1.5'
23
+ spec.add_development_dependency 'rake'
24
+ spec.add_development_dependency 'racc'
25
+ spec.add_development_dependency 'rspec', '>= 3.0.0'
26
+ end
@@ -0,0 +1,275 @@
1
+ describe Rhcl do
2
+ it 'basic' do
3
+ dumped = Rhcl.dump(
4
+ {"foo" => "bar"}
5
+ )
6
+
7
+ expect(dumped).to eq 'foo = "bar"'
8
+ end
9
+
10
+ it 'decode_policy' do
11
+ dumped = Rhcl.dump(
12
+ {"key"=>
13
+ {""=>{"policy"=>"read"},
14
+ "foo/"=>{"policy"=>"write"},
15
+ "foo/bar/"=>{"policy"=>"read"},
16
+ "foo/bar/baz"=>{"policy"=>"deny"}}}
17
+ )
18
+
19
+ expect(dumped).to eq <<-EOS.strip
20
+ key {
21
+ "" {
22
+ policy = "read"
23
+ }
24
+ "foo/" {
25
+ policy = "write"
26
+ }
27
+ "foo/bar/" {
28
+ policy = "read"
29
+ }
30
+ "foo/bar/baz" {
31
+ policy = "deny"
32
+ }
33
+ }
34
+ EOS
35
+ end
36
+
37
+ it 'decode_tf_variable' do
38
+ dumped = Rhcl.dump(
39
+ {"variable"=>
40
+ {"foo"=>{"default"=>"bar", "description"=>"bar"},
41
+ "amis"=>{"default"=>{"east"=>"foo"}}}}
42
+ )
43
+
44
+ expect(dumped).to eq <<-EOS.strip
45
+ variable {
46
+ foo {
47
+ default = "bar"
48
+ description = "bar"
49
+ }
50
+ amis {
51
+ default {
52
+ east = "foo"
53
+ }
54
+ }
55
+ }
56
+ EOS
57
+ end
58
+
59
+ it 'empty' do
60
+ dumped = Rhcl.dump(
61
+ {"resource"=>{"aws_instance"=>{"db"=>{}}}}
62
+ )
63
+
64
+ expect(dumped).to eq <<-EOS.strip
65
+ resource {
66
+ aws_instance {
67
+ db {
68
+
69
+ }
70
+ }
71
+ }
72
+ EOS
73
+ end
74
+
75
+ it 'flat' do
76
+ dumped = Rhcl.dump(
77
+ {"foo"=>"bar", "Key"=>7}
78
+ )
79
+
80
+ expect(dumped).to eq <<-EOS.strip
81
+ foo = "bar"
82
+ Key = 7
83
+ EOS
84
+ end
85
+
86
+ it 'structure' do
87
+ dumped = Rhcl.dump(
88
+ {"foo"=>{"baz"=>{"key"=>7, "foo"=>"bar"}}}
89
+ )
90
+
91
+ expect(dumped).to eq <<-EOS.strip
92
+ foo {
93
+ baz {
94
+ key = 7
95
+ foo = "bar"
96
+ }
97
+ }
98
+ EOS
99
+ end
100
+
101
+ it 'structure2' do
102
+ dumped = Rhcl.dump(
103
+ {"foo"=>{"baz"=>{"key"=>7, "foo"=>"bar"}, "key"=>7}}
104
+ )
105
+
106
+ expect(dumped).to eq <<-EOS.strip
107
+ foo {
108
+ baz {
109
+ key = 7
110
+ foo = "bar"
111
+ }
112
+ key = 7
113
+ }
114
+ EOS
115
+ end
116
+
117
+ it 'structure_flat' do
118
+ dumped = Rhcl.dump(
119
+ {"foo"=>{"baz"=>{"key"=>7, "foo"=>"bar"}}}
120
+ )
121
+
122
+ expect(dumped).to eq <<-EOS.strip
123
+ foo {
124
+ baz {
125
+ key = 7
126
+ foo = "bar"
127
+ }
128
+ }
129
+ EOS
130
+ end
131
+
132
+ it 'structure_flatmap' do
133
+ dumped = Rhcl.dump(
134
+ {"foo"=>{"key"=>7, "foo"=>"bar"}}
135
+ )
136
+
137
+ expect(dumped).to eq <<-EOS.strip
138
+ foo {
139
+ key = 7
140
+ foo = "bar"
141
+ }
142
+ EOS
143
+ end
144
+
145
+ it 'structure_multi' do
146
+ dumped = Rhcl.dump(
147
+ {"foo"=>{"baz"=>{"key"=>7}, "bar"=>{"key"=>12}}}
148
+ )
149
+
150
+ expect(dumped).to eq <<-EOS.strip
151
+ foo {
152
+ baz {
153
+ key = 7
154
+ }
155
+ bar {
156
+ key = 12
157
+ }
158
+ }
159
+ EOS
160
+ end
161
+
162
+ it 'array' do
163
+ dumped = Rhcl.dump(
164
+ {"foo"=>[1, 2, "baz"], "bar"=>"baz"}
165
+ )
166
+
167
+ expect(dumped).to eq <<-EOS.strip
168
+ foo = [1, 2, "baz"]
169
+ bar = "baz"
170
+ EOS
171
+ end
172
+
173
+ it 'object' do
174
+ dumped = Rhcl.dump(
175
+ {"foo"=>{"bar"=>[1, 2, "baz"]}}
176
+ )
177
+
178
+ expect(dumped).to eq <<-EOS.strip
179
+ foo {
180
+ bar = [1, 2, "baz"]
181
+ }
182
+ EOS
183
+ end
184
+
185
+ it 'types' do
186
+ dumped = Rhcl.dump(
187
+ {"foo"=>"bar",
188
+ "bar"=>7,
189
+ "baz"=>[1, 2, 3],
190
+ "foo2"=>-12,
191
+ "bar2"=>3.14159,
192
+ "bar3"=>-3.14159,
193
+ "hoge"=>true,
194
+ "fuga"=>false}
195
+ )
196
+
197
+ expect(dumped).to eq <<-EOS.strip
198
+ foo = "bar"
199
+ bar = 7
200
+ baz = [1, 2, 3]
201
+ foo2 = -12
202
+ bar2 = 3.14159
203
+ bar3 = -3.14159
204
+ hoge = true
205
+ fuga = false
206
+ EOS
207
+ end
208
+
209
+ it 'complex' do
210
+ dumped = Rhcl.dump(
211
+ {"variable"=>{"foo"=>{"default"=>"bar", "description"=>"bar"}},
212
+ "provider"=>
213
+ {"aws"=>{"access_key"=>"foo", "secret_key"=>"bar"},
214
+ "do"=>{"api_key"=>"${var.foo}"}},
215
+ "resource"=>
216
+ {"aws_security_group"=>{"firewall"=>{"count"=>5}},
217
+ "aws_instance"=>
218
+ {"web"=>
219
+ {"ami"=>"${var.foo}",
220
+ "security_groups"=>["foo", "${aws_security_group.firewall.foo}"],
221
+ "network_interface"=>
222
+ {"device_index"=>0, "description"=>"Main network interface"}},
223
+ "db"=>
224
+ {"security_groups"=>"${aws_security_group.firewall.*.id}",
225
+ "VPC"=>"foo",
226
+ "depends_on"=>["aws_instance.web"]}}},
227
+ "output"=>{"web_ip"=>{"value"=>"${aws_instance.web.private_ip}"}}}
228
+ )
229
+
230
+ expect(dumped).to eq <<-EOS.strip
231
+ variable {
232
+ foo {
233
+ default = "bar"
234
+ description = "bar"
235
+ }
236
+ }
237
+ provider {
238
+ aws {
239
+ access_key = "foo"
240
+ secret_key = "bar"
241
+ }
242
+ do {
243
+ api_key = "${var.foo}"
244
+ }
245
+ }
246
+ resource {
247
+ aws_security_group {
248
+ firewall {
249
+ count = 5
250
+ }
251
+ }
252
+ aws_instance {
253
+ web {
254
+ ami = "${var.foo}"
255
+ security_groups = ["foo", "${aws_security_group.firewall.foo}"]
256
+ network_interface {
257
+ device_index = 0
258
+ description = "Main network interface"
259
+ }
260
+ }
261
+ db {
262
+ security_groups = "${aws_security_group.firewall.*.id}"
263
+ VPC = "foo"
264
+ depends_on = ["aws_instance.web"]
265
+ }
266
+ }
267
+ }
268
+ output {
269
+ web_ip {
270
+ value = "${aws_instance.web.private_ip}"
271
+ }
272
+ }
273
+ EOS
274
+ end
275
+ end