mt-lang 0.2.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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/AUTHORS +3 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +21 -0
- data/README.md +1208 -0
- data/Rakefile +332 -0
- data/bin/mtc +8 -0
- data/bin/profile-mtc-checks +133 -0
- data/bin/tracy-profiler +0 -0
- data/docs/build-guide.md +536 -0
- data/docs/index.html +2778 -0
- data/docs/language-design.md +1519 -0
- data/docs/language-manual.md +1534 -0
- data/lib/milk_tea/base.rb +49 -0
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
- data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
- data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
- data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
- data/lib/milk_tea/bindings/bindgen.rb +207 -0
- data/lib/milk_tea/bindings/cli.rb +121 -0
- data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
- data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
- data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
- data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
- data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
- data/lib/milk_tea/bindings/steamworks.rb +629 -0
- data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
- data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
- data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
- data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
- data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
- data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
- data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
- data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
- data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
- data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
- data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
- data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
- data/lib/milk_tea/bindings.rb +23 -0
- data/lib/milk_tea/core/ast.rb +311 -0
- data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
- data/lib/milk_tea/core/binding_types.rb +18 -0
- data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
- data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
- data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
- data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
- data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
- data/lib/milk_tea/core/c_backend/statements.rb +563 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
- data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
- data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
- data/lib/milk_tea/core/c_backend.rb +287 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
- data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
- data/lib/milk_tea/core/compile_time.rb +329 -0
- data/lib/milk_tea/core/control_flow/builder.rb +582 -0
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
- data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
- data/lib/milk_tea/core/control_flow/graph.rb +90 -0
- data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
- data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
- data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
- data/lib/milk_tea/core/control_flow/termination.rb +31 -0
- data/lib/milk_tea/core/control_flow.rb +34 -0
- data/lib/milk_tea/core/cst.rb +48 -0
- data/lib/milk_tea/core/cst_builder.rb +19 -0
- data/lib/milk_tea/core/ir.rb +85 -0
- data/lib/milk_tea/core/keywords.rb +97 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
- data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
- data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
- data/lib/milk_tea/core/lexer/indentation.rb +62 -0
- data/lib/milk_tea/core/lexer/numbers.rb +96 -0
- data/lib/milk_tea/core/lexer/recovery.rb +32 -0
- data/lib/milk_tea/core/lexer/strings.rb +167 -0
- data/lib/milk_tea/core/lexer/symbols.rb +71 -0
- data/lib/milk_tea/core/lexer/trivia.rb +53 -0
- data/lib/milk_tea/core/lexer.rb +430 -0
- data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
- data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
- data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
- data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
- data/lib/milk_tea/core/lowering/async.rb +714 -0
- data/lib/milk_tea/core/lowering/block.rb +1052 -0
- data/lib/milk_tea/core/lowering/calls.rb +1565 -0
- data/lib/milk_tea/core/lowering/declarations.rb +214 -0
- data/lib/milk_tea/core/lowering/dyn.rb +206 -0
- data/lib/milk_tea/core/lowering/events.rb +1054 -0
- data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
- data/lib/milk_tea/core/lowering/functions.rb +242 -0
- data/lib/milk_tea/core/lowering/loops.rb +1087 -0
- data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
- data/lib/milk_tea/core/lowering/proc.rb +419 -0
- data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
- data/lib/milk_tea/core/lowering/scans.rb +220 -0
- data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
- data/lib/milk_tea/core/lowering/utils.rb +1453 -0
- data/lib/milk_tea/core/lowering.rb +378 -0
- data/lib/milk_tea/core/module_binder.rb +181 -0
- data/lib/milk_tea/core/module_loader/errors.rb +21 -0
- data/lib/milk_tea/core/module_loader.rb +479 -0
- data/lib/milk_tea/core/module_path_resolver.rb +153 -0
- data/lib/milk_tea/core/module_roots.rb +88 -0
- data/lib/milk_tea/core/parser/attributes.rb +71 -0
- data/lib/milk_tea/core/parser/blocks.rb +119 -0
- data/lib/milk_tea/core/parser/declarations.rb +749 -0
- data/lib/milk_tea/core/parser/expressions.rb +624 -0
- data/lib/milk_tea/core/parser/recovery.rb +131 -0
- data/lib/milk_tea/core/parser/statements.rb +756 -0
- data/lib/milk_tea/core/parser/types.rb +271 -0
- data/lib/milk_tea/core/parser.rb +400 -0
- data/lib/milk_tea/core/prelude_installer.rb +29 -0
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
- data/lib/milk_tea/core/pretty_printer.rb +17 -0
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
- data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
- data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
- data/lib/milk_tea/core/token.rb +26 -0
- data/lib/milk_tea/core/token_stream.rb +30 -0
- data/lib/milk_tea/core/types/layout.rb +243 -0
- data/lib/milk_tea/core/types/predicates.rb +609 -0
- data/lib/milk_tea/core/types/registry.rb +83 -0
- data/lib/milk_tea/core/types/types.rb +1696 -0
- data/lib/milk_tea/core/types/visitor.rb +442 -0
- data/lib/milk_tea/core.rb +25 -0
- data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
- data/lib/milk_tea/dap/protocol.rb +58 -0
- data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
- data/lib/milk_tea/dap/server/debug_map.rb +291 -0
- data/lib/milk_tea/dap/server/handlers.rb +374 -0
- data/lib/milk_tea/dap/server/launch.rb +261 -0
- data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
- data/lib/milk_tea/dap/server/utilities.rb +160 -0
- data/lib/milk_tea/dap/server/wire.rb +55 -0
- data/lib/milk_tea/dap/server.rb +131 -0
- data/lib/milk_tea/dap/session.rb +152 -0
- data/lib/milk_tea/dap.rb +6 -0
- data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
- data/lib/milk_tea/lsp/diagnostics.rb +611 -0
- data/lib/milk_tea/lsp/protocol.rb +104 -0
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
- data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
- data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
- data/lib/milk_tea/lsp/server/completion.rb +1099 -0
- data/lib/milk_tea/lsp/server/configuration.rb +167 -0
- data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
- data/lib/milk_tea/lsp/server/definition.rb +779 -0
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
- data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
- data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
- data/lib/milk_tea/lsp/server/formatting.rb +575 -0
- data/lib/milk_tea/lsp/server/hover.rb +1465 -0
- data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
- data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
- data/lib/milk_tea/lsp/server/progress.rb +47 -0
- data/lib/milk_tea/lsp/server/references.rb +433 -0
- data/lib/milk_tea/lsp/server/rename.rb +598 -0
- data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
- data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
- data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
- data/lib/milk_tea/lsp/server/utilities.rb +520 -0
- data/lib/milk_tea/lsp/server.rb +415 -0
- data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
- data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
- data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
- data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
- data/lib/milk_tea/lsp/workspace/store.rb +154 -0
- data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
- data/lib/milk_tea/lsp/workspace.rb +127 -0
- data/lib/milk_tea/lsp.rb +13 -0
- data/lib/milk_tea/packages/atomic_write.rb +63 -0
- data/lib/milk_tea/packages/dependency_solver.rb +194 -0
- data/lib/milk_tea/packages/graph.rb +139 -0
- data/lib/milk_tea/packages/lock.rb +421 -0
- data/lib/milk_tea/packages/manager_cli.rb +485 -0
- data/lib/milk_tea/packages/manifest.rb +356 -0
- data/lib/milk_tea/packages/manifest_editor.rb +134 -0
- data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
- data/lib/milk_tea/packages/registry_store.rb +420 -0
- data/lib/milk_tea/packages/services.rb +41 -0
- data/lib/milk_tea/packages/source_cache.rb +71 -0
- data/lib/milk_tea/packages/source_fetcher.rb +124 -0
- data/lib/milk_tea/packages/source_resolver.rb +389 -0
- data/lib/milk_tea/packages/version.rb +164 -0
- data/lib/milk_tea/packages.rb +16 -0
- data/lib/milk_tea/tooling/asset_pack.rb +141 -0
- data/lib/milk_tea/tooling/build.rb +1124 -0
- data/lib/milk_tea/tooling/build_cache.rb +240 -0
- data/lib/milk_tea/tooling/cli.rb +2688 -0
- data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
- data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
- data/lib/milk_tea/tooling/debug_map.rb +248 -0
- data/lib/milk_tea/tooling/docs_app.rb +369 -0
- data/lib/milk_tea/tooling/error_formatter.rb +86 -0
- data/lib/milk_tea/tooling/formatter.rb +787 -0
- data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
- data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
- data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
- data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
- data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
- data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
- data/lib/milk_tea/tooling/linter/rules.rb +593 -0
- data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
- data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
- data/lib/milk_tea/tooling/linter.rb +798 -0
- data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
- data/lib/milk_tea/tooling/public/css/docs.css +166 -0
- data/lib/milk_tea/tooling/public/js/docs.js +94 -0
- data/lib/milk_tea/tooling/run.rb +408 -0
- data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
- data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
- data/lib/milk_tea/tooling/views/404.erb +7 -0
- data/lib/milk_tea/tooling/views/index.erb +87 -0
- data/lib/milk_tea/tooling/views/layout.erb +69 -0
- data/lib/milk_tea/tooling/views/module.erb +97 -0
- data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
- data/lib/milk_tea/tooling.rb +20 -0
- data/lib/milk_tea.rb +8 -0
- metadata +426 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
class CBackend
|
|
5
|
+
module ControlFlowEmission
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
INVERTED_BOOLEAN_OPS = {
|
|
9
|
+
"==" => "!=",
|
|
10
|
+
"!=" => "==",
|
|
11
|
+
"<" => ">=",
|
|
12
|
+
"<=" => ">",
|
|
13
|
+
">" => "<=",
|
|
14
|
+
">=" => "<",
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
def loop_continue_label_name(statements)
|
|
18
|
+
return if statements.empty?
|
|
19
|
+
|
|
20
|
+
label = statements.last
|
|
21
|
+
return unless label.is_a?(IR::LabelStmt)
|
|
22
|
+
return unless label.name.include?("loop_continue")
|
|
23
|
+
|
|
24
|
+
label.name
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def loop_break_label_name(statements, remaining_statements)
|
|
28
|
+
return if remaining_statements.empty?
|
|
29
|
+
|
|
30
|
+
label = remaining_statements.first
|
|
31
|
+
return unless label.is_a?(IR::LabelStmt)
|
|
32
|
+
return unless statements_reference_label?(statements, label.name)
|
|
33
|
+
|
|
34
|
+
label.name
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def statements_reference_label?(statements, label_name)
|
|
38
|
+
statements.any? { |statement| statement_references_label?(statement, label_name) }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def statement_references_label?(statement, label_name)
|
|
42
|
+
case statement
|
|
43
|
+
when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
|
|
44
|
+
statements_reference_label?(statement.body, label_name)
|
|
45
|
+
when IR::IfStmt
|
|
46
|
+
statements_reference_label?(statement.then_body, label_name) ||
|
|
47
|
+
(statement.else_body && statements_reference_label?(statement.else_body, label_name))
|
|
48
|
+
when IR::SwitchStmt
|
|
49
|
+
statement.cases.any? { |switch_case| statements_reference_label?(switch_case.body, label_name) }
|
|
50
|
+
when IR::GotoStmt
|
|
51
|
+
statement.label == label_name
|
|
52
|
+
else
|
|
53
|
+
false
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def statements_need_explicit_break_label_after_emission?(statements, label_name, loop_break_label_active:)
|
|
58
|
+
statements.any? do |statement|
|
|
59
|
+
statement_needs_explicit_break_label_after_emission?(statement, label_name, loop_break_label_active:)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def statement_needs_explicit_break_label_after_emission?(statement, label_name, loop_break_label_active:)
|
|
64
|
+
case statement
|
|
65
|
+
when IR::BlockStmt, IR::IfStmt
|
|
66
|
+
then_body = statement.is_a?(IR::IfStmt) ? statement.then_body : statement.body
|
|
67
|
+
else_body = statement.is_a?(IR::IfStmt) ? statement.else_body : nil
|
|
68
|
+
statements_need_explicit_break_label_after_emission?(then_body, label_name, loop_break_label_active:) ||
|
|
69
|
+
(else_body && statements_need_explicit_break_label_after_emission?(else_body, label_name, loop_break_label_active:))
|
|
70
|
+
when IR::WhileStmt, IR::ForStmt
|
|
71
|
+
statements_need_explicit_break_label_after_emission?(statement.body, label_name, loop_break_label_active: false)
|
|
72
|
+
when IR::SwitchStmt
|
|
73
|
+
return false unless statement_references_label?(statement, label_name)
|
|
74
|
+
|
|
75
|
+
active = loop_break_label_active && switch_emittable_as_if?(statement, loop_break_label: label_name)
|
|
76
|
+
statements_need_explicit_break_label_after_emission?(
|
|
77
|
+
statement.cases.flat_map(&:body),
|
|
78
|
+
label_name,
|
|
79
|
+
loop_break_label_active: active,
|
|
80
|
+
)
|
|
81
|
+
when IR::GotoStmt
|
|
82
|
+
statement.label == label_name && !loop_break_label_active
|
|
83
|
+
else
|
|
84
|
+
false
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def switch_emittable_as_if?(statement, loop_break_label: nil)
|
|
89
|
+
return false unless loop_break_label
|
|
90
|
+
return false unless statement.exhaustive
|
|
91
|
+
return false unless statement.cases.length == 2
|
|
92
|
+
return false unless side_effect_free_expression?(statement.expression)
|
|
93
|
+
return false unless statement_references_label?(statement, loop_break_label)
|
|
94
|
+
|
|
95
|
+
statement.cases.count { |switch_case| switch_case.is_a?(IR::SwitchDefaultCase) } <= 1
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def emit_switch_as_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
|
|
99
|
+
explicit_cases = statement.cases.select { |switch_case| switch_case.is_a?(IR::SwitchCase) }
|
|
100
|
+
default_case = statement.cases.find { |switch_case| switch_case.is_a?(IR::SwitchDefaultCase) }
|
|
101
|
+
|
|
102
|
+
condition, then_body, else_body = if default_case
|
|
103
|
+
explicit_case = explicit_cases.first
|
|
104
|
+
[
|
|
105
|
+
IR::Binary.new(operator: "==", left: statement.expression, right: explicit_case.value, type: nil),
|
|
106
|
+
strip_terminal_switch_break(explicit_case.body),
|
|
107
|
+
strip_terminal_switch_break(default_case.body),
|
|
108
|
+
]
|
|
109
|
+
else
|
|
110
|
+
[
|
|
111
|
+
IR::Binary.new(operator: "==", left: statement.expression, right: explicit_cases.first.value, type: nil),
|
|
112
|
+
strip_terminal_switch_break(explicit_cases.first.body),
|
|
113
|
+
strip_terminal_switch_break(explicit_cases.last.body),
|
|
114
|
+
]
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
emit_if_statement(
|
|
118
|
+
IR::IfStmt.new(condition:, then_body:, else_body:),
|
|
119
|
+
level,
|
|
120
|
+
function:,
|
|
121
|
+
used_labels:,
|
|
122
|
+
loop_continue_label:,
|
|
123
|
+
loop_break_label:,
|
|
124
|
+
)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def strip_terminal_switch_break(statements)
|
|
128
|
+
return statements unless statements.last.is_a?(IR::BreakStmt)
|
|
129
|
+
|
|
130
|
+
statements[0...-1]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def canonicalize_top_guarded_while(statement)
|
|
134
|
+
return statement unless constant_boolean_value(statement.condition) == true
|
|
135
|
+
return statement if statement.body.empty?
|
|
136
|
+
|
|
137
|
+
break_condition = top_guard_break_condition(statement.body.first)
|
|
138
|
+
return statement unless break_condition
|
|
139
|
+
|
|
140
|
+
IR::WhileStmt.new(
|
|
141
|
+
condition: invert_break_guard_condition(break_condition),
|
|
142
|
+
body: statement.body.drop(1),
|
|
143
|
+
)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def top_guard_break_condition(statement)
|
|
147
|
+
return unless statement.is_a?(IR::IfStmt)
|
|
148
|
+
return unless statement.else_body.nil? || statement.else_body.empty?
|
|
149
|
+
return unless statement.then_body.length == 1 && statement.then_body.first.is_a?(IR::BreakStmt)
|
|
150
|
+
|
|
151
|
+
statement.condition
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def invert_break_guard_condition(expression)
|
|
155
|
+
case expression
|
|
156
|
+
when IR::BooleanLiteral
|
|
157
|
+
IR::BooleanLiteral.new(value: !expression.value, type: expression.type)
|
|
158
|
+
when IR::Unary
|
|
159
|
+
return expression.operand if expression.operator == "not"
|
|
160
|
+
when IR::Binary
|
|
161
|
+
if (operator = inverted_boolean_operator(expression.operator))
|
|
162
|
+
return IR::Binary.new(operator:, left: expression.left, right: expression.right, type: expression.type)
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
IR::Unary.new(operator: "not", operand: expression, type: expression.type)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def inverted_boolean_operator(operator)
|
|
170
|
+
INVERTED_BOOLEAN_OPS[operator]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def emit_if_statement(statement, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil)
|
|
174
|
+
indent = INDENT * level
|
|
175
|
+
|
|
176
|
+
case constant_boolean_value(statement.condition)
|
|
177
|
+
when true
|
|
178
|
+
return emit_statement(IR::BlockStmt.new(body: statement.then_body), level, function:, used_labels:, loop_continue_label:, loop_break_label:)
|
|
179
|
+
when false
|
|
180
|
+
return [] unless statement.else_body && !statement.else_body.empty?
|
|
181
|
+
|
|
182
|
+
return emit_statement(IR::BlockStmt.new(body: statement.else_body), level, function:, used_labels:, loop_continue_label:, loop_break_label:)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
lines = ["#{indent}if (#{emit_expression(statement.condition)}) {"]
|
|
186
|
+
lines.concat(emit_statement_sequence(statement.then_body, level + 1, function:, used_labels:, loop_continue_label:, loop_break_label:))
|
|
187
|
+
|
|
188
|
+
nested_else_if = nested_else_if_statement(statement.else_body)
|
|
189
|
+
if nested_else_if
|
|
190
|
+
nested_lines = emit_if_statement(nested_else_if, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
|
|
191
|
+
lines << "#{indent}} else #{nested_lines.first.sub(/^#{Regexp.escape(indent)}/, "") }"
|
|
192
|
+
lines.concat(nested_lines.drop(1))
|
|
193
|
+
return lines
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
if statement.else_body && !statement.else_body.empty?
|
|
197
|
+
lines << "#{indent}} else {"
|
|
198
|
+
lines.concat(emit_statement_sequence(statement.else_body, level + 1, function:, used_labels:, loop_continue_label:, loop_break_label:))
|
|
199
|
+
end
|
|
200
|
+
lines << "#{indent}}"
|
|
201
|
+
lines
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def nested_else_if_statement(else_body)
|
|
205
|
+
return unless else_body && else_body.length == 1
|
|
206
|
+
|
|
207
|
+
nested = else_body.first
|
|
208
|
+
nested if nested.is_a?(IR::IfStmt)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def body_terminates?(statements)
|
|
212
|
+
return false if statements.empty?
|
|
213
|
+
|
|
214
|
+
statement_terminates?(statements.last)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def body_needs_fallback_return?(statements)
|
|
218
|
+
return true if statements.empty?
|
|
219
|
+
|
|
220
|
+
!statement_prevents_c_fallthrough?(statements.last)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def constant_boolean_value(expression)
|
|
224
|
+
case expression
|
|
225
|
+
when IR::BooleanLiteral
|
|
226
|
+
expression.value
|
|
227
|
+
when IR::Unary
|
|
228
|
+
operand = constant_boolean_value(expression.operand)
|
|
229
|
+
return nil if operand.nil? || expression.operator != "not"
|
|
230
|
+
|
|
231
|
+
!operand
|
|
232
|
+
when IR::Binary
|
|
233
|
+
left_int = constant_integer_value(expression.left)
|
|
234
|
+
right_int = constant_integer_value(expression.right)
|
|
235
|
+
if !left_int.nil? && !right_int.nil?
|
|
236
|
+
return left_int == right_int if expression.operator == "=="
|
|
237
|
+
return left_int != right_int if expression.operator == "!="
|
|
238
|
+
return left_int < right_int if expression.operator == "<"
|
|
239
|
+
return left_int <= right_int if expression.operator == "<="
|
|
240
|
+
return left_int > right_int if expression.operator == ">"
|
|
241
|
+
return left_int >= right_int if expression.operator == ">="
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
left_bool = constant_boolean_value(expression.left)
|
|
245
|
+
right_bool = constant_boolean_value(expression.right)
|
|
246
|
+
if !left_bool.nil? && !right_bool.nil?
|
|
247
|
+
return left_bool == right_bool if expression.operator == "=="
|
|
248
|
+
return left_bool != right_bool if expression.operator == "!="
|
|
249
|
+
return left_bool && right_bool if expression.operator == "and"
|
|
250
|
+
return left_bool || right_bool if expression.operator == "or"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
nil
|
|
254
|
+
else
|
|
255
|
+
nil
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def constant_integer_value(expression)
|
|
260
|
+
case expression
|
|
261
|
+
when IR::IntegerLiteral
|
|
262
|
+
expression.value
|
|
263
|
+
when IR::Unary
|
|
264
|
+
operand = constant_integer_value(expression.operand)
|
|
265
|
+
return nil if operand.nil?
|
|
266
|
+
|
|
267
|
+
return operand if expression.operator == "+"
|
|
268
|
+
return -operand if expression.operator == "-"
|
|
269
|
+
|
|
270
|
+
nil
|
|
271
|
+
else
|
|
272
|
+
nil
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def statement_terminates?(statement)
|
|
277
|
+
case statement
|
|
278
|
+
when IR::ReturnStmt
|
|
279
|
+
true
|
|
280
|
+
when IR::BreakStmt, IR::ContinueStmt
|
|
281
|
+
true
|
|
282
|
+
when IR::GotoStmt
|
|
283
|
+
true
|
|
284
|
+
when IR::BlockStmt
|
|
285
|
+
body_terminates?(statement.body)
|
|
286
|
+
when IR::IfStmt
|
|
287
|
+
statement.else_body && body_terminates?(statement.then_body) && body_terminates?(statement.else_body)
|
|
288
|
+
when IR::SwitchStmt
|
|
289
|
+
switch_statement_prevents_outer_fallthrough?(statement)
|
|
290
|
+
else
|
|
291
|
+
false
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def statement_prevents_c_fallthrough?(statement)
|
|
296
|
+
case statement
|
|
297
|
+
when IR::ReturnStmt
|
|
298
|
+
true
|
|
299
|
+
when IR::BlockStmt
|
|
300
|
+
!body_needs_fallback_return?(statement.body)
|
|
301
|
+
when IR::IfStmt
|
|
302
|
+
statement.else_body && !body_needs_fallback_return?(statement.then_body) && !body_needs_fallback_return?(statement.else_body)
|
|
303
|
+
when IR::SwitchStmt
|
|
304
|
+
switch_statement_prevents_outer_fallthrough?(statement)
|
|
305
|
+
when IR::WhileStmt
|
|
306
|
+
constant_boolean_value(statement.condition) == true && !contains_visible_loop_exit?(statement.body)
|
|
307
|
+
else
|
|
308
|
+
false
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def statement_prevents_sequential_fallthrough?(statement)
|
|
313
|
+
case statement
|
|
314
|
+
when IR::ReturnStmt, IR::BreakStmt, IR::ContinueStmt, IR::GotoStmt
|
|
315
|
+
true
|
|
316
|
+
when IR::BlockStmt
|
|
317
|
+
!body_has_sequential_fallthrough?(statement.body)
|
|
318
|
+
when IR::IfStmt
|
|
319
|
+
statement.else_body && !body_has_sequential_fallthrough?(statement.then_body) && !body_has_sequential_fallthrough?(statement.else_body)
|
|
320
|
+
when IR::SwitchStmt
|
|
321
|
+
switch_statement_prevents_outer_fallthrough?(statement)
|
|
322
|
+
when IR::WhileStmt
|
|
323
|
+
constant_boolean_value(statement.condition) == true && !contains_visible_loop_exit?(statement.body)
|
|
324
|
+
else
|
|
325
|
+
false
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def switch_statement_prevents_outer_fallthrough?(statement)
|
|
330
|
+
return false unless statement.exhaustive && statement.cases.any?
|
|
331
|
+
|
|
332
|
+
statement.cases.all? { |switch_case| switch_case_body_prevents_outer_fallthrough?(switch_case.body) }
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def switch_case_body_prevents_outer_fallthrough?(statements)
|
|
336
|
+
return false if statements.empty?
|
|
337
|
+
|
|
338
|
+
switch_case_statement_prevents_outer_fallthrough?(statements.last)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def switch_case_statement_prevents_outer_fallthrough?(statement)
|
|
342
|
+
case statement
|
|
343
|
+
when IR::ReturnStmt, IR::ContinueStmt, IR::GotoStmt
|
|
344
|
+
true
|
|
345
|
+
when IR::BreakStmt
|
|
346
|
+
false
|
|
347
|
+
when IR::BlockStmt
|
|
348
|
+
switch_case_body_prevents_outer_fallthrough?(statement.body)
|
|
349
|
+
when IR::IfStmt
|
|
350
|
+
statement.else_body &&
|
|
351
|
+
switch_case_body_prevents_outer_fallthrough?(statement.then_body) &&
|
|
352
|
+
switch_case_body_prevents_outer_fallthrough?(statement.else_body)
|
|
353
|
+
when IR::SwitchStmt
|
|
354
|
+
switch_statement_prevents_outer_fallthrough?(statement)
|
|
355
|
+
when IR::WhileStmt
|
|
356
|
+
constant_boolean_value(statement.condition) == true && !contains_visible_loop_exit?(statement.body)
|
|
357
|
+
else
|
|
358
|
+
false
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def body_has_sequential_fallthrough?(statements)
|
|
363
|
+
return true if statements.empty?
|
|
364
|
+
|
|
365
|
+
!statement_prevents_sequential_fallthrough?(statements.last)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def contains_visible_loop_exit?(statements)
|
|
369
|
+
statements.any? do |statement|
|
|
370
|
+
case statement
|
|
371
|
+
when IR::BreakStmt, IR::GotoStmt
|
|
372
|
+
true
|
|
373
|
+
when IR::BlockStmt
|
|
374
|
+
contains_visible_loop_exit?(statement.body)
|
|
375
|
+
when IR::IfStmt
|
|
376
|
+
contains_visible_loop_exit?(statement.then_body) || (statement.else_body && contains_visible_loop_exit?(statement.else_body))
|
|
377
|
+
when IR::SwitchStmt
|
|
378
|
+
statement.cases.any? { |switch_case| contains_visible_loop_exit?(switch_case.body) }
|
|
379
|
+
when IR::WhileStmt, IR::ForStmt
|
|
380
|
+
false
|
|
381
|
+
else
|
|
382
|
+
false
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
def collect_used_labels(statements)
|
|
388
|
+
labels = []
|
|
389
|
+
collect_used_labels_from_statements(statements, labels)
|
|
390
|
+
labels.uniq
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def collect_used_labels_from_statements(statements, labels)
|
|
394
|
+
statements.each do |statement|
|
|
395
|
+
case statement
|
|
396
|
+
when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
|
|
397
|
+
collect_used_labels_from_statements(statement.body, labels)
|
|
398
|
+
when IR::IfStmt
|
|
399
|
+
collect_used_labels_from_statements(statement.then_body, labels)
|
|
400
|
+
collect_used_labels_from_statements(statement.else_body, labels) if statement.else_body
|
|
401
|
+
when IR::SwitchStmt
|
|
402
|
+
statement.cases.each do |switch_case|
|
|
403
|
+
collect_used_labels_from_statements(switch_case.body, labels)
|
|
404
|
+
end
|
|
405
|
+
when IR::GotoStmt
|
|
406
|
+
labels << statement.label
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
end
|