code-ruby-parser 0.1.0

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.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.overcommit.yml +4 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +32 -0
  6. data/bin/code-parser +27 -0
  7. data/bin/format +3 -0
  8. data/bin/template-parser +27 -0
  9. data/docs/class.code +9 -0
  10. data/docs/meetup.code +14 -0
  11. data/docs/rain.code +23 -0
  12. data/docs/slack.code +17 -0
  13. data/docs/stripe.code +7 -0
  14. data/docs/twitter.code +7 -0
  15. data/lib/code/parser/addition.rb +13 -0
  16. data/lib/code/parser/and_operator.rb +13 -0
  17. data/lib/code/parser/bitwise_and.rb +13 -0
  18. data/lib/code/parser/bitwise_or.rb +13 -0
  19. data/lib/code/parser/boolean.rb +13 -0
  20. data/lib/code/parser/call.rb +174 -0
  21. data/lib/code/parser/chained_call.rb +41 -0
  22. data/lib/code/parser/class.rb +56 -0
  23. data/lib/code/parser/code.rb +20 -0
  24. data/lib/code/parser/comments.rb +46 -0
  25. data/lib/code/parser/dictionnary.rb +48 -0
  26. data/lib/code/parser/equal.rb +39 -0
  27. data/lib/code/parser/equality.rb +20 -0
  28. data/lib/code/parser/error/syntax_error.rb +36 -0
  29. data/lib/code/parser/error.rb +6 -0
  30. data/lib/code/parser/function.rb +109 -0
  31. data/lib/code/parser/greater_than.rb +13 -0
  32. data/lib/code/parser/group.rb +15 -0
  33. data/lib/code/parser/identifier.rb +54 -0
  34. data/lib/code/parser/if.rb +81 -0
  35. data/lib/code/parser/if_modifier.rb +39 -0
  36. data/lib/code/parser/list.rb +20 -0
  37. data/lib/code/parser/multiplication.rb +13 -0
  38. data/lib/code/parser/negation.rb +18 -0
  39. data/lib/code/parser/not_keyword.rb +24 -0
  40. data/lib/code/parser/nothing.rb +13 -0
  41. data/lib/code/parser/number.rb +39 -0
  42. data/lib/code/parser/operation.rb +44 -0
  43. data/lib/code/parser/or_keyword.rb +13 -0
  44. data/lib/code/parser/or_operator.rb +13 -0
  45. data/lib/code/parser/power.rb +33 -0
  46. data/lib/code/parser/range.rb +13 -0
  47. data/lib/code/parser/rescue.rb +38 -0
  48. data/lib/code/parser/shift.rb +13 -0
  49. data/lib/code/parser/statement.rb +9 -0
  50. data/lib/code/parser/string.rb +61 -0
  51. data/lib/code/parser/ternary.rb +73 -0
  52. data/lib/code/parser/unary_minus.rb +23 -0
  53. data/lib/code/parser/while.rb +36 -0
  54. data/lib/code/parser.rb +237 -0
  55. data/lib/code-ruby-parser.rb +7 -0
  56. data/lib/code.rb +2 -0
  57. data/lib/template/parser.rb +32 -0
  58. data/lib/template-ruby-parser.rb +7 -0
  59. data/lib/template.rb +2 -0
  60. data/spec/code/parser/addition_spec.rb +26 -0
  61. data/spec/code/parser/and_operator_spec.rb +26 -0
  62. data/spec/code/parser/bitwise_and_spec.rb +26 -0
  63. data/spec/code/parser/bitwise_or_spec.rb +26 -0
  64. data/spec/code/parser/boolean_spec.rb +13 -0
  65. data/spec/code/parser/call_spec.rb +52 -0
  66. data/spec/code/parser/chained_call_spec.rb +33 -0
  67. data/spec/code/parser/class_spec.rb +32 -0
  68. data/spec/code/parser/code_spec.rb +13 -0
  69. data/spec/code/parser/dictionnary_spec.rb +40 -0
  70. data/spec/code/parser/equal_spec.rb +42 -0
  71. data/spec/code/parser/equality_spec.rb +26 -0
  72. data/spec/code/parser/function_spec.rb +43 -0
  73. data/spec/code/parser/greater_than_spec.rb +26 -0
  74. data/spec/code/parser/group_spec.rb +13 -0
  75. data/spec/code/parser/if_modifier_spec.rb +26 -0
  76. data/spec/code/parser/if_spec.rb +39 -0
  77. data/spec/code/parser/list_spec.rb +27 -0
  78. data/spec/code/parser/multiplication_spec.rb +26 -0
  79. data/spec/code/parser/negation_spec.rb +13 -0
  80. data/spec/code/parser/not_keyword_spec.rb +21 -0
  81. data/spec/code/parser/nothing_spec.rb +20 -0
  82. data/spec/code/parser/number_spec.rb +24 -0
  83. data/spec/code/parser/or_keyword_spec.rb +26 -0
  84. data/spec/code/parser/or_operator_spec.rb +26 -0
  85. data/spec/code/parser/power_spec.rb +21 -0
  86. data/spec/code/parser/range_spec.rb +21 -0
  87. data/spec/code/parser/rescue_spec.rb +26 -0
  88. data/spec/code/parser/shift_spec.rb +26 -0
  89. data/spec/code/parser/string_spec.rb +27 -0
  90. data/spec/code/parser/ternary_spec.rb +26 -0
  91. data/spec/code/parser/unary_minus_spec.rb +21 -0
  92. data/spec/code/parser/while_spec.rb +32 -0
  93. data/spec/spec_helper.rb +6 -0
  94. data/spec/template/parser_spec.rb +13 -0
  95. data/template-ruby-parser.gemspec +16 -0
  96. metadata +171 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 75b549d03f89ef60a67380c6bb2ffb9f8c29ae367f1d78b1535d085be7ad1735
