jade-lang 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +23 -0
- data/LICENSE +21 -0
- data/README.md +386 -0
- data/exe/jade +6 -0
- data/lib/jade/ast/node.rb +44 -0
- data/lib/jade/ast/nodes.rb +35 -0
- data/lib/jade/ast/pretty_printer.rb +50 -0
- data/lib/jade/ast.rb +723 -0
- data/lib/jade/calendar/runtime.rb +15 -0
- data/lib/jade/cli/fmt.rb +96 -0
- data/lib/jade/cli/lsp.rb +13 -0
- data/lib/jade/cli/q.rb +113 -0
- data/lib/jade/cli.rb +43 -0
- data/lib/jade/clock/runtime.rb +13 -0
- data/lib/jade/codegen/boundary/cache.rb +94 -0
- data/lib/jade/codegen/boundary/specialized/list.rb +65 -0
- data/lib/jade/codegen/boundary/specialized/maybe.rb +40 -0
- data/lib/jade/codegen/boundary/specialized/record.rb +165 -0
- data/lib/jade/codegen/boundary/specialized/scalar.rb +67 -0
- data/lib/jade/codegen/boundary/specialized.rb +106 -0
- data/lib/jade/codegen/boundary.rb +189 -0
- data/lib/jade/codegen/constructor_reference.rb +18 -0
- data/lib/jade/codegen/context.rb +96 -0
- data/lib/jade/codegen/emitter.rb +81 -0
- data/lib/jade/codegen/function_call.rb +367 -0
- data/lib/jade/codegen/function_declaration.rb +199 -0
- data/lib/jade/codegen/helpers.rb +103 -0
- data/lib/jade/codegen/implementation.rb +178 -0
- data/lib/jade/codegen/inline.rb +89 -0
- data/lib/jade/codegen/inlines.rb +326 -0
- data/lib/jade/codegen/method_names.rb +54 -0
- data/lib/jade/codegen/pattern/constructor.rb +57 -0
- data/lib/jade/codegen/port_decoder.rb +77 -0
- data/lib/jade/codegen/pretty.rb +53 -0
- data/lib/jade/codegen/transforms/fold_shape.rb +222 -0
- data/lib/jade/codegen/transforms/self_call.rb +80 -0
- data/lib/jade/codegen/transforms/tail_call.rb +120 -0
- data/lib/jade/codegen/variant_declaration.rb +41 -0
- data/lib/jade/codegen.rb +400 -0
- data/lib/jade/compiler.rb +69 -0
- data/lib/jade/decode.rb +320 -0
- data/lib/jade/diagnostics/renderer.rb +121 -0
- data/lib/jade/diagnostics.rb +77 -0
- data/lib/jade/did_you_mean.rb +16 -0
- data/lib/jade/entry.rb +177 -0
- data/lib/jade/error.rb +72 -0
- data/lib/jade/formatter/accesses.rb +37 -0
- data/lib/jade/formatter/bindings.rb +29 -0
- data/lib/jade/formatter/body.rb +50 -0
- data/lib/jade/formatter/calls.rb +51 -0
- data/lib/jade/formatter/case_of.rb +31 -0
- data/lib/jade/formatter/case_of_branch.rb +59 -0
- data/lib/jade/formatter/collections.rb +78 -0
- data/lib/jade/formatter/declarations.rb +178 -0
- data/lib/jade/formatter/exposing.rb +48 -0
- data/lib/jade/formatter/function_declaration.rb +72 -0
- data/lib/jade/formatter/helper.rb +122 -0
- data/lib/jade/formatter/if_then_else.rb +64 -0
- data/lib/jade/formatter/infix_application.rb +69 -0
- data/lib/jade/formatter/lambda.rb +50 -0
- data/lib/jade/formatter/leaves.rb +111 -0
- data/lib/jade/formatter/module_node.rb +26 -0
- data/lib/jade/formatter/pattern.rb +61 -0
- data/lib/jade/formatter/type.rb +67 -0
- data/lib/jade/formatter.rb +38 -0
- data/lib/jade/frontend/comment_attacher.rb +121 -0
- data/lib/jade/frontend/desugaring/placeholder.rb +39 -0
- data/lib/jade/frontend/desugaring/resolved.rb +63 -0
- data/lib/jade/frontend/desugaring.rb +217 -0
- data/lib/jade/frontend/fixity_fixer.rb +209 -0
- data/lib/jade/frontend/forward_declaration/body.rb +30 -0
- data/lib/jade/frontend/forward_declaration/error/bad_import.rb +19 -0
- data/lib/jade/frontend/forward_declaration/error/exposed_type_not_found.rb +18 -0
- data/lib/jade/frontend/forward_declaration/error/exposed_value_not_found.rb +18 -0
- data/lib/jade/frontend/forward_declaration/error/module_not_found.rb +18 -0
- data/lib/jade/frontend/forward_declaration/error/private_type_expansion.rb +19 -0
- data/lib/jade/frontend/forward_declaration/error/tuple_arity_overflow.rb +25 -0
- data/lib/jade/frontend/forward_declaration/error/type_not_found.rb +29 -0
- data/lib/jade/frontend/forward_declaration/error/type_not_lowerable.rb +16 -0
- data/lib/jade/frontend/forward_declaration/error/unknown_extends_interface.rb +18 -0
- data/lib/jade/frontend/forward_declaration/error.rb +11 -0
- data/lib/jade/frontend/forward_declaration/function_declaration.rb +32 -0
- data/lib/jade/frontend/forward_declaration/helper.rb +91 -0
- data/lib/jade/frontend/forward_declaration/implementation.rb +63 -0
- data/lib/jade/frontend/forward_declaration/implementation_function.rb +39 -0
- data/lib/jade/frontend/forward_declaration/import_declaration.rb +115 -0
- data/lib/jade/frontend/forward_declaration/interface_declaration.rb +66 -0
- data/lib/jade/frontend/forward_declaration/interop_import_declaration.rb +99 -0
- data/lib/jade/frontend/forward_declaration/module.rb +98 -0
- data/lib/jade/frontend/forward_declaration/struct_declaration.rb +42 -0
- data/lib/jade/frontend/forward_declaration/type_declaration.rb +42 -0
- data/lib/jade/frontend/forward_declaration.rb +71 -0
- data/lib/jade/frontend/pattern_analysis/exhaustiveness.rb +65 -0
- data/lib/jade/frontend/pattern_analysis/matrix.rb +235 -0
- data/lib/jade/frontend/pattern_analysis.rb +40 -0
- data/lib/jade/frontend/semantic_analysis/assign.rb +20 -0
- data/lib/jade/frontend/semantic_analysis/body.rb +33 -0
- data/lib/jade/frontend/semantic_analysis/case_of.rb +19 -0
- data/lib/jade/frontend/semantic_analysis/case_of_branch.rb +20 -0
- data/lib/jade/frontend/semantic_analysis/char_literal.rb +14 -0
- data/lib/jade/frontend/semantic_analysis/constructor_reference.rb +64 -0
- data/lib/jade/frontend/semantic_analysis/error/circular_extends.rb +19 -0
- data/lib/jade/frontend/semantic_analysis/error/constant_not_callable.rb +24 -0
- data/lib/jade/frontend/semantic_analysis/error/constructor_not_found.rb +34 -0
- data/lib/jade/frontend/semantic_analysis/error/constructor_pattern_arity_mismatch.rb +24 -0
- data/lib/jade/frontend/semantic_analysis/error/duplicate_field.rb +18 -0
- data/lib/jade/frontend/semantic_analysis/error/duplicate_function_declaration.rb +25 -0
- data/lib/jade/frontend/semantic_analysis/error/duplicate_record_field.rb +19 -0
- data/lib/jade/frontend/semantic_analysis/error/invalid_list_rest_pattern.rb +21 -0
- data/lib/jade/frontend/semantic_analysis/error/kwargs_on_non_constructor.rb +17 -0
- data/lib/jade/frontend/semantic_analysis/error/missing_exposing_clause.rb +17 -0
- data/lib/jade/frontend/semantic_analysis/error/missing_extends_implementation.rb +21 -0
- data/lib/jade/frontend/semantic_analysis/error/missing_field.rb +20 -0
- data/lib/jade/frontend/semantic_analysis/error/missing_implementation_function.rb +19 -0
- data/lib/jade/frontend/semantic_analysis/error/module_not_found.rb +22 -0
- data/lib/jade/frontend/semantic_analysis/error/nested_task_port.rb +19 -0
- data/lib/jade/frontend/semantic_analysis/error/non_task_port.rb +22 -0
- data/lib/jade/frontend/semantic_analysis/error/orphan_implementation.rb +20 -0
- data/lib/jade/frontend/semantic_analysis/error/predicate_must_return_bool.rb +22 -0
- data/lib/jade/frontend/semantic_analysis/error/predicate_name_not_allowed.rb +25 -0
- data/lib/jade/frontend/semantic_analysis/error/shadowing_error.rb +22 -0
- data/lib/jade/frontend/semantic_analysis/error/type_args_mismatch.rb +25 -0
- data/lib/jade/frontend/semantic_analysis/error/type_param_required.rb +19 -0
- data/lib/jade/frontend/semantic_analysis/error/unbound_type_variable.rb +22 -0
- data/lib/jade/frontend/semantic_analysis/error/undefined_variable.rb +29 -0
- data/lib/jade/frontend/semantic_analysis/error/unknown_field.rb +20 -0
- data/lib/jade/frontend/semantic_analysis/error/unknown_implementation_function.rb +19 -0
- data/lib/jade/frontend/semantic_analysis/error/unused_interface_type_param.rb +24 -0
- data/lib/jade/frontend/semantic_analysis/error/value_not_exposed.rb +23 -0
- data/lib/jade/frontend/semantic_analysis/error/variable_not_found.rb +25 -0
- data/lib/jade/frontend/semantic_analysis/error.rb +40 -0
- data/lib/jade/frontend/semantic_analysis/function_call.rb +60 -0
- data/lib/jade/frontend/semantic_analysis/function_declaration.rb +58 -0
- data/lib/jade/frontend/semantic_analysis/grouping.rb +17 -0
- data/lib/jade/frontend/semantic_analysis/helper.rb +152 -0
- data/lib/jade/frontend/semantic_analysis/if_then_else.rb +20 -0
- data/lib/jade/frontend/semantic_analysis/implementation.rb +143 -0
- data/lib/jade/frontend/semantic_analysis/implementation_function.rb +16 -0
- data/lib/jade/frontend/semantic_analysis/import_declaration.rb +14 -0
- data/lib/jade/frontend/semantic_analysis/interface_declaration.rb +45 -0
- data/lib/jade/frontend/semantic_analysis/interop_import_declaration.rb +69 -0
- data/lib/jade/frontend/semantic_analysis/keyed_call/validation.rb +109 -0
- data/lib/jade/frontend/semantic_analysis/keyed_call.rb +88 -0
- data/lib/jade/frontend/semantic_analysis/lambda.rb +23 -0
- data/lib/jade/frontend/semantic_analysis/list.rb +17 -0
- data/lib/jade/frontend/semantic_analysis/literal.rb +23 -0
- data/lib/jade/frontend/semantic_analysis/member_access.rb +87 -0
- data/lib/jade/frontend/semantic_analysis/module_node.rb +27 -0
- data/lib/jade/frontend/semantic_analysis/pattern_binding.rb +27 -0
- data/lib/jade/frontend/semantic_analysis/pattern_constructor.rb +47 -0
- data/lib/jade/frontend/semantic_analysis/pattern_list.rb +33 -0
- data/lib/jade/frontend/semantic_analysis/pattern_literal.rb +17 -0
- data/lib/jade/frontend/semantic_analysis/pattern_record.rb +25 -0
- data/lib/jade/frontend/semantic_analysis/pattern_wildcard.rb +14 -0
- data/lib/jade/frontend/semantic_analysis/qualified_access.rb +14 -0
- data/lib/jade/frontend/semantic_analysis/record_access.rb +14 -0
- data/lib/jade/frontend/semantic_analysis/record_field.rb +17 -0
- data/lib/jade/frontend/semantic_analysis/record_literal.rb +21 -0
- data/lib/jade/frontend/semantic_analysis/record_update.rb +21 -0
- data/lib/jade/frontend/semantic_analysis/struct_declaration.rb +44 -0
- data/lib/jade/frontend/semantic_analysis/tuple.rb +17 -0
- data/lib/jade/frontend/semantic_analysis/type_declaration.rb +69 -0
- data/lib/jade/frontend/semantic_analysis/variable_reference.rb +27 -0
- data/lib/jade/frontend/semantic_analysis/variant_declaration.rb +18 -0
- data/lib/jade/frontend/semantic_analysis.rb +161 -0
- data/lib/jade/frontend/type_checking/canonicalize.rb +97 -0
- data/lib/jade/frontend/type_checking/constraints/deriving/decodable.rb +144 -0
- data/lib/jade/frontend/type_checking/constraints/deriving/encodable.rb +144 -0
- data/lib/jade/frontend/type_checking/constraints/deriving/eq.rb +265 -0
- data/lib/jade/frontend/type_checking/constraints/deriving/helpers.rb +59 -0
- data/lib/jade/frontend/type_checking/constraints/deriving.rb +28 -0
- data/lib/jade/frontend/type_checking/constraints.rb +101 -0
- data/lib/jade/frontend/type_checking/definition.rb +71 -0
- data/lib/jade/frontend/type_checking/env.rb +79 -0
- data/lib/jade/frontend/type_checking/error/case_of_branches_type_mismatch.rb +19 -0
- data/lib/jade/frontend/type_checking/error/derivation_failed.rb +21 -0
- data/lib/jade/frontend/type_checking/error/function_body_type_mismatch.rb +23 -0
- data/lib/jade/frontend/type_checking/error/function_call_type_mismatch.rb +37 -0
- data/lib/jade/frontend/type_checking/error/if_branch_type_mismatch.rb +19 -0
- data/lib/jade/frontend/type_checking/error/if_branches_type_mismatch.rb +18 -0
- data/lib/jade/frontend/type_checking/error/if_condition_type_mismatch.rb +17 -0
- data/lib/jade/frontend/type_checking/error/implementation_type_mismatch.rb +20 -0
- data/lib/jade/frontend/type_checking/error/list_item_type_mismatch.rb +19 -0
- data/lib/jade/frontend/type_checking/error/missing_implementation.rb +20 -0
- data/lib/jade/frontend/type_checking/error/missing_patterns.rb +26 -0
- data/lib/jade/frontend/type_checking/error/pattern_type_mismatch.rb +13 -0
- data/lib/jade/frontend/type_checking/error/port_not_decodable.rb +38 -0
- data/lib/jade/frontend/type_checking/error/record_access_type_mismatch.rb +14 -0
- data/lib/jade/frontend/type_checking/error/type_mismatch.rb +23 -0
- data/lib/jade/frontend/type_checking/error/unresolved_constraint.rb +20 -0
- data/lib/jade/frontend/type_checking/error.rb +18 -0
- data/lib/jade/frontend/type_checking/expected.rb +23 -0
- data/lib/jade/frontend/type_checking/generalization.rb +17 -0
- data/lib/jade/frontend/type_checking/generalizer.rb +38 -0
- data/lib/jade/frontend/type_checking/inference/assign.rb +58 -0
- data/lib/jade/frontend/type_checking/inference/body.rb +45 -0
- data/lib/jade/frontend/type_checking/inference/case_of.rb +102 -0
- data/lib/jade/frontend/type_checking/inference/constructor_reference.rb +21 -0
- data/lib/jade/frontend/type_checking/inference/function_call.rb +132 -0
- data/lib/jade/frontend/type_checking/inference/function_declaration.rb +70 -0
- data/lib/jade/frontend/type_checking/inference/grouping.rb +18 -0
- data/lib/jade/frontend/type_checking/inference/helpers.rb +34 -0
- data/lib/jade/frontend/type_checking/inference/if_then_else.rb +46 -0
- data/lib/jade/frontend/type_checking/inference/implementation.rb +150 -0
- data/lib/jade/frontend/type_checking/inference/import_declaration.rb +19 -0
- data/lib/jade/frontend/type_checking/inference/interface_declaration.rb +18 -0
- data/lib/jade/frontend/type_checking/inference/interop_import_declaration.rb +18 -0
- data/lib/jade/frontend/type_checking/inference/lambda.rb +87 -0
- data/lib/jade/frontend/type_checking/inference/list.rb +52 -0
- data/lib/jade/frontend/type_checking/inference/literal.rb +24 -0
- data/lib/jade/frontend/type_checking/inference/module.rb +18 -0
- data/lib/jade/frontend/type_checking/inference/pattern.rb +135 -0
- data/lib/jade/frontend/type_checking/inference/qualified_access.rb +23 -0
- data/lib/jade/frontend/type_checking/inference/record_access.rb +35 -0
- data/lib/jade/frontend/type_checking/inference/record_field.rb +19 -0
- data/lib/jade/frontend/type_checking/inference/record_literal.rb +24 -0
- data/lib/jade/frontend/type_checking/inference/record_update.rb +37 -0
- data/lib/jade/frontend/type_checking/inference/struct_declaration.rb +18 -0
- data/lib/jade/frontend/type_checking/inference/type_declaration.rb +18 -0
- data/lib/jade/frontend/type_checking/inference/variable_reference.rb +27 -0
- data/lib/jade/frontend/type_checking/inference.rb +27 -0
- data/lib/jade/frontend/type_checking/instantiation.rb +24 -0
- data/lib/jade/frontend/type_checking/loader.rb +80 -0
- data/lib/jade/frontend/type_checking/placeholder.rb +12 -0
- data/lib/jade/frontend/type_checking/port_resolution.rb +123 -0
- data/lib/jade/frontend/type_checking/result.rb +41 -0
- data/lib/jade/frontend/type_checking/scheme.rb +20 -0
- data/lib/jade/frontend/type_checking/state.rb +52 -0
- data/lib/jade/frontend/type_checking/substitution.rb +93 -0
- data/lib/jade/frontend/type_checking/unification.rb +282 -0
- data/lib/jade/frontend/type_checking/var_gen.rb +33 -0
- data/lib/jade/frontend/type_checking.rb +129 -0
- data/lib/jade/frontend/unused_analysis.rb +41 -0
- data/lib/jade/frontend/usage_analysis/reference_index.rb +53 -0
- data/lib/jade/frontend/usage_analysis.rb +195 -0
- data/lib/jade/frontend.rb +101 -0
- data/lib/jade/interop/boundary.rb +68 -0
- data/lib/jade/interop/error.rb +84 -0
- data/lib/jade/interop/lowering/error.rb +32 -0
- data/lib/jade/interop/lowering.rb +53 -0
- data/lib/jade/interop/runtime.rb +24 -0
- data/lib/jade/interop.rb +1 -0
- data/lib/jade/lexer.rb +189 -0
- data/lib/jade/lsp/converters.rb +542 -0
- data/lib/jade/lsp/handlers.rb +340 -0
- data/lib/jade/lsp/server.rb +63 -0
- data/lib/jade/lsp/snippets.rb +100 -0
- data/lib/jade/lsp/state.rb +25 -0
- data/lib/jade/lsp.rb +16 -0
- data/lib/jade/module_loader/cache.rb +56 -0
- data/lib/jade/module_loader/dependency_graph.rb +23 -0
- data/lib/jade/module_loader/dependency_resolver.rb +48 -0
- data/lib/jade/module_loader/normalize.rb +34 -0
- data/lib/jade/module_loader/topological_sort.rb +41 -0
- data/lib/jade/module_loader.rb +127 -0
- data/lib/jade/parsing/combinators.rb +291 -0
- data/lib/jade/parsing/error.rb +154 -0
- data/lib/jade/parsing/token.rb +12 -0
- data/lib/jade/parsing/type.rb +92 -0
- data/lib/jade/parsing.rb +674 -0
- data/lib/jade/port.rb +1 -0
- data/lib/jade/registry.rb +79 -0
- data/lib/jade/result.rb +121 -0
- data/lib/jade/runtime.rb +127 -0
- data/lib/jade/source.rb +62 -0
- data/lib/jade/stdlib/basics.rb +214 -0
- data/lib/jade/stdlib/bytes.rb +70 -0
- data/lib/jade/stdlib/calendar.rb +405 -0
- data/lib/jade/stdlib/char.rb +27 -0
- data/lib/jade/stdlib/clock.rb +342 -0
- data/lib/jade/stdlib/compiled.rb +48 -0
- data/lib/jade/stdlib/decode/params.rb +154 -0
- data/lib/jade/stdlib/decode.rb +315 -0
- data/lib/jade/stdlib/dict.rb +134 -0
- data/lib/jade/stdlib/encode.rb +143 -0
- data/lib/jade/stdlib/intrinsics.rb +280 -0
- data/lib/jade/stdlib/list.rb +214 -0
- data/lib/jade/stdlib/maybe.rb +73 -0
- data/lib/jade/stdlib/result.rb +131 -0
- data/lib/jade/stdlib/set.rb +123 -0
- data/lib/jade/stdlib/string.rb +65 -0
- data/lib/jade/stdlib/task.rb +55 -0
- data/lib/jade/stdlib/tuple.rb +21 -0
- data/lib/jade/stdlib.rb +112 -0
- data/lib/jade/symbol/anonymous_record.rb +7 -0
- data/lib/jade/symbol/base.rb +15 -0
- data/lib/jade/symbol/constructor.rb +11 -0
- data/lib/jade/symbol/derived_function.rb +5 -0
- data/lib/jade/symbol/function.rb +15 -0
- data/lib/jade/symbol/function_type.rb +7 -0
- data/lib/jade/symbol/implementation.rb +17 -0
- data/lib/jade/symbol/implementation_template.rb +13 -0
- data/lib/jade/symbol/interface.rb +11 -0
- data/lib/jade/symbol/interface_function.rb +18 -0
- data/lib/jade/symbol/interop_function.rb +22 -0
- data/lib/jade/symbol/lambda.rb +7 -0
- data/lib/jade/symbol/parser.rb +79 -0
- data/lib/jade/symbol/partial_application.rb +7 -0
- data/lib/jade/symbol/record_type.rb +8 -0
- data/lib/jade/symbol/stdlib_function.rb +15 -0
- data/lib/jade/symbol/stdlib_implementation.rb +7 -0
- data/lib/jade/symbol/struct.rb +15 -0
- data/lib/jade/symbol/type_application.rb +8 -0
- data/lib/jade/symbol/type_ref.rb +11 -0
- data/lib/jade/symbol/union.rb +15 -0
- data/lib/jade/symbol/value_ref.rb +15 -0
- data/lib/jade/symbol/variable.rb +7 -0
- data/lib/jade/symbol/variant.rb +11 -0
- data/lib/jade/symbol.rb +162 -0
- data/lib/jade/task.rb +103 -0
- data/lib/jade/tasks/rspec.rb +266 -0
- data/lib/jade/tasks.rb +70 -0
- data/lib/jade/type/anonymous_record.rb +33 -0
- data/lib/jade/type/application.rb +21 -0
- data/lib/jade/type/base.rb +9 -0
- data/lib/jade/type/constraint.rb +17 -0
- data/lib/jade/type/constructor.rb +19 -0
- data/lib/jade/type/function.rb +18 -0
- data/lib/jade/type/partial_application.rb +17 -0
- data/lib/jade/type/unit.rb +15 -0
- data/lib/jade/type/var.rb +21 -0
- data/lib/jade/type.rb +259 -0
- data/lib/jade/version.rb +3 -0
- data/lib/jade.rb +55 -0
- metadata +387 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module Pattern
|
|
4
|
+
module Constructor
|
|
5
|
+
extend self
|
|
6
|
+
extend Helpers
|
|
7
|
+
|
|
8
|
+
def generate(node, registry)
|
|
9
|
+
node => AST::Pattern::Constructor(symbol:, patterns:)
|
|
10
|
+
constructor = registry.lookup(symbol)
|
|
11
|
+
qualified = to_qualified(constructor.qualified_name)
|
|
12
|
+
|
|
13
|
+
if keyed_variant?(constructor)
|
|
14
|
+
generate_keyed(qualified, patterns.first, registry)
|
|
15
|
+
else
|
|
16
|
+
generate_positional(qualified, patterns, registry)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def keyed_variant?(constructor)
|
|
23
|
+
constructor.args in [Symbol::RecordType]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def generate_positional(qualified, patterns, registry)
|
|
27
|
+
patterns
|
|
28
|
+
.map { generate_node(it, registry) }
|
|
29
|
+
.join(', ')
|
|
30
|
+
.then { it.empty? ? it : "(#{it})" }
|
|
31
|
+
.then { "#{qualified}#{it}" }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Keyed variants have a single inner pattern that conceptually matches
|
|
35
|
+
# the record-shaped payload. The runtime class itself carries the
|
|
36
|
+
# record's fields, so we project the pattern onto the variant directly:
|
|
37
|
+
# binding/wildcard captures the whole instance; record patterns
|
|
38
|
+
# destructure via Ruby's Data deconstruct_keys.
|
|
39
|
+
def generate_keyed(qualified, pattern, registry)
|
|
40
|
+
case pattern
|
|
41
|
+
in AST::Pattern::Binding(name:)
|
|
42
|
+
"#{qualified} => #{name}"
|
|
43
|
+
|
|
44
|
+
in AST::Pattern::Wildcard
|
|
45
|
+
qualified
|
|
46
|
+
|
|
47
|
+
in AST::Pattern::Record(fields:)
|
|
48
|
+
fields
|
|
49
|
+
.map { generate_node(it, registry) }
|
|
50
|
+
.join(', ')
|
|
51
|
+
.then { "#{qualified}(#{it})" }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module PortDecoder
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
PASS = "Jade::Decode::Decoder[Jade::Decode::Desc::Pass[]]"
|
|
7
|
+
|
|
8
|
+
# `dictionaries` is the call site's constraint-attachment list; only
|
|
9
|
+
# used when an arm is a Dict marker.
|
|
10
|
+
def task_call(interop_fn, registry, dictionaries = [])
|
|
11
|
+
[
|
|
12
|
+
interop_fn.interop_module_name,
|
|
13
|
+
":#{interop_fn.name}",
|
|
14
|
+
decoder(interop_fn, :ok, registry, dictionaries),
|
|
15
|
+
decoder(interop_fn, :err, registry, dictionaries),
|
|
16
|
+
]
|
|
17
|
+
.join(', ')
|
|
18
|
+
.then { "Jade::Runtime.task_call(#{it})" }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
# Per-arm decoder. PortResolution stamps one of:
|
|
24
|
+
# - :pass — Decode.Value or Never arm, no decoder needed
|
|
25
|
+
# - Symbol::Implementation — concrete OR partial impl (with marker
|
|
26
|
+
# deps for free vars). We push a synthetic dict_env mapping the
|
|
27
|
+
# port's vars to the call-site dictionaries so generate_impl_dispatch
|
|
28
|
+
# can resolve markers uniformly.
|
|
29
|
+
# - Symbol::InteropFunction::Dict — bare-var arm, decoder comes
|
|
30
|
+
# from the caller's threaded dictionary at the given constraint index
|
|
31
|
+
def decoder(interop_fn, arm, registry, dictionaries)
|
|
32
|
+
case interop_fn.decoders.fetch(arm)
|
|
33
|
+
in Symbol::InteropFunction::PASS
|
|
34
|
+
PASS
|
|
35
|
+
|
|
36
|
+
in Symbol::Implementation => impl
|
|
37
|
+
emit_impl_decoder(impl, interop_fn, dictionaries, registry)
|
|
38
|
+
|
|
39
|
+
in Symbol::InteropFunction::Dict(constraint_index:)
|
|
40
|
+
"#{FunctionCall.dispatch_value(dictionaries.fetch(constraint_index), registry)}[\"decoder\"]"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def emit_impl_decoder(impl, interop_fn, dictionaries, registry)
|
|
45
|
+
synthetic_env = build_synthetic_dict_env(impl, interop_fn, dictionaries, registry)
|
|
46
|
+
|
|
47
|
+
Codegen.with_dict_env(synthetic_env) {
|
|
48
|
+
FunctionCall.generate_impl_dispatch(impl, registry).fetch('decoder')
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Maps [iface, port_var_id] → ruby expression for the call-site dict.
|
|
53
|
+
# For concrete impls (no markers) this is empty — generate_impl_dispatch
|
|
54
|
+
# never reads dict_env. For partial impls, each marker dep's Type::Var
|
|
55
|
+
# gets resolved to the dictionaries[i] corresponding to its name.
|
|
56
|
+
def build_synthetic_dict_env(impl, interop_fn, dictionaries, registry)
|
|
57
|
+
collect_marker_vars(impl).to_h do |iface, var|
|
|
58
|
+
idx = interop_fn.constraints.index { |i, n| i == iface && n == var.name }
|
|
59
|
+
[[iface, var.id], FunctionCall.dispatch_value(dictionaries.fetch(idx), registry)]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def collect_marker_vars(impl, acc = [])
|
|
64
|
+
impl.deps.each do |dep|
|
|
65
|
+
case dep
|
|
66
|
+
in Symbol::Implementation
|
|
67
|
+
collect_marker_vars(dep, acc)
|
|
68
|
+
|
|
69
|
+
in Type::Constraint(interface:, type: Type::Var => var)
|
|
70
|
+
acc << [interface, var]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
acc.uniq { |iface, var| [iface, var.id] }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module Pretty
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
INDENT = " "
|
|
7
|
+
|
|
8
|
+
def newline(count = 1)
|
|
9
|
+
"\n" * count
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def indent(str, levels = 1)
|
|
13
|
+
str.gsub(/^(?!$)/, INDENT * levels)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def block(header, body, footer = "end")
|
|
17
|
+
body.empty? ? "#{header}\n#{footer}" : "#{header}\n#{indent(body)}\n#{footer}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def lambda(params, body)
|
|
21
|
+
multiline?(body) ? block("->(#{params}) {", body, "}") : "->(#{params}) { #{body} }"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def hash(pairs)
|
|
25
|
+
pairs
|
|
26
|
+
.map { |k, v| "#{k.inspect} => #{v}" }
|
|
27
|
+
.join(', ')
|
|
28
|
+
.then { "{ #{it} }" }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def array(items)
|
|
32
|
+
"[#{items.join(', ')}]"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# `callee(a, b, c)` on one line when it fits, multi-line with trailing
|
|
36
|
+
# comma when it doesn't or when an arg is already multi-line. Pass
|
|
37
|
+
# `open:`/`close:` to emit `Struct[a, b, c]`-shaped construction.
|
|
38
|
+
def call(callee, args, width: 80, open: '(', close: ')')
|
|
39
|
+
"#{callee}#{open}#{args.join(', ')}#{close}".then do |oneline|
|
|
40
|
+
if oneline.length <= width && args.none? { multiline?(it) }
|
|
41
|
+
oneline
|
|
42
|
+
else
|
|
43
|
+
"#{callee}#{open}\n#{indent(args.join(",\n"))},\n#{close}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def multiline?(str)
|
|
49
|
+
str.include?("\n")
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module Transforms
|
|
4
|
+
module FoldShape
|
|
5
|
+
extend self
|
|
6
|
+
extend Helpers
|
|
7
|
+
|
|
8
|
+
ACC_NAME = '__fold_acc__'
|
|
9
|
+
|
|
10
|
+
# The right-fold shape:
|
|
11
|
+
#
|
|
12
|
+
# def f(xs) ->
|
|
13
|
+
# case xs
|
|
14
|
+
# of [] -> BASE (no self-call)
|
|
15
|
+
# of [h | rest] -> COMBINE (exactly one f(rest), strict)
|
|
16
|
+
#
|
|
17
|
+
# Returns a shape hash, or nil if the body doesn't match.
|
|
18
|
+
def shape_for(body, self_sym, param_names, registry)
|
|
19
|
+
return nil unless param_names.size == 1
|
|
20
|
+
|
|
21
|
+
sig = [self_sym, 1]
|
|
22
|
+
expr = unwrap_body(body)
|
|
23
|
+
return nil unless expr.is_a?(AST::CaseOf)
|
|
24
|
+
return nil unless expr.branches.size == 2
|
|
25
|
+
|
|
26
|
+
list_arg = param_names.first
|
|
27
|
+
return nil unless subject_is_arg?(expr.expression, list_arg)
|
|
28
|
+
|
|
29
|
+
empty_branch = expr.branches.find { empty_list_pattern?(it.pattern) }
|
|
30
|
+
cons_branch = expr.branches.find { cons_pattern?(it.pattern) }
|
|
31
|
+
return nil unless empty_branch && cons_branch
|
|
32
|
+
return nil if SelfCall.contains_self_call?(
|
|
33
|
+
empty_branch.body, sig, registry
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
head_name = head_binding_name(cons_branch.pattern)
|
|
37
|
+
rest_name = rest_binding_name(cons_branch.pattern)
|
|
38
|
+
return nil unless head_name && rest_name
|
|
39
|
+
|
|
40
|
+
calls = strict_self_calls(cons_branch.body, sig, registry)
|
|
41
|
+
return nil unless calls&.size == 1
|
|
42
|
+
|
|
43
|
+
call = calls.first
|
|
44
|
+
return nil unless call.args.size == 1
|
|
45
|
+
|
|
46
|
+
arg = call.args.first
|
|
47
|
+
return nil unless arg in AST::VariableReference(name: ^rest_name)
|
|
48
|
+
return nil unless rest_used_only?(cons_branch.body, rest_name, call)
|
|
49
|
+
|
|
50
|
+
{
|
|
51
|
+
list_arg: list_arg,
|
|
52
|
+
base: empty_branch.body,
|
|
53
|
+
head_name: head_name,
|
|
54
|
+
combine: cons_branch.body,
|
|
55
|
+
self_call: call,
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Emits `xs.reverse.reduce(BASE) { |acc, head| COMBINE' }`. The
|
|
60
|
+
# `reverse` gives right-associative combine order regardless of the
|
|
61
|
+
# combiner's algebraic properties. Task effects are unaffected by
|
|
62
|
+
# build order — only the final Task chain shape determines run-time
|
|
63
|
+
# behavior.
|
|
64
|
+
def generate_body(shape, registry)
|
|
65
|
+
shape => { list_arg:, base:, head_name:, combine:, self_call: }
|
|
66
|
+
|
|
67
|
+
base_code = generate_node(base, registry)
|
|
68
|
+
combine_code = substitute(combine, self_call)
|
|
69
|
+
.then { generate_node(it, registry) }
|
|
70
|
+
|
|
71
|
+
"#{list_arg}.reverse.reduce(#{base_code}) " \
|
|
72
|
+
"{ |#{ACC_NAME}, #{head_name}| #{combine_code} }"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def unwrap_body(node)
|
|
78
|
+
case node
|
|
79
|
+
in AST::Body(expressions: [single]) then unwrap_body(single)
|
|
80
|
+
in AST::Grouping(expression:) then unwrap_body(expression)
|
|
81
|
+
else node
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def subject_is_arg?(expression, arg_name)
|
|
86
|
+
expression in AST::VariableReference(name: ^arg_name)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def empty_list_pattern?(pattern)
|
|
90
|
+
pattern in AST::Pattern::List(patterns: [], rest: nil)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Only binding/wildcard heads qualify. Literal or constructor heads
|
|
94
|
+
# (`[True | rest]`, `[1 | rest]`) make the source function partial
|
|
95
|
+
# over the head; folding it would silently widen it to total.
|
|
96
|
+
def cons_pattern?(pattern)
|
|
97
|
+
case pattern
|
|
98
|
+
in AST::Pattern::List(
|
|
99
|
+
patterns: [AST::Pattern::Binding | AST::Pattern::Wildcard],
|
|
100
|
+
rest: AST::Pattern::Binding
|
|
101
|
+
) then true
|
|
102
|
+
else false
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def rest_binding_name(pattern)
|
|
107
|
+
case pattern
|
|
108
|
+
in AST::Pattern::List(rest: AST::Pattern::Binding(name:)) then name
|
|
109
|
+
else nil
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def head_binding_name(pattern)
|
|
114
|
+
case pattern
|
|
115
|
+
in AST::Pattern::List(patterns: [AST::Pattern::Binding(name:)])
|
|
116
|
+
name
|
|
117
|
+
|
|
118
|
+
in AST::Pattern::List(patterns: [AST::Pattern::Wildcard])
|
|
119
|
+
'_'
|
|
120
|
+
|
|
121
|
+
else
|
|
122
|
+
nil
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# The emitted block binds only `head` and `acc` — not `rest`. Any
|
|
127
|
+
# reference to `rest` in COMBINE outside the chosen self-call's
|
|
128
|
+
# argument would emit unbound Ruby.
|
|
129
|
+
def rest_used_only?(node, rest_name, call)
|
|
130
|
+
return true if node.equal?(call)
|
|
131
|
+
return false if node in AST::VariableReference(name: ^rest_name)
|
|
132
|
+
|
|
133
|
+
SelfCall.child_values(node)
|
|
134
|
+
.all? { rest_check_value(it, rest_name, call) }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def rest_check_value(value, rest_name, call)
|
|
138
|
+
case value
|
|
139
|
+
when Array then value.all? { rest_check_value(it, rest_name, call) }
|
|
140
|
+
when AST::Node then rest_used_only?(value, rest_name, call)
|
|
141
|
+
else true
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Collects self-calls in strict (always-evaluated) positions. Returns
|
|
146
|
+
# nil when any self-call sits in a lazy position — a Lambda body,
|
|
147
|
+
# an If branch, or a Case branch body — because `reduce` walks every
|
|
148
|
+
# element unconditionally and can't skip or duplicate the recursive
|
|
149
|
+
# step the way the source might.
|
|
150
|
+
def strict_self_calls(node, sig, registry)
|
|
151
|
+
deep = ->(n) {
|
|
152
|
+
SelfCall.contains_self_call_anywhere?(n, sig, registry)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
case node
|
|
156
|
+
in AST::FunctionCall if SelfCall.self_call?(node, sig, registry)
|
|
157
|
+
return nil if node.args.any? { deep.call(it) }
|
|
158
|
+
[node]
|
|
159
|
+
|
|
160
|
+
in AST::Lambda
|
|
161
|
+
deep.call(node) ? nil : []
|
|
162
|
+
|
|
163
|
+
in AST::IfThenElse(condition:, if_branch:, else_branch:)
|
|
164
|
+
return nil if deep.call(if_branch)
|
|
165
|
+
return nil if deep.call(else_branch)
|
|
166
|
+
strict_self_calls(condition, sig, registry)
|
|
167
|
+
|
|
168
|
+
in AST::CaseOf(expression:, branches:)
|
|
169
|
+
return nil if branches.any? { deep.call(it.body) }
|
|
170
|
+
strict_self_calls(expression, sig, registry)
|
|
171
|
+
|
|
172
|
+
else
|
|
173
|
+
SelfCall.child_values(node)
|
|
174
|
+
.map { collect_strict_value(it, sig, registry) }
|
|
175
|
+
.then { it.any?(&:nil?) ? nil : it.flatten(1) }
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def collect_strict_value(value, sig, registry)
|
|
180
|
+
case value
|
|
181
|
+
when Array
|
|
182
|
+
value
|
|
183
|
+
.map { collect_strict_value(it, sig, registry) }
|
|
184
|
+
.then { it.any?(&:nil?) ? nil : it.flatten(1) }
|
|
185
|
+
when AST::Node
|
|
186
|
+
strict_self_calls(value, sig, registry)
|
|
187
|
+
else
|
|
188
|
+
[]
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# AST nodes are immutable Data; rebuilds the tree with `target`
|
|
193
|
+
# replaced by a fresh `__fold_acc__` reference.
|
|
194
|
+
def substitute(node, target)
|
|
195
|
+
return acc_var_ref if node.equal?(target)
|
|
196
|
+
return node unless node.is_a?(AST::Node)
|
|
197
|
+
|
|
198
|
+
node
|
|
199
|
+
.to_h
|
|
200
|
+
.transform_values { substitute_value(it, target) }
|
|
201
|
+
.then { node.class.new(**it) }
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def substitute_value(value, target)
|
|
205
|
+
case value
|
|
206
|
+
when Array then value.map { substitute_value(it, target) }
|
|
207
|
+
when AST::Node then substitute(value, target)
|
|
208
|
+
else value
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def acc_var_ref
|
|
213
|
+
AST::VariableReference.new(
|
|
214
|
+
name: ACC_NAME,
|
|
215
|
+
symbol: Symbol::Variable.new(name: ACC_NAME, decl_span: nil),
|
|
216
|
+
range: nil,
|
|
217
|
+
)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module Transforms
|
|
4
|
+
# AST-walking primitives for recognizing self-recursive calls. A
|
|
5
|
+
# "self-call signature" is the pair `[self_sym, arity]`: a ValueRef
|
|
6
|
+
# carrying module_name + name, paired with the function's user-declared
|
|
7
|
+
# arity (partial applications never qualify).
|
|
8
|
+
module SelfCall
|
|
9
|
+
extend self
|
|
10
|
+
|
|
11
|
+
METADATA_KEYS = %i[
|
|
12
|
+
range symbol id
|
|
13
|
+
leading_comments trailing_comments dangling_comments
|
|
14
|
+
trailing_comma
|
|
15
|
+
].freeze
|
|
16
|
+
|
|
17
|
+
# Defensive on `node` — anything that isn't a FunctionCall returns
|
|
18
|
+
# false, so callers don't need to type-check before calling.
|
|
19
|
+
def self_call?(node, sig, registry)
|
|
20
|
+
return false unless node.is_a?(AST::FunctionCall)
|
|
21
|
+
|
|
22
|
+
self_sym, arity = sig
|
|
23
|
+
resolved = resolve(node.callee.symbol, registry)
|
|
24
|
+
|
|
25
|
+
resolved.is_a?(Symbol::Function) &&
|
|
26
|
+
resolved.module_name == self_sym.module_name &&
|
|
27
|
+
resolved.name == self_sym.name &&
|
|
28
|
+
node.args.size == arity
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Scope-aware: stops at Lambda boundaries. A self-call inside a
|
|
32
|
+
# lambda body belongs to that lambda, not to the enclosing function.
|
|
33
|
+
def contains_self_call?(node, sig, registry)
|
|
34
|
+
return true if self_call?(node, sig, registry)
|
|
35
|
+
return false if node.is_a?(AST::Lambda)
|
|
36
|
+
|
|
37
|
+
child_descendants(node)
|
|
38
|
+
.any? { contains_self_call?(it, sig, registry) }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Scope-blind: descends into Lambda bodies. For disqualifying any
|
|
42
|
+
# subtree that textually mentions a self-call at any depth.
|
|
43
|
+
def contains_self_call_anywhere?(node, sig, registry)
|
|
44
|
+
return true if self_call?(node, sig, registry)
|
|
45
|
+
|
|
46
|
+
child_descendants(node)
|
|
47
|
+
.any? { contains_self_call_anywhere?(it, sig, registry) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def child_values(node)
|
|
51
|
+
node
|
|
52
|
+
.to_h
|
|
53
|
+
.reject { |k, _| METADATA_KEYS.include?(k) }
|
|
54
|
+
.values
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def resolve(symbol, registry)
|
|
60
|
+
case symbol
|
|
61
|
+
in Symbol::ValueRef => ref then registry.lookup(ref)
|
|
62
|
+
in s then s
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Flat list of direct AST::Node descendants, dipping into array
|
|
67
|
+
# fields (e.g. FunctionCall#args) but skipping scalars.
|
|
68
|
+
def child_descendants(node)
|
|
69
|
+
child_values(node).flat_map do |v|
|
|
70
|
+
case v
|
|
71
|
+
when Array then v.grep(AST::Node)
|
|
72
|
+
when AST::Node then [v]
|
|
73
|
+
else []
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module Transforms
|
|
4
|
+
module TailCall
|
|
5
|
+
extend self
|
|
6
|
+
extend Helpers
|
|
7
|
+
|
|
8
|
+
def tail_recursive?(body, self_sym, arity, registry)
|
|
9
|
+
classify(body, [self_sym, arity], registry) == :tail
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def generate_body(body, registry, self_sym, param_names)
|
|
13
|
+
[self_sym, param_names.size]
|
|
14
|
+
.then { emit_tail(body, registry, it, param_names) }
|
|
15
|
+
.then { Pretty.block("loop do", it) }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def classify(node, sig, registry)
|
|
21
|
+
has_self = ->(n) { SelfCall.contains_self_call?(n, sig, registry) }
|
|
22
|
+
|
|
23
|
+
case node
|
|
24
|
+
in AST::FunctionCall if SelfCall.self_call?(node, sig, registry)
|
|
25
|
+
node.args.any? { has_self.call(it) } ? :non_tail : :tail
|
|
26
|
+
|
|
27
|
+
in AST::Body(expressions:)
|
|
28
|
+
*leading, last = expressions
|
|
29
|
+
if leading.any? { has_self.call(it) }
|
|
30
|
+
:non_tail
|
|
31
|
+
else
|
|
32
|
+
classify(last, sig, registry)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
in AST::IfThenElse(condition:, if_branch:, else_branch:)
|
|
36
|
+
if has_self.call(condition)
|
|
37
|
+
:non_tail
|
|
38
|
+
else
|
|
39
|
+
combine(
|
|
40
|
+
classify(if_branch, sig, registry),
|
|
41
|
+
classify(else_branch, sig, registry),
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
in AST::CaseOf(expression:, branches:)
|
|
46
|
+
if has_self.call(expression)
|
|
47
|
+
:non_tail
|
|
48
|
+
else
|
|
49
|
+
branches
|
|
50
|
+
.map { classify(it.body, sig, registry) }
|
|
51
|
+
.reduce(:none) { |acc, c| combine(acc, c) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
in AST::Grouping(expression:)
|
|
55
|
+
classify(expression, sig, registry)
|
|
56
|
+
|
|
57
|
+
else
|
|
58
|
+
has_self.call(node) ? :non_tail : :none
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def combine(a, b)
|
|
63
|
+
return :non_tail if a == :non_tail || b == :non_tail
|
|
64
|
+
return :tail if a == :tail || b == :tail
|
|
65
|
+
:none
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def emit_tail(node, registry, sig, params)
|
|
69
|
+
case node
|
|
70
|
+
in AST::FunctionCall if SelfCall.self_call?(node, sig, registry)
|
|
71
|
+
node.args
|
|
72
|
+
.map { generate_node(it, registry) }
|
|
73
|
+
.join(', ')
|
|
74
|
+
.then { "#{params.join(', ')} = #{it}" }
|
|
75
|
+
|
|
76
|
+
in AST::Body(expressions:)
|
|
77
|
+
*leading, last = expressions
|
|
78
|
+
[
|
|
79
|
+
*leading.map { generate_node(it, registry) },
|
|
80
|
+
emit_tail(last, registry, sig, params),
|
|
81
|
+
].join("\n")
|
|
82
|
+
|
|
83
|
+
in AST::IfThenElse(condition:, if_branch:, else_branch:)
|
|
84
|
+
[
|
|
85
|
+
"if (#{generate_node(condition, registry)})",
|
|
86
|
+
Pretty.indent(emit_tail(if_branch, registry, sig, params)),
|
|
87
|
+
"else",
|
|
88
|
+
Pretty.indent(emit_tail(else_branch, registry, sig, params)),
|
|
89
|
+
"end",
|
|
90
|
+
].join("\n")
|
|
91
|
+
|
|
92
|
+
in AST::CaseOf(expression:, branches:)
|
|
93
|
+
subject = generate_node(expression, registry)
|
|
94
|
+
branches
|
|
95
|
+
.map { emit_tail_branch(it, registry, sig, params) }
|
|
96
|
+
.join("\n")
|
|
97
|
+
.then { "case #{subject}\n#{it}\nend" }
|
|
98
|
+
|
|
99
|
+
in AST::Grouping(expression:)
|
|
100
|
+
emit_tail(expression, registry, sig, params)
|
|
101
|
+
|
|
102
|
+
else
|
|
103
|
+
"break #{generate_node(node, registry)}"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def emit_tail_branch(branch, registry, sig, params)
|
|
108
|
+
pat = generate_node(branch.pattern, registry)
|
|
109
|
+
body = emit_tail(branch.body, registry, sig, params)
|
|
110
|
+
|
|
111
|
+
if Pretty.multiline?(body)
|
|
112
|
+
"in #{pat} then\n#{Pretty.indent(body)}"
|
|
113
|
+
else
|
|
114
|
+
"in #{pat} then #{body}"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module VariantDeclaration
|
|
4
|
+
extend self
|
|
5
|
+
extend Helpers
|
|
6
|
+
|
|
7
|
+
def generate(node, sibling_names)
|
|
8
|
+
node => AST::VariantDeclaration(name:, args:, symbol:)
|
|
9
|
+
|
|
10
|
+
impls = symbol.qualified_name
|
|
11
|
+
.then { "::#{to_qualified(it)}" }
|
|
12
|
+
.then { Codegen.dispatched_methods[it] || [] }
|
|
13
|
+
|
|
14
|
+
sibling_names
|
|
15
|
+
.map { |s| "def #{predicate_name(s)}; #{s == name}; end" }
|
|
16
|
+
.then { [it.join(Pretty.newline), *impls] }
|
|
17
|
+
.reject(&:empty?)
|
|
18
|
+
.join(Pretty.newline(2))
|
|
19
|
+
.then { Pretty.block("#{name} = #{data_define(fields_for(args))} do", it) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def fields_for(args)
|
|
25
|
+
case args
|
|
26
|
+
in [AST::TypeRecord(fields:)]
|
|
27
|
+
fields.keys
|
|
28
|
+
else
|
|
29
|
+
args.map.with_index { |_, i| "_#{i + 1}" }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def predicate_name(variant_name)
|
|
34
|
+
variant_name
|
|
35
|
+
.gsub(/([a-z])([A-Z])/, '\1_\2')
|
|
36
|
+
.downcase
|
|
37
|
+
.then { "#{it}?" }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|