Commit 656a431

mo khan <mo@mokhan.ca>
2021-05-10 03:05:08
feat: parse array assignments
1 parent a7f4009
Changed files (2)
lib
spandx
terraform
parsers
spec
unit
terraform
lib/spandx/terraform/parsers/hcl.rb
@@ -6,39 +6,46 @@ module Spandx
       class Hcl < Parslet::Parser
         rule(:alpha) { match['a-zA-Z'] }
         rule(:assign) { str('=') }
+        rule(:comma) { str(',') }
+        rule(:comment) { (str('#') | str('//')) >> ((str("\n") >> str("\r").maybe).absent? >> any).repeat >> eol }
         rule(:crlf) { match('[\r\n]') }
         rule(:digit) { match('\d') }
         rule(:dot) { str('.') }
         rule(:eol) { whitespace? >> crlf.repeat }
+        rule(:greater_than_or_equal_to) { str('>=') }
         rule(:hyphen) { str('-') }
+        rule(:lbracket) { str('[') }
         rule(:lcurly) { str('{') }
         rule(:major) { number }
         rule(:major_minor) { (number >> dot >> number) }
         rule(:major_minor_patch) { number >> dot >> number >> dot >> number }
+        rule(:multiline_comment) { str('/*') >> (str('*/').absent? >> any).repeat >> str('*/') }
         rule(:number) { digit.repeat }
         rule(:pre_release) { hyphen >> (alpha | digit).repeat }
         rule(:pre_release?) { pre_release.maybe }
         rule(:quote) { str('"') }
+        rule(:rbracket) { str(']') }
         rule(:rcurly) { str('}') }
         rule(:space) { match('\s') }
         rule(:tilda_wacka) { str('~>') }
         rule(:version) { number >> dot >> number >> dot >> number >> pre_release? }
-        rule(:comment) { (str('#') | str('//')) >> ((str("\n") >> str("\r").maybe).absent? >> any).repeat >> eol }
-        rule(:multiline_comment) { str('/*') >> (str('*/').absent? >> any).repeat >> str('*/') }
         rule(:whitespace) { (multiline_comment | comment | space).repeat }
         rule(:whitespace?) { whitespace.maybe }
-        rule(:greater_than_or_equal_to) { str('>=') }
-
-        rule :attribute_name do
-          alpha.repeat
-        end
 
-        rule :assignment do
-          whitespace? >> attribute_name >> whitespace >> assign >> whitespace >> assign >> whitespace >> match('[0-9A-Za-z.~> ]') >> quote >> eol
+        rule(:pessimistic_version_constraint) do
+          tilda_wacka >> whitespace >> (
+            major_minor_patch |
+            major_minor |
+            major
+          )
         end
 
-        rule :value do
-          match('[0-9A-Za-z.~> ]').repeat
+        rule(:greater_than_or_equal_to_version) do
+          greater_than_or_equal_to >> whitespace >> (
+            major_minor_patch |
+            major_minor |
+            major
+          )
         end
 
         rule(:version_constraint) do
@@ -53,24 +60,20 @@ module Spandx
           str('constraints') >> whitespace >> assign >> whitespace >> quote >> version_constraint.as(:constraints) >> quote
         end
 
-        rule(:pessimistic_version_constraint) do
-          tilda_wacka >> whitespace >> (
-            major_minor_patch |
-            major_minor |
-            major
-          )
+        rule :string do
+          quote >> match('[0-9A-Za-z.~> :=/]').repeat.as(:value) >> quote
         end
 
-        rule(:greater_than_or_equal_to_version) do
-          greater_than_or_equal_to >> whitespace >> (
-            major_minor_patch |
-            major_minor |
-            major
-          )
+        rule :array_item do
+          whitespace >> string >> comma >> eol
+        end
+
+        rule :array do
+          lbracket >> eol >> array_item.repeat >> rbracket
         end
 
         rule :argument do
