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,129 @@
|
|
|
1
|
+
require 'jade/frontend/type_checking/canonicalize'
|
|
2
|
+
require 'jade/frontend/type_checking/constraints'
|
|
3
|
+
require 'jade/frontend/type_checking/definition'
|
|
4
|
+
require 'jade/frontend/type_checking/env'
|
|
5
|
+
require 'jade/frontend/type_checking/error'
|
|
6
|
+
require 'jade/frontend/type_checking/expected'
|
|
7
|
+
require 'jade/frontend/type_checking/inference'
|
|
8
|
+
require 'jade/frontend/type_checking/loader'
|
|
9
|
+
require 'jade/frontend/type_checking/port_resolution'
|
|
10
|
+
require 'jade/frontend/type_checking/result'
|
|
11
|
+
require 'jade/frontend/type_checking/state'
|
|
12
|
+
require 'jade/frontend/type_checking/substitution'
|
|
13
|
+
require 'jade/frontend/type_checking/unification'
|
|
14
|
+
|
|
15
|
+
require 'jade/frontend/type_checking/generalizer'
|
|
16
|
+
|
|
17
|
+
module Jade
|
|
18
|
+
module Frontend
|
|
19
|
+
module TypeChecking
|
|
20
|
+
extend Inference::Helpers
|
|
21
|
+
extend self
|
|
22
|
+
|
|
23
|
+
def check(entry, registry)
|
|
24
|
+
Loader
|
|
25
|
+
.load(entry, registry)
|
|
26
|
+
.then { check_node(entry.ast, registry, State.init(it, skip_constraints: true), Expected.infer(it.fresh)) }
|
|
27
|
+
.then { Generalizer.generalize(it.first.env) }
|
|
28
|
+
.then { check_node(entry.ast, registry, State.init(it), Expected.infer(it.fresh)) }
|
|
29
|
+
.then { finalize(*it, registry) }
|
|
30
|
+
.map { Canonicalize.run(entry.ast, it, registry) }
|
|
31
|
+
.map { it.canonicalize_node_types }
|
|
32
|
+
.map { entry.with(env: it) }
|
|
33
|
+
.and_then { PortResolution.resolve(it, registry) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def finalize(state, result, registry)
|
|
37
|
+
state.env => { bindings:, entry_name: }
|
|
38
|
+
|
|
39
|
+
errors = bindings
|
|
40
|
+
.select do |k,v|
|
|
41
|
+
# filter locals
|
|
42
|
+
b_entry_name = k.split('.')[0..-2].join(',')
|
|
43
|
+
b_entry_name == entry_name
|
|
44
|
+
end
|
|
45
|
+
.reject { |k, _| interface_function?(k, registry) }
|
|
46
|
+
.reject { |k, _| interop_function?(k, registry) }
|
|
47
|
+
.values
|
|
48
|
+
.flat_map(&:constraints)
|
|
49
|
+
.flat_map { Constraints.solve_at_finalize(it, registry, entry_name) }
|
|
50
|
+
|
|
51
|
+
# TODO: impl declarations need their own finalization pass here.
|
|
52
|
+
# Unresolved constraints from impl function bodies (e.g. Eq(a) from
|
|
53
|
+
# `one.id == other.id` inside `impl Eq for Pepe(a)`) should be promoted
|
|
54
|
+
# to impl-level requirements — making the impl an ImplementationTemplate
|
|
55
|
+
# with those constraints — rather than being dropped silently.
|
|
56
|
+
|
|
57
|
+
state
|
|
58
|
+
.with(errors: state.errors + errors)
|
|
59
|
+
.to_result
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def interface_function?(qname, registry)
|
|
63
|
+
*module_parts, name = qname.split('.')
|
|
64
|
+
Symbol
|
|
65
|
+
.value_ref(module_parts.join('.'), name)
|
|
66
|
+
.then { registry.lookup(it) }
|
|
67
|
+
.is_a?(Symbol::InterfaceFunction)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Polymorphic-port constraints represent caller requirements
|
|
71
|
+
# (resolved at each call site), not body obligations to verify here.
|
|
72
|
+
def interop_function?(qname, registry)
|
|
73
|
+
*module_parts, name = qname.split('.')
|
|
74
|
+
Symbol
|
|
75
|
+
.value_ref(module_parts.join('.'), name)
|
|
76
|
+
.then { registry.lookup(it) }
|
|
77
|
+
.is_a?(Symbol::InteropFunction)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def check_repl(node, registry, env = Env.new)
|
|
81
|
+
check_node(node, registry, env, Expected.infer(env.fresh))
|
|
82
|
+
.to_result
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def check_node(node, registry, state, expected_type)
|
|
86
|
+
case node
|
|
87
|
+
in AST::Body then Inference::Body
|
|
88
|
+
in AST::CaseOf then Inference::CaseOf
|
|
89
|
+
in AST::ConstructorReference then Inference::ConstructorReference
|
|
90
|
+
in AST::FunctionCall then Inference::FunctionCall
|
|
91
|
+
in AST::FunctionDeclaration then Inference::FunctionDeclaration
|
|
92
|
+
in AST::Grouping then Inference::Grouping
|
|
93
|
+
in AST::IfThenElse then Inference::IfThenElse
|
|
94
|
+
in AST::Implementation then Inference::Implementation
|
|
95
|
+
in AST::ImportDeclaration then Inference::ImportDeclaration
|
|
96
|
+
in AST::InteropImportDeclaration then Inference::InteropImportDeclaration
|
|
97
|
+
in AST::Lambda then Inference::Lambda
|
|
98
|
+
in AST::List then Inference::List
|
|
99
|
+
in AST::Literal | AST::CharLiteral then Inference::Literal
|
|
100
|
+
in AST::Module then Inference::Module
|
|
101
|
+
in AST::QualifiedAccess then Inference::QualifiedAccess
|
|
102
|
+
in AST::RecordAccess then Inference::RecordAccess
|
|
103
|
+
in AST::StructDeclaration then Inference::StructDeclaration
|
|
104
|
+
in AST::InterfaceDeclaration then Inference::InterfaceDeclaration
|
|
105
|
+
in AST::RecordField then Inference::RecordField
|
|
106
|
+
in AST::RecordLiteral then Inference::RecordLiteral
|
|
107
|
+
in AST::RecordUpdate then Inference::RecordUpdate
|
|
108
|
+
in AST::TypeDeclaration then Inference::TypeDeclaration
|
|
109
|
+
in AST::Assign then Inference::Assign
|
|
110
|
+
in AST::VariableReference then Inference::VariableReference
|
|
111
|
+
end
|
|
112
|
+
.infer(node, registry, state, expected_type)
|
|
113
|
+
.then { |s, r| [pin_node_type(s, node, r), r] }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
# Records the inferred type at this node so post-finalize the LSP
|
|
119
|
+
# (and anyone else) can look up `node.id -> type`. Types contain
|
|
120
|
+
# Type::Var here; `canonicalize_node_types` resolves them once the
|
|
121
|
+
# substitution is final.
|
|
122
|
+
def pin_node_type(state, node, result)
|
|
123
|
+
return state unless node.respond_to?(:id) && result.respond_to?(:type)
|
|
124
|
+
|
|
125
|
+
state.with(env: state.env.pin_type(node.id, result.type))
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
# Emits a warning per module-private function that no reference in
|
|
4
|
+
# the module's own usage_index touches. Runs after UsageAnalysis so
|
|
5
|
+
# it can read the index. Never fails; only appends to diagnostics.
|
|
6
|
+
#
|
|
7
|
+
# Scope is intentionally narrow: only Symbol::Function for now. Other
|
|
8
|
+
# kinds (Constructor, Variant, Union, Struct, InterfaceFunction) need
|
|
9
|
+
# separate filter rules — e.g. variants are reachable via pattern
|
|
10
|
+
# match, interface fns are dispatched, not directly called — and are
|
|
11
|
+
# deferred.
|
|
12
|
+
module UnusedAnalysis
|
|
13
|
+
extend self
|
|
14
|
+
|
|
15
|
+
def analyze(entry, _registry)
|
|
16
|
+
entry
|
|
17
|
+
.defined_values
|
|
18
|
+
.values
|
|
19
|
+
.select { unused?(it, entry) }
|
|
20
|
+
.reduce(entry.diagnostics) { |list, sym| list.add(warning(sym, entry)) }
|
|
21
|
+
.then { entry.with(diagnostics: it) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def unused?(symbol, entry)
|
|
27
|
+
symbol.is_a?(Jade::Symbol::Function) &&
|
|
28
|
+
symbol.decl_span &&
|
|
29
|
+
!entry.exposed_value(symbol.name) &&
|
|
30
|
+
!entry.usage_index.ever_referenced?(symbol)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def warning(symbol, entry)
|
|
34
|
+
Diagnostics::Diagnostic.warning(
|
|
35
|
+
"unused function `#{symbol.name}`",
|
|
36
|
+
primary: Diagnostics::Label[entry.source, symbol.decl_span, 'never used'],
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Frontend
|
|
3
|
+
module UsageAnalysis
|
|
4
|
+
Reference = Data.define(:symbol_key, :kind, :range)
|
|
5
|
+
|
|
6
|
+
ReferenceIndex = Data.define(:references) do
|
|
7
|
+
def initialize(references: {})
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.empty
|
|
12
|
+
new(references: {})
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def for(symbol)
|
|
16
|
+
references[ReferenceIndex.key_for(symbol)] || []
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def passed_as_value?(symbol)
|
|
20
|
+
self.for(symbol).any? { it.kind == :as_value }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def ever_referenced?(symbol)
|
|
24
|
+
self.for(symbol).any?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def references_in(module_name)
|
|
28
|
+
references
|
|
29
|
+
.values
|
|
30
|
+
.flatten
|
|
31
|
+
.select { it.symbol_key.first == module_name }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Locals key on `decl_span`; module-level symbols on
|
|
35
|
+
# `[module_name, name]`. NOTE: when `:type_annotation` refs land
|
|
36
|
+
# they may point at a `Symbol::Variable` that's a *type*
|
|
37
|
+
# variable, which would collide with value-level locals under
|
|
38
|
+
# `[:local, decl_span]`. Revisit the keying scheme then —
|
|
39
|
+
# likely split into `[:local_value, ...]` vs `[:local_type, ...]`.
|
|
40
|
+
def self.key_for(symbol)
|
|
41
|
+
case symbol
|
|
42
|
+
in Symbol::Variable
|
|
43
|
+
[:local, symbol.decl_span]
|
|
44
|
+
in Symbol::ValueRef | Symbol::TypeRef
|
|
45
|
+
[symbol.module_name, symbol.name]
|
|
46
|
+
else
|
|
47
|
+
[symbol.module_name, symbol.name]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require 'jade/frontend/usage_analysis/reference_index'
|
|
2
|
+
|
|
3
|
+
module Jade
|
|
4
|
+
module Frontend
|
|
5
|
+
# Walks a resolved AST and builds a ReferenceIndex of where each
|
|
6
|
+
# symbol is used. Runs after SemanticAnalysis (which attaches symbols)
|
|
7
|
+
# and before TypeChecking. Never fails — attaches `usage_index` to
|
|
8
|
+
# the entry and returns it.
|
|
9
|
+
#
|
|
10
|
+
# Reference kinds:
|
|
11
|
+
# :called - in callee position of a FunctionCall
|
|
12
|
+
# :as_value - bare reference (passed as value, returned, etc.)
|
|
13
|
+
# :constructed - constructor applied with args
|
|
14
|
+
# :pattern_match - constructor used in a pattern match
|
|
15
|
+
# :type_annotation - type appearing in a signature, variant args,
|
|
16
|
+
# struct fields, interface signatures, etc.
|
|
17
|
+
# :exposed - name listed in `module M exposing (...)`
|
|
18
|
+
module UsageAnalysis
|
|
19
|
+
extend self
|
|
20
|
+
|
|
21
|
+
def analyze(entry, _registry)
|
|
22
|
+
walk(entry.ast, :as_value, entry)
|
|
23
|
+
.group_by(&:symbol_key)
|
|
24
|
+
.freeze
|
|
25
|
+
.then { entry.with(usage_index: ReferenceIndex.new(references: it)) }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def walk(node, ctx, entry)
|
|
31
|
+
case node
|
|
32
|
+
in AST::Module(exposing:, body:)
|
|
33
|
+
walk_exposing(exposing, entry) + walk(body, :as_value, entry)
|
|
34
|
+
|
|
35
|
+
in AST::Body(expressions:)
|
|
36
|
+
expressions.flat_map { walk(it, :as_value, entry) }
|
|
37
|
+
|
|
38
|
+
in AST::FunctionDeclaration(body:, params:, return_type:)
|
|
39
|
+
walk(body, :as_value, entry) +
|
|
40
|
+
params.flat_map { walk_type(it.type, entry) } +
|
|
41
|
+
walk_type(return_type, entry)
|
|
42
|
+
|
|
43
|
+
in AST::FunctionCall(callee:, args:)
|
|
44
|
+
walk(callee, :called, entry) + args.flat_map { walk(it, :as_value, entry) }
|
|
45
|
+
|
|
46
|
+
in AST::VariableReference(symbol:, range:)
|
|
47
|
+
ref(symbol, ctx, range)
|
|
48
|
+
|
|
49
|
+
in AST::ConstructorReference(symbol:, range:)
|
|
50
|
+
ref(symbol, ctx == :called ? :constructed : :as_value, range)
|
|
51
|
+
|
|
52
|
+
in AST::QualifiedAccess(symbol:, range:)
|
|
53
|
+
ref(symbol, ctx, range)
|
|
54
|
+
|
|
55
|
+
in AST::Lambda(body:, params:)
|
|
56
|
+
walk(body, :as_value, entry) +
|
|
57
|
+
params.flat_map { walk(it, :as_value, entry) }
|
|
58
|
+
|
|
59
|
+
in AST::Assign(pattern:, expression:)
|
|
60
|
+
walk(expression, :as_value, entry) + walk(pattern, :as_value, entry)
|
|
61
|
+
|
|
62
|
+
in AST::IfThenElse(condition:, if_branch:, else_branch:)
|
|
63
|
+
walk(condition, :as_value, entry) +
|
|
64
|
+
walk(if_branch, :as_value, entry) +
|
|
65
|
+
walk(else_branch, :as_value, entry)
|
|
66
|
+
|
|
67
|
+
in AST::CaseOf(expression:, branches:)
|
|
68
|
+
walk(expression, :as_value, entry) +
|
|
69
|
+
branches.flat_map { walk(it, :as_value, entry) }
|
|
70
|
+
|
|
71
|
+
in AST::CaseOfBranch(pattern:, body:)
|
|
72
|
+
walk(pattern, :as_value, entry) + walk(body, :as_value, entry)
|
|
73
|
+
|
|
74
|
+
in AST::Pattern::Constructor(constructor:, patterns:, symbol:)
|
|
75
|
+
# Don't walk `constructor` — it's a bare ConstructorReference
|
|
76
|
+
# and walking it would record a spurious :as_value for every
|
|
77
|
+
# pattern match.
|
|
78
|
+
ref(symbol, :pattern_match, constructor.range) +
|
|
79
|
+
patterns.flat_map { walk(it, :as_value, entry) }
|
|
80
|
+
|
|
81
|
+
in AST::Pattern::List(patterns:, rest:)
|
|
82
|
+
rest_refs = rest ? walk(rest, :as_value, entry) : []
|
|
83
|
+
patterns.flat_map { walk(it, :as_value, entry) } + rest_refs
|
|
84
|
+
|
|
85
|
+
in AST::Pattern::Record(fields:)
|
|
86
|
+
fields.flat_map { walk(it.pattern, :as_value, entry) }
|
|
87
|
+
|
|
88
|
+
in AST::Pattern::Literal | AST::Pattern::Binding | AST::Pattern::Wildcard
|
|
89
|
+
[]
|
|
90
|
+
|
|
91
|
+
in AST::Grouping(expression:)
|
|
92
|
+
walk(expression, ctx, entry)
|
|
93
|
+
|
|
94
|
+
in AST::List(items:)
|
|
95
|
+
items.flat_map { walk(it, :as_value, entry) }
|
|
96
|
+
|
|
97
|
+
in AST::RecordLiteral(fields:)
|
|
98
|
+
fields.flat_map { walk(it, :as_value, entry) }
|
|
99
|
+
|
|
100
|
+
in AST::RecordUpdate(base:, fields:)
|
|
101
|
+
walk(base, :as_value, entry) + fields.flat_map { walk(it, :as_value, entry) }
|
|
102
|
+
|
|
103
|
+
in AST::RecordField(value:)
|
|
104
|
+
walk(value, :as_value, entry)
|
|
105
|
+
|
|
106
|
+
in AST::RecordAccess(target:)
|
|
107
|
+
walk(target, :as_value, entry)
|
|
108
|
+
|
|
109
|
+
in AST::Implementation(applied_type:, functions:)
|
|
110
|
+
walk_type(applied_type, entry) +
|
|
111
|
+
functions.flat_map { walk(it, :as_value, entry) }
|
|
112
|
+
|
|
113
|
+
in AST::ImplementationFunction(fn:)
|
|
114
|
+
walk(fn, :as_value, entry)
|
|
115
|
+
|
|
116
|
+
in AST::TypeDeclaration(variants:)
|
|
117
|
+
variants.flat_map { it.args.flat_map { walk_type(it, entry) } }
|
|
118
|
+
|
|
119
|
+
in AST::StructDeclaration(record_type:)
|
|
120
|
+
walk_type(record_type, entry)
|
|
121
|
+
|
|
122
|
+
in AST::InterfaceDeclaration(functions:)
|
|
123
|
+
functions.flat_map { walk_type(it.type, entry) }
|
|
124
|
+
|
|
125
|
+
in AST::InteropImportDeclaration(functions:)
|
|
126
|
+
functions.flat_map { walk_type(it.type, entry) }
|
|
127
|
+
|
|
128
|
+
in AST::ImportDeclaration | AST::VariantDeclaration |
|
|
129
|
+
AST::Literal | AST::CharLiteral |
|
|
130
|
+
AST::MemberAccess | AST::KeyedCall
|
|
131
|
+
# ImportDeclaration's exposing list is handled when we land
|
|
132
|
+
# in importer modules — see walk_exposing. KeyedCall and
|
|
133
|
+
# MemberAccess are lowered away during semantic_analysis, so
|
|
134
|
+
# the branches are defensive against partial ASTs.
|
|
135
|
+
[]
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def walk_type(node, entry)
|
|
140
|
+
case node
|
|
141
|
+
in nil
|
|
142
|
+
[]
|
|
143
|
+
|
|
144
|
+
in AST::TypeName(type:, range:)
|
|
145
|
+
entry.types[type]
|
|
146
|
+
.then { it ? [Reference[ReferenceIndex.key_for(it), :type_annotation, range]] : [] }
|
|
147
|
+
|
|
148
|
+
in AST::TypeApplication(constructor:, args:)
|
|
149
|
+
walk_type(constructor, entry) + args.flat_map { walk_type(it, entry) }
|
|
150
|
+
|
|
151
|
+
in AST::TypeFunction(params:, return_type:)
|
|
152
|
+
params.flat_map { walk_type(it, entry) } + walk_type(return_type, entry)
|
|
153
|
+
|
|
154
|
+
in AST::TypeRecord(fields:)
|
|
155
|
+
fields.values.flat_map { walk_type(it, entry) }
|
|
156
|
+
|
|
157
|
+
in AST::TypeTuple(items:)
|
|
158
|
+
items.flat_map { walk_type(it, entry) }
|
|
159
|
+
|
|
160
|
+
in AST::TypeVar | AST::TypeUnit | AST::QualifiedTypeName |
|
|
161
|
+
AST::TypeParam
|
|
162
|
+
[]
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def walk_exposing(node, entry)
|
|
167
|
+
case node
|
|
168
|
+
in AST::ExposeList(items:)
|
|
169
|
+
items.flat_map { walk_expose_item(it, entry) }
|
|
170
|
+
|
|
171
|
+
in AST::ExposeAll | AST::ExposeNone
|
|
172
|
+
[]
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def walk_expose_item(item, entry)
|
|
177
|
+
case item
|
|
178
|
+
in AST::ExposeValue(name:, range:)
|
|
179
|
+
exposed_ref(entry.lookup_value(name), range)
|
|
180
|
+
|
|
181
|
+
in AST::ExposeType | AST::ExposeTypeExpand
|
|
182
|
+
exposed_ref(entry.lookup_type(item.name), item.range)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def exposed_ref(symbol, range)
|
|
187
|
+
symbol ? [Reference[ReferenceIndex.key_for(symbol), :exposed, range]] : []
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def ref(symbol, kind, range)
|
|
191
|
+
[Reference[ReferenceIndex.key_for(symbol), kind, range]]
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require 'jade/registry'
|
|
2
|
+
require 'jade/symbol'
|
|
3
|
+
require 'jade/type'
|
|
4
|
+
require 'jade/stdlib'
|
|
5
|
+
require 'jade/did_you_mean'
|
|
6
|
+
|
|
7
|
+
require 'jade/frontend/comment_attacher'
|
|
8
|
+
require 'jade/frontend/forward_declaration'
|
|
9
|
+
require 'jade/frontend/semantic_analysis'
|
|
10
|
+
require 'jade/frontend/usage_analysis'
|
|
11
|
+
require 'jade/frontend/unused_analysis'
|
|
12
|
+
require 'jade/frontend/type_checking'
|
|
13
|
+
require 'jade/frontend/fixity_fixer'
|
|
14
|
+
require 'jade/frontend/desugaring'
|
|
15
|
+
require 'jade/frontend/desugaring/resolved'
|
|
16
|
+
|
|
17
|
+
module Jade
|
|
18
|
+
module Frontend
|
|
19
|
+
extend self
|
|
20
|
+
|
|
21
|
+
# On error, wraps the failure as `[latest_processed_entry, errors]` so
|
|
22
|
+
# tolerant callers can recover the AST as of the last successful stage
|
|
23
|
+
# instead of falling back to the original pre-frontend entry.
|
|
24
|
+
def run_entry(initial, registry)
|
|
25
|
+
latest = initial
|
|
26
|
+
capture = ->(entry) { latest = entry }
|
|
27
|
+
|
|
28
|
+
initial
|
|
29
|
+
.then { FixityFixer.fix_entry(it).tap(&capture) }
|
|
30
|
+
.then { Desugaring.desugar_entry(it).tap(&capture) }
|
|
31
|
+
.then { ForwardDeclaration.declare_entry(it, registry).map { it.tap(&capture) } }
|
|
32
|
+
.and_then { SemanticAnalysis.analyze(it, registry.update_module(it)).map { it.tap(&capture) } }
|
|
33
|
+
.map { Desugaring.desugar_resolved_entry(it, registry.update_module(it)).tap(&capture) }
|
|
34
|
+
.map { UsageAnalysis.analyze(it, registry.update_module(it)).tap(&capture) }
|
|
35
|
+
.map { UnusedAnalysis.analyze(it, registry.update_module(it)).tap(&capture) }
|
|
36
|
+
.and_then { TypeChecking.check(it, registry.update_module(it)).map { it.tap(&capture) } }
|
|
37
|
+
.map_error { |errs| [latest, errs] }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def run(ast)
|
|
41
|
+
run_up_to_semantic_analysis(ast)
|
|
42
|
+
.and_then do |(entry, registry)|
|
|
43
|
+
TypeChecking
|
|
44
|
+
.check(entry, registry)
|
|
45
|
+
.map { [it.ast, registry.update_module(it)] }
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def run_repl(ast, registry, current_entry, scope, env, var_gen)
|
|
50
|
+
registry ||= registry_with_basics
|
|
51
|
+
current_entry ||= entry_with_basics('JadeRepl')
|
|
52
|
+
scope ||= SemanticAnalysis::Scope.new
|
|
53
|
+
env ||= TypeChecking::Env.new
|
|
54
|
+
var_gen ||= TypeChecking::VarGen.new
|
|
55
|
+
|
|
56
|
+
ForwardDeclaration
|
|
57
|
+
.declare(ast, registry, current_entry)
|
|
58
|
+
.then { |entry| FixityFixer.fix(ast).then { [it, entry] } }
|
|
59
|
+
.then do |fixed_ast, updated_entry|
|
|
60
|
+
updated_registry = registry.update_module(updated_entry)
|
|
61
|
+
SemanticAnalysis
|
|
62
|
+
.analyze_repl(fixed_ast, updated_registry, scope, updated_entry)
|
|
63
|
+
.and_then do |(enhanced_ast, new_scope)|
|
|
64
|
+
enhanced_ast = Desugaring.desugar_resolved(enhanced_ast, updated_registry)
|
|
65
|
+
TypeChecking.check_repl(enhanced_ast, updated_registry, env, var_gen)
|
|
66
|
+
.map { |type, new_env| [enhanced_ast, type, updated_registry, updated_entry, new_scope, new_env] }
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def run_up_to_semantic_analysis(ast)
|
|
72
|
+
registry, current_entry = entry_with_basics(ast)
|
|
73
|
+
|
|
74
|
+
FixityFixer.fix(ast)
|
|
75
|
+
.then { Desugaring.desugar(it) }
|
|
76
|
+
.then { |enh_ast| ForwardDeclaration.declare(enh_ast, registry, current_entry).map { [enh_ast, it] } }
|
|
77
|
+
.and_then do |enh_ast, entry|
|
|
78
|
+
SemanticAnalysis.analyze(entry.with(ast: enh_ast), registry.update_module(entry))
|
|
79
|
+
end
|
|
80
|
+
.map { Desugaring.desugar_resolved_entry(it, registry.update_module(it)) }
|
|
81
|
+
.map { UsageAnalysis.analyze(it, registry.update_module(it)) }
|
|
82
|
+
.map { [it, registry.update_module(it)] }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def entry_with_basics(ast)
|
|
86
|
+
entry =
|
|
87
|
+
case ast
|
|
88
|
+
in AST::Module(name:)
|
|
89
|
+
name
|
|
90
|
+
else
|
|
91
|
+
'__Test__'
|
|
92
|
+
end
|
|
93
|
+
.then { Registry.entry(it).with(ast:) }
|
|
94
|
+
|
|
95
|
+
Stdlib.load(Registry.new)
|
|
96
|
+
.add_module(entry)
|
|
97
|
+
.then { Stdlib.apply(it) }
|
|
98
|
+
.then { [it, it.modules[entry.name]] }
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'jade/decode'
|
|
2
|
+
require 'jade/result'
|
|
3
|
+
require 'jade/interop/error'
|
|
4
|
+
|
|
5
|
+
module Jade
|
|
6
|
+
module Interop
|
|
7
|
+
module Boundary
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
# Boundary-side decode: succeed → return the value, fail → raise. The
|
|
11
|
+
# Result wrap/unwrap that user-level Decode.from_value uses is dead
|
|
12
|
+
# weight at the boundary because failure always raises anyway. Skipping
|
|
13
|
+
# it removes one allocation per arg per Ruby→Jade call.
|
|
14
|
+
def decode_or_raise(decoder, value)
|
|
15
|
+
case Jade::Decode::Runner.run(decoder, value)
|
|
16
|
+
in Jade::Result::Ok[v] then v
|
|
17
|
+
in Jade::Result::Err[e] then raise Jade::Interop::DecodeError.new(e, value)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Specialized fast-path validators. Emitted by codegen for known-shape
|
|
22
|
+
# argument types in place of the generic `decode_or_raise` path —
|
|
23
|
+
# avoids constructing a Decoder descriptor and walking the
|
|
24
|
+
# interpreter for primitives.
|
|
25
|
+
def integer(label, v)
|
|
26
|
+
::Integer === v ? v : type_error!(label, v)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def string(label, v)
|
|
30
|
+
::String === v ? v.dup : type_error!(label, v)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def bool(label, v)
|
|
34
|
+
v == true || v == false ? v : type_error!(label, v)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def float(label, v)
|
|
38
|
+
::Numeric === v ? v.to_f : type_error!(label, v)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def list_of(klass, label, v)
|
|
42
|
+
v.is_a?(::Array) && v.all? { klass === _1 } ? v : type_error!(label, v)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Validates that v is an Array but doesn't check element types — used
|
|
46
|
+
# when the per-element decoder isn't a simple `is_a?` (e.g. nested
|
|
47
|
+
# structs). The caller maps a decoder over the result.
|
|
48
|
+
def array(label, v)
|
|
49
|
+
v.is_a?(::Array) ? v : type_error!(label, v)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def hash(label, v)
|
|
53
|
+
case v
|
|
54
|
+
when ::Hash then v
|
|
55
|
+
when ::Data then v.to_h.transform_keys(&:to_s)
|
|
56
|
+
else type_error!(label, v)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def type_error!(label, v)
|
|
61
|
+
raise Jade::Interop::DecodeError.new(
|
|
62
|
+
Jade::Decode::WrongType[label, Jade::Decode.type_name(v)],
|
|
63
|
+
v,
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Interop
|
|
3
|
+
class Error < StandardError; end
|
|
4
|
+
|
|
5
|
+
class PortNotRegistered < Error
|
|
6
|
+
def initialize(module_name, function_name)
|
|
7
|
+
super(
|
|
8
|
+
"Port `#{function_name}` on `#{module_name}` is not a Jade port. " \
|
|
9
|
+
"Add `extend Jade::Port` to `#{module_name}` and declare it with " \
|
|
10
|
+
"`task :#{function_name} do |t, ...| ... end`."
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Raised when a Ruby caller invokes a Jade function whose signature
|
|
16
|
+
# has no public boundary — typically a polymorphic fn, a fn with
|
|
17
|
+
# function-typed args, or a fn over a type whose Decodable/Encodable
|
|
18
|
+
# can't be derived. The user's options are to add an explicit
|
|
19
|
+
# `implements Decodable/Encodable`, restructure the signature with
|
|
20
|
+
# decodable types, or accept that the fn is Jade-internal only.
|
|
21
|
+
class NotExposed < Error
|
|
22
|
+
def initialize(module_name:, function_name:, hint: nil)
|
|
23
|
+
["#{module_name}.#{function_name} is not exposed to Ruby.", hint]
|
|
24
|
+
.compact
|
|
25
|
+
.join(' ')
|
|
26
|
+
.then { super(it) }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Raised by bang-suffixed Task wrappers when the underlying Task ran
|
|
31
|
+
# to the Err arm. The encoded err value is available on `.error` for
|
|
32
|
+
# structured handling — pattern-match on it for shape-specific
|
|
33
|
+
# recovery, or just inspect for logging.
|
|
34
|
+
class TaskError < Error
|
|
35
|
+
attr_reader :error
|
|
36
|
+
|
|
37
|
+
def initialize(error)
|
|
38
|
+
@error = error
|
|
39
|
+
super("Task returned an error: #{error.inspect}")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class DecodeError < Error
|
|
44
|
+
attr_reader :decode_error, :value
|
|
45
|
+
|
|
46
|
+
def initialize(decode_error, value)
|
|
47
|
+
@decode_error = decode_error
|
|
48
|
+
@value = value
|
|
49
|
+
super(format(decode_error, value))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def format(error, value)
|
|
55
|
+
path, leaf = unwind(error)
|
|
56
|
+
location = path.empty? ? "value" : path.join
|
|
57
|
+
|
|
58
|
+
case leaf
|
|
59
|
+
in Jade::Decode::WrongType[expected, got]
|
|
60
|
+
"Port returned a value that failed to decode at #{location}: expected #{expected}, got #{got} (#{value.inspect})"
|
|
61
|
+
|
|
62
|
+
in Jade::Decode::MissingField[key]
|
|
63
|
+
"Port returned a value that failed to decode at #{location}: missing field `#{key}` (#{value.inspect})"
|
|
64
|
+
|
|
65
|
+
in Jade::Decode::Custom[msg]
|
|
66
|
+
"Port returned a value that failed to decode at #{location}: #{msg} (#{value.inspect})"
|
|
67
|
+
|
|
68
|
+
in Jade::Decode::Multiple[errors]
|
|
69
|
+
errors
|
|
70
|
+
.map { format(it, value) }
|
|
71
|
+
.join("; ")
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def unwind(error, path = [])
|
|
76
|
+
case error
|
|
77
|
+
in Jade::Decode::AtField[key, inner] then unwind(inner, path + [".#{key}"])
|
|
78
|
+
in Jade::Decode::AtIndex[idx, inner] then unwind(inner, path + ["[#{idx}]"])
|
|
79
|
+
else [path, error]
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|