syntax_tree 4.2.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 444c1c74319e55d9c3a656ff1796e6658237c0433a257eb6e2c66d8625d47243
4
- data.tar.gz: 2662a60c92265c6b257f6545ee1947269e1990f96003c60caef790eeb871eb32
3
+ metadata.gz: 4ca1eae46ba326b73e129d5b066d144aeac2743947956efc1453a3539c2caac9
4
+ data.tar.gz: 7c833e7b7bf25df7a82653d9dddb30aa172320a625a0655c52a29640c7a2154f
5
5
  SHA512:
6
- metadata.gz: 9dddc08b2d7c2c3c7cec089f301b88220cf18a625ef7b155ad71810eaefd4cfe54c063279a4f817bc823b1eb586a222b106aa5889f3b736da901ca0404feedb5
7
- data.tar.gz: eaabea185c2f53aaf62ea76b1bb89987d7c472538025ae3f3e457f97eafe38b2533895f504100ed534785ffebaa18a013dd8d1f27432da974beedf0b7202861c
6
+ metadata.gz: 1720e9ef9dd52399564607a9eca7794875e41f36f8ee2c4af2d97fb973c38ec0bf3a3cc254d26f3d656e67256543afed7c818e274395af122790ae6730ccab8c
7
+ data.tar.gz: 0f00dbe7739f71bfa1b81ff204fdd4dc505277a65e374810d2f482c6ef3c81078b39f864d55d8deed001a5b7a294491feb0b930948546bcd4194a88058ca9783
@@ -12,10 +12,12 @@ jobs:
12
12
  - '3.0'
13
13
  - '3.1'
14
14
  - head
15
+ - truffleruby-head
15
16
  name: CI
16
17
  runs-on: ubuntu-latest
17
18
  env:
18
19
  CI: true
20
+ TESTOPTS: --verbose
19
21
  steps:
20
22
  - uses: actions/checkout@master
21
23
  - uses: ruby/setup-ruby@v1
data/.rubocop.yml CHANGED
@@ -46,6 +46,9 @@ Naming/MethodParameterName:
46
46
  Naming/RescuedExceptionsVariableName:
47
47
  PreferredName: error
48
48
 
49
+ Style/CaseEquality:
50
+ Enabled: false
51
+
49
52
  Style/ExplicitBlockArgument:
50
53
  Enabled: false
51
54
 
