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
data/lib/jade/cli/fmt.rb
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'jade/source'
|
|
2
|
+
require 'jade/lexer'
|
|
3
|
+
require 'jade/ast'
|
|
4
|
+
require 'jade/parsing'
|
|
5
|
+
require 'jade/formatter'
|
|
6
|
+
require 'jade/frontend/comment_attacher'
|
|
7
|
+
|
|
8
|
+
module Jade
|
|
9
|
+
module CLI
|
|
10
|
+
module Fmt
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def run(argv)
|
|
14
|
+
mode = :stdout
|
|
15
|
+
file = nil
|
|
16
|
+
|
|
17
|
+
argv.each do |arg|
|
|
18
|
+
case arg
|
|
19
|
+
when '-i', '--in-place' then mode = :in_place
|
|
20
|
+
when '-c', '--check' then mode = :check
|
|
21
|
+
when '-h', '--help' then usage
|
|
22
|
+
when /\A-/
|
|
23
|
+
warn "unknown option: #{arg}"
|
|
24
|
+
usage
|
|
25
|
+
else
|
|
26
|
+
usage if file
|
|
27
|
+
file = arg
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
format(file, mode)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def format(file, mode)
|
|
35
|
+
source_text = read_source(file)
|
|
36
|
+
source = Source.new(uri: file || 'stdin', text: source_text)
|
|
37
|
+
|
|
38
|
+
case Parsing.parse(Lexer.tokenize(source), source:)
|
|
39
|
+
in Ok([ast, comments])
|
|
40
|
+
emit(Formatter.format(ast, comments:, source:) + "\n",
|
|
41
|
+
source_text, file, mode)
|
|
42
|
+
|
|
43
|
+
in Err(error)
|
|
44
|
+
warn "Parse error: #{error.message}"
|
|
45
|
+
exit 2
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def read_source(file)
|
|
50
|
+
case
|
|
51
|
+
when file then File.read(file)
|
|
52
|
+
when !$stdin.tty? then $stdin.read
|
|
53
|
+
else usage
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def emit(formatted, source_text, file, mode)
|
|
58
|
+
case mode
|
|
59
|
+
when :in_place
|
|
60
|
+
usage unless file
|
|
61
|
+
write_in_place(formatted, source_text, file)
|
|
62
|
+
|
|
63
|
+
when :check
|
|
64
|
+
exit 0 if formatted == source_text
|
|
65
|
+
warn "#{file || 'stdin'}: not formatted"
|
|
66
|
+
exit 1
|
|
67
|
+
|
|
68
|
+
when :stdout
|
|
69
|
+
print formatted
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def write_in_place(formatted, source_text, file)
|
|
74
|
+
return if formatted == source_text
|
|
75
|
+
|
|
76
|
+
File.write(file, formatted)
|
|
77
|
+
warn "Formatted #{file}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def usage
|
|
81
|
+
warn <<~USAGE
|
|
82
|
+
Usage: jade fmt [options] [file]
|
|
83
|
+
|
|
84
|
+
Options:
|
|
85
|
+
-i, --in-place Rewrite the file in place.
|
|
86
|
+
-c, --check Exit 0 if formatted, 1 if drift, 2 on parse error.
|
|
87
|
+
Does not write.
|
|
88
|
+
-h, --help Show this message.
|
|
89
|
+
|
|
90
|
+
Reads from stdin when no file is given.
|
|
91
|
+
USAGE
|
|
92
|
+
exit 1
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
data/lib/jade/cli/lsp.rb
ADDED
data/lib/jade/cli/q.rb
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
require 'jade'
|
|
2
|
+
require 'jade/lsp'
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Jade
|
|
6
|
+
module CLI
|
|
7
|
+
# Headless query interface to the same compiler intelligence the LSP
|
|
8
|
+
# server uses. Intended for tools and agents that want a quick JSON
|
|
9
|
+
# answer instead of speaking JSON-RPC.
|
|
10
|
+
module Q
|
|
11
|
+
USAGE = <<~TXT.freeze
|
|
12
|
+
Usage: jade q COMMAND [ARGS]
|
|
13
|
+
|
|
14
|
+
hover FILE:LINE:COL Type info at the given position.
|
|
15
|
+
symbols FILE Document symbols (outline) for FILE.
|
|
16
|
+
defn FILE:LINE:COL Goto-definition location.
|
|
17
|
+
refs FILE:LINE:COL Find all references (incl. declaration).
|
|
18
|
+
|
|
19
|
+
FILE is relative to the project root (cwd).
|
|
20
|
+
LINE and COL are 0-indexed (LSP convention).
|
|
21
|
+
|
|
22
|
+
Compile cache lives at .jade/cache, so repeat queries are fast.
|
|
23
|
+
TXT
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
def run(argv)
|
|
28
|
+
case argv[0]
|
|
29
|
+
when 'hover' then dump(hover(argv[1]))
|
|
30
|
+
when 'symbols' then dump(symbols(argv[1]))
|
|
31
|
+
when 'defn' then dump(defn(argv[1]))
|
|
32
|
+
when 'refs' then dump(refs(argv[1]))
|
|
33
|
+
else
|
|
34
|
+
warn USAGE
|
|
35
|
+
exit 1
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def hover(arg)
|
|
40
|
+
file, line, col = parse_position(arg)
|
|
41
|
+
Context.new(file).then do |ctx|
|
|
42
|
+
Jade::LSP::Converters.hover_for_path(
|
|
43
|
+
ctx.path_at(line, col), ctx.registry, ctx.entry
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def symbols(file)
|
|
49
|
+
fail 'FILE required' unless file
|
|
50
|
+
|
|
51
|
+
Context.new(file).entry.then do |entry|
|
|
52
|
+
entry.ast.body.expressions.filter_map do |node|
|
|
53
|
+
Jade::LSP::Converters.to_document_symbol(node, entry.source)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def defn(arg)
|
|
59
|
+
file, line, col = parse_position(arg)
|
|
60
|
+
Context.new(file).then do |ctx|
|
|
61
|
+
Jade::LSP::Converters.definition_for_path(
|
|
62
|
+
ctx.path_at(line, col), ctx.registry, ctx.entry, ctx.source_root
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def refs(arg)
|
|
68
|
+
file, line, col = parse_position(arg)
|
|
69
|
+
Context.new(file).then do |ctx|
|
|
70
|
+
Jade::LSP::Converters.references_for_path(
|
|
71
|
+
ctx.path_at(line, col), ctx.registry, ctx.entry, ctx.source_root,
|
|
72
|
+
include_declaration: true,
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def parse_position(arg)
|
|
78
|
+
fail 'FILE:LINE:COL required' unless arg
|
|
79
|
+
fail "expected FILE:LINE:COL, got #{arg.inspect}" unless arg.count(':') == 2
|
|
80
|
+
|
|
81
|
+
arg.split(':').then { |(file, line, col)| [file, line.to_i, col.to_i] }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def dump(result)
|
|
85
|
+
puts JSON.pretty_generate(result)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
class Context
|
|
89
|
+
attr_reader :file, :source_root, :registry, :entry
|
|
90
|
+
|
|
91
|
+
def initialize(file)
|
|
92
|
+
@file = file
|
|
93
|
+
@source_root = Dir.pwd
|
|
94
|
+
@registry = Jade::ModuleLoader.load(
|
|
95
|
+
@source_root, file,
|
|
96
|
+
cache_dir: File.join(@source_root, '.jade/cache'),
|
|
97
|
+
tolerant: true,
|
|
98
|
+
)
|
|
99
|
+
@entry = @registry
|
|
100
|
+
.modules
|
|
101
|
+
.each_value
|
|
102
|
+
.find { it.source&.uri == file } || fail("no module at #{file}")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def path_at(line, col)
|
|
106
|
+
Jade::LSP::Converters
|
|
107
|
+
.position_to_offset(entry.source, line, col)
|
|
108
|
+
.then { entry.ast.find_at_path(it) }
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
data/lib/jade/cli.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'jade'
|
|
2
|
+
|
|
3
|
+
module Jade
|
|
4
|
+
module CLI
|
|
5
|
+
SUBCOMMANDS = {
|
|
6
|
+
'fmt' => 'Fmt',
|
|
7
|
+
'lsp' => 'Lsp',
|
|
8
|
+
'q' => 'Q',
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def run(argv)
|
|
14
|
+
sub, *rest = argv
|
|
15
|
+
|
|
16
|
+
case sub
|
|
17
|
+
when nil, '-h', '--help', 'help'
|
|
18
|
+
usage
|
|
19
|
+
|
|
20
|
+
when *SUBCOMMANDS.keys
|
|
21
|
+
require "jade/cli/#{sub}"
|
|
22
|
+
const_get(SUBCOMMANDS.fetch(sub)).run(rest)
|
|
23
|
+
|
|
24
|
+
else
|
|
25
|
+
warn "jade: unknown command #{sub.inspect}\n\n"
|
|
26
|
+
usage($stderr)
|
|
27
|
+
exit 1
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def usage(io = $stdout)
|
|
32
|
+
io.puts <<~TXT
|
|
33
|
+
Usage: jade COMMAND [ARGS]
|
|
34
|
+
|
|
35
|
+
fmt Format .jd source (stdin or file).
|
|
36
|
+
lsp Run the language server (stdio JSON-RPC).
|
|
37
|
+
q Headless query interface (hover/symbols/defn/refs).
|
|
38
|
+
|
|
39
|
+
Run `jade COMMAND --help` for command-specific options.
|
|
40
|
+
TXT
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module Boundary
|
|
4
|
+
# Per-module cache mapping each type a boundary wrapper needs to
|
|
5
|
+
# decode/encode to a const name (`BOUNDARY_DEC_0`, etc.) that holds
|
|
6
|
+
# the value once at module load. `collect` walks the body to build
|
|
7
|
+
# the map; `decoder_for` / `encoder_for` consult it from wrapper
|
|
8
|
+
# codegen, falling back to the raw `Boundary` spec when uncached
|
|
9
|
+
# (e.g. when emission runs outside a Module).
|
|
10
|
+
module Cache
|
|
11
|
+
extend self
|
|
12
|
+
extend Helpers
|
|
13
|
+
|
|
14
|
+
def decoder_for(type, registry)
|
|
15
|
+
Codegen.boundary_cache[:decoders][type] || Boundary.decoder_for(type, registry)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def encoder_for(type, registry)
|
|
19
|
+
Codegen.boundary_cache[:encoders][type] || Boundary.encoder_for(type, registry)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def task_arms(task_type, registry)
|
|
23
|
+
task_type => Type::Application(args: [ok_t, err_t])
|
|
24
|
+
[encoder_for(ok_t, registry), encoder_for(err_t, registry)]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def collect(body, registry)
|
|
28
|
+
per_fn = body
|
|
29
|
+
.expressions
|
|
30
|
+
.filter_map do
|
|
31
|
+
boundary_types(it, registry) if it.is_a?(AST::FunctionDeclaration)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
{
|
|
35
|
+
decoders: cache_map(per_fn.flat_map { it[:decoders] }, 'DEC') { |t|
|
|
36
|
+
Boundary::Specialized.decode_expr(t, '_', registry)
|
|
37
|
+
},
|
|
38
|
+
encoders: cache_map(per_fn.flat_map { it[:encoders] }, 'ENC') { |t|
|
|
39
|
+
Boundary::Specialized.identity_encoder?(t) ||
|
|
40
|
+
Boundary::Specialized.encode_expr(t, '_', registry)
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Types with a specialized inline emission don't need a cached
|
|
46
|
+
# constant — the wrapper emits the validation directly.
|
|
47
|
+
def cache_map(types, tag, &specialized)
|
|
48
|
+
types
|
|
49
|
+
.reject(&specialized)
|
|
50
|
+
.uniq
|
|
51
|
+
.each_with_index
|
|
52
|
+
.map { |t, i| [t, "BOUNDARY_#{tag}_#{i}"] }
|
|
53
|
+
.to_h
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def constants(cache, registry)
|
|
57
|
+
cache[:decoders].map { |type, name| "#{name} = #{Boundary.decoder_for(type, registry)}" } +
|
|
58
|
+
cache[:encoders].map { |type, name| "#{name} = #{Boundary.encoder_for(type, registry)}" }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# Returns the {decoders:, encoders:} type lists this fn's wrapper
|
|
64
|
+
# would touch, or `nil` when the fn produces no wrapper (not
|
|
65
|
+
# exposed, polymorphic, or ineligible).
|
|
66
|
+
def boundary_types(fn_node, registry)
|
|
67
|
+
symbol = fn_node.symbol
|
|
68
|
+
return nil unless registry.get(symbol.module_name).exposed_value(symbol.name)
|
|
69
|
+
return nil unless dict_constraints(symbol, registry).empty?
|
|
70
|
+
|
|
71
|
+
fn_type = fn_type_for(symbol, registry)
|
|
72
|
+
return nil unless Boundary.eligible?(fn_type, registry)
|
|
73
|
+
|
|
74
|
+
args, return_type = Type.signature(fn_type)
|
|
75
|
+
|
|
76
|
+
case return_type
|
|
77
|
+
in Type::Application(constructor: Type::Constructor(name: 'Task.Task'), args: arms)
|
|
78
|
+
arms
|
|
79
|
+
else
|
|
80
|
+
[return_type]
|
|
81
|
+
end
|
|
82
|
+
.then { { decoders: args, encoders: it } }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def fn_type_for(symbol, registry)
|
|
86
|
+
registry
|
|
87
|
+
.get(symbol.module_name)
|
|
88
|
+
.env
|
|
89
|
+
.then { it.substitution.apply(it.bindings[symbol.qualified_name].type) }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module Boundary
|
|
4
|
+
module Specialized
|
|
5
|
+
# `List(t)` where `t` is itself specializable. Two shapes:
|
|
6
|
+
#
|
|
7
|
+
# - `List(scalar)` — emits a single `Array#all?` C-loop check via
|
|
8
|
+
# `Boundary.list_of`, then passes the array through.
|
|
9
|
+
# - `List(specializable)` — validates Array shape with
|
|
10
|
+
# `Boundary.array`, then maps the inner decoder over each element.
|
|
11
|
+
module List
|
|
12
|
+
extend self
|
|
13
|
+
|
|
14
|
+
def decode(type, input, registry)
|
|
15
|
+
inner = inner_of(type) or return nil
|
|
16
|
+
|
|
17
|
+
scalar_optimized(inner, input) ||
|
|
18
|
+
generic_decode(type, inner, input, registry)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def encode(type, value_expr, registry)
|
|
22
|
+
inner = inner_of(type) or return nil
|
|
23
|
+
return nil if Specialized.identity_encoder?(inner)
|
|
24
|
+
|
|
25
|
+
elem = Specialized.encode_expr(inner, '_1', registry) or return nil
|
|
26
|
+
"#{value_expr}.map { #{elem} }"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def identity_encoder?(type)
|
|
30
|
+
inner = inner_of(type) or return false
|
|
31
|
+
Specialized.identity_encoder?(inner)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def specializable?(type, registry, seen)
|
|
35
|
+
inner = inner_of(type) or return false
|
|
36
|
+
Specialized.specializable_field?(inner, registry, seen)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def inner_of(type)
|
|
40
|
+
return nil unless Specialized.qname_of(type) == 'List.List'
|
|
41
|
+
args = Specialized.args_of(type)
|
|
42
|
+
args&.size == 1 ? args[0] : nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# `List(scalar)` fast path: validate elements with a single
|
|
48
|
+
# C-loop `all?` check, no per-element decoder call.
|
|
49
|
+
def scalar_optimized(inner, input)
|
|
50
|
+
qname = Scalar.qname_for(inner) or return nil
|
|
51
|
+
klass = Scalar::LIST_ELEM_CLASS[qname]
|
|
52
|
+
label = "List(#{Scalar::LABEL[qname]})".inspect
|
|
53
|
+
"Jade::Interop::Boundary.list_of(#{klass}, #{label}, #{input})"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def generic_decode(type, inner, input, registry)
|
|
57
|
+
elem = Specialized.decode_expr(inner, '_1', registry) or return nil
|
|
58
|
+
label = "List(#{Specialized.type_label(inner)})".inspect
|
|
59
|
+
"Jade::Interop::Boundary.array(#{label}, #{input}).map { #{elem} }"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module Boundary
|
|
4
|
+
module Specialized
|
|
5
|
+
# `Maybe(t)` where `t` is itself specializable. Both decode and
|
|
6
|
+
# encode bind the input via `.then { it ... }` so a complex
|
|
7
|
+
# `value_expr` (e.g. a full `Internal.X(...)` call) isn't
|
|
8
|
+
# re-evaluated.
|
|
9
|
+
module Maybe
|
|
10
|
+
extend self
|
|
11
|
+
|
|
12
|
+
def decode(type, input, registry)
|
|
13
|
+
inner = inner_of(type) or return nil
|
|
14
|
+
elem = Specialized.decode_expr(inner, 'it', registry) or return nil
|
|
15
|
+
|
|
16
|
+
"#{input}.then { it.nil? ? Jade::Maybe::Nothing[] : Jade::Maybe::Just[#{elem}] }"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def encode(type, value_expr, registry)
|
|
20
|
+
inner = inner_of(type) or return nil
|
|
21
|
+
inner_enc = Specialized.encode_expr(inner, 'it._1', registry) || 'it._1'
|
|
22
|
+
|
|
23
|
+
"#{value_expr}.then { it.is_a?(::Jade::Maybe::Just) ? #{inner_enc} : nil }"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def specializable?(type, registry, seen)
|
|
27
|
+
inner = inner_of(type) or return false
|
|
28
|
+
Specialized.specializable_field?(inner, registry, seen)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def inner_of(type)
|
|
32
|
+
return nil unless Specialized.qname_of(type) == 'Maybe.Maybe'
|
|
33
|
+
args = Specialized.args_of(type)
|
|
34
|
+
args&.size == 1 ? args[0] : nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module Codegen
|
|
3
|
+
module Boundary
|
|
4
|
+
module Specialized
|
|
5
|
+
# Structs whose fields are all specializable. Each specializable
|
|
6
|
+
# struct gets a pair of `def self.decode_<name>` / `encode_<name>`
|
|
7
|
+
# helper methods emitted at module level; the wrapper body for
|
|
8
|
+
# `birthday(person: Person) -> Person` becomes
|
|
9
|
+
# `encode_person(Internal.birthday(decode_person(person)))`.
|
|
10
|
+
#
|
|
11
|
+
# Cycle detection: a struct referencing itself (directly or
|
|
12
|
+
# mutually) falls back to the descriptor cache path. The `seen`
|
|
13
|
+
# set carries qnames of structs we're currently inside; revisiting
|
|
14
|
+
# one means we'd loop, so we bail with nil.
|
|
15
|
+
module Record
|
|
16
|
+
extend self
|
|
17
|
+
extend Helpers
|
|
18
|
+
|
|
19
|
+
def decode(type, input, registry)
|
|
20
|
+
struct = struct_for(type, registry) or return nil
|
|
21
|
+
"#{decode_helper_name(struct)}(#{input})"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def encode(type, value_expr, registry)
|
|
25
|
+
struct = struct_for(type, registry) or return nil
|
|
26
|
+
"#{encode_helper_name(struct)}(#{value_expr})"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def specializable?(type, registry, seen)
|
|
30
|
+
specializable_struct(type, registry, seen) ? true : false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# All specializable structs reachable from any exposed
|
|
34
|
+
# function's boundary signature, transitively (through nested
|
|
35
|
+
# struct fields and `List` / `Maybe` wrappers). Each one needs
|
|
36
|
+
# `decode_<name>` / `encode_<name>` helper methods emitted.
|
|
37
|
+
def collect_helpers(body, registry)
|
|
38
|
+
body.expressions
|
|
39
|
+
.filter { it.is_a?(AST::FunctionDeclaration) }
|
|
40
|
+
.flat_map { fn_reachable_structs(it, registry) }
|
|
41
|
+
.uniq
|
|
42
|
+
.then { transitive_closure(it, registry) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def emit_helpers(structs, registry)
|
|
46
|
+
structs.flat_map do
|
|
47
|
+
[decode_helper(it, registry), encode_helper(it, registry)]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def struct_for(type, registry)
|
|
52
|
+
specializable_struct(type, registry, ::Set.new)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Returns specializable structs reachable from `type` through
|
|
56
|
+
# any depth of `List` / `Maybe` / nested struct fields. Used by
|
|
57
|
+
# `collect_helpers` to seed the closure walk.
|
|
58
|
+
def structs_in(type, registry)
|
|
59
|
+
if (struct = struct_for(type, registry))
|
|
60
|
+
[struct]
|
|
61
|
+
elsif (inner = List.inner_of(type))
|
|
62
|
+
structs_in(inner, registry)
|
|
63
|
+
elsif (inner = Maybe.inner_of(type))
|
|
64
|
+
structs_in(inner, registry)
|
|
65
|
+
else
|
|
66
|
+
[]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def specializable_struct(type, registry, seen)
|
|
73
|
+
struct = lookup_struct(type, registry) or return nil
|
|
74
|
+
return nil if seen.include?(struct.qualified_name)
|
|
75
|
+
|
|
76
|
+
struct.record_type.fields.values
|
|
77
|
+
.all? { Specialized.specializable_field?(it, registry, seen + [struct.qualified_name]) }
|
|
78
|
+
.then { it ? struct : nil }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def lookup_struct(type, registry)
|
|
82
|
+
return nil unless Specialized.args_of(type) == []
|
|
83
|
+
qname = Specialized.qname_of(type) or return nil
|
|
84
|
+
|
|
85
|
+
parts = qname.split('.')
|
|
86
|
+
ref = Symbol.type_ref(parts[0..-2].join('.'), parts[-1])
|
|
87
|
+
sym = registry.lookup(ref) or return nil
|
|
88
|
+
sym.is_a?(Symbol::Struct) ? sym : nil
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Reachability walk from `seeds` through nested struct fields.
|
|
92
|
+
# Pure-functional: each call builds a fresh `collected + frontier`.
|
|
93
|
+
def transitive_closure(seeds, registry, collected = ::Set.new)
|
|
94
|
+
frontier = seeds.reject { collected.include?(it) }
|
|
95
|
+
return collected.to_a if frontier.empty?
|
|
96
|
+
|
|
97
|
+
transitive_closure(
|
|
98
|
+
frontier.flat_map { nested_structs(it, registry) },
|
|
99
|
+
registry,
|
|
100
|
+
collected + frontier,
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def nested_structs(struct, registry)
|
|
105
|
+
struct.record_type.fields.values.flat_map { structs_in(it, registry) }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def fn_reachable_structs(fn_node, registry)
|
|
109
|
+
symbol = fn_node.symbol
|
|
110
|
+
return [] unless registry.get(symbol.module_name).exposed_value(symbol.name)
|
|
111
|
+
|
|
112
|
+
fn_type = registry.get(symbol.module_name)
|
|
113
|
+
.env
|
|
114
|
+
.then { it.substitution.apply(it.bindings[symbol.qualified_name].type) }
|
|
115
|
+
|
|
116
|
+
args, ret = Type.signature(fn_type)
|
|
117
|
+
(args + [ret]).flat_map { structs_in(it, registry) }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def decode_helper_name(struct)
|
|
121
|
+
"decode_#{snake(struct.name)}"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def encode_helper_name(struct)
|
|
125
|
+
"encode_#{snake(struct.name)}"
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def decode_helper(struct, registry)
|
|
129
|
+
ctor = "::#{to_qualified(struct.module_name)}::#{struct.name}"
|
|
130
|
+
hash_call = "Jade::Interop::Boundary.hash(#{struct.name.inspect}, value)"
|
|
131
|
+
|
|
132
|
+
struct.record_type.fields
|
|
133
|
+
.map { |k, t| field_decode(k, t, registry) }
|
|
134
|
+
.then { Pretty.call(ctor, it, open: '[', close: ']') }
|
|
135
|
+
.then { Pretty.block("#{hash_call}.then do |h|", it) }
|
|
136
|
+
.then { Pretty.block("def self.#{decode_helper_name(struct)}(value)", it) }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def encode_helper(struct, registry)
|
|
140
|
+
struct.record_type.fields
|
|
141
|
+
.map { |k, t| "#{k.inspect} => #{field_encode_value(t, "p.#{k}", registry)}" }
|
|
142
|
+
.then { "{ #{it.join(', ')} }" }
|
|
143
|
+
.then { Pretty.block("def self.#{encode_helper_name(struct)}(p)", it) }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def field_decode(key, type, registry)
|
|
147
|
+
Specialized.decode_expr(type, "h[#{key.to_s.inspect}]", registry) ||
|
|
148
|
+
fail("non-specializable field type for #{key}: #{type}")
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def field_encode_value(type, accessor, registry)
|
|
152
|
+
Specialized.encode_expr(type, accessor, registry) || accessor
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def snake(name)
|
|
156
|
+
name
|
|
157
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
158
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
159
|
+
.downcase
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|