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,367 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module FunctionCall
|
|
4
|
+
extend self
|
|
5
|
+
extend Helpers
|
|
6
|
+
|
|
7
|
+
def generate(node, registry)
|
|
8
|
+
node => AST::FunctionCall(callee:, args:, dictionaries:)
|
|
9
|
+
|
|
10
|
+
variant_sym = keyed_variant_constructor(callee, registry)
|
|
11
|
+
return generate_keyed_variant_call(variant_sym, args, registry) if variant_sym
|
|
12
|
+
|
|
13
|
+
try_operator_call(callee, args, registry)
|
|
14
|
+
.then { return it if it }
|
|
15
|
+
|
|
16
|
+
Inline.try_for(callee, args, dictionaries, registry)
|
|
17
|
+
.then { return it if it }
|
|
18
|
+
|
|
19
|
+
return constructor_call(callee, args, registry) if constructor_callee?(callee, registry)
|
|
20
|
+
|
|
21
|
+
[generate_many(args, registry), generate_dict_args(callee, dictionaries, registry)]
|
|
22
|
+
.reject(&:empty?)
|
|
23
|
+
.join(', ')
|
|
24
|
+
.then { "#{generate_callee(callee, args, registry, dictionaries)}#{invocation_op(callee, registry)}(#{it})" }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def constructor_call(callee, args, registry)
|
|
28
|
+
resolve_callee_symbol(callee, registry)
|
|
29
|
+
.then { to_qualified(it.qualified_name) }
|
|
30
|
+
.then { "#{it}[#{generate_many(args, registry)}]" }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def constructor_callee?(callee, registry)
|
|
34
|
+
return true if callee.is_a?(AST::ConstructorReference)
|
|
35
|
+
|
|
36
|
+
resolve_callee_symbol(callee, registry).is_a?(Symbol::Constructor)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Direct-def call sites (`Foo::Internal.name(args)`) for plain user fns;
|
|
40
|
+
# `.call(args)` for everything else (lambdas, Procs, Methods).
|
|
41
|
+
def invocation_op(callee, registry)
|
|
42
|
+
case callee.symbol
|
|
43
|
+
in Symbol::ValueRef => ref then invocation_op_for(registry.lookup(ref))
|
|
44
|
+
in symbol then invocation_op_for(symbol)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def invocation_op_for(symbol)
|
|
49
|
+
symbol.is_a?(Symbol::Function) ? '' : '.call'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def try_operator_call(callee, args, registry)
|
|
53
|
+
MethodNames
|
|
54
|
+
.call_operator(callee_qname(callee, registry))
|
|
55
|
+
&.then { |op| emit_operator(op, args, registry) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def callee_qname(callee, registry)
|
|
59
|
+
case resolve_callee_symbol(callee, registry)
|
|
60
|
+
in Symbol::InterfaceFunction | Symbol::StdlibFunction => sym then sym.qualified_name
|
|
61
|
+
else nil
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def emit_operator(op, args, registry)
|
|
66
|
+
args
|
|
67
|
+
.map { generate_node(it, registry) }
|
|
68
|
+
.then { |(a, b)| op == 'compare' ? "#{a}.compare(#{b})" : "(#{a} #{op} #{b})" }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def generate_impl_dispatch(impl, registry)
|
|
72
|
+
impl
|
|
73
|
+
.deps
|
|
74
|
+
.map { |dep| dispatch_for_dep(dep, registry) }
|
|
75
|
+
.then do |dep_dispatches|
|
|
76
|
+
impl.functions.transform_values do |fn|
|
|
77
|
+
generate_impl_fn(fn, dep_dispatches, impl.functions, registry)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# An Implementation's dep is one of two dictionary-slot shapes: a
|
|
83
|
+
# concrete Implementation (recurse to a `{fn_name => ruby_code}` hash)
|
|
84
|
+
# or a Type::Constraint(Var) marker for a free-var dep that's late-bound
|
|
85
|
+
# from the caller's dict env (emit the raw dict reference as a String —
|
|
86
|
+
# build_impl_arg threads it into the impl_arg slot without re-wrapping).
|
|
87
|
+
def dispatch_for_dep(dep, registry)
|
|
88
|
+
case dep
|
|
89
|
+
in Symbol::Implementation
|
|
90
|
+
generate_impl_dispatch(dep, registry)
|
|
91
|
+
|
|
92
|
+
in Type::Constraint(type: Type::Var)
|
|
93
|
+
dispatch_value(dep, registry)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def dispatch_value(entry, registry)
|
|
98
|
+
case entry
|
|
99
|
+
in Type::Constraint(interface:, type: Type::Var(id:))
|
|
100
|
+
Codegen.dict_env[[interface, id]]
|
|
101
|
+
|
|
102
|
+
in Symbol::Implementation
|
|
103
|
+
Pretty.hash(generate_impl_dispatch(entry, registry))
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Polymorphic fn referenced as a value (not called). Wraps the fn with
|
|
108
|
+
# its dispatched dictionaries so the result is a monomorphic callable
|
|
109
|
+
# matching the type at the use site. Returns nil when the symbol
|
|
110
|
+
# doesn't need wrapping; the caller falls back to its default
|
|
111
|
+
# reference emission.
|
|
112
|
+
def reference_with_dictionaries(symbol, dictionaries, registry)
|
|
113
|
+
return nil if dictionaries.empty?
|
|
114
|
+
|
|
115
|
+
case symbol
|
|
116
|
+
in Symbol::StdlibFunction => fn if fn.constraints.any?
|
|
117
|
+
dictionaries
|
|
118
|
+
.map { dispatch_dict(it, registry) }
|
|
119
|
+
.then { generate_impl_fn(fn.codegen, it, {}, registry) }
|
|
120
|
+
|
|
121
|
+
in Symbol::Function => fn if dict_constraints(fn, registry).any?
|
|
122
|
+
param_names = fn.params.size.times.map { param_synthetic_name(it) }
|
|
123
|
+
|
|
124
|
+
fn_constraints(fn, registry)
|
|
125
|
+
.each_with_index
|
|
126
|
+
.filter_map { |c, i| dispatch_value(dictionaries[i], registry) if c.type.is_a?(Type::Var) }
|
|
127
|
+
.then { (param_names + it).join(', ') }
|
|
128
|
+
.then { "#{to_qualified(fn.module_name)}::Internal.#{fn_target_name(fn, registry)}(#{it})" }
|
|
129
|
+
.then { Pretty.lambda(param_names.join(', '), it) }
|
|
130
|
+
|
|
131
|
+
in Symbol::InterfaceFunction => fn
|
|
132
|
+
dispatch_value(dictionaries.first, registry)
|
|
133
|
+
&.then { "#{it}[#{fn.name.inspect}]" } ||
|
|
134
|
+
fail("no dict in scope to reference interface method `#{fn.qualified_name}` as a value")
|
|
135
|
+
|
|
136
|
+
else
|
|
137
|
+
nil
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
|
|
143
|
+
def keyed_variant_constructor(callee, registry)
|
|
144
|
+
case resolve_callee_symbol(callee, registry)
|
|
145
|
+
in Symbol::Constructor(args: [Symbol::RecordType]) => resolved
|
|
146
|
+
resolved
|
|
147
|
+
|
|
148
|
+
else
|
|
149
|
+
nil
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def generate_keyed_variant_call(constructor, args, registry)
|
|
154
|
+
qualified = to_qualified(constructor.qualified_name)
|
|
155
|
+
record_fields = constructor.args[0].fields.keys
|
|
156
|
+
|
|
157
|
+
args[0] => arg
|
|
158
|
+
case arg
|
|
159
|
+
in AST::RecordLiteral(fields:)
|
|
160
|
+
fields_by_key = fields.to_h { [it.key, it.value] }
|
|
161
|
+
record_fields
|
|
162
|
+
.map { generate_node(fields_by_key.fetch(it), registry) }
|
|
163
|
+
.join(', ')
|
|
164
|
+
.then { "#{qualified}[#{it}]" }
|
|
165
|
+
else
|
|
166
|
+
"#{qualified}[**#{generate_node(arg, registry)}.to_h]"
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def generate_callee(callee, args, registry, dictionaries)
|
|
172
|
+
return generate_node(callee, registry) if callee in AST::RecordAccess | AST::FunctionCall | AST::Grouping
|
|
173
|
+
|
|
174
|
+
case callee.symbol
|
|
175
|
+
in Symbol::ValueRef
|
|
176
|
+
registry
|
|
177
|
+
.lookup(callee.symbol)
|
|
178
|
+
.then { generate_callee(callee.with(symbol: it), args, registry, dictionaries) }
|
|
179
|
+
|
|
180
|
+
in Symbol::InteropFunction
|
|
181
|
+
registry
|
|
182
|
+
.lookup(callee.symbol.to_ref)
|
|
183
|
+
.then { PortDecoder.task_call(it, registry, dictionaries) }
|
|
184
|
+
|
|
185
|
+
in Symbol::StdlibFunction => symbol if symbol.constraints.any?
|
|
186
|
+
dictionaries
|
|
187
|
+
.map { |entry| dispatch_dict(entry, registry) }
|
|
188
|
+
.then { generate_impl_fn(symbol.codegen, it, {}, registry) }
|
|
189
|
+
|
|
190
|
+
in Symbol::StdlibFunction
|
|
191
|
+
callee.symbol.codegen
|
|
192
|
+
|
|
193
|
+
in Symbol::Variable(name:)
|
|
194
|
+
name
|
|
195
|
+
|
|
196
|
+
in Symbol::Lambda
|
|
197
|
+
generate_node(callee, registry)
|
|
198
|
+
|
|
199
|
+
in Symbol::Function => fn_sym
|
|
200
|
+
to_qualified(fn_sym.module_name) + "::Internal." + fn_target_name(fn_sym, registry)
|
|
201
|
+
|
|
202
|
+
in Symbol::StdlibImplementation => symbol
|
|
203
|
+
dictionaries
|
|
204
|
+
.reduce({}) { |acc, entry| acc.merge dispatch_dict(entry, registry) }
|
|
205
|
+
.then { generate_stdlib_implementation(symbol, registry, it) }
|
|
206
|
+
|
|
207
|
+
in Symbol::InterfaceFunction => symbol if dictionaries.any?
|
|
208
|
+
dispatch_lookup(dictionaries.first, symbol.name, registry) {
|
|
209
|
+
runtime_dispatch(symbol, args, registry)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
in Symbol::InterfaceFunction => symbol
|
|
213
|
+
runtime_dispatch(symbol, args, registry)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# When a user fn has var-typed constraints, two definitions are emitted:
|
|
218
|
+
# `name` (Ruby-boundary wrapper, no dicts) and `__name__impl__` (takes
|
|
219
|
+
# dicts). Jade-internal calls target the latter.
|
|
220
|
+
def fn_target_name(fn_sym, registry)
|
|
221
|
+
return fn_sym.name if dict_constraints(fn_sym, registry).empty?
|
|
222
|
+
|
|
223
|
+
fn_impl_synthetic_name(fn_sym.name)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Returns the list of dict args to pass after regular args. Only
|
|
227
|
+
# Symbol::Function callees take dict params; other branches dispatch
|
|
228
|
+
# via `dictionaries` directly inside generate_callee. dictionaries are
|
|
229
|
+
# attached in callee constraint order; only var-typed slots need a
|
|
230
|
+
# runtime dict (others are resolved at finalize).
|
|
231
|
+
def generate_dict_args(callee, dictionaries, registry)
|
|
232
|
+
symbol =
|
|
233
|
+
case callee.symbol
|
|
234
|
+
in Symbol::ValueRef then registry.lookup(callee.symbol)
|
|
235
|
+
else callee.symbol
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
return "" unless symbol.is_a?(Symbol::Function)
|
|
239
|
+
|
|
240
|
+
fn_constraints(symbol, registry)
|
|
241
|
+
.each_with_index
|
|
242
|
+
.filter_map { |c, i| dispatch_value(dictionaries[i], registry) if c.type.is_a?(Type::Var) }
|
|
243
|
+
.join(', ')
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Ruby-block intrinsics (Dict's `Eq k`, etc.) ignore dispatches — the
|
|
247
|
+
# `in String` body of `generate_impl_fn` drops them.
|
|
248
|
+
def dispatch_dict(entry, registry)
|
|
249
|
+
case entry
|
|
250
|
+
in Symbol::Implementation
|
|
251
|
+
generate_impl_dispatch(entry, registry)
|
|
252
|
+
|
|
253
|
+
in Type::Constraint(interface:, type: Type::Var(id:))
|
|
254
|
+
Codegen.dict_env[[interface, id]] ||
|
|
255
|
+
fail("no dict in scope for #{interface}")
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Generates a Ruby expression for `dict[fn_name]` from a dictionary entry.
|
|
260
|
+
# Falls back to the supplied block when a var-typed marker has no entry
|
|
261
|
+
# in the current dict_env (e.g. an anonymous lambda's body).
|
|
262
|
+
def dispatch_lookup(entry, fn_name, registry, &fallback)
|
|
263
|
+
case entry
|
|
264
|
+
in Type::Constraint(interface:, type: Type::Var(id:))
|
|
265
|
+
Codegen
|
|
266
|
+
.dict_env[[interface, id]]
|
|
267
|
+
&.then { "#{it}[#{fn_name.inspect}]" } || fallback.call
|
|
268
|
+
|
|
269
|
+
in Symbol::Implementation
|
|
270
|
+
generate_impl_dispatch(entry, registry)[fn_name]
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def runtime_dispatch(symbol, args, registry)
|
|
275
|
+
generate_node(args.first, registry)
|
|
276
|
+
.then { "Jade::Runtime.impl_for(#{symbol.interface.qualified_name.inspect}, #{it})[#{symbol.name.inspect}]" }
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def generate_impl_fn(fn, dep_dispatches, sibling_fns, registry)
|
|
280
|
+
case fn
|
|
281
|
+
in Symbol::DerivedFunction(params:, body:)
|
|
282
|
+
inner = params.empty? \
|
|
283
|
+
? emit(body, registry)
|
|
284
|
+
: Pretty.lambda(params.join(', '), emit(body, registry))
|
|
285
|
+
return inner if dep_dispatches.empty?
|
|
286
|
+
|
|
287
|
+
Pretty.lambda("impl_arg", inner) + ".call(#{build_impl_arg(dep_dispatches)})"
|
|
288
|
+
|
|
289
|
+
# Impl-dispatch dicts hold evaluated values, not callables —
|
|
290
|
+
# `{ 'decoder' => <Decoder>, 'compare' => <Proc(a, b)> }`.
|
|
291
|
+
# `impl_arg[i]['decoder'].desc` works directly; we never `.call`
|
|
292
|
+
# it. So constant slots like `Decode.int` get invoked at synth
|
|
293
|
+
# time; only multi-arg slots keep their Proc shape.
|
|
294
|
+
#
|
|
295
|
+
# The desugar pass that normally auto-invokes zero-arg refs
|
|
296
|
+
# operates on AST. These dicts are built straight from
|
|
297
|
+
# `Symbol::Implementation.functions` at codegen, no AST in
|
|
298
|
+
# between, so the equivalent invoke happens here instead.
|
|
299
|
+
in Symbol::StdlibFunction if fn.constant?
|
|
300
|
+
"#{fn.codegen}.call()"
|
|
301
|
+
|
|
302
|
+
in Symbol::StdlibFunction
|
|
303
|
+
fn.codegen
|
|
304
|
+
|
|
305
|
+
# Ruby-block intrinsic that declares a constraint for type-system
|
|
306
|
+
# honesty but doesn't consume the dict at runtime (Dict ops use Ruby
|
|
307
|
+
# `==`, etc.). The dispatches are dropped — the block sees only the
|
|
308
|
+
# data args, same as the no-constraint path.
|
|
309
|
+
in String
|
|
310
|
+
fn
|
|
311
|
+
|
|
312
|
+
in Symbol::StdlibImplementation
|
|
313
|
+
sibling_dispatch = sibling_fns
|
|
314
|
+
.reject { |_, sib| sib.is_a?(Symbol::StdlibImplementation) }
|
|
315
|
+
.transform_values { |sib| generate_impl_fn(sib, dep_dispatches, sibling_fns, registry) }
|
|
316
|
+
Pretty.lambda(fn.params.join(', '), build_std_impl_str(fn.body, sibling_dispatch, registry))
|
|
317
|
+
|
|
318
|
+
in Symbol::ValueRef
|
|
319
|
+
registry.lookup(fn).then { generate_impl_fn(it, dep_dispatches, sibling_fns, registry) }
|
|
320
|
+
|
|
321
|
+
# 0-arg fn: Ruby auto-invokes on bare reference. Result is the
|
|
322
|
+
# dispatch-slot value (decoder, encoder template, ...) ready to use.
|
|
323
|
+
in Symbol::Function => fn if fn.constant?
|
|
324
|
+
"#{to_qualified(fn.module_name)}::Internal.#{fn.name}"
|
|
325
|
+
|
|
326
|
+
in Symbol::Function => fn
|
|
327
|
+
"#{to_qualified(fn.module_name)}::Internal.method(:#{fn.name})"
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def build_impl_arg(dep_dispatches)
|
|
332
|
+
dep_dispatches
|
|
333
|
+
.map { it.is_a?(String) ? it : Pretty.hash(it) }
|
|
334
|
+
.then { Pretty.array(it) }
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# Stdlib intrinsics implementation language.
|
|
338
|
+
|
|
339
|
+
def generate_stdlib_implementation(symbol, registry, dispatch)
|
|
340
|
+
Pretty.lambda(symbol.params.join(', '), build_std_impl_str(symbol.body, dispatch, registry))
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def build_std_impl_str(body, dispatch, registry)
|
|
344
|
+
case body
|
|
345
|
+
in [:call, fn, args]
|
|
346
|
+
args.map { build_std_impl_str(it, dispatch, registry) }.join(', ')
|
|
347
|
+
.then { "#{build_std_impl_str(fn, dispatch, registry)}.call(#{it})" }
|
|
348
|
+
|
|
349
|
+
in String
|
|
350
|
+
body
|
|
351
|
+
|
|
352
|
+
in [:impl, impl]
|
|
353
|
+
dispatch[impl]
|
|
354
|
+
|
|
355
|
+
in [:fn, name]
|
|
356
|
+
*mod_parts, fn_name = name.split('.')
|
|
357
|
+
sym = Symbol.value_ref(mod_parts.join('.'), fn_name)
|
|
358
|
+
registry.lookup(sym).then { generate_impl_fn(it, [], {}, registry) }
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def emit(ir, registry)
|
|
363
|
+
Emitter.emit(ir)
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module FunctionDeclaration
|
|
4
|
+
extend self
|
|
5
|
+
extend Helpers
|
|
6
|
+
|
|
7
|
+
def generate_boundary_wrapper(node, registry)
|
|
8
|
+
node => AST::FunctionDeclaration(name:, params:, symbol:)
|
|
9
|
+
|
|
10
|
+
entry = registry.get(symbol.module_name)
|
|
11
|
+
return nil unless entry&.exposed_value(name)
|
|
12
|
+
|
|
13
|
+
if !dict_constraints(symbol, registry).empty?
|
|
14
|
+
return not_exposed_stub(symbol, 'polymorphic — no extractable witness for type variable')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
fn_type = fn_type_for(symbol, registry)
|
|
18
|
+
unless Codegen::Boundary.eligible?(fn_type, registry)
|
|
19
|
+
return not_exposed_stub(symbol, ineligibility_reason(fn_type, registry))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
args, return_type = Type.signature(fn_type)
|
|
23
|
+
param_names = params.map(&:name)
|
|
24
|
+
|
|
25
|
+
if task_return?(return_type)
|
|
26
|
+
task_wrapper_pair(name, args, param_names, return_type, registry)
|
|
27
|
+
else
|
|
28
|
+
eligible_wrapper(name, args, param_names, return_type, registry)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def generate(node, registry)
|
|
33
|
+
node => AST::FunctionDeclaration(name:, params:, body:, symbol:)
|
|
34
|
+
|
|
35
|
+
var_cs = dict_constraints(symbol, registry)
|
|
36
|
+
param_names = params.map { generate_node(it, registry) }
|
|
37
|
+
dict_params = var_cs.each_index.map { dict_synthetic_name(it) }
|
|
38
|
+
|
|
39
|
+
body_code = build_dict_env(var_cs)
|
|
40
|
+
.then { Codegen.with_dict_env(it) { emit_body(body, symbol, param_names, registry) } }
|
|
41
|
+
|
|
42
|
+
target = var_cs.empty? ? name : fn_impl_synthetic_name(name)
|
|
43
|
+
sig = (param_names + dict_params).join(', ')
|
|
44
|
+
sig_str = sig.empty? ? '' : "(#{sig})"
|
|
45
|
+
|
|
46
|
+
Pretty.block("def #{target}#{sig_str}", body_code)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def emit_body(body, self_sym, param_names, registry)
|
|
52
|
+
if Transforms::TailCall.tail_recursive?(body, self_sym, param_names.size, registry)
|
|
53
|
+
Transforms::TailCall.generate_body(body, registry, self_sym, param_names)
|
|
54
|
+
elsif (shape = Transforms::FoldShape.shape_for(body, self_sym, param_names, registry))
|
|
55
|
+
Transforms::FoldShape.generate_body(shape, registry)
|
|
56
|
+
else
|
|
57
|
+
generate_node(body, registry)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def eligible_wrapper(name, args, param_names, return_type, registry)
|
|
62
|
+
decoded_args(args, param_names, registry)
|
|
63
|
+
.then { Pretty.call("Internal.#{name}", it) }
|
|
64
|
+
.then { encode_return(return_type, it, registry) }
|
|
65
|
+
.then { Pretty.block(boundary_def_header(name, param_names), it) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def boundary_def_header(name, param_names)
|
|
69
|
+
"def self.#{name}(#{param_names.join(', ')})"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def decoded_args(args, param_names, registry)
|
|
73
|
+
args.zip(param_names).map { |t, pname| decode_call(t, pname, registry) }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def decode_call(arg_type, pname, registry)
|
|
77
|
+
Codegen::Boundary::Specialized.decode_expr(arg_type, pname, registry) ||
|
|
78
|
+
Codegen::Boundary::Cache.decoder_for(arg_type, registry)
|
|
79
|
+
.then { "Jade::Interop::Boundary.decode_or_raise(#{it}, #{pname})" }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def encode_return(return_type, call_expr, registry)
|
|
83
|
+
if Codegen::Boundary::Specialized.identity_encoder?(return_type)
|
|
84
|
+
call_expr
|
|
85
|
+
elsif (expr = Codegen::Boundary::Specialized.encode_expr(return_type, call_expr, registry))
|
|
86
|
+
expr
|
|
87
|
+
else
|
|
88
|
+
Codegen::Boundary::Cache
|
|
89
|
+
.encoder_for(return_type, registry)
|
|
90
|
+
.then { |encoder| "#{encoder}.call(#{call_expr})" }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def task_wrapper_pair(name, args, param_names, task_return, registry)
|
|
95
|
+
ok_enc, err_enc = Codegen::Boundary::Cache.task_arms(task_return, registry)
|
|
96
|
+
|
|
97
|
+
[
|
|
98
|
+
task_run_def(name, args, param_names, ok_enc, err_enc, registry),
|
|
99
|
+
task_bang_def(name, param_names),
|
|
100
|
+
].join(Pretty.newline(2))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def task_run_def(name, args, param_names, ok_enc, err_enc, registry)
|
|
104
|
+
decoded_args(args, param_names, registry)
|
|
105
|
+
.then { Pretty.call("Internal.#{name}", it) }
|
|
106
|
+
.then { task_run_body(it, ok_enc, err_enc) }
|
|
107
|
+
.then { Pretty.block(boundary_def_header(name, param_names), it) }
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def task_run_body(call_expr, ok_enc, err_enc)
|
|
111
|
+
[
|
|
112
|
+
"case #{call_expr}.run",
|
|
113
|
+
"in Jade::Result::Ok[v] then [\"ok\", #{ok_enc}.call(v)]",
|
|
114
|
+
"in Jade::Result::Err[e] then [\"err\", #{err_enc}.call(e)]",
|
|
115
|
+
'end',
|
|
116
|
+
].join(Pretty.newline)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def task_bang_def(name, param_names)
|
|
120
|
+
[
|
|
121
|
+
"case #{name}#{paren_or_empty(param_names)}",
|
|
122
|
+
'in ["ok", v] then v',
|
|
123
|
+
'in ["err", e] then raise Jade::Interop::TaskError.new(e)',
|
|
124
|
+
'end',
|
|
125
|
+
]
|
|
126
|
+
.join(Pretty.newline)
|
|
127
|
+
.then do
|
|
128
|
+
Pretty.block(boundary_def_header("#{name}!", param_names), it)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def task_return?(type)
|
|
133
|
+
type in Type::Application(constructor: Type::Constructor(name: 'Task.Task'))
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def paren_or_empty(items)
|
|
137
|
+
items.empty? ? '' : "(#{items.join(', ')})"
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def not_exposed_stub(symbol, reason)
|
|
141
|
+
"raise Jade::Interop::NotExposed.new(" \
|
|
142
|
+
"module_name: #{to_qualified(symbol.module_name).inspect}, " \
|
|
143
|
+
"function_name: #{symbol.name.to_sym.inspect}, " \
|
|
144
|
+
"hint: #{reason.inspect})"
|
|
145
|
+
.then { Pretty.block("def self.#{symbol.name}(*)", it) }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def ineligibility_reason(fn_type, registry)
|
|
149
|
+
args, ret = Type.signature(fn_type)
|
|
150
|
+
|
|
151
|
+
arg_ineligibility_reason(args, registry) ||
|
|
152
|
+
return_ineligibility_reason(ret, registry) ||
|
|
153
|
+
fail("unreachable: eligible? returned false but no specific reason found for #{fn_type}")
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def arg_ineligibility_reason(args, registry)
|
|
157
|
+
args.each_with_index do |arg, i|
|
|
158
|
+
if Codegen::Boundary.decoder_for(arg, registry).nil?
|
|
159
|
+
return "argument #{i + 1} of type #{arg} has no Decodable instance"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
nil
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def return_ineligibility_reason(ret, registry)
|
|
167
|
+
case ret
|
|
168
|
+
in Type::Application(constructor: Type::Constructor(name: 'Task.Task'), args: [ok_t, err_t])
|
|
169
|
+
if Codegen::Boundary.encoder_for(ok_t, registry).nil?
|
|
170
|
+
"Task ok arm of type #{ok_t} has no Encodable instance"
|
|
171
|
+
|
|
172
|
+
elsif Codegen::Boundary.encoder_for(err_t, registry).nil?
|
|
173
|
+
"Task err arm of type #{err_t} has no Encodable instance"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
else
|
|
177
|
+
if Codegen::Boundary.encoder_for(ret, registry).nil?
|
|
178
|
+
"return type #{ret} has no Encodable instance"
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def fn_type_for(symbol, registry)
|
|
184
|
+
registry
|
|
185
|
+
.get(symbol.module_name)
|
|
186
|
+
.env
|
|
187
|
+
.then { it.substitution.apply(it.bindings[symbol.qualified_name].type) }
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def build_dict_env(var_cs)
|
|
191
|
+
var_cs
|
|
192
|
+
.each_with_index
|
|
193
|
+
.reduce({}) do |env, (c, i)|
|
|
194
|
+
env.merge([c.interface, c.type.id] => dict_synthetic_name(i))
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module Helpers
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
def generate_many(nodes, registry, sep = ", ")
|
|
7
|
+
nodes.map do
|
|
8
|
+
next yield(it) if block_given?
|
|
9
|
+
|
|
10
|
+
generate_node(it, registry)
|
|
11
|
+
end.join(sep)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_qualified(module_name)
|
|
15
|
+
base = module_name.gsub('.', '::')
|
|
16
|
+
Stdlib.stdlib_name?(module_name) ? "Jade::#{base}" : base
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def data_define(fields)
|
|
20
|
+
return "Data.define" if fields.empty?
|
|
21
|
+
|
|
22
|
+
"Data.define(#{fields.map { ":#{it}" }.join(', ')})"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def generate_node(node, registry)
|
|
26
|
+
Codegen.generate(node, registry)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def resolve_callee_symbol(callee, registry)
|
|
30
|
+
case callee.symbol
|
|
31
|
+
in Symbol::ValueRef => ref then registry.lookup(ref)
|
|
32
|
+
in symbol then symbol
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def param_synthetic_name(index)
|
|
37
|
+
"__p#{index}__"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def impl_synthetic_name(interface, type_name, fn_name)
|
|
41
|
+
sanitized = fn_name.gsub(/[^a-zA-Z0-9_]/) { |c| "x#{c.ord.to_s(16)}" }
|
|
42
|
+
"__impl_#{interface}_#{type_name}_#{sanitized}__"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def dict_synthetic_name(index)
|
|
46
|
+
"__dict#{index}__"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def fn_impl_synthetic_name(name)
|
|
50
|
+
"__#{name}__impl__"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def fn_constraints(fn_symbol, registry)
|
|
54
|
+
env = registry.get(fn_symbol.module_name).env
|
|
55
|
+
|
|
56
|
+
env
|
|
57
|
+
.bindings[fn_symbol.qualified_name]
|
|
58
|
+
.constraints
|
|
59
|
+
.map { env.substitution.apply(it) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Subset of fn_constraints that need a runtime dict param: those whose
|
|
63
|
+
# type is a bare Type::Var. Other constraints (e.g. Eq(Maybe(α)) where α
|
|
64
|
+
# is unbound but the outer constructor is concrete) are resolved at
|
|
65
|
+
# finalize via the impl table — no dict threaded for them.
|
|
66
|
+
def dict_constraints(fn_symbol, registry)
|
|
67
|
+
fn_constraints(fn_symbol, registry).select { |c| c.type.is_a?(Type::Var) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Ruby classes that back primitive Jade types. Mirrors stdlib's
|
|
71
|
+
# `native_type` declarations so user impls on primitives register under
|
|
72
|
+
# the right Ruby class. (Lifting this onto Symbol::Union itself is
|
|
73
|
+
# tracked in plans/lift-native-types-into-symbol-table.md.)
|
|
74
|
+
NATIVE_RUBY_CLASSES = {
|
|
75
|
+
'Basics.Int' => ['Integer'],
|
|
76
|
+
'Basics.Float' => ['Float'],
|
|
77
|
+
'Basics.Bool' => ['TrueClass', 'FalseClass'],
|
|
78
|
+
'String.String' => ['String'],
|
|
79
|
+
'Char.Char' => ['String'],
|
|
80
|
+
}.freeze
|
|
81
|
+
|
|
82
|
+
# Returns the Ruby class names that values of `type_ref` may have at
|
|
83
|
+
# runtime. Strings here go straight into emitted Ruby. Returns [] for
|
|
84
|
+
# types that have no concrete runtime representation (interfaces).
|
|
85
|
+
def ruby_classes_for_type(type_ref, registry)
|
|
86
|
+
qname = type_ref.qualified_name
|
|
87
|
+
return NATIVE_RUBY_CLASSES[qname] if NATIVE_RUBY_CLASSES.key?(qname)
|
|
88
|
+
|
|
89
|
+
case registry.lookup(type_ref)
|
|
90
|
+
in Symbol::Union(variants:)
|
|
91
|
+
variants.map { "::#{to_qualified(it.qualified_name)}" }
|
|
92
|
+
|
|
93
|
+
in Symbol::Struct
|
|
94
|
+
["::#{to_qualified(qname)}"]
|
|
95
|
+
|
|
96
|
+
in Symbol::Interface
|
|
97
|
+
[]
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|