4
+ data.tar.gz: 7b92207a50f9ace1308eb0344780fc01d1adcc44629cefc7780ee7271ec14f57
5
+ SHA512:
6
+ metadata.gz: 330c5c233c99a1148e0769bf19bc966e5422891d23a58d7663ef57d09381cd0a7d88ed8a338374d1ead99cd54564eb903148bf50680ccf64056df549db127a20
7
+ data.tar.gz: 522ad02a718830b2d23a14855db77f5c463d5348dc953213b151c80cf1bc3e28b0a84b204109db0320700ed22bb41675b8c4023d1aa7f98bc385996a33b74e26
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ tmp/
data/.overcommit.yml ADDED
@@ -0,0 +1,4 @@
1
+ PreCommit:
2
+ Format:
3
+ enabled: true
4
+ command: ["bin/format"]
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rspec"
4
+ gem "syntax_tree"
5
+ gem "profile"
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.5.0)
5
+ prettier_print (1.0.2)
6
+ profile (0.4.0)
7
+ rspec (3.12.0)
8
+ rspec-core (~> 3.12.0)
9
+ rspec-expectations (~> 3.12.0)
10
+ rspec-mocks (~> 3.12.0)
11
+ rspec-core (3.12.0)
12
+ rspec-support (~> 3.12.0)
13
+ rspec-expectations (3.12.0)
14
+ diff-lcs (>= 1.2.0, < 2.0)
15
+ rspec-support (~> 3.12.0)
16
+ rspec-mocks (3.12.0)
17
+ diff-lcs (>= 1.2.0, < 2.0)
18
+ rspec-support (~> 3.12.0)
19
+ rspec-support (3.12.0)
20
+ syntax_tree (4.3.0)
21
+ prettier_print (>= 1.0.2)
22
+
23
+ PLATFORMS
24
+ arm64-darwin-21
25
+
26
+ DEPENDENCIES
27
+ profile
28
+ rspec
29
+ syntax_tree
30
+
31
+ BUNDLED WITH
32
+ 2.3.22
data/bin/code-parser ADDED
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+ require_relative "../lib/code-ruby-parser"
5
+ require "json"
6
+
7
+ options = {}
8
+
9
+ OptionParser
10
+ .new do |opts|
11
+ opts.banner = "Usage: bin/code-parser [options]"
12
+
13
+ opts.on(
14
+ "-i INPUT",
15
+ "--input=INPUT",
16
+ "Input in the code language (String or File)"
17
+ ) do |input|
18
+ input = File.read(input) if File.exists?(input)
19
+
20
+ options[:input] = input
21
+ end
22
+ end
23
+ .parse!
24
+
25
+ input = options.fetch(:input, "")
26
+
27
+ puts JSON.pretty_generate(::Code::Parser.parse(input))
data/bin/format ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env fish
2
+
3
+ stree write Gemfile lib/**/*.rb spec/**/*.rb bin/code-parser
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+ require_relative "../lib/code-ruby-parser"
5
+ require "json"
6
+
7
+ options = {}
8
+
9
+ OptionParser
10
+ .new do |opts|
11
+ opts.banner = "Usage: bin/template-parser [options]"
12
+
13
+ opts.on(
14
+ "-i INPUT",
15
+ "--input=INPUT",
16
+ "Input in the code language (String or File)"
17
+ ) do |input|
18
+ input = File.read(input) if File.exists?(input)
19
+
20
+ options[:input] = input
21
+ end
22
+ end
23
+ .parse!
24
+
25
+ input = options.fetch(:input, "")
26
+
27
+ puts JSON.pretty_generate(::Template::Parser.parse(input))
data/docs/class.code ADDED
@@ -0,0 +1,9 @@
1
+ class ApiClient
2
+ get = (url) => {
3
+ fetch(url)
4
+ }
5
+ end
6
+
7
+ class Twitter < ApiClient
8
+ self.base_url = "https://api.twitter.com"
9
+ end
data/docs/meetup.code ADDED
@@ -0,0 +1,14 @@
1
+ url = fetch(:url) { "https://www.meetup.com/parisrb" }
2
+ response = HTTP.get(url)
3
+ times = response.css(".groupHome-eventsList-upcomingEvents time")
4
+ times = times.map(&:datetime).map do |time|
5
+ Time.from_epoch(time.to_integer)
6
+ end
7
+
8
+ times.each do |time|
9
+ if (1.day.from_now...(1.day.from_now + 1.hour)).include?(time)
10
+ Sms.send("Event in one day {url}")
11
+ elsif (2.hours.from_now...(1.hour.from_now)).include(time)
12
+ Sms.send("Event in two hours {url}")
13
+ end
14
+ end
data/docs/rain.code ADDED
@@ -0,0 +1,23 @@
1
+ paris = { latitude: 48.8566, longitude: 2.4022 }
2
+ latitude = fetch(:latitude) { paris.latitude }
3
+ longitude = fetch(:longitude) { paris.longitude }
4
+ duration = fetch(:duration) { 1.hour }
5
+ hour = duration.from_now.hour
6
+
7
+ response = HTTP.get(
8
+ "https://api.open-meteo.com/v1/forecast",
9
+ parameters: {
10
+ latitude: latitude,
11
+ longitude: longitude,
12
+ hourly: :precipitation
13
+ }
14
+ )
15
+
16
+ precipitation = response.hourly.precipitation[hour]
17
+ precipitation = (precipitation * 100).to_integer
18
+
19
+ if precipitation > 0
20
+ Sms.send(
21
+ "There will be {precipitation}% of precipitation in the next hour"
22
+ )
23
+ end
data/docs/slack.code ADDED
@@ -0,0 +1,17 @@
1
+ Slack.send("Who is leading the syncs?", channel: "#team-template")
2
+
3
+ Twitter.send("What do you want to do this week?")
4
+
5
+ Discord.send("Who will be the game master this week?")
6
+
7
+ Email.send(
8
+ subject: "What did you do last week?",
9
+ from: "dorian@thoughtbot.com",
10
+ reply_to: "rob@thoughtbot.com"
11
+ )
12
+
13
+ Email.send(
14
+ subject: "What do you want to do this week?",
15
+ from: "dorian@thoughtbot.com",
16
+ reply_to: "rob@thoughtbot.com"
17
+ )
data/docs/stripe.code ADDED
@@ -0,0 +1,7 @@
1
+ event = Stripe::Webhook.event
2
+
3
+ if event.type == "payment_intent.succeeded"
4
+ payment_intent = event.data.object
5
+ amount = Money.format(payment_intent.amount, payment_intent.currency)
6
+ Sms.send("You got paid {amount}")
7
+ end
data/docs/twitter.code ADDED
@@ -0,0 +1,7 @@
1
+ twitter_username = fetch(:twitter_username) { "dorianmariefr" }
2
+
3
+ Twitter.search("to:{twitter_username}", type: :recent).each do |tweet|
4
+ next if Storage.exists?(tweet.id)
5
+ Storage.create!(tweet.id)
6
+ Sms.send("New mention: {tweet.user.screen_name}: {tweet.text}")
7
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class Addition < ::Code::Parser
4
+ def parse
5
+ parse_subclass(
6
+ ::Code::Parser::Operation,
7
+ operators: [PLUS, MINUS],
8
+ subclass: ::Code::Parser::Multiplication
9
+ )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class AndOperator < ::Code::Parser
4
+ def parse
5
+ parse_subclass(
6
+ ::Code::Parser::Operation,
7
+ operators: [AMPERSAND + AMPERSAND],
8
+ subclass: ::Code::Parser::GreaterThan
9
+ )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class BitwiseAnd < ::Code::Parser
4
+ def parse
5
+ parse_subclass(
6
+ ::Code::Parser::Operation,
7
+ operators: [AMPERSAND],
8
+ subclass: ::Code::Parser::Shift
9
+ )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class BitwiseOr < ::Code::Parser
4
+ def parse
5
+ parse_subclass(
6
+ ::Code::Parser::Operation,
7
+ operators: [PIPE, CARET],
8
+ subclass: ::Code::Parser::BitwiseAnd
9
+ )
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ class Code
2
+ class Parser
3
+ class Boolean < ::Code::Parser
4
+ def parse
5
+ if match(BOOLEAN_KEYWORDS)
6
+ { boolean: buffer }
7
+ else
8
+ parse_subclass(::Code::Parser::Nothing)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,174 @@
1
+ class Code
2
+ class Parser
3
+ class Call < ::Code::Parser
4
+ def parse
5
+ identifier = parse_subclass(::Code::Parser::Identifier)
6
+ return unless identifier
7
+
8
+ if match(OPENING_PARENTHESIS)
9
+ arguments = []
10
+
11
+ arguments << (parse_keyword_argument || parse_regular_argument)
12
+
13
+ while match(COMMA) && !end_of_input?
14
+ arguments << (parse_keyword_argument || parse_regular_argument)
15
+ end
16
+
17
+ arguments.compact!
18
+
19
+ match(CLOSING_PARENTHESIS)
20
+ else
21
+ arguments = nil
22
+ end
23
+
24
+ previous_cursor = cursor
25
+ comments = parse_comments
26
+
27
+ if match(DO_KEYWORD)
28
+ block = parse_block
29
+ match(END_KEYWORD)
30
+ elsif match(OPENING_CURLY_BRACKET)
31
+ block = parse_block
32
+ match(CLOSING_CURLY_BRACKET)
33
+ else
34
+ @cursor = previous_cursor
35
+ buffer!
36
+ block = nil
37
+ end
38
+
39
+ {
40
+ call: {
41
+ **identifier,
42
+ arguments: arguments,
43
+ block: block,
44
+ comments: comments
45
+ }.compact
46
+ }
47
+ end
48
+
49
+ private
50
+
51
+ def parse_block
52
+ comments = parse_comments
53
+
54
+ if match(PIPE)
55
+ parameters = []
56
+
57
+ parameters << (parse_keyword_parameter || parse_regular_parameter)
58
+
59
+ while match(COMMA) && !end_of_input?
60
+ parameters << (parse_keyword_parameter || parse_regular_parameter)
61
+ end
62
+
63
+ match(PIPE)
64
+
65
+ {
66
+ comments: comments,
67
+ parameters: parameters.compact,
68
+ body: parse_code
69
+ }.compact
70
+ else
71
+ { comments: comments, body: parse_code }.compact
72
+ end
73
+ end
74
+
75
+ def parse_keyword_argument
76
+ previous_cursor = cursor
77
+
78
+ comments_before = parse_comments
79
+ key = parse_subclass(::Code::Parser::Statement)
80
+ comments_after = parse_comments
81
+
82
+ if key && match(COLON) || match(EQUAL + GREATER)
83
+ default = parse_code
84
+ default = nil if default.empty?
85
+ {
86
+ comments_before: comments_before,
87
+ comments_after: comments_after,
88
+ default: default,
89
+ keyword: true,
90
+ statement: key
91
+ }.compact
92
+ else
93
+ @cursor = previous_cursor
94
+ buffer!
95
+ return
96
+ end
97
+ end
98
+
99
+ def parse_regular_argument
100
+ code = parse_code
101
+ return if code.empty?
102
+ code
103
+ end
104
+
105
+ def parse_keyword_parameter
106
+ previous_cursor = cursor
107
+
108
+ comments_before = parse_comments
109
+ key = parse_subclass(::Code::Parser::Identifier)
110
+ comments_after = parse_comments
111
+
112
+ if key && match(COLON) || match(EQUAL + GREATER)
113
+ default = parse_default
114
+
115
+ {
116
+ comments_before: comments_before,
117
+ comments_after: comments_after,
118
+ default: default,
119
+ keyword: true,
120
+ **key
121
+ }.compact
122
+ else
123
+ @cursor = previous_cursor
124
+ buffer!
125
+ return
126
+ end
127
+ end
128
+
129
+ def parse_regular_parameter
130
+ previous_cursor = cursor
131
+ comments_before = parse_comments
132
+ identifier = parse_subclass(::Code::Parser::Identifier)
133
+ if identifier
134
+ comments_after = parse_comments
135
+
136
+ if match(EQUAL)
137
+ default = parse_default
138
+
139
+ {
140
+ comments_before: comments_before,
141
+ comments_after: comments_after,
142
+ default: default,
143
+ **identifier
144
+ }.compact
145
+ else
146
+ {
147
+ comments_before: comments_before,
148
+ comments_after: comments_after,
149
+ **identifier
150
+ }.compact
151
+ end
152
+ else
153
+ @cursor = previous_cursor
154
+ buffer!
155
+ return
156
+ end
157
+ end
158
+
159
+ def parse_default
160
+ comments_before = parse_comments
161
+ statement = parse_subclass(::Code::Parser::BitwiseAnd)
162
+ comments_after = parse_comments
163
+
164
+ default = {
165
+ comments_before: comments_before,
166
+ statement: statement,
167
+ comments_after: comments_after
168
+ }.compact
169
+
170
+ default.empty? ? nil : default
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,41 @@
1
+ class Code
2
+ class Parser
3
+ class ChainedCall < ::Code::Parser
4
+ def parse
5
+ previous_cursor = cursor
6
+ left = parse_dictionnary
7
+ comments_before = parse_comments
8
+
9
+ if left && match([DOT, COLON + COLON])
10
+ comments_after = parse_comments
11
+ right = parse_dictionnary
12
+ if right
13
+ chained_call = [{ left: left, right: right }]
14
+ while match([DOT, COLON + COLON]) && other_right = parse_dictionnary
15
+ chained_call << { right: other_right }
16
+ end
17
+ {
18
+ chained_call: {
19
+ calls: chained_call,
20
+ comments_before: comments_before,
21
+ comments_after: comments_after
22
+ }.compact
23
+ }
24
+ else
25
+ @cursor = previous_cursor
26
+ buffer!
27
+ parse_dictionnary
28
+ end
29
+ else
30
+ @cursor = previous_cursor
31
+ buffer!
32
+ parse_dictionnary
33
+ end
34
+ end
35
+
36
+ def parse_dictionnary
37
+ parse_subclass(::Code::Parser::Dictionnary)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,56 @@
1
+ class Code
2
+ class Parser
3
+ class Class < ::Code::Parser
4
+ def parse
5
+ previous_cursor = cursor
6
+
7
+ if match(CLASS_KEYWORD)
8
+ first_comments = parse_comments
9
+
10
+ name = parse_subclass(::Code::Parser::Identifier, simple: true)
11
+
12
+ if name
13
+ second_comments = parse_comments
14
+
15
+ if match(LESSER)
16
+ previous_cursor = cursor
17
+ third_comments = parse_comments
18
+ superclass =
19
+ parse_subclass(::Code::Parser::Identifier, simple: true)
20
+
21
+ if !superclass
22
+ @cursor = previous_cursor
23
+ buffer!
24
+ third_comments = nil
25
+ end
26
+ else
27
+ third_comments = nil
28
+ superclass = nil
29
+ end
30
+
31
+ body = parse_code
32
+
33
+ match(END_KEYWORD)
34
+
35
+ {
36
+ class: {
37
+ name: name,
38
+ superclass: superclass,
39
+ first_comments: first_comments,
40
+ second_comments: second_comments,
41
+ third_comments: third_comments,
42
+ body: body
43
+ }.compact
44
+ }
45
+ else
46
+ @cursor = previous_cursor
47
+ buffer!
48
+ parse_subclass(::Code::Parser::While)
49
+ end
50
+ else
51
+ parse_subclass(::Code::Parser::While)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,20 @@
1
+ class Code
2
+ class Parser
3
+ class Code < ::Code::Parser
4
+ def parse
5
+ output = []
6
+
7
+ comments = parse_comments
8
+ output << { comments: comments } if comments
9
+
10
+ while code = parse_subclass(::Code::Parser::Statement)
11
+ output << code
12
+ comments = parse_comments
13
+ output << { comments: comments } if comments
14
+ end
15
+
16
+ output
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,46 @@
1
+ class Code
2
+ class Parser
3
+ class Comments < ::Code::Parser
4
+ def initialize(input, whitespace: WHITESPACE, **kargs)
5
+ super(input, **kargs)
6
+ @whitespace = whitespace
7
+ @has_line_comments = whitespace.include?(NEWLINE)
8
+ end
9
+
10
+ def parse
11
+ comments = []
12
+
13
+ while next?(whitespace) || next?(SLASH + ASTERISK) ||
14
+ (line_comments? && (next?(SLASH + SLASH) || next?(HASH)))
15
+ consume while next?(whitespace)
16
+ buffer!
17
+
18
+ if match(SLASH + SLASH) || match(HASH)
19
+ consume while !next?(NEWLINE) && !end_of_input?
20
+ match(NEWLINE)
21
+ comments << buffer
22
+ end
23
+
24
+ consume while next?(whitespace)
25
+ buffer!
26
+
27
+ if match(SLASH + ASTERISK)
28
+ consume while !next?(ASTERISK + SLASH) && !end_of_input?
29
+ match(ASTERISK + SLASH)
30
+ comments << buffer
31
+ end
32
+ end
33
+
34
+ comments.empty? ? nil : comments
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :whitespace, :has_line_comments
41
+
42
+ def line_comments?
43
+ !!has_line_comments
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,48 @@
1
+ class Code
2
+ class Parser
3
+ class Dictionnary < ::Code::Parser
4
+ def parse
5
+ if match(OPENING_CURLY_BRACKET)
6
+ dictionnary = []
7
+
8
+ comments = parse_comments
9
+
10
+ dictionnary << parse_key_value
11
+
12
+ dictionnary << parse_key_value while match(COMMA) && !end_of_input?
13
+
14
+ match(CLOSING_CURLY_BRACKET)
15
+
16
+ { dictionnary: dictionnary.compact, comments: comments }
17
+ else
18
+ parse_subclass(::Code::Parser::List)
19
+ end
20
+ end
21
+
22
+ def parse_key_value
23
+ comments_before = parse_comments
24
+
25
+ key = parse_subclass(::Code::Parser::Statement)
26
+
27
+ comments_after = parse_comments
28
+
29
+ return unless key
30
+
31
+ if match(COLON) || match(EQUAL + GREATER)
32
+ {
33
+ key: key,
34
+ value: parse_code,
35
+ comments_before: comments_before,
36
+ comments_after: comments_after
37
+ }.compact
38
+ else
39
+ {
40
+ key: key,
41
+ comments_before: comments_before,
42
+ comments_after: comments_after
43
+ }.compact
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end