data/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [4.3.0] - 2022-10-28
10
+
11
+ ### Added
12
+
13
+ - [#183](https://github.com/ruby-syntax-tree/syntax_tree/pull/183) - Support TruffleRuby by eliminating internal pattern matching in some places and stopping some tests from running in other places.
14
+ - [#184](https://github.com/ruby-syntax-tree/syntax_tree/pull/184) - Remove internal pattern matching entirely.
15
+
16
+ ### Changed
17
+
18
+ - [#183](https://github.com/ruby-syntax-tree/syntax_tree/pull/183) - Pattern matching works against dynamic symbols now.
19
+ - [#184](https://github.com/ruby-syntax-tree/syntax_tree/pull/184) - Exit with the correct exit status within the rake tasks.
20
+
9
21
  ## [4.2.0] - 2022-10-25
10
22
 
11
23
  ### Added
@@ -414,7 +426,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
414
426
 
415
427
  - 🎉 Initial release! 🎉
416
428
 
417
- [unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v4.2.0...HEAD
429
+ [unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v4.3.0...HEAD
430
+ [4.3.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v4.2.0...v4.3.0
418
431
  [4.2.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v4.1.0...v4.2.0
419
432
  [4.1.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v4.0.2...v4.1.0
420
433
  [4.0.2]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v4.0.1...v4.0.2
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- syntax_tree (4.2.0)
4
+ syntax_tree (4.3.0)
5
5
  prettier_print (>= 1.0.2)
6
6
 
7
7
  GEM
@@ -192,9 +192,10 @@ module SyntaxTree
192
192
  # would match the first expression of the input given.
193
193
  class Expr < Action
194
194
  def run(item)
195
- case item.handler.parse(item.source)
196
- in Program[statements: Statements[body: [expression]]]
197
- puts expression.construct_keys
195
+ program = item.handler.parse(item.source)
196
+
197
+ if (expressions = program.statements.body) && expressions.size == 1
198
+ puts expressions.first.construct_keys
198
199
  else
199
200
  warn("The input to `stree expr` must be a single expression.")
200
201
  exit(1)
@@ -69,11 +69,10 @@ module SyntaxTree
69
69
  #
70
70
  def visit_binary(node)
71
71
  case stack[-2]
72
- in Assign | OpAssign
72
+ when Assign, OpAssign
73
73
  parentheses(node.location)
74
- in Binary[operator: operator] if operator != node.operator
75
- parentheses(node.location)
76
- else
74
+ when Binary
75
+ parentheses(node.location) if stack[-2].operator != node.operator
77
76
  end
78
77
 
79
78
  super
@@ -91,9 +90,8 @@ module SyntaxTree
91
90
  #
92
91
  def visit_if_op(node)
93
92
  case stack[-2]
94
- in Assign | Binary | IfOp | OpAssign
93
+ when Assign, Binary, IfOp, OpAssign
95
94
  parentheses(node.location)
96
- else
97
95
  end
98
96
 
99
97
  super
@@ -13,6 +13,50 @@ module SyntaxTree
13
13
  # stree lsp
14
14
  #
15
15
  class LanguageServer
16
+ # This is a small module that effectively mirrors pattern matching. We're
17
+ # using it so that we can support truffleruby without having to ignore the
18
+ # language server.
19
+ module Request
20
+ # Represents a hash pattern.
21
+ class Shape
22
+ attr_reader :values
23
+
24
+ def initialize(values)
25
+ @values = values
26
+ end
27
+
28
+ def ===(other)
29
+ values.all? do |key, value|
30
+ value == :any ? other.key?(key) : value === other[key]
31
+ end
32
+ end
33
+ end
34
+
35
+ # Represents an array pattern.
36
+ class Tuple
37
+ attr_reader :values
38
+
39
+ def initialize(values)
40
+ @values = values
41
+ end
42
+
43
+ def ===(other)
44
+ values.each_with_index.all? { |value, index| value === other[index] }
45
+ end
46
+ end
47
+
48
+ def self.[](value)
49
+ case value
50
+ when Array
51
+ Tuple.new(value.map { |child| self[child] })
52
+ when Hash
53
+ Shape.new(value.transform_values { |child| self[child] })
54
+ else
55
+ value
56
+ end
57
+ end
58
+ end
59
+
16
60
  attr_reader :input, :output, :print_width
17
61
 
18
62
  def initialize(
@@ -39,30 +83,33 @@ module SyntaxTree
39
83
 
40
84
  # stree-ignore
41
85
  case request
42
- in { method: "initialize", id: }
86
+ when Request[method: "initialize", id: :any]
43
87
  store.clear
44
- write(id: id, result: { capabilities: capabilities })
45
- in { method: "initialized" }
88
+ write(id: request[:id], result: { capabilities: capabilities })
89
+ when Request[method: "initialized"]
46
90
  # ignored
47
- in { method: "shutdown" } # tolerate missing ID to be a good citizen
91
+ when Request[method: "shutdown"] # tolerate missing ID to be a good citizen
48
92
  store.clear
49
93
  write(id: request[:id], result: {})
50
94
  return
51
- in { method: "textDocument/didChange", params: { textDocument: { uri: }, contentChanges: [{ text: }, *] } }
52
- store[uri] = text
53
- in { method: "textDocument/didOpen", params: { textDocument: { uri:, text: } } }
54
- store[uri] = text
55
- in { method: "textDocument/didClose", params: { textDocument: { uri: } } }
56
- store.delete(uri)
57
- in { method: "textDocument/formatting", id:, params: { textDocument: { uri: } } }
95
+ when Request[method: "textDocument/didChange", params: { textDocument: { uri: :any }, contentChanges: [{ text: :any }] }]
96
+ store[request.dig(:params, :textDocument, :uri)] = request.dig(:params, :contentChanges, 0, :text)
97
+ when Request[method: "textDocument/didOpen", params: { textDocument: { uri: :any, text: :any } }]
98
+ store[request.dig(:params, :textDocument, :uri)] = request.dig(:params, :textDocument, :text)
99
+ when Request[method: "textDocument/didClose", params: { textDocument: { uri: :any } }]
100
+ store.delete(request.dig(:params, :textDocument, :uri))
101
+ when Request[method: "textDocument/formatting", id: :any, params: { textDocument: { uri: :any } }]
102
+ uri = request.dig(:params, :textDocument, :uri)
58
103
  contents = store[uri]
59
- write(id: id, result: contents ? format(contents, uri.split(".").last) : nil)
60
- in { method: "textDocument/inlayHint", id:, params: { textDocument: { uri: } } }
104
+ write(id: request[:id], result: contents ? format(contents, uri.split(".").last) : nil)
105
+ when Request[method: "textDocument/inlayHint", id: :any, params: { textDocument: { uri: :any } }]
106
+ uri = request.dig(:params, :textDocument, :uri)
61
107
  contents = store[uri]
62
- write(id: id, result: contents ? inlay_hints(contents) : nil)
63
- in { method: "syntaxTree/visualizing", id:, params: { textDocument: { uri: } } }
64
- write(id: id, result: PP.pp(SyntaxTree.parse(store[uri]), +""))
65
- in { method: %r{\$/.+} }
108
+ write(id: request[:id], result: contents ? inlay_hints(contents) : nil)
109
+ when Request[method: "syntaxTree/visualizing", id: :any, params: { textDocument: { uri: :any } }]
110
+ uri = request.dig(:params, :textDocument, :uri)
111
+ write(id: request[:id], result: PP.pp(SyntaxTree.parse(store[uri]), +""))
112
+ when Request[method: %r{\$/.+}]
66
113
  # ignored
67
114
  else
68
115
  raise ArgumentError, "Unhandled: #{request}"
@@ -75,98 +75,213 @@ module SyntaxTree
75
75
 
76
76
  private
77
77
 
78
+ # Shortcut for combining two procs into one that returns true if both return
79
+ # true.
78
80
  def combine_and(left, right)
79
- ->(node) { left.call(node) && right.call(node) }
81
+ ->(other) { left.call(other) && right.call(other) }
80
82
  end
81
83
 
84
+ # Shortcut for combining two procs into one that returns true if either
85
+ # returns true.
82
86
  def combine_or(left, right)
83
- ->(node) { left.call(node) || right.call(node) }
87
+ ->(other) { left.call(other) || right.call(other) }
84
88
  end
85
89
 
86
- def compile_node(root)
87
- case root
88
- in AryPtn[constant:, requireds:, rest: nil, posts: []]
89
- compiled_constant = compile_node(constant) if constant
90
+ # Raise an error because the given node is not supported.
91
+ def compile_error(node)
92
+ raise CompilationError, PP.pp(node, +"").chomp
93
+ end
94
+
95
+ # There are a couple of nodes (string literals, dynamic symbols, and regexp)
96
+ # that contain list of parts. This can include plain string content,
97
+ # interpolated expressions, and interpolated variables. We only support
98
+ # plain string content, so this method will extract out the plain string
99
+ # content if it is the only element in the list.
100
+ def extract_string(node)
101
+ parts = node.parts
90
102
 
91
- preprocessed = requireds.map { |required| compile_node(required) }
103
+ if parts.length == 1 && (part = parts.first) && part.is_a?(TStringContent)
104
+ part.value
105
+ end
106
+ end
92
107
 
93
- compiled_requireds = ->(node) do
94
- deconstructed = node.deconstruct
108
+ # in [foo, bar, baz]
109
+ def compile_aryptn(node)
110
+ compile_error(node) if !node.rest.nil? || node.posts.any?
95
111
 
96
- deconstructed.length == preprocessed.length &&
97
- preprocessed
98
- .zip(deconstructed)
99
- .all? { |(matcher, value)| matcher.call(value) }
100
- end
112
+ constant = node.constant
113
+ compiled_constant = compile_node(constant) if constant
101
114
 
102
- if compiled_constant
103
- combine_and(compiled_constant, compiled_requireds)
104
- else
105
- compiled_requireds
106
- end
107
- in Binary[left:, operator: :|, right:]
108
- combine_or(compile_node(left), compile_node(right))
109
- in Const[value:] if SyntaxTree.const_defined?(value)
115
+ preprocessed = node.requireds.map { |required| compile_node(required) }
116
+
117
+ compiled_requireds = ->(other) do
118
+ deconstructed = other.deconstruct
119
+
120
+ deconstructed.length == preprocessed.length &&
121
+ preprocessed
122
+ .zip(deconstructed)
123
+ .all? { |(matcher, value)| matcher.call(value) }
124
+ end
125
+
126
+ if compiled_constant
127
+ combine_and(compiled_constant, compiled_requireds)
128
+ else
129
+ compiled_requireds
130
+ end
131
+ end
132
+
133
+ # in foo | bar
134
+ def compile_binary(node)
135
+ compile_error(node) if node.operator != :|
136
+
137
+ combine_or(compile_node(node.left), compile_node(node.right))
138
+ end
139
+
140
+ # in Ident
141
+ # in String
142
+ def compile_const(node)
143
+ value = node.value
144
+
145
+ if SyntaxTree.const_defined?(value)
110
146
  clazz = SyntaxTree.const_get(value)
111
147
 
112
- ->(node) { node.is_a?(clazz) }
113
- in Const[value:] if Object.const_defined?(value)
148
+ ->(other) { clazz === other }
149
+ elsif Object.const_defined?(value)
114
150
  clazz = Object.const_get(value)
115
151
 
116
- ->(node) { node.is_a?(clazz) }
117
- in ConstPathRef[
118
- parent: VarRef[value: Const[value: "SyntaxTree"]], constant:
119
- ]
120
- compile_node(constant)
121
- in DynaSymbol[parts: []]
152
+ ->(other) { clazz === other }
153
+ else
154
+ compile_error(node)
155
+ end
156
+ end
157
+
158
+ # in SyntaxTree::Ident
159
+ def compile_const_path_ref(node)
160
+ parent = node.parent
161
+ compile_error(node) if !parent.is_a?(VarRef) || !parent.value.is_a?(Const)
162
+
163
+ if parent.value.value == "SyntaxTree"
164
+ compile_node(node.constant)
165
+ else
166
+ compile_error(node)
167
+ end
168
+ end
169
+
170
+ # in :""
171
+ # in :"foo"
172
+ def compile_dyna_symbol(node)
173
+ if node.parts.empty?
122
174
  symbol = :""
123
175
 
124
- ->(node) { node == symbol }
125
- in DynaSymbol[parts: [TStringContent[value:]]]
176
+ ->(other) { symbol === other }
177
+ elsif (value = extract_string(node))
126
178
  symbol = value.to_sym
127
179
 
128
- ->(attribute) { attribute == value }
129
- in HshPtn[constant:, keywords:, keyword_rest: nil]
130
- compiled_constant = compile_node(constant)
131
-
132
- preprocessed =
133
- keywords.to_h do |keyword, value|
134
- raise NoMatchingPatternError unless keyword.is_a?(Label)
135
- [keyword.value.chomp(":").to_sym, compile_node(value)]
136
- end
180
+ ->(other) { symbol === other }
181
+ else
182
+ compile_error(root)
183
+ end
184
+ end
137
185
 
138
- compiled_keywords = ->(node) do
139
- deconstructed = node.deconstruct_keys(preprocessed.keys)
186
+ # in Ident[value: String]
187
+ # in { value: String }
188
+ def compile_hshptn(node)
189
+ compile_error(node) unless node.keyword_rest.nil?
190
+ compiled_constant = compile_node(node.constant) if node.constant
140
191
 
141
- preprocessed.all? do |keyword, matcher|
142
- matcher.call(deconstructed[keyword])
143
- end
192
+ preprocessed =
193
+ node.keywords.to_h do |keyword, value|
194
+ compile_error(node) unless keyword.is_a?(Label)
195
+ [keyword.value.chomp(":").to_sym, compile_node(value)]
144
196
  end
145
197
 
146
- if compiled_constant
147
- combine_and(compiled_constant, compiled_keywords)
148
- else
149
- compiled_keywords
198
+ compiled_keywords = ->(other) do
199
+ deconstructed = other.deconstruct_keys(preprocessed.keys)
200
+
201
+ preprocessed.all? do |keyword, matcher|
202
+ matcher.call(deconstructed[keyword])
150
203
  end
151
- in RegexpLiteral[parts: [TStringContent[value:]]]
204
+ end
205
+
206
+ if compiled_constant
207
+ combine_and(compiled_constant, compiled_keywords)
208
+ else
209
+ compiled_keywords
210
+ end
211
+ end
212
+
213
+ # in /foo/
214
+ def compile_regexp_literal(node)
215
+ if (value = extract_string(node))
152
216
  regexp = /#{value}/
153
217
 
154
- ->(attribute) { regexp.match?(attribute) }
155
- in StringLiteral[parts: []]
156
- ->(attribute) { attribute == "" }
157
- in StringLiteral[parts: [TStringContent[value:]]]
158
- ->(attribute) { attribute == value }
159
- in SymbolLiteral[value:]
160
- symbol = value.value.to_sym
218
+ ->(attribute) { regexp === attribute }
219
+ else
220
+ compile_error(node)
221
+ end
222
+ end
223
+
224
+ # in ""
225
+ # in "foo"
226
+ def compile_string_literal(node)
227
+ if node.parts.empty?
228
+ ->(attribute) { "" === attribute }
229
+ elsif (value = extract_string(node))
230
+ ->(attribute) { value === attribute }
231
+ else
232
+ compile_error(node)
233
+ end
234
+ end
235
+
236
+ # in :+
237
+ # in :foo
238
+ def compile_symbol_literal(node)
239
+ symbol = node.value.value.to_sym
161
240
 
162
- ->(attribute) { attribute == symbol }
163
- in VarRef[value: Const => value]
241
+ ->(attribute) { symbol === attribute }
242
+ end
243
+
244
+ # in Foo
245
+ # in nil
246
+ def compile_var_ref(node)
247
+ value = node.value
248
+
249
+ if value.is_a?(Const)
164
250
  compile_node(value)
165
- in VarRef[value: Kw[value: "nil"]]
166
- ->(attribute) { attribute.nil? }
251
+ elsif value.is_a?(Kw) && value.value.nil?
252
+ ->(attribute) { nil === attribute }
253
+ else
254
+ compile_error(node)
255
+ end
256
+ end
257
+
258
+ # Compile any kind of node. Dispatch out to the individual compilation
259
+ # methods based on the type of node.
260
+ def compile_node(node)
261
+ case node
262
+ when AryPtn
263
+ compile_aryptn(node)
264
+ when Binary
265
+ compile_binary(node)
266
+ when Const
267
+ compile_const(node)
268
+ when ConstPathRef
269
+ compile_const_path_ref(node)
270
+ when DynaSymbol
271
+ compile_dyna_symbol(node)
272
+ when HshPtn
273
+ compile_hshptn(node)
274
+ when RegexpLiteral
275
+ compile_regexp_literal(node)
276
+ when StringLiteral
277
+ compile_string_literal(node)
278
+ when SymbolLiteral
279
+ compile_symbol_literal(node)
280
+ when VarRef
281
+ compile_var_ref(node)
282
+ else
283
+ compile_error(node)
167
284
  end
168
- rescue NoMatchingPatternError
169
- raise CompilationError, PP.pp(root, +"").chomp
170
285
  end
171
286
  end
172
287
  end
@@ -78,7 +78,7 @@ module SyntaxTree
78
78
 
79
79
  arguments << "--ignore-files=#{ignore_files}" if ignore_files != ""
80
80
 
81
- SyntaxTree::CLI.run(arguments + Array(source_files))
81
+ abort if SyntaxTree::CLI.run(arguments + Array(source_files)) != 0
82
82
  end
83
83
  end
84
84
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SyntaxTree
4
- VERSION = "4.2.0"
4
+ VERSION = "4.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntax_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Newton
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-25 00:00:00.000000000 Z
11
+ date: 2022-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prettier_print