hcl-checker 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1e9ca69634e7dd4731e80da15c9fd2e8280e66c8
4
+ data.tar.gz: c81ab5286cdf5f7ddf84476452943521cecf51fb
5
+ SHA512:
6
+ metadata.gz: 6330733fbb5f204069c258172ef11b8ba92bccf88428b92166bdc0cae0d382f9a6752f237dc7cc2e2499368511a73d38503434ff96598f1e685113ea519dcf0c
7
+ data.tar.gz: 94ef46e376226e8984a092038777b9a36561ddd73a00ac44fa430bedf866c936ad53e0afa1663654246bc06e4736023490d26d70f3ff607dcfdbdc6491a792e8
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.2
5
+ before_install: gem install bundler -v 1.16.1
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at marcelo.castellani@totvs.com.br. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in hcl-checker.gemspec
6
+ gemspec
@@ -0,0 +1,35 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hcl-checker (1.0.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.3)
10
+ rake (10.4.2)
11
+ rspec (3.7.0)
12
+ rspec-core (~> 3.7.0)
13
+ rspec-expectations (~> 3.7.0)
14
+ rspec-mocks (~> 3.7.0)
15
+ rspec-core (3.7.1)
16
+ rspec-support (~> 3.7.0)
17
+ rspec-expectations (3.7.0)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.7.0)
20
+ rspec-mocks (3.7.0)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.7.0)
23
+ rspec-support (3.7.0)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ bundler (~> 1.16)
30
+ hcl-checker!
31
+ rake (~> 10.0)
32
+ rspec (~> 3.0)
33
+
34
+ BUNDLED WITH
35
+ 1.16.1
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Marcelo Castellani
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,90 @@
1
+ # Hcl::Checker
2
+
3
+ **Hashicorp Configuration Language** syntax checker and parser.
4
+
5
+ Parser originally created by [Sikula](https://github.com/sikula) and available
6
+ at [Ruby HCL Repository](https://github.com/sikula/ruby-hcl). Only works with
7
+ [HCL Version 1](https://github.com/hashicorp/hcl).
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'hcl-checker'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install hcl-checker
24
+
25
+ ## Usage
26
+
27
+ Load HCL string:
28
+
29
+ ```
30
+ hcl_string = 'provider "aws" {
31
+ region = "${var.aws_region}"
32
+ access_key = "${var.aws_access_key}"
33
+ secret_key = "${var.aws_secret_key}"
34
+ }
35
+
36
+ resource "aws_vpc" "default" {
37
+ cidr_block = "10.0.0.0/16"
38
+ enable_dns_hostnames = true
39
+ tags {
40
+ Name = "Event Store VPC"
41
+ }
42
+ }'
43
+ ```
44
+
45
+ You can validate the `hcl_string` contents with `valid?` method. This will
46
+ return `true` if is a valid HCL or `false` if not.
47
+
48
+ ```
49
+ 2.3.2 :014 > HCL::Checker.valid? hcl_string
50
+ => true
51
+ ```
52
+
53
+ You can parse the `hcl_string` into a `Hash` with `parse` method.
54
+
55
+ ```
56
+ 2.3.2 :015 > HCL::Checker.parse(hcl_string)
57
+ => {"provider"=>{"aws"=>{"region"=>"${var.aws_region}", "access_key"=>"${var.aws_access_key}", "secret_key"=>"${var.aws_secret_key}"}}, "resource"=>{"aws_vpc"=>{"default"=>{"cidr_block"=>"10.0.0.0/16", "enable_dns_hostnames"=>true, "tags"=>{"Name"=>"Event Store VPC"}}}}}
58
+ ```
59
+
60
+ ## Development
61
+
62
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
63
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
64
+ prompt that will allow you to experiment.
65
+
66
+ To install this gem onto your local machine, run `bundle exec rake install`. To
67
+ release a new version, update the version number in `version.rb`, and then run
68
+ `bundle exec rake release`, which will create a git tag for the version, push
69
+ git commits and tags, and push the `.gem` file to
70
+ [rubygems.org](https://rubygems.org).
71
+
72
+ ### Lexer
73
+ - Built using the `rexical` library (https://github.com/tenderlove/rexical)
74
+
75
+ ### Parser
76
+ - Built using the `racc` library (https://github.com/tenderlove/racc)
77
+
78
+ ## Contributing
79
+
80
+ Bug reports and pull requests are welcome on GitHub at
81
+ https://github.com/mfcastellani/hcl-checker. This project is intended to be a
82
+ safe, welcoming space for collaboration, and contributors are expected to adhere
83
+ to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
84
+
85
+ ## License
86
+
87
+ The gem is available as open source under the terms of the [MIT
88
+ License](https://opensource.org/licenses/MIT).
89
+
90
+
@@ -0,0 +1,18 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new do |c|
5
+ options = ["--color"]
6
+ options += ["--format", "documentation"]
7
+ c.rspec_opts = options
8
+ end
9
+
10
+ desc "Generate HCL Lexer"
11
+ task :lexer do
12
+ `rex ./assets/lexer.rex -o ./lib/hcl/lexer.rb`
13
+ end
14
+
15
+ desc "Generate HCL Parser"
16
+ task :parser do
17
+ `racc ./assets/parse.y -o ./lib/parser.rb`
18
+ end
@@ -0,0 +1,109 @@
1
+
2
+ class HCLLexer
3
+ option
4
+ independent
5
+
6
+ macro
7
+ NEWLINE \n|\r
8
+ BLANK \s+
9
+ COMMENT \#.*|\/\/.*$
10
+ MCOMMENTIN \/\*
11
+ BOOL true|false
12
+ NUMBER -?\d+
13
+ FLOAT \-?\d+\.\d+
14
+ COMMA \,
15
+ IDENTIFIER [a-zA-Z_][a-zA-Z0-9_\-\.]*
16
+ EQUAL \=
17
+ QUOTE \"
18
+ MINUS \-
19
+ LEFTBRACE \{
20
+ RIGHTBRACE \}
21
+ LEFTBRACKET \[
22
+ RIGHTBRACKET \]
23
+
24
+ rule
25
+ # [:state] pattern [actions]
26
+ #-------------------------------------------------------------------------------
27
+ {BLANK} # ignore pattern
28
+ {COMMENT} # ignore pattern
29
+ {NEWLINE} # ignore pattern
30
+ #-------------------------------------------------------------------------------
31
+ {MCOMMENTIN} { consume_comment(text) }
32
+ {BOOL} { [:BOOL, to_boolean(text)]}
33
+ {NUMBER} { [:NUMBER, text.to_i] }
34
+ {FLOAT} { [:FLOAT, text.to_f] }
35
+ {QUOTE} { [:STRING, consume_string(text)] }
36
+ #-------------------------------------------------------------------------------
37
+ {LEFTBRACE} { [:LEFTBRACE, text]}
38
+ {RIGHTBRACE} { [:RIGHTBRACE, text]}
39
+ {LEFTBRACKET} { [:LEFTBRACKET, text]}
40
+ {RIGHTBRACKET} { [:RIGHTBRACKET, text]}
41
+ #-------------------------------------------------------------------------------
42
+ {COMMA} { [:COMMA, text]}
43
+ {IDENTIFIER} { [:IDENTIFIER, text]}
44
+ {EQUAL} { [:EQUAL, text]}
45
+ {MINUS} { [:MINUS, text]}
46
+
47
+
48
+ inner
49
+
50
+ def lex(input)
51
+ scan_setup(input)
52
+ tokens = []
53
+ while token = next_token
54
+ tokens << token
55
+ end
56
+ tokens
57
+ end
58
+
59
+
60
+ def to_boolean(input)
61
+ input =
62
+ if input =~ /true/
63
+ true
64
+ elsif input =~ /false/
65
+ false
66
+ end
67
+ return input
68
+ end
69
+
70
+
71
+ def consume_comment(input)
72
+ nested = 1
73
+
74
+ until nested.zero?
75
+ case(text = @ss.scan_until(%r{/\*|\*/|\z}) )
76
+ when %r{/\*\z}
77
+ nested =+ 1
78
+ when %r{\*/\z}
79
+ nested -= 1
80
+ else
81
+ break
82
+ end
83
+ end
84
+ end
85
+
86
+
87
+ def consume_string(input)
88
+ result = ''
89
+ nested = 0
90
+
91
+ begin
92
+ case(text = @ss.scan_until(%r{\"|\$\{|\}|\\}))
93
+ when %r{\$\{\z}
94
+ nested += 1
95
+ when %r{\}\z}
96
+ nested -= 1
97
+ when %r{\\\z}
98
+ result += text.chop + @ss.getch
99
+ next
100
+ end
101
+
102
+ result += text
103
+ end until nested == 0 && text =~ %r{\"\z}
104
+
105
+ result.chop
106
+ end
107
+
108
+
109
+ end
@@ -0,0 +1,167 @@
1
+ class HCLParser
2
+ token BOOL
3
+ FLOAT
4
+ NUMBER
5
+ COMMA
6
+ COMMAEND
7
+ IDENTIFIER
8
+ EQUAL
9
+ STRING
10
+ MINUS
11
+ LEFTBRACE
12
+ RIGHTBRACE
13
+ LEFTBRACKET
14
+ RIGHTBRACKET
15
+ PERIOD
16
+ EPLUS
17
+ EMINUS
18
+
19
+ rule
20
+ target: top { @result = flatten_objectlist(val[0])}
21
+
22
+ top:
23
+ { result = val[0] }
24
+ | objectlist
25
+ ;
26
+
27
+
28
+ objectlist:
29
+ objectitem
30
+ { result = [val[0]] }
31
+ | objectlist objectitem
32
+ { result = val[0] << val[1] }
33
+ ;
34
+
35
+ object:
36
+ LEFTBRACE objectlist RIGHTBRACE
37
+ { result = flatten_objectlist(val[1]) }
38
+ | LEFTBRACE RIGHTBRACE
39
+ { return }
40
+ ;
41
+
42
+ objectkey:
43
+ IDENTIFIER
44
+ { result = val[0] }
45
+ | STRING
46
+ { result = val[0] }
47
+ ;
48
+
49
+ objectitem:
50
+ objectkey EQUAL number
51
+ { result = val[0], val[2] }
52
+ | objectkey EQUAL BOOL
53
+ { result = val[0], val[2] }
54
+ | objectkey EQUAL STRING
55
+ { result = val[0], val[2] }
56
+ | objectkey EQUAL object
57
+ { result = val[0], val[2] }
58
+ | objectkey EQUAL list
59
+ { result = val[0], val[2] }
60
+ | block
61
+ { result = val[0] }
62
+ ;
63
+
64
+ block:
65
+ block_id object
66
+ { result = val[0], val[1] }
67
+ | block_id block
68
+ { result = val[0], {val[1][0] => val[1][1]} }
69
+ ;
70
+
71
+ block_id:
72
+ IDENTIFIER
73
+ { result = val[0] }
74
+ | STRING
75
+ { result = val[0] }
76
+ ;
77
+
78
+ list:
79
+ LEFTBRACKET listitems RIGHTBRACKET
80
+ { result = val[1] }
81
+ | LEFTBRACKET RIGHTBRACKET
82
+ { return }
83
+ ;
84
+
85
+ listitems:
86
+ listitem
87
+ { result = [val[0]] }
88
+ | listitems COMMA listitem
89
+ { result = val[0] << val[2] }
90
+ | listitems COMMA
91
+ { result = val[0] }
92
+ ;
93
+
94
+ listitem:
95
+ number
96
+ { result = val[0] }
97
+ | STRING
98
+ { result = val[0] }
99
+ ;
100
+
101
+ number:
102
+ NUMBER
103
+ { result = val[0] }
104
+ | FLOAT
105
+ { result = val[0] }
106
+ ;
107
+
108
+
109
+ end
110
+
111
+ ---- header
112
+ require_relative './lexer'
113
+
114
+ ---- inner
115
+ #//
116
+ #// HCL is unclear on what one should do when duplicate
117
+ #// keys are encountered.
118
+ #//
119
+ #// from decoder.go: if we're at the root or we're directly within
120
+ #// a list, decode to hashes, otherwise lists
121
+ #//
122
+ #// from object.go: there is a flattened list structure
123
+ #//
124
+ def flatten_objectlist(list)
125
+ list.each_with_object({}) do |a, h|
126
+ h[a.first] =
127
+ case a.last
128
+ when Hash
129
+ deep_merge(h[a.first] || {}, a.last)
130
+ else
131
+ h[a.first] = a.last
132
+ end
133
+ end
134
+ end
135
+
136
+
137
+ def on_error(error_token_id, error_value, value_stack)
138
+ header = "parse error found on value: #{error_value}"
139
+ raise Racc::ParseError, header
140
+ end
141
+
142
+
143
+ def parse(input)
144
+ @lexer = HCLLexer.new.lex(input)
145
+ do_parse
146
+ return @result
147
+ end
148
+
149
+
150
+ def next_token
151
+ @lexer.shift
152
+ end
153
+
154
+ def deep_merge(hash1, hash2)
155
+ hash2.keys.each do |key|
156
+ value1 = hash1[key]
157
+ value2 = hash2[key]
158
+
159
+ if value1.is_a?(Hash) && value2.is_a?(Hash)
160
+ hash1[key] = deep_merge(value1, value2)
161
+ else
162
+ hash1[key] = value2
163
+ end
164
+ end
165
+
166
+ hash1
167
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "hcl/checker"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,24 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'hcl/checker/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'hcl-checker'
7
+ spec.version = HCL::Checker::VERSION
8
+ spec.authors = ['Marcelo Castellani']
9
+ spec.email = ['marcelo.castellani@totvs.com.br']
10
+
11
+ spec.summary = 'Hashicorp Configuration Language parser for Ruby'
12
+ spec.description = 'This gem cam parse HCL version 1 and checks if syntax is ok.'
13
+ spec.homepage = 'https://github.com/mfcastellani/hcl-checker'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.16'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rspec', '~> 3.0'
24
+ end
@@ -0,0 +1,21 @@
1
+ require 'hcl/checker/version'
2
+ require_relative 'lexer'
3
+ require_relative 'parser'
4
+
5
+ module HCL
6
+ module Checker
7
+ def self.valid?(value)
8
+ ret = HCLParser.new.parse(value)
9
+ return true if ret.is_a? Hash
10
+ false
11
+ rescue
12
+ false
13
+ end
14
+
15
+ def self.parse(value)
16
+ HCLParser.new.parse(value)
17
+ rescue
18
+ nil
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ module HCL
2
+ module Checker
3
+ VERSION = '1.0.0'
4
+ end
5
+ end
@@ -0,0 +1,166 @@
1
+ #--
2
+ # DO NOT MODIFY!!!!
3
+ # This file is automatically generated by rex 1.0.5
4
+ # from lexical definition file "./assets/lexer.rex".
5
+ #++
6
+
7
+
8
+ class HCLLexer
9
+ require 'strscan'
10
+
11
+ class ScanError < StandardError ; end
12
+
13
+ attr_reader :lineno
14
+ attr_reader :filename
15
+ attr_accessor :state
16
+
17
+ def scan_setup(str)
18
+ @ss = StringScanner.new(str)
19
+ @lineno = 1
20
+ @state = nil
21
+ end
22
+
23
+ def action
24
+ yield
25
+ end
26
+
27
+ def scan_str(str)
28
+ scan_setup(str)
29
+ do_parse
30
+ end
31
+ alias :scan :scan_str
32
+
33
+ def load_file( filename )
34
+ @filename = filename
35
+ open(filename, "r") do |f|
36
+ scan_setup(f.read)
37
+ end
38
+ end
39
+
40
+ def scan_file( filename )
41
+ load_file(filename)
42
+ do_parse
43
+ end
44
+
45
+
46
+ def next_token
47
+ return if @ss.eos?
48
+
49
+ # skips empty actions
50
+ until token = _next_token or @ss.eos?; end
51
+ token
52
+ end
53
+
54
+ def _next_token
55
+ text = @ss.peek(1)
56
+ @lineno += 1 if text == "\n"
57
+ token = case @state
58
+ when nil
59
+ case
60
+ when (text = @ss.scan(/\s+/))
61
+ ;
62
+
63
+ when (text = @ss.scan(/\#.*|\/\/.*$/))
64
+ ;
65
+
66
+ when (text = @ss.scan(/\n|\r/))
67
+ ;
68
+
69
+ when (text = @ss.scan(/\/\*/))
70
+ action { consume_comment(text) }
71
+
72
+ when (text = @ss.scan(/true|false/))
73
+ action { [:BOOL, to_boolean(text)]}
74
+
75
+ when (text = @ss.scan(/-?\d+/))
76
+ action { [:NUMBER, text.to_i] }
77
+
78
+ when (text = @ss.scan(/\-?\d+\.\d+/))
79
+ action { [:FLOAT, text.to_f] }
80
+
81
+ when (text = @ss.scan(/\"/))
82
+ action { [:STRING, consume_string(text)] }
83
+
84
+ when (text = @ss.scan(/\{/))
85
+ action { [:LEFTBRACE, text]}
86
+
87
+ when (text = @ss.scan(/\}/))
88
+ action { [:RIGHTBRACE, text]}
89
+
90
+ when (text = @ss.scan(/\[/))
91
+ action { [:LEFTBRACKET, text]}
92
+
93
+ when (text = @ss.scan(/\]/))
94
+ action { [:RIGHTBRACKET, text]}
95
+
96
+ when (text = @ss.scan(/\,/))
97
+ action { [:COMMA, text]}
98
+
99
+ when (text = @ss.scan(/[a-zA-Z_][a-zA-Z0-9_\-\.]*/))
100
+ action { [:IDENTIFIER, text]}
101
+
102
+ when (text = @ss.scan(/\=/))
103
+ action { [:EQUAL, text]}
104
+
105
+ when (text = @ss.scan(/\-/))
106
+ action { [:MINUS, text]}
107
+
108
+ else
109
+ text = @ss.string[@ss.pos .. -1]
110
+ raise ScanError, "can not match: '" + text + "'"
111
+ end # if
112
+
113
+ else
114
+ raise ScanError, "undefined state: '" + state.to_s + "'"
115
+ end # case state
116
+ token
117
+ end # def _next_token
118
+
119
+ def lex(input)
120
+ scan_setup(input)
121
+ tokens = []
122
+ while token = next_token
123
+ tokens << token
124
+ end
125
+ tokens
126
+ end
127
+ def to_boolean(input)
128
+ input =
129
+ if input =~ /true/
130
+ true
131
+ elsif input =~ /false/
132
+ false
133
+ end
134
+ return input
135
+ end
136
+ def consume_comment(input)
137
+ nested = 1
138
+ until nested.zero?
139
+ case(text = @ss.scan_until(%r{/\*|\*/|\z}) )
140
+ when %r{/\*\z}
141
+ nested =+ 1
142
+ when %r{\*/\z}
143
+ nested -= 1
144
+ else
145
+ break
146
+ end
147
+ end
148
+ end
149
+ def consume_string(input)
150
+ result = ''
151
+ nested = 0
152
+ begin
153
+ case(text = @ss.scan_until(%r{\"|\$\{|\}|\\}))
154
+ when %r{\$\{\z}
155
+ nested += 1
156
+ when %r{\}\z}
157
+ nested -= 1
158
+ when %r{\\\z}
159
+ result += text.chop + @ss.getch
160
+ next
161
+ end
162
+ result += text
163
+ end until nested == 0 && text =~ %r{\"\z}
164
+ result.chop
165
+ end
166
+ end # class
@@ -0,0 +1,421 @@
1
+ #
2
+ # DO NOT MODIFY!!!!
3
+ # This file is automatically generated by Racc 1.4.12
4
+ # from Racc grammer file "".
5
+ #
6
+
7
+ require 'racc/parser.rb'
8
+
9
+ require_relative './lexer'
10
+
11
+ class HCLParser < Racc::Parser
12
+
13
+ module_eval(<<'...end parse.y/module_eval...', 'parse.y', 115)
14
+ #//
15
+ #// HCL is unclear on what one should do when duplicate
16
+ #// keys are encountered.
17
+ #//
18
+ #// from decoder.go: if we're at the root or we're directly within
19
+ #// a list, decode to hashes, otherwise lists
20
+ #//
21
+ #// from object.go: there is a flattened list structure
22
+ #//
23
+ def flatten_objectlist(list)
24
+ list.each_with_object({}) do |a, h|
25
+ h[a.first] =
26
+ case a.last
27
+ when Hash
28
+ deep_merge(h[a.first] || {}, a.last)
29
+ else
30
+ h[a.first] = a.last
31
+ end
32
+ end
33
+ end
34
+
35
+
36
+ def on_error(error_token_id, error_value, value_stack)
37
+ header = "parse error found on value: #{error_value}"
38
+ raise Racc::ParseError, header
39
+ end
40
+
41
+
42
+ def parse(input)
43
+ @lexer = HCLLexer.new.lex(input)
44
+ do_parse
45
+ return @result
46
+ end
47
+
48
+
49
+ def next_token
50
+ @lexer.shift
51
+ end
52
+
53
+ def deep_merge(hash1, hash2)
54
+ hash2.keys.each do |key|
55
+ value1 = hash1[key]
56
+ value2 = hash2[key]
57
+
58
+ if value1.is_a?(Hash) && value2.is_a?(Hash)
59
+ hash1[key] = deep_merge(value1, value2)
60
+ else
61
+ hash1[key] = value2
62
+ end
63
+ end
64
+
65
+ hash1
66
+ end
67
+ ...end parse.y/module_eval...
68
+ ##### State transition tables begin ###
69
+
70
+ racc_action_table = [
71
+ 20, 26, 25, 36, 26, 25, 5, 21, 6, 13,
72
+ 33, 24, 35, 26, 25, 30, 5, 12, 6, 33,
73
+ 5, 34, 6, -9, 16, 28, 17, 5, 13, 6,
74
+ -8, 10, 18 ]
75
+
76
+ racc_action_check = [
77
+ 12, 12, 12, 29, 24, 24, 3, 12, 3, 12,
78
+ 24, 12, 29, 36, 36, 24, 27, 7, 27, 36,
79
+ 13, 27, 13, 6, 9, 13, 9, 0, 9, 0,
80
+ 5, 1, 10 ]
81
+
82
+ racc_action_pointer = [
83
+ 20, 31, nil, -1, nil, 22, 15, 9, nil, 17,
84
+ 32, nil, -2, 13, nil, nil, nil, nil, nil, nil,
85
+ nil, nil, nil, nil, 1, nil, nil, 9, nil, -2,
86
+ nil, nil, nil, nil, nil, nil, 10, nil ]
87
+
88
+ racc_action_default = [
89
+ -2, -29, -1, -3, -4, -18, -19, -29, -15, -29,
90
+ -29, -5, -29, -29, -16, -17, -18, -19, 38, -10,
91
+ -11, -12, -13, -14, -29, -27, -28, -29, -7, -29,
92
+ -21, -22, -25, -26, -6, -20, -24, -23 ]
93
+
94
+ racc_goto_table = [
95
+ 11, 3, 14, 31, 19, 22, 2, 23, 15, 29,
96
+ 1, nil, nil, nil, 27, 37, nil, nil, nil, nil,
97
+ nil, nil, nil, nil, 11 ]
98
+
99
+ racc_goto_check = [
100
+ 4, 3, 5, 12, 7, 5, 2, 8, 9, 11,
101
+ 1, nil, nil, nil, 3, 12, nil, nil, nil, nil,
102
+ nil, nil, nil, nil, 4 ]
103
+
104
+ racc_goto_pointer = [
105
+ nil, 10, 6, 1, -3, -7, nil, -8, -5, -1,
106
+ nil, -15, -21 ]
107
+
108
+ racc_goto_default = [
109
+ nil, nil, nil, nil, 4, nil, 7, 32, nil, 8,
110
+ 9, nil, nil ]
111
+
112
+ racc_reduce_table = [
113
+ 0, 0, :racc_error,
114
+ 1, 19, :_reduce_1,
115
+ 0, 20, :_reduce_2,
116
+ 1, 20, :_reduce_none,
117
+ 1, 21, :_reduce_4,
118
+ 2, 21, :_reduce_5,
119
+ 3, 23, :_reduce_6,
120
+ 2, 23, :_reduce_7,
121
+ 1, 24, :_reduce_8,
122
+ 1, 24, :_reduce_9,
123
+ 3, 22, :_reduce_10,
124
+ 3, 22, :_reduce_11,
125
+ 3, 22, :_reduce_12,
126
+ 3, 22, :_reduce_13,
127
+ 3, 22, :_reduce_14,
128
+ 1, 22, :_reduce_15,
129
+ 2, 27, :_reduce_16,
130
+ 2, 27, :_reduce_17,
131
+ 1, 28, :_reduce_18,
132
+ 1, 28, :_reduce_19,
133
+ 3, 26, :_reduce_20,
134
+ 2, 26, :_reduce_21,
135
+ 1, 29, :_reduce_22,
136
+ 3, 29, :_reduce_23,
137
+ 2, 29, :_reduce_24,
138
+ 1, 30, :_reduce_25,
139
+ 1, 30, :_reduce_26,
140
+ 1, 25, :_reduce_27,
141
+ 1, 25, :_reduce_28 ]
142
+
143
+ racc_reduce_n = 29
144
+
145
+ racc_shift_n = 38
146
+
147
+ racc_token_table = {
148
+ false => 0,
149
+ :error => 1,
150
+ :BOOL => 2,
151
+ :FLOAT => 3,
152
+ :NUMBER => 4,
153
+ :COMMA => 5,
154
+ :COMMAEND => 6,
155
+ :IDENTIFIER => 7,
156
+ :EQUAL => 8,
157
+ :STRING => 9,
158
+ :MINUS => 10,
159
+ :LEFTBRACE => 11,
160
+ :RIGHTBRACE => 12,
161
+ :LEFTBRACKET => 13,
162
+ :RIGHTBRACKET => 14,
163
+ :PERIOD => 15,
164
+ :EPLUS => 16,
165
+ :EMINUS => 17 }
166
+
167
+ racc_nt_base = 18
168
+
169
+ racc_use_result_var = true
170
+
171
+ Racc_arg = [
172
+ racc_action_table,
173
+ racc_action_check,
174
+ racc_action_default,
175
+ racc_action_pointer,
176
+ racc_goto_table,
177
+ racc_goto_check,
178
+ racc_goto_default,
179
+ racc_goto_pointer,
180
+ racc_nt_base,
181
+ racc_reduce_table,
182
+ racc_token_table,
183
+ racc_shift_n,
184
+ racc_reduce_n,
185
+ racc_use_result_var ]
186
+
187
+ Racc_token_to_s_table = [
188
+ "$end",
189
+ "error",
190
+ "BOOL",
191
+ "FLOAT",
192
+ "NUMBER",
193
+ "COMMA",
194
+ "COMMAEND",
195
+ "IDENTIFIER",
196
+ "EQUAL",
197
+ "STRING",
198
+ "MINUS",
199
+ "LEFTBRACE",
200
+ "RIGHTBRACE",
201
+ "LEFTBRACKET",
202
+ "RIGHTBRACKET",
203
+ "PERIOD",
204
+ "EPLUS",
205
+ "EMINUS",
206
+ "$start",
207
+ "target",
208
+ "top",
209
+ "objectlist",
210
+ "objectitem",
211
+ "object",
212
+ "objectkey",
213
+ "number",
214
+ "list",
215
+ "block",
216
+ "block_id",
217
+ "listitems",
218
+ "listitem" ]
219
+
220
+ Racc_debug_parser = false
221
+
222
+ ##### State transition tables end #####
223
+
224
+ # reduce 0 omitted
225
+
226
+ module_eval(<<'.,.,', 'parse.y', 19)
227
+ def _reduce_1(val, _values, result)
228
+ @result = flatten_objectlist(val[0])
229
+ result
230
+ end
231
+ .,.,
232
+
233
+ module_eval(<<'.,.,', 'parse.y', 22)
234
+ def _reduce_2(val, _values, result)
235
+ result = val[0]
236
+ result
237
+ end
238
+ .,.,
239
+
240
+ # reduce 3 omitted
241
+
242
+ module_eval(<<'.,.,', 'parse.y', 29)
243
+ def _reduce_4(val, _values, result)
244
+ result = [val[0]]
245
+ result
246
+ end
247
+ .,.,
248
+
249
+ module_eval(<<'.,.,', 'parse.y', 31)
250
+ def _reduce_5(val, _values, result)
251
+ result = val[0] << val[1]
252
+ result
253
+ end
254
+ .,.,
255
+
256
+ module_eval(<<'.,.,', 'parse.y', 36)
257
+ def _reduce_6(val, _values, result)
258
+ result = flatten_objectlist(val[1])
259
+ result
260
+ end
261
+ .,.,
262
+
263
+ module_eval(<<'.,.,', 'parse.y', 38)
264
+ def _reduce_7(val, _values, result)
265
+ return
266
+ result
267
+ end
268
+ .,.,
269
+
270
+ module_eval(<<'.,.,', 'parse.y', 43)
271
+ def _reduce_8(val, _values, result)
272
+ result = val[0]
273
+ result
274
+ end
275
+ .,.,
276
+
277
+ module_eval(<<'.,.,', 'parse.y', 45)
278
+ def _reduce_9(val, _values, result)
279
+ result = val[0]
280
+ result
281
+ end
282
+ .,.,
283
+
284
+ module_eval(<<'.,.,', 'parse.y', 50)
285
+ def _reduce_10(val, _values, result)
286
+ result = val[0], val[2]
287
+ result
288
+ end
289
+ .,.,
290
+
291
+ module_eval(<<'.,.,', 'parse.y', 52)
292
+ def _reduce_11(val, _values, result)
293
+ result = val[0], val[2]
294
+ result
295
+ end
296
+ .,.,
297
+
298
+ module_eval(<<'.,.,', 'parse.y', 54)
299
+ def _reduce_12(val, _values, result)
300
+ result = val[0], val[2]
301
+ result
302
+ end
303
+ .,.,
304
+
305
+ module_eval(<<'.,.,', 'parse.y', 56)
306
+ def _reduce_13(val, _values, result)
307
+ result = val[0], val[2]
308
+ result
309
+ end
310
+ .,.,
311
+
312
+ module_eval(<<'.,.,', 'parse.y', 58)
313
+ def _reduce_14(val, _values, result)
314
+ result = val[0], val[2]
315
+ result
316
+ end
317
+ .,.,
318
+
319
+ module_eval(<<'.,.,', 'parse.y', 60)
320
+ def _reduce_15(val, _values, result)
321
+ result = val[0]
322
+ result
323
+ end
324
+ .,.,
325
+
326
+ module_eval(<<'.,.,', 'parse.y', 65)
327
+ def _reduce_16(val, _values, result)
328
+ result = val[0], val[1]
329
+ result
330
+ end
331
+ .,.,
332
+
333
+ module_eval(<<'.,.,', 'parse.y', 67)
334
+ def _reduce_17(val, _values, result)
335
+ result = val[0], {val[1][0] => val[1][1]}
336
+ result
337
+ end
338
+ .,.,
339
+
340
+ module_eval(<<'.,.,', 'parse.y', 72)
341
+ def _reduce_18(val, _values, result)
342
+ result = val[0]
343
+ result
344
+ end
345
+ .,.,
346
+
347
+ module_eval(<<'.,.,', 'parse.y', 74)
348
+ def _reduce_19(val, _values, result)
349
+ result = val[0]
350
+ result
351
+ end
352
+ .,.,
353
+
354
+ module_eval(<<'.,.,', 'parse.y', 79)
355
+ def _reduce_20(val, _values, result)
356
+ result = val[1]
357
+ result
358
+ end
359
+ .,.,
360
+
361
+ module_eval(<<'.,.,', 'parse.y', 81)
362
+ def _reduce_21(val, _values, result)
363
+ return
364
+ result
365
+ end
366
+ .,.,
367
+
368
+ module_eval(<<'.,.,', 'parse.y', 86)
369
+ def _reduce_22(val, _values, result)
370
+ result = [val[0]]
371
+ result
372
+ end
373
+ .,.,
374
+
375
+ module_eval(<<'.,.,', 'parse.y', 88)
376
+ def _reduce_23(val, _values, result)
377
+ result = val[0] << val[2]
378
+ result
379
+ end
380
+ .,.,
381
+
382
+ module_eval(<<'.,.,', 'parse.y', 90)
383
+ def _reduce_24(val, _values, result)
384
+ result = val[0]
385
+ result
386
+ end
387
+ .,.,
388
+
389
+ module_eval(<<'.,.,', 'parse.y', 95)
390
+ def _reduce_25(val, _values, result)
391
+ result = val[0]
392
+ result
393
+ end
394
+ .,.,
395
+
396
+ module_eval(<<'.,.,', 'parse.y', 97)
397
+ def _reduce_26(val, _values, result)
398
+ result = val[0]
399
+ result
400
+ end
401
+ .,.,
402
+
403
+ module_eval(<<'.,.,', 'parse.y', 102)
404
+ def _reduce_27(val, _values, result)
405
+ result = val[0]
406
+ result
407
+ end
408
+ .,.,
409
+
410
+ module_eval(<<'.,.,', 'parse.y', 104)
411
+ def _reduce_28(val, _values, result)
412
+ result = val[0]
413
+ result
414
+ end
415
+ .,.,
416
+
417
+ def _reduce_none(val, _values, result)
418
+ val[0]
419
+ end
420
+
421
+ end # class HCLParser
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hcl-checker
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Marcelo Castellani
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: This gem cam parse HCL version 1 and checks if syntax is ok.
56
+ email:
57
+ - marcelo.castellani@totvs.com.br
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - CODE_OF_CONDUCT.md
66
+ - Gemfile
67
+ - Gemfile.lock
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - assets/lexer.rex
72
+ - assets/parse.y
73
+ - bin/console
74
+ - bin/setup
75
+ - hcl-checker.gemspec
76
+ - lib/hcl/checker.rb
77
+ - lib/hcl/checker/version.rb
78
+ - lib/hcl/lexer.rb
79
+ - lib/hcl/parser.rb
80
+ homepage: https://github.com/mfcastellani/hcl-checker
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.6.14
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Hashicorp Configuration Language parser for Ruby
104
+ test_files: []