mt-lang 0.3.8 → 0.3.10
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.
- checksums.yaml +4 -4
- data/lib/milk_tea/base.rb +1 -2
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
- data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
- data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
- data/lib/milk_tea/bindings/steamworks.rb +12 -16
- data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
- data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
- data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
- data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
- data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
- data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
- data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
- data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
- data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
- data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
- data/lib/milk_tea/core/ast.rb +103 -45
- data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
- data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
- data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
- data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
- data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
- data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
- data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
- data/lib/milk_tea/core/c_backend/statements.rb +528 -530
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
- data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
- data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
- data/lib/milk_tea/core/c_backend.rb +17 -9
- data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
- data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
- data/lib/milk_tea/core/compile_time.rb +32 -38
- data/lib/milk_tea/core/control_flow/builder.rb +3 -5
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
- data/lib/milk_tea/core/ir.rb +10 -10
- data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
- data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
- data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
- data/lib/milk_tea/core/lexer/indentation.rb +0 -2
- data/lib/milk_tea/core/lexer/numbers.rb +0 -2
- data/lib/milk_tea/core/lexer/recovery.rb +0 -2
- data/lib/milk_tea/core/lexer/strings.rb +0 -2
- data/lib/milk_tea/core/lexer/symbols.rb +0 -2
- data/lib/milk_tea/core/lexer/trivia.rb +0 -2
- data/lib/milk_tea/core/lexer.rb +0 -2
- data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
- data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
- data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
- data/lib/milk_tea/core/lowering/async.rb +627 -630
- data/lib/milk_tea/core/lowering/block.rb +413 -416
- data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
- data/lib/milk_tea/core/lowering/declarations.rb +180 -183
- data/lib/milk_tea/core/lowering/dyn.rb +48 -50
- data/lib/milk_tea/core/lowering/events.rb +983 -985
- data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
- data/lib/milk_tea/core/lowering/functions.rb +185 -188
- data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
- data/lib/milk_tea/core/lowering/proc.rb +363 -366
- data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
- data/lib/milk_tea/core/lowering/scans.rb +179 -181
- data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
- data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
- data/lib/milk_tea/core/lowering.rb +5 -7
- data/lib/milk_tea/core/module_binder.rb +0 -2
- data/lib/milk_tea/core/module_loader/errors.rb +4 -2
- data/lib/milk_tea/core/module_loader.rb +113 -20
- data/lib/milk_tea/core/module_path_resolver.rb +0 -2
- data/lib/milk_tea/core/module_roots.rb +6 -8
- data/lib/milk_tea/core/parser/attributes.rb +0 -2
- data/lib/milk_tea/core/parser/blocks.rb +0 -2
- data/lib/milk_tea/core/parser/declarations.rb +0 -2
- data/lib/milk_tea/core/parser/expressions.rb +31 -14
- data/lib/milk_tea/core/parser/recovery.rb +0 -2
- data/lib/milk_tea/core/parser/statements.rb +0 -2
- data/lib/milk_tea/core/parser/types.rb +8 -10
- data/lib/milk_tea/core/parser.rb +1 -3
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
- data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
- data/lib/milk_tea/core/types/layout.rb +21 -22
- data/lib/milk_tea/core/types/predicates.rb +0 -2
- data/lib/milk_tea/core/types/registry.rb +16 -18
- data/lib/milk_tea/core/types/visitor.rb +0 -4
- data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
- data/lib/milk_tea/dap/protocol.rb +1 -1
- data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
- data/lib/milk_tea/dap/server/debug_map.rb +0 -2
- data/lib/milk_tea/dap/server/handlers.rb +0 -2
- data/lib/milk_tea/dap/server/launch.rb +0 -2
- data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
- data/lib/milk_tea/dap/server/utilities.rb +0 -2
- data/lib/milk_tea/dap/server/wire.rb +0 -2
- data/lib/milk_tea/dap/server.rb +0 -2
- data/lib/milk_tea/lsp/diagnostics.rb +16 -16
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
- data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
- data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
- data/lib/milk_tea/lsp/server/completion.rb +873 -875
- data/lib/milk_tea/lsp/server/configuration.rb +129 -131
- data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
- data/lib/milk_tea/lsp/server/definition.rb +564 -565
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
- data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
- data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
- data/lib/milk_tea/lsp/server/formatting.rb +478 -480
- data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
- data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
- data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
- data/lib/milk_tea/lsp/server/progress.rb +0 -2
- data/lib/milk_tea/lsp/server/references.rb +316 -318
- data/lib/milk_tea/lsp/server/rename.rb +445 -447
- data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
- data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
- data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
- data/lib/milk_tea/lsp/server/utilities.rb +397 -399
- data/lib/milk_tea/lsp/server.rb +0 -3
- data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
- data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
- data/lib/milk_tea/packages/atomic_write.rb +4 -5
- data/lib/milk_tea/packages/dependency_solver.rb +15 -15
- data/lib/milk_tea/packages/lock.rb +4 -4
- data/lib/milk_tea/packages/manager_cli.rb +1 -1
- data/lib/milk_tea/packages/manifest.rb +3 -3
- data/lib/milk_tea/packages/registry_store.rb +4 -6
- data/lib/milk_tea/packages/services.rb +16 -16
- data/lib/milk_tea/packages/source_cache.rb +4 -6
- data/lib/milk_tea/packages/source_resolver.rb +44 -44
- data/lib/milk_tea/packages/version.rb +8 -8
- data/lib/milk_tea/tooling/build.rb +3 -7
- data/lib/milk_tea/tooling/build_cache.rb +0 -2
- data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
- data/lib/milk_tea/tooling/debug_map.rb +8 -10
- data/lib/milk_tea/tooling/docs_app.rb +0 -2
- data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
- data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
- data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
- data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
- data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
- data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
- data/lib/milk_tea/tooling/linter/rules.rb +87 -89
- data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
- data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
- data/lib/milk_tea/tooling/linter.rb +0 -2
- data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
- data/lib/milk_tea/tooling/run.rb +0 -2
- data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
- data/std/cli.mt +13 -32
- data/std/color.mt +28 -28
- data/std/encoding.mt +4 -4
- data/std/graph.mt +1 -2
- data/std/hash.mt +14 -14
- data/std/http/server.mt +3 -3
- data/std/input.mt +1 -1
- data/std/json.mt +1 -1
- data/std/linear_algebra.mt +6 -6
- data/std/net/mux.mt +1 -1
- data/std/noise.mt +23 -23
- data/std/parse.mt +2 -2
- data/std/process.mt +3 -4
- data/std/sparse_set.mt +1 -1
- data/std/steering.mt +5 -5
- data/std/str.mt +1 -1
- data/std/tween.mt +4 -4
- data/std/utility.mt +3 -3
- metadata +2 -2
|
@@ -9,9 +9,7 @@ module MilkTea
|
|
|
9
9
|
FormatString FormatTextPart FormatExprPart
|
|
10
10
|
].to_set.freeze
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def format_all(content:, tokens:, ast:, parse_errors:, facts:, snapshot:, path:, semantic_entries: nil)
|
|
12
|
+
def self.format_all(content:, tokens:, ast:, parse_errors:, facts:, snapshot:, path:, semantic_entries: nil)
|
|
15
13
|
sections = []
|
|
16
14
|
sections << format_header(content, path)
|
|
17
15
|
sections << format_tokens(tokens, semantic_entries:)
|
|
@@ -25,7 +23,7 @@ module MilkTea
|
|
|
25
23
|
|
|
26
24
|
# ── Header ────────────────────────────────────────────────────────────────────
|
|
27
25
|
|
|
28
|
-
def format_header(content, path)
|
|
26
|
+
def self.format_header(content, path)
|
|
29
27
|
lines = content.count("\n") + 1
|
|
30
28
|
bytes = content.bytesize
|
|
31
29
|
["File: #{path}", "Content: #{lines} lines, #{bytes} bytes"].join("\n")
|
|
@@ -33,7 +31,7 @@ module MilkTea
|
|
|
33
31
|
|
|
34
32
|
# ── Tokens ─────────────────────────────────────────────────────────────────────
|
|
35
33
|
|
|
36
|
-
def format_tokens(tokens, semantic_entries: nil)
|
|
34
|
+
def self.format_tokens(tokens, semantic_entries: nil)
|
|
37
35
|
return '── Tokens 0 ──' if tokens.nil? || tokens.empty?
|
|
38
36
|
|
|
39
37
|
max_len = tokens.reject { |t| [:newline, :indent, :dedent].include?(t.type) }
|
|
@@ -69,7 +67,7 @@ module MilkTea
|
|
|
69
67
|
lines.join("\n")
|
|
70
68
|
end
|
|
71
69
|
|
|
72
|
-
def build_sema_map(entries)
|
|
70
|
+
def self.build_sema_map(entries)
|
|
73
71
|
return {} unless entries.is_a?(Array) && !entries.empty?
|
|
74
72
|
|
|
75
73
|
entries.each_with_object({}) do |e, map|
|
|
@@ -80,7 +78,7 @@ module MilkTea
|
|
|
80
78
|
|
|
81
79
|
# ── AST ────────────────────────────────────────────────────────────────────────
|
|
82
80
|
|
|
83
|
-
def format_ast(ast)
|
|
81
|
+
def self.format_ast(ast)
|
|
84
82
|
return '── AST (not available) ──' unless ast
|
|
85
83
|
|
|
86
84
|
counts = %w[imports directives declarations].filter_map do |m|
|
|
@@ -93,7 +91,7 @@ module MilkTea
|
|
|
93
91
|
buf.join("\n")
|
|
94
92
|
end
|
|
95
93
|
|
|
96
|
-
def format_ast_node(node, prefix:, is_last:, buf:)
|
|
94
|
+
def self.format_ast_node(node, prefix:, is_last:, buf:)
|
|
97
95
|
return if node.nil?
|
|
98
96
|
|
|
99
97
|
if node.is_a?(Array)
|
|
@@ -152,11 +150,11 @@ module MilkTea
|
|
|
152
150
|
end
|
|
153
151
|
end
|
|
154
152
|
|
|
155
|
-
def compact_ast_node?(node)
|
|
153
|
+
def self.compact_ast_node?(node)
|
|
156
154
|
COMPACT_AST_TYPES.include?(node.class.name.split('::').last)
|
|
157
155
|
end
|
|
158
156
|
|
|
159
|
-
def compact_ast_repr(node)
|
|
157
|
+
def self.compact_ast_repr(node)
|
|
160
158
|
kind = node.class.name.split('::').last
|
|
161
159
|
case kind
|
|
162
160
|
when 'QualifiedName'
|
|
@@ -197,17 +195,17 @@ module MilkTea
|
|
|
197
195
|
end
|
|
198
196
|
end
|
|
199
197
|
|
|
200
|
-
def type_arg_str(arg)
|
|
198
|
+
def self.type_arg_str(arg)
|
|
201
199
|
return value_repr(arg) unless arg.respond_to?(:value)
|
|
202
200
|
|
|
203
201
|
val = arg.value
|
|
204
202
|
ast_node?(val) && compact_ast_node?(val) ? compact_ast_repr(val) : value_repr(val)
|
|
205
203
|
end
|
|
206
204
|
|
|
207
|
-
def ast_node_header(node)
|
|
205
|
+
def self.ast_node_header(node)
|
|
208
206
|
name_attr = node.respond_to?(:name) ? node.name : nil
|
|
209
|
-
loc = if node.
|
|
210
|
-
col = node.
|
|
207
|
+
loc = if node.line
|
|
208
|
+
col = node.column ? ", Col #{node.column}" : ''
|
|
211
209
|
" [Ln #{node.line}#{col}]"
|
|
212
210
|
else
|
|
213
211
|
''
|
|
@@ -216,21 +214,21 @@ module MilkTea
|
|
|
216
214
|
name_attr ? "#{kind} ◆ #{name_attr}#{loc}" : "#{kind}#{loc}"
|
|
217
215
|
end
|
|
218
216
|
|
|
219
|
-
def ast_node?(val)
|
|
217
|
+
def self.ast_node?(val)
|
|
220
218
|
val.class.name.to_s.start_with?('MilkTea::AST::')
|
|
221
219
|
rescue StandardError
|
|
222
220
|
false
|
|
223
221
|
end
|
|
224
222
|
|
|
225
|
-
def array_of_ast_nodes?(val)
|
|
223
|
+
def self.array_of_ast_nodes?(val)
|
|
226
224
|
val.is_a?(Array) && val.any? { |e| ast_node?(e) }
|
|
227
225
|
end
|
|
228
226
|
|
|
229
|
-
def skip_child_value?(val)
|
|
227
|
+
def self.skip_child_value?(val)
|
|
230
228
|
val.nil? || val == false || val == [] || val == {} || val == :private
|
|
231
229
|
end
|
|
232
230
|
|
|
233
|
-
def keep_child?(val)
|
|
231
|
+
def self.keep_child?(val)
|
|
234
232
|
return false if skip_child_value?(val)
|
|
235
233
|
return true if val.is_a?(Array) && val.any?
|
|
236
234
|
return true if ast_node?(val) || val.class.name.to_s.start_with?('MilkTea::')
|
|
@@ -238,19 +236,19 @@ module MilkTea
|
|
|
238
236
|
true
|
|
239
237
|
end
|
|
240
238
|
|
|
241
|
-
def symbol_icon(count)
|
|
239
|
+
def self.symbol_icon(count)
|
|
242
240
|
count > 0 ? '◆' : '◇'
|
|
243
241
|
end
|
|
244
242
|
|
|
245
|
-
def compact_array_repr(arr)
|
|
243
|
+
def self.compact_array_repr(arr)
|
|
246
244
|
arr.map { |e| value_repr(e) }.join(', ')
|
|
247
245
|
end
|
|
248
246
|
|
|
249
|
-
def simple_value_array?(arr)
|
|
247
|
+
def self.simple_value_array?(arr)
|
|
250
248
|
arr.all? { |e| e.is_a?(String) || e.is_a?(Symbol) || e.is_a?(Numeric) || e.nil? }
|
|
251
249
|
end
|
|
252
250
|
|
|
253
|
-
def value_repr(val)
|
|
251
|
+
def self.value_repr(val)
|
|
254
252
|
case val
|
|
255
253
|
when nil then 'nil'
|
|
256
254
|
when true then 'true'
|
|
@@ -275,13 +273,13 @@ module MilkTea
|
|
|
275
273
|
|
|
276
274
|
# ── Parse Errors ────────────────────────────────────────────────────────────────
|
|
277
275
|
|
|
278
|
-
def format_parse_errors(errors)
|
|
276
|
+
def self.format_parse_errors(errors)
|
|
279
277
|
return '── Parse Errors 0 ──' if errors.nil? || errors.empty?
|
|
280
278
|
|
|
281
279
|
buf = ["── Parse Errors #{errors.length} " + '─' * 55]
|
|
282
280
|
errors.each do |e|
|
|
283
|
-
line = e.
|
|
284
|
-
col = e.
|
|
281
|
+
line = e.line || '?'
|
|
282
|
+
col = e.column || '?'
|
|
285
283
|
buf << " ERROR [Ln #{line}, Col #{col}]: #{e.message}"
|
|
286
284
|
end
|
|
287
285
|
buf.join("\n")
|
|
@@ -289,7 +287,7 @@ module MilkTea
|
|
|
289
287
|
|
|
290
288
|
# ── Facts ───────────────────────────────────────────────────────────────────────
|
|
291
289
|
|
|
292
|
-
def format_facts(facts)
|
|
290
|
+
def self.format_facts(facts)
|
|
293
291
|
return '── Facts (not available) ──' unless facts
|
|
294
292
|
|
|
295
293
|
counts = []
|
|
@@ -356,7 +354,7 @@ module MilkTea
|
|
|
356
354
|
buf.join("\n")
|
|
357
355
|
end
|
|
358
356
|
|
|
359
|
-
def format_function_binding(name, binding, indent:)
|
|
357
|
+
def self.format_function_binding(name, binding, indent:)
|
|
360
358
|
buf = []
|
|
361
359
|
buf << "#{indent}#{name}"
|
|
362
360
|
buf << "#{indent} type: #{type_repr(binding.type)}"
|
|
@@ -377,14 +375,14 @@ module MilkTea
|
|
|
377
375
|
buf
|
|
378
376
|
end
|
|
379
377
|
|
|
380
|
-
def type_repr(type)
|
|
378
|
+
def self.type_repr(type)
|
|
381
379
|
return 'nil' unless type
|
|
382
380
|
return type.to_s if type.respond_to?(:to_s)
|
|
383
381
|
|
|
384
382
|
type.class.name&.split('::')&.last || type.class.to_s
|
|
385
383
|
end
|
|
386
384
|
|
|
387
|
-
def value_binding_repr(vb)
|
|
385
|
+
def self.value_binding_repr(vb)
|
|
388
386
|
mutable = vb.mutable ? 'mut' : ' '
|
|
389
387
|
kind = vb.kind.to_s
|
|
390
388
|
type = vb.flow_type || vb.storage_type
|
|
@@ -393,7 +391,7 @@ module MilkTea
|
|
|
393
391
|
|
|
394
392
|
# ── Bindings ────────────────────────────────────────────────────────────────────
|
|
395
393
|
|
|
396
|
-
def format_bindings(facts)
|
|
394
|
+
def self.format_bindings(facts)
|
|
397
395
|
return '── Bindings (not available) ──' unless facts&.binding_resolution
|
|
398
396
|
|
|
399
397
|
res = facts.binding_resolution
|
|
@@ -427,7 +425,7 @@ module MilkTea
|
|
|
427
425
|
|
|
428
426
|
# ── Diagnostics ─────────────────────────────────────────────────────────────────
|
|
429
427
|
|
|
430
|
-
def format_diagnostics(snapshot)
|
|
428
|
+
def self.format_diagnostics(snapshot)
|
|
431
429
|
return '── Diagnostics (not available) ──' unless snapshot
|
|
432
430
|
|
|
433
431
|
diags = snapshot.diagnostics
|
|
@@ -445,8 +443,8 @@ module MilkTea
|
|
|
445
443
|
diags.each do |d|
|
|
446
444
|
severity = d.respond_to?(:severity) ? d.severity.to_s.upcase : '?'
|
|
447
445
|
code = d.respond_to?(:code) ? d.code : '?'
|
|
448
|
-
line = d.
|
|
449
|
-
col = d.
|
|
446
|
+
line = d.line || '?'
|
|
447
|
+
col = d.column || '?'
|
|
450
448
|
msg = d.respond_to?(:message) ? d.message : d.to_s
|
|
451
449
|
buf << " #{severity[0]} #{severity.ljust(7)} #{code} [Ln #{line}, Col #{col}]: #{msg}"
|
|
452
450
|
end
|
|
@@ -19,7 +19,7 @@ module MilkTea
|
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
Function = Data.define(:name, :linkage_name, :
|
|
22
|
+
Function = Data.define(:name, :linkage_name, :path, :line, :params, :locals) do
|
|
23
23
|
def to_h(base_dir: nil)
|
|
24
24
|
payload = {
|
|
25
25
|
"name" => name,
|
|
@@ -27,7 +27,7 @@ module MilkTea
|
|
|
27
27
|
"params" => params.map(&:to_h),
|
|
28
28
|
"locals" => locals.map(&:to_h),
|
|
29
29
|
}
|
|
30
|
-
serialized_source_path = DebugMap.path_for_payload(
|
|
30
|
+
serialized_source_path = DebugMap.path_for_payload(self.path, base_dir)
|
|
31
31
|
payload["sourcePath"] = serialized_source_path if serialized_source_path
|
|
32
32
|
payload["line"] = line if line
|
|
33
33
|
payload
|
|
@@ -57,7 +57,7 @@ module MilkTea
|
|
|
57
57
|
Function.new(
|
|
58
58
|
name: function.fetch("name"),
|
|
59
59
|
linkage_name: function.fetch("cName"),
|
|
60
|
-
|
|
60
|
+
path: path_from_payload(function["sourcePath"], base_dir),
|
|
61
61
|
line: function["line"],
|
|
62
62
|
params: load_entries(function["params"]),
|
|
63
63
|
locals: load_entries(function["locals"])
|
|
@@ -82,18 +82,18 @@ module MilkTea
|
|
|
82
82
|
|
|
83
83
|
def self.from_ir(ir_program, binary_path:)
|
|
84
84
|
functions = ir_program.functions.map do |function|
|
|
85
|
-
source_path = first_statement_source_path(function.body) || ir_program.
|
|
85
|
+
source_path = first_statement_source_path(function.body) || ir_program.path
|
|
86
86
|
Function.new(
|
|
87
87
|
name: function.name.to_s,
|
|
88
88
|
linkage_name: function.linkage_name.to_s,
|
|
89
|
-
|
|
89
|
+
path: source_path ? File.expand_path(source_path) : nil,
|
|
90
90
|
line: first_statement_line(function.body),
|
|
91
91
|
params: function.params.map { |param| Entry.new(name: param.name.to_s, linkage_name: param.linkage_name.to_s, line: nil) },
|
|
92
92
|
locals: collect_locals(function.body)
|
|
93
93
|
)
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
new(binary_path:, program_source_path: ir_program.
|
|
96
|
+
new(binary_path:, program_source_path: ir_program.path, functions:)
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
def write(path)
|
|
@@ -165,8 +165,6 @@ module MilkTea
|
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
class << self
|
|
168
|
-
private
|
|
169
|
-
|
|
170
168
|
def load_entries(entries)
|
|
171
169
|
Array(entries).map do |entry|
|
|
172
170
|
Entry.new(name: entry.fetch("name"), linkage_name: entry.fetch("cName"), line: entry["line"])
|
|
@@ -199,8 +197,8 @@ module MilkTea
|
|
|
199
197
|
|
|
200
198
|
def first_statement_source_path(statements)
|
|
201
199
|
Array(statements).each do |statement|
|
|
202
|
-
if statement.respond_to?(:
|
|
203
|
-
return statement.
|
|
200
|
+
if statement.respond_to?(:path) && statement.path
|
|
201
|
+
return statement.path
|
|
204
202
|
end
|
|
205
203
|
|
|
206
204
|
nested = nested_statements(statement)
|
|
@@ -3,14 +3,12 @@
|
|
|
3
3
|
module MilkTea
|
|
4
4
|
class Linter
|
|
5
5
|
module LinterDocTags
|
|
6
|
-
private
|
|
7
|
-
|
|
8
6
|
def emit_doc_tag_warnings(source_file)
|
|
9
7
|
return if @source_lines.empty?
|
|
10
|
-
|
|
8
|
+
|
|
11
9
|
each_doc_comment_definition(source_file) do |declaration, doc_lines|
|
|
12
10
|
parsed = parse_doc_tag_block(doc_lines)
|
|
13
|
-
|
|
11
|
+
|
|
14
12
|
parsed[:errors].each do |error|
|
|
15
13
|
@warnings << Warning.new(
|
|
16
14
|
path: @path,
|
|
@@ -22,7 +20,7 @@ module MilkTea
|
|
|
22
20
|
severity: :hint,
|
|
23
21
|
)
|
|
24
22
|
end
|
|
25
|
-
|
|
23
|
+
|
|
26
24
|
validate_doc_tags_for_declaration(declaration, parsed[:tags])
|
|
27
25
|
end
|
|
28
26
|
end
|
|
@@ -35,34 +33,34 @@ module MilkTea
|
|
|
35
33
|
declaration
|
|
36
34
|
end
|
|
37
35
|
end
|
|
38
|
-
|
|
36
|
+
|
|
39
37
|
declarations.each do |declaration|
|
|
40
38
|
docs = collect_doc_comment_block_for_line(declaration.line)
|
|
41
39
|
next if docs.nil? || docs.empty?
|
|
42
|
-
|
|
40
|
+
|
|
43
41
|
yield declaration, docs
|
|
44
42
|
end
|
|
45
43
|
end
|
|
46
|
-
|
|
44
|
+
|
|
47
45
|
def collect_doc_comment_block_for_line(line)
|
|
48
46
|
index = line.to_i - 2
|
|
49
47
|
return nil if index.negative?
|
|
50
|
-
|
|
48
|
+
|
|
51
49
|
docs = []
|
|
52
50
|
while index >= 0
|
|
53
51
|
stripped = @source_lines[index].to_s.strip
|
|
54
52
|
break if stripped.empty?
|
|
55
53
|
break unless stripped.start_with?('##')
|
|
56
|
-
|
|
54
|
+
|
|
57
55
|
docs << {
|
|
58
56
|
line: index + 1,
|
|
59
57
|
text: stripped.sub(/\A##\s?/, ''),
|
|
60
58
|
}
|
|
61
59
|
index -= 1
|
|
62
60
|
end
|
|
63
|
-
|
|
61
|
+
|
|
64
62
|
return nil if docs.empty?
|
|
65
|
-
|
|
63
|
+
|
|
66
64
|
docs.reverse
|
|
67
65
|
end
|
|
68
66
|
def parse_doc_tag_block(lines)
|
|
@@ -73,16 +71,16 @@ module MilkTea
|
|
|
73
71
|
see: [],
|
|
74
72
|
}
|
|
75
73
|
errors = []
|
|
76
|
-
|
|
74
|
+
|
|
77
75
|
lines.each do |entry|
|
|
78
76
|
text = entry[:text].to_s
|
|
79
77
|
match = text.match(/\A\s*@([A-Za-z_][A-Za-z0-9_-]*)(?:\s+(.*))?\z/)
|
|
80
78
|
next unless match
|
|
81
|
-
|
|
79
|
+
|
|
82
80
|
tag = match[1].to_s.downcase
|
|
83
81
|
payload = match[2].to_s.strip
|
|
84
82
|
column = doc_tag_column(entry[:line])
|
|
85
|
-
|
|
83
|
+
|
|
86
84
|
case tag
|
|
87
85
|
when 'param'
|
|
88
86
|
if payload.empty?
|
|
@@ -94,7 +92,7 @@ module MilkTea
|
|
|
94
92
|
}
|
|
95
93
|
next
|
|
96
94
|
end
|
|
97
|
-
|
|
95
|
+
|
|
98
96
|
name_match = payload.match(/\A([A-Za-z_][A-Za-z0-9_]*)(?:\s+(.*))?\z/)
|
|
99
97
|
unless name_match
|
|
100
98
|
errors << {
|
|
@@ -105,7 +103,7 @@ module MilkTea
|
|
|
105
103
|
}
|
|
106
104
|
next
|
|
107
105
|
end
|
|
108
|
-
|
|
106
|
+
|
|
109
107
|
tags[:params] << {
|
|
110
108
|
name: name_match[1],
|
|
111
109
|
text: name_match[2].to_s.strip,
|
|
@@ -139,7 +137,7 @@ module MilkTea
|
|
|
139
137
|
}
|
|
140
138
|
end
|
|
141
139
|
end
|
|
142
|
-
|
|
140
|
+
|
|
143
141
|
{
|
|
144
142
|
tags: tags,
|
|
145
143
|
errors: errors,
|
|
@@ -149,7 +147,7 @@ module MilkTea
|
|
|
149
147
|
callable = declaration.respond_to?(:params) && declaration.respond_to?(:return_type)
|
|
150
148
|
param_tags = Array(tags[:params])
|
|
151
149
|
return_tag = tags[:returns]
|
|
152
|
-
|
|
150
|
+
|
|
153
151
|
unless callable
|
|
154
152
|
([*param_tags, return_tag].compact).each do |tag_entry|
|
|
155
153
|
@warnings << Warning.new(
|
|
@@ -164,14 +162,14 @@ module MilkTea
|
|
|
164
162
|
end
|
|
165
163
|
return
|
|
166
164
|
end
|
|
167
|
-
|
|
165
|
+
|
|
168
166
|
params = Array(declaration.params)
|
|
169
167
|
param_names = params.map(&:name)
|
|
170
168
|
param_name_set = param_names.to_set
|
|
171
|
-
|
|
169
|
+
|
|
172
170
|
param_tags.each do |tag_entry|
|
|
173
171
|
next if param_name_set.include?(tag_entry[:name])
|
|
174
|
-
|
|
172
|
+
|
|
175
173
|
@warnings << Warning.new(
|
|
176
174
|
path: @path,
|
|
177
175
|
line: tag_entry[:line],
|
|
@@ -183,12 +181,12 @@ module MilkTea
|
|
|
183
181
|
symbol_name: tag_entry[:name],
|
|
184
182
|
)
|
|
185
183
|
end
|
|
186
|
-
|
|
184
|
+
|
|
187
185
|
return unless return_tag
|
|
188
|
-
|
|
186
|
+
|
|
189
187
|
return_type_text = declaration.return_type.to_s
|
|
190
188
|
return unless return_type_text == 'void'
|
|
191
|
-
|
|
189
|
+
|
|
192
190
|
@warnings << Warning.new(
|
|
193
191
|
path: @path,
|
|
194
192
|
line: return_tag[:line],
|
|
@@ -199,12 +197,12 @@ module MilkTea
|
|
|
199
197
|
severity: :hint,
|
|
200
198
|
)
|
|
201
199
|
end
|
|
202
|
-
|
|
200
|
+
|
|
203
201
|
def doc_tag_column(line)
|
|
204
202
|
text = @source_lines[line.to_i - 1].to_s
|
|
205
203
|
at_index = text.index('@')
|
|
206
204
|
return 1 if at_index.nil?
|
|
207
|
-
|
|
205
|
+
|
|
208
206
|
at_index + 1
|
|
209
207
|
end
|
|
210
208
|
end
|
|
@@ -5,9 +5,8 @@ module MilkTea
|
|
|
5
5
|
FixEdit = Data.define(:start_line, :start_char, :end_line, :end_char, :new_text)
|
|
6
6
|
|
|
7
7
|
module FixEngine
|
|
8
|
-
module_function
|
|
9
8
|
|
|
10
|
-
def edits_for_rule(code, lines, warning)
|
|
9
|
+
def self.edits_for_rule(code, lines, warning)
|
|
11
10
|
case code
|
|
12
11
|
when "prefer-let" then prefer_let_edits(lines, warning)
|
|
13
12
|
when "redundant-ignored-match-binding" then redundant_ignored_match_binding_edits(lines, warning)
|
|
@@ -24,8 +23,8 @@ module MilkTea
|
|
|
24
23
|
end
|
|
25
24
|
end
|
|
26
25
|
|
|
27
|
-
def apply_fix_edits(lines, edits)
|
|
28
|
-
edits.sort_by { |e| [-e.start_line, -e.start_char] }.
|
|
26
|
+
def self.apply_fix_edits(lines, edits)
|
|
27
|
+
edits.sort_by { |e| [-e.start_line, -e.start_char] }.each do |edit|
|
|
29
28
|
if edit.start_line == edit.end_line
|
|
30
29
|
line = lines[edit.start_line]
|
|
31
30
|
next unless line
|
|
@@ -40,7 +39,7 @@ module MilkTea
|
|
|
40
39
|
lines
|
|
41
40
|
end
|
|
42
41
|
|
|
43
|
-
def edits_to_lsp_text_edits(edits, uri)
|
|
42
|
+
def self.edits_to_lsp_text_edits(edits, uri)
|
|
44
43
|
edits.map do |edit|
|
|
45
44
|
{
|
|
46
45
|
range: {
|
|
@@ -54,7 +53,7 @@ module MilkTea
|
|
|
54
53
|
|
|
55
54
|
# ── per-rule edit generators ──────────────────────────────────────────
|
|
56
55
|
|
|
57
|
-
def prefer_let_edits(lines, warning)
|
|
56
|
+
def self.prefer_let_edits(lines, warning)
|
|
58
57
|
return [] unless warning.line
|
|
59
58
|
|
|
60
59
|
line_idx = warning.line - 1
|
|
@@ -65,7 +64,7 @@ module MilkTea
|
|
|
65
64
|
[FixEdit.new(start_line: line_idx, start_char: 0, end_line: line_idx + 1, end_char: 0, new_text: new_line)]
|
|
66
65
|
end
|
|
67
66
|
|
|
68
|
-
def redundant_ignored_match_binding_edits(lines, warning)
|
|
67
|
+
def self.redundant_ignored_match_binding_edits(lines, warning)
|
|
69
68
|
return [] unless warning.line && warning.column
|
|
70
69
|
|
|
71
70
|
line_idx = warning.line - 1
|
|
@@ -78,7 +77,7 @@ module MilkTea
|
|
|
78
77
|
[FixEdit.new(start_line: line_idx, start_char: span[:start_char], end_line: line_idx, end_char: span[:end_char], new_text: "")]
|
|
79
78
|
end
|
|
80
79
|
|
|
81
|
-
def prefer_let_else_edits(lines, warning)
|
|
80
|
+
def self.prefer_let_else_edits(lines, warning)
|
|
82
81
|
return [] unless warning.line
|
|
83
82
|
|
|
84
83
|
fix = Linter.build_prefer_let_else_fix(lines, warning.line - 1, symbol_name: warning.symbol_name)
|
|
@@ -87,11 +86,11 @@ module MilkTea
|
|
|
87
86
|
[FixEdit.new(start_line: fix[:start_line_idx], start_char: 0, end_line: fix[:end_line_idx] + 1, end_char: 0, new_text: fix[:new_text])]
|
|
88
87
|
end
|
|
89
88
|
|
|
90
|
-
def prefer_var_else_edits(lines, warning)
|
|
89
|
+
def self.prefer_var_else_edits(lines, warning)
|
|
91
90
|
prefer_let_else_edits(lines, warning)
|
|
92
91
|
end
|
|
93
92
|
|
|
94
|
-
def redundant_bool_compare_edits(lines, warning)
|
|
93
|
+
def self.redundant_bool_compare_edits(lines, warning)
|
|
95
94
|
return [] unless warning.line && warning.column && warning.length
|
|
96
95
|
|
|
97
96
|
line_idx = warning.line - 1
|
|
@@ -109,7 +108,7 @@ module MilkTea
|
|
|
109
108
|
[FixEdit.new(start_line: line_idx, start_char:, end_line: line_idx, end_char:, new_text: replacement)]
|
|
110
109
|
end
|
|
111
110
|
|
|
112
|
-
def redundant_else_edits(lines, warning)
|
|
111
|
+
def self.redundant_else_edits(lines, warning)
|
|
113
112
|
return [] unless warning.line
|
|
114
113
|
|
|
115
114
|
diag_idx = warning.line - 1
|
|
@@ -144,7 +143,7 @@ module MilkTea
|
|
|
144
143
|
[FixEdit.new(start_line: else_idx, start_char: 0, end_line: body_end_idx + 1, end_char: 0, new_text: new_body)]
|
|
145
144
|
end
|
|
146
145
|
|
|
147
|
-
def redundant_return_edits(lines, warning)
|
|
146
|
+
def self.redundant_return_edits(lines, warning)
|
|
148
147
|
return [] unless warning.line
|
|
149
148
|
|
|
150
149
|
line_idx = warning.line - 1
|
|
@@ -153,7 +152,7 @@ module MilkTea
|
|
|
153
152
|
[FixEdit.new(start_line: line_idx, start_char: 0, end_line: line_idx + 1, end_char: 0, new_text: "")]
|
|
154
153
|
end
|
|
155
154
|
|
|
156
|
-
def unused_import_edits(lines, warning)
|
|
155
|
+
def self.unused_import_edits(lines, warning)
|
|
157
156
|
return [] unless warning.line
|
|
158
157
|
|
|
159
158
|
line_idx = warning.line - 1
|
|
@@ -162,7 +161,7 @@ module MilkTea
|
|
|
162
161
|
[FixEdit.new(start_line: line_idx, start_char: 0, end_line: line_idx + 1, end_char: 0, new_text: "")]
|
|
163
162
|
end
|
|
164
163
|
|
|
165
|
-
def trailing_list_comma_edits(lines, warning)
|
|
164
|
+
def self.trailing_list_comma_edits(lines, warning)
|
|
166
165
|
return [] unless warning.line && warning.column
|
|
167
166
|
|
|
168
167
|
line_idx = warning.line - 1
|
|
@@ -176,7 +175,7 @@ module MilkTea
|
|
|
176
175
|
[FixEdit.new(start_line: line_idx, start_char: char_idx, end_line: line_idx, end_char: char_idx + 1, new_text: "")]
|
|
177
176
|
end
|
|
178
177
|
|
|
179
|
-
def redundant_type_annotation_edits(lines, warning)
|
|
178
|
+
def self.redundant_type_annotation_edits(lines, warning)
|
|
180
179
|
return [] unless warning.line
|
|
181
180
|
|
|
182
181
|
line_idx = warning.line - 1
|
|
@@ -198,7 +197,7 @@ module MilkTea
|
|
|
198
197
|
[FixEdit.new(start_line: line_idx, start_char: 0, end_line: line_idx + 1, end_char: 0, new_text: new_line)]
|
|
199
198
|
end
|
|
200
199
|
|
|
201
|
-
def redundant_cast_edits(lines, warning)
|
|
200
|
+
def self.redundant_cast_edits(lines, warning)
|
|
202
201
|
return [] unless warning.line && warning.column
|
|
203
202
|
|
|
204
203
|
line_idx = warning.line - 1
|
|
@@ -208,20 +207,34 @@ module MilkTea
|
|
|
208
207
|
start = warning.column - 1
|
|
209
208
|
return [] unless start >= 0 && start < line.length
|
|
210
209
|
|
|
211
|
-
# The warning column points at the cast target type. Delete the
|
|
212
|
-
# `TargetType<-` prefix, keeping the inner expression as-is. This
|
|
213
|
-
# covers both bare (`ulong<-x`) and parenthesized (`uint<-(a - b)`)
|
|
214
|
-
# forms; type refs never contain `<`, so the first `<-` at/after the
|
|
215
|
-
# column is the cast arrow.
|
|
216
210
|
arrow = line.index("<-", start)
|
|
217
211
|
return [] unless arrow
|
|
218
212
|
|
|
219
213
|
type_ref = line[start...arrow]
|
|
220
214
|
return [] unless type_ref.match?(/\A[A-Za-z_][A-Za-z0-9_\[\]\.,\s@]*\z/)
|
|
221
215
|
|
|
222
|
-
|
|
223
|
-
|
|
216
|
+
arrow_end = arrow + 2
|
|
217
|
+
|
|
218
|
+
# When only the parentheses are redundant (the cast itself is still
|
|
219
|
+
# needed), remove just the `(` and matching `)`.
|
|
220
|
+
if warning.message&.include?("parentheses")
|
|
221
|
+
return [] unless arrow_end < line.length && line[arrow_end] == "("
|
|
222
|
+
|
|
223
|
+
open_paren = arrow_end
|
|
224
|
+
close_paren = match_paren(line, open_paren)
|
|
225
|
+
return [] unless close_paren
|
|
226
|
+
|
|
227
|
+
return [FixEdit.new(start_line: line_idx, start_char: open_paren, end_line: line_idx, end_char: open_paren + 1, new_text: ""),
|
|
228
|
+
FixEdit.new(start_line: line_idx, start_char: close_paren, end_line: line_idx, end_char: close_paren + 1, new_text: "")]
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Cast to same type / own->ptr / widening are redundant — remove the
|
|
232
|
+
# `TargetType<-` prefix. When the inner expression is then left
|
|
233
|
+
# parenthesized with no operators, also strip the orphaned parens.
|
|
224
234
|
new_start = start
|
|
235
|
+
|
|
236
|
+
# When the cast is inside an `unsafe:` expression, also remove the
|
|
237
|
+
# `unsafe: ` wrapper.
|
|
225
238
|
unsafe_keyword = line.rindex("unsafe:", new_start)
|
|
226
239
|
if unsafe_keyword
|
|
227
240
|
prefix = line[unsafe_keyword...new_start]
|
|
@@ -230,7 +243,57 @@ module MilkTea
|
|
|
230
243
|
end
|
|
231
244
|
end
|
|
232
245
|
|
|
233
|
-
[FixEdit.new(start_line: line_idx, start_char: new_start, end_line: line_idx, end_char:
|
|
246
|
+
edits = [FixEdit.new(start_line: line_idx, start_char: new_start, end_line: line_idx, end_char: arrow_end, new_text: "")]
|
|
247
|
+
|
|
248
|
+
if arrow_end < line.length && line[arrow_end] == "("
|
|
249
|
+
close_paren = match_paren(line, arrow_end)
|
|
250
|
+
if close_paren && !inner_has_top_level_operators?(line[arrow_end + 1...close_paren])
|
|
251
|
+
edits << FixEdit.new(start_line: line_idx, start_char: arrow_end, end_line: line_idx, end_char: arrow_end + 1, new_text: "")
|
|
252
|
+
edits << FixEdit.new(start_line: line_idx, start_char: close_paren, end_line: line_idx, end_char: close_paren + 1, new_text: "")
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
edits
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def self.match_paren(line, open_pos)
|
|
260
|
+
depth = 0
|
|
261
|
+
(open_pos...line.length).each do |i|
|
|
262
|
+
case line[i]
|
|
263
|
+
when "(" then depth += 1
|
|
264
|
+
when ")" then depth -= 1; return i if depth == 0
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
nil
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
BINARY_OP_CHARS = %w[+ - * / % & | ^ < > = ! ? :].freeze
|
|
271
|
+
|
|
272
|
+
def self.inner_has_top_level_operators?(inner)
|
|
273
|
+
depth = 0
|
|
274
|
+
i = 0
|
|
275
|
+
while i < inner.length
|
|
276
|
+
case inner[i]
|
|
277
|
+
when "(", "[", "{"
|
|
278
|
+
depth += 1
|
|
279
|
+
when ")", "]", "}"
|
|
280
|
+
depth -= 1 if depth > 0
|
|
281
|
+
when *BINARY_OP_CHARS
|
|
282
|
+
if depth == 0 && (inner[i] == "-" || inner[i] == "~") && start_of_expression?(inner, i)
|
|
283
|
+
i += 1
|
|
284
|
+
next
|
|
285
|
+
end
|
|
286
|
+
return true if depth == 0
|
|
287
|
+
end
|
|
288
|
+
i += 1
|
|
289
|
+
end
|
|
290
|
+
false
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def self.start_of_expression?(inner, pos)
|
|
294
|
+
return true if pos == 0
|
|
295
|
+
prev = pos - 1
|
|
296
|
+
prev >= 0 && (inner[prev] == " " || inner[prev] == "\t" || inner[prev] == "(")
|
|
234
297
|
end
|
|
235
298
|
end
|
|
236
299
|
end
|