-          alpha.repeat.as(:name) >> whitespace >> assign >> whitespace >> quote >> value.as(:value) >> quote
+          alpha.repeat.as(:name) >> whitespace >> assign >> whitespace >> (array.as(:values) | string)
         end
 
         rule :arguments do
spec/unit/terraform/parsers/hcl_spec.rb
@@ -15,6 +15,20 @@ RSpec.describe Spandx::Terraform::Parsers::Hcl do
           provider "registry.terraform.io/hashicorp/aws" {
             version     = "3.39.0"
             constraints = "~> 3.27"
+            hashes = [
+              "h1:fjlp3Pd3QsTLghNm7TUh/KnEMM2D3tLb7jsDLs8oWUE=",
+              "zh:2014b397dd93fa55f2f2d1338c19e5b2b77b025a76a6b1fceea0b8696e984b9c",
+              "zh:23d59c68ab50148a0f5c911a801734e9934a1fccd41118a8efb5194135cbd360",
+              "zh:412eab41d4934ca9c47083faa128e4cd585c3bb44ad718e40d67091aebc02f4e",
+              "zh:4b75e0a259b56d97e66b7d69f3f25bd4cc7be2440c0fe35529f46de7d40a49d3",
+              "zh:694a32519dcca5bd8605d06481d16883d55160d97c1f4039deb13c6ca8de8427",
+              "zh:6a0bcef43c2d9a97aeaaac3c5d1d6728dc2464a51a014f118c691c79029d0903",
+              "zh:6d78fc7c663247ca2a80f276008dcdafece4cac75e2639bbce188c08b796040a",
+              "zh:78f846a505d7b64b67feed1527d4d2b40130dadaf8e3112113685e148f49b156",
+              "zh:881bc969432d3ef6ec70f5a762c3415e037904338579b0a360c6818b74d26e59",
+              "zh:96c1ca80c1d693a3eef80489adb45c076ee8e6878e461d6c29b05388d4b95f48",
+              "zh:9be5fa342272586fc6e319e20f21c0c5c801b05dcf7d59e473ad0882c9ecfa70",
+            ]
           }
 
           /*
@@ -32,6 +46,23 @@ RSpec.describe Spandx::Terraform::Parsers::Hcl do
         expect(subject[:blocks][0][:arguments]).to match_array([
           { name: 'version', value: '3.39.0' },
           { name: 'constraints', value: '~> 3.27' },
+          {
+            name: 'hashes',
+            values: [
+              { value: 'h1:fjlp3Pd3QsTLghNm7TUh/KnEMM2D3tLb7jsDLs8oWUE=' },
+              { value: 'zh:2014b397dd93fa55f2f2d1338c19e5b2b77b025a76a6b1fceea0b8696e984b9c' },
+              { value: 'zh:23d59c68ab50148a0f5c911a801734e9934a1fccd41118a8efb5194135cbd360' },
+              { value: 'zh:412eab41d4934ca9c47083faa128e4cd585c3bb44ad718e40d67091aebc02f4e' },
+              { value: 'zh:4b75e0a259b56d97e66b7d69f3f25bd4cc7be2440c0fe35529f46de7d40a49d3' },
+              { value: 'zh:694a32519dcca5bd8605d06481d16883d55160d97c1f4039deb13c6ca8de8427' },
+              { value: 'zh:6a0bcef43c2d9a97aeaaac3c5d1d6728dc2464a51a014f118c691c79029d0903' },
+              { value: 'zh:6d78fc7c663247ca2a80f276008dcdafece4cac75e2639bbce188c08b796040a' },
+              { value: 'zh:78f846a505d7b64b67feed1527d4d2b40130dadaf8e3112113685e148f49b156' },
+              { value: 'zh:881bc969432d3ef6ec70f5a762c3415e037904338579b0a360c6818b74d26e59' },
+              { value: 'zh:96c1ca80c1d693a3eef80489adb45c076ee8e6878e461d6c29b05388d4b95f48' },
+              { value: 'zh:9be5fa342272586fc6e319e20f21c0c5c801b05dcf7d59e473ad0882c9ecfa70' },
+            ]
+          },
         ])
       end
     end
@@ -103,17 +134,49 @@ RSpec.describe Spandx::Terraform::Parsers::Hcl do
 
   (0..9).each { |digit| specify { expect(parser.digit).to parse(digit.to_s) } }
   specify { expect(parser.assign).to parse('=') }
+  specify { expect(parser.comment).to parse('# Manual edits may be lost in future updates.') }
+  specify { expect(parser.comment).to parse('# This file is maintained automatically by "terraform init".') }
+  specify { expect(parser.comment).to parse('// This file is maintained automatically by "terraform init".') }
   specify { expect(parser.crlf).to parse("\n") }
   specify { expect(parser.crlf).to parse("\r") }
   specify { expect(parser.dot).to parse('.') }
-  specify { expect(parser.eol).to parse("\n") }
   specify { expect(parser.eol).to parse(" \n") }
+  specify { expect(parser.eol).to parse("\n") }
   specify { expect(parser.lcurly).to parse('{') }
   specify { expect(parser.number).to parse('123') }
-  specify { expect(parser.rcurly).to parse('}') }
   specify { expect(parser.quote).to parse('"') }
+  specify { expect(parser.rcurly).to parse('}') }
   specify { expect(parser.space).to parse(' ') }
-  specify { expect(parser.comment).to parse('# This file is maintained automatically by "terraform init".') }
-  specify { expect(parser.comment).to parse('# Manual edits may be lost in future updates.') }
-  specify { expect(parser.comment).to parse('// This file is maintained automatically by "terraform init".') }
+  specify { expect(parser.whitespace).to parse('# This is a comment') }
+  specify { expect(parser.whitespace).to parse('// This is a comment') }
+
+  specify do
+    expect(parser.whitespace).to parse(<<~HCL)
+      /*
+        This is a multi-line comment
+        that spans multiple lines
+      */
+    HCL
+  end
+
+  specify { expect(parser.argument).to parse('constraints = "~> 3.27"') }
+
+  specify do
+    expect(parser.argument).to parse(<<~HCL.chomp)
+      hashes = [
+        "h1:fjlp3Pd3QsTLghNm7TUh/KnEMM2D3tLb7jsDLs8oWUE=",
+        "zh:2014b397dd93fa55f2f2d1338c19e5b2b77b025a76a6b1fceea0b8696e984b9c",
+        "zh:23d59c68ab50148a0f5c911a801734e9934a1fccd41118a8efb5194135cbd360",
+        "zh:412eab41d4934ca9c47083faa128e4cd585c3bb44ad718e40d67091aebc02f4e",
+        "zh:4b75e0a259b56d97e66b7d69f3f25bd4cc7be2440c0fe35529f46de7d40a49d3",
+        "zh:694a32519dcca5bd8605d06481d16883d55160d97c1f4039deb13c6ca8de8427",
+        "zh:6a0bcef43c2d9a97aeaaac3c5d1d6728dc2464a51a014f118c691c79029d0903",
+        "zh:6d78fc7c663247ca2a80f276008dcdafece4cac75e2639bbce188c08b796040a",
+        "zh:78f846a505d7b64b67feed1527d4d2b40130dadaf8e3112113685e148f49b156",
+        "zh:881bc969432d3ef6ec70f5a762c3415e037904338579b0a360c6818b74d26e59",
+        "zh:96c1ca80c1d693a3eef80489adb45c076ee8e6878e461d6c29b05388d4b95f48",
+        "zh:9be5fa342272586fc6e319e20f21c0c5c801b05dcf7d59e473ad0882c9ecfa70",
+      ]
+    HCL
+  end
 end