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
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: ae124aca6294ee2cc9d2ed1cb7cf05c7224aaa429277b8e3c883f4735e11aa9b
|
|
4
|
+
data.tar.gz: 35e00d927319976b642638959808b586214c08bf084135197099d88270d776c6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 57bc05a0cbabfa09e033aa3c6627fcbefdf08b436b8ec20c4fe6a6d9e702f51c4f9a39ad783530896f1cc640a97fe649da79c00a43c30c82478747ec00f98017
|
|
7
|
+
data.tar.gz: db704c4e1c98eef3d9fd5a0df63539a3128e2c8f08976004691b281967f8f3621fd5e6969313d53df2c17b55195a4f8b0d27d69fc33001973428a0ab86b561d4
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0]
|
|
10
|
+
|
|
11
|
+
Initial release.
|
|
12
|
+
|
|
13
|
+
- Functional, type-safe language that compiles to readable Ruby.
|
|
14
|
+
- Hindley–Milner type inference with union types, records, and pattern
|
|
15
|
+
matching.
|
|
16
|
+
- Standard library (`Maybe`, `Result`, `List`, `String`, `Dict`, and more).
|
|
17
|
+
- Interfaces with instance resolution (`Eq`, `Comparable`).
|
|
18
|
+
- Ruby interop with typed boundaries (`uses ... with`).
|
|
19
|
+
- `jade` CLI dispatcher: `fmt`, `lsp`, `q`.
|
|
20
|
+
- Language server and headless query interface for editor/agent tooling.
|
|
21
|
+
|
|
22
|
+
[Unreleased]: https://github.com/agustinrhcp/jade/compare/v0.1.0...HEAD
|
|
23
|
+
[0.1.0]: https://github.com/agustinrhcp/jade/releases/tag/v0.1.0
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Agustin Cornu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
# Jade
|
|
2
|
+
|
|
3
|
+
[](https://github.com/agustinrhcp/jade/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
A statically typed, functional language that compiles to readable Ruby.
|
|
6
|
+
Inspired by Elm. Type inference, union types, exhaustive pattern matching, and
|
|
7
|
+
typed boundaries to Ruby.
|
|
8
|
+
|
|
9
|
+
## What it looks like
|
|
10
|
+
|
|
11
|
+
```jade
|
|
12
|
+
module Greeter exposing (greet)
|
|
13
|
+
|
|
14
|
+
def greet(name: Maybe(String)) -> String
|
|
15
|
+
case name
|
|
16
|
+
in Just(n) then "Hello, " ++ n
|
|
17
|
+
in Nothing then "Hello, stranger"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
compiles to:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
module Greeter
|
|
26
|
+
extend self
|
|
27
|
+
|
|
28
|
+
module Internal
|
|
29
|
+
extend self
|
|
30
|
+
|
|
31
|
+
def greet(name)
|
|
32
|
+
case name
|
|
33
|
+
in Jade::Maybe::Just(n) then ("Hello, " + n)
|
|
34
|
+
in Jade::Maybe::Nothing then "Hello, stranger"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.greet(name)
|
|
40
|
+
# validate the incoming Ruby value, then call the pure function
|
|
41
|
+
Internal.greet(decode(name))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
There's no runtime VM and no FFI. The pure logic lives in `Internal`; the public
|
|
47
|
+
`Greeter.greet` decodes the untrusted Ruby argument (`nil`-or-`String`) into a
|
|
48
|
+
`Maybe` before handing it to the typed core. Calling it from Ruby:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
Greeter.greet("Ada") # => "Hello, Ada"
|
|
52
|
+
Greeter.greet(nil) # => "Hello, stranger"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
**`Maybe` instead of `nil`.** `Maybe(a)` makes absence explicit, and the
|
|
58
|
+
compiler flags any `case` that forgets the `Nothing` branch. Errors are values
|
|
59
|
+
too:
|
|
60
|
+
|
|
61
|
+
```jade
|
|
62
|
+
module Accounts exposing (withdraw)
|
|
63
|
+
|
|
64
|
+
def withdraw(balance: Int, amount: Int) -> Result(Int, String)
|
|
65
|
+
amount > balance ? Err("insufficient funds") : Ok(balance - amount)
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Union types and exhaustive pattern matching.** Add a variant and every
|
|
70
|
+
`case` that needs a new branch becomes a compile error:
|
|
71
|
+
|
|
72
|
+
```jade
|
|
73
|
+
module Shapes exposing (area_of)
|
|
74
|
+
|
|
75
|
+
type Shape
|
|
76
|
+
= Circle(Float)
|
|
77
|
+
| Rectangle(Float, Float)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def area_of(shape: Shape) -> Float
|
|
81
|
+
case shape
|
|
82
|
+
in Circle(r) then 3.14 * r * r
|
|
83
|
+
in Rectangle(w, h) then w * h
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Records with structural update and field access:**
|
|
89
|
+
|
|
90
|
+
```jade
|
|
91
|
+
module Users exposing (birthday, name_of)
|
|
92
|
+
|
|
93
|
+
struct User = {
|
|
94
|
+
name: String,
|
|
95
|
+
age: Int
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def birthday(user: User) -> User
|
|
100
|
+
{ user | age: user.age + 1 }
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def name_of(user: User) -> String
|
|
105
|
+
user.name
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Pipes.** `|>` passes a value into the next function:
|
|
110
|
+
|
|
111
|
+
```jade
|
|
112
|
+
module Pipeline exposing (shout)
|
|
113
|
+
|
|
114
|
+
def shout(words: List(String)) -> String
|
|
115
|
+
words
|
|
116
|
+
|> List.map(String.to_upper)
|
|
117
|
+
|> String.join(" ")
|
|
118
|
+
end
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Everything above is inferred end to end — annotations on `def` signatures are
|
|
122
|
+
checked, not required internally.
|
|
123
|
+
|
|
124
|
+
## More of the language
|
|
125
|
+
|
|
126
|
+
**Lambdas and currying.** Lambdas are `(params) -> { body }`. A `_` in an
|
|
127
|
+
argument position curries that call — each `_` becomes a parameter, left to
|
|
128
|
+
right, so `discount(10, _)` is a one-argument function:
|
|
129
|
+
|
|
130
|
+
```jade
|
|
131
|
+
module Pricing exposing (net)
|
|
132
|
+
|
|
133
|
+
def discount(rate: Int, price: Int) -> Int
|
|
134
|
+
price - price * rate / 100
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def net(prices: List(Int)) -> List(Int)
|
|
139
|
+
prices
|
|
140
|
+
|> List.map(discount(10, _))
|
|
141
|
+
|> List.filter((p) -> { p > 50 })
|
|
142
|
+
end
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Interfaces.** `==` / `!=` (Eq), `compare` (Comparable — returns `LT` / `EQ` /
|
|
146
|
+
`GT`), and `++` (Appendable) are built in and resolve from the argument types
|
|
147
|
+
at compile time, no annotation needed. You can define your own, with
|
|
148
|
+
implementations dispatched by type:
|
|
149
|
+
|
|
150
|
+
```jade
|
|
151
|
+
module Shows exposing (describe)
|
|
152
|
+
|
|
153
|
+
struct Person = {
|
|
154
|
+
name: String,
|
|
155
|
+
age: Int
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
interface Show(a) with
|
|
160
|
+
show : a -> String
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
implements Show(Person) with
|
|
165
|
+
show: (p) -> { p.name ++ " (" ++ String.from_int(p.age) ++ ")" }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def describe(p: Person) -> String
|
|
170
|
+
show(p)
|
|
171
|
+
end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Modules and imports.** One module per file; `exposing` lists the public
|
|
175
|
+
surface. Pull names in by module, or selectively:
|
|
176
|
+
|
|
177
|
+
```jade
|
|
178
|
+
module App exposing (run)
|
|
179
|
+
|
|
180
|
+
import Mathx exposing (double)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def run(n: Int) -> Int
|
|
184
|
+
double(n) + 1
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## JSON
|
|
189
|
+
|
|
190
|
+
`Decode.from_json` derives a decoder from the return type and `Encode.encode`
|
|
191
|
+
derives the encoder, so a struct round-trips without a hand-written decoder or
|
|
192
|
+
encoder. A missing field comes back as an `Err`, not a `nil`:
|
|
193
|
+
|
|
194
|
+
```jade
|
|
195
|
+
module Api exposing (parse, render)
|
|
196
|
+
|
|
197
|
+
import Encode
|
|
198
|
+
import Decode exposing (DecodeError)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
struct User = {
|
|
202
|
+
name: String,
|
|
203
|
+
age: Int
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def parse(json: String) -> Result(User, DecodeError)
|
|
208
|
+
Decode.from_json(json)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def render(user: User) -> String
|
|
213
|
+
Encode.encode_to_string(Encode.encode(user))
|
|
214
|
+
end
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
Api::Internal.parse('{"name":"Ada","age":40}')
|
|
219
|
+
# => Ok(User(name: "Ada", age: 40))
|
|
220
|
+
|
|
221
|
+
Api::Internal.parse('{"name":"Ada"}')
|
|
222
|
+
# => Err(MissingField("age"))
|
|
223
|
+
|
|
224
|
+
Api::Internal.render(Api::User.new(name: "Ada", age: 40))
|
|
225
|
+
# => "{\"name\":\"Ada\",\"age\":40}"
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
When you need them, the pieces are explicit too: `Decode.field`,
|
|
229
|
+
`Decode.list`, and `Decode.succeed(User(_, _)) |> Decode.required(...)` build
|
|
230
|
+
decoders by hand — the `_` currying again.
|
|
231
|
+
|
|
232
|
+
## Side-effect-free testing
|
|
233
|
+
|
|
234
|
+
Jade functions are pure; effects go through `Task`, declared in a `uses`
|
|
235
|
+
block. A function that doesn't use a `Task` takes data in and returns data, so
|
|
236
|
+
you test it by passing values and asserting on the result — no mocks.
|
|
237
|
+
|
|
238
|
+
When you do hit a boundary, you stub the `Task`. Here's a Jade module that
|
|
239
|
+
calls a Ruby mailer, and the RSpec that drives it:
|
|
240
|
+
|
|
241
|
+
```jade
|
|
242
|
+
# src/signup.jd
|
|
243
|
+
module Signup exposing (run)
|
|
244
|
+
|
|
245
|
+
uses Mailer with
|
|
246
|
+
deliver : String -> Task(Bool, String)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def run(email: String) -> Task(Bool, String)
|
|
251
|
+
deliver(email)
|
|
252
|
+
end
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
# spec/signup_spec.rb
|
|
257
|
+
require 'jade'
|
|
258
|
+
require 'jade/tasks'
|
|
259
|
+
require 'jade/tasks/rspec'
|
|
260
|
+
|
|
261
|
+
Jade.setup { |c| c.source_root = 'src' }
|
|
262
|
+
|
|
263
|
+
module Mailer
|
|
264
|
+
extend Jade::Port
|
|
265
|
+
task(:deliver) { |t, email| t.ok(Mail.welcome(email).deliver) }
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
Jade.require('signup')
|
|
269
|
+
|
|
270
|
+
RSpec.describe 'Signup' do
|
|
271
|
+
include Jade::Tasks::RSpec
|
|
272
|
+
|
|
273
|
+
it 'sends a welcome mail to the new address' do
|
|
274
|
+
all_calls_to(Mailer.deliver) { |t, _email| t.ok(true) }
|
|
275
|
+
|
|
276
|
+
expect(Signup::Internal.run('ada@example.com').run).to be_ok(true)
|
|
277
|
+
expect(Mailer.deliver).to have_been_called.with('ada@example.com')
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
it 'surfaces a delivery failure as Err' do
|
|
281
|
+
all_calls_to(Mailer.deliver) { |t, _email| t.err("smtp down") }
|
|
282
|
+
|
|
283
|
+
expect(Signup::Internal.run('ada@example.com').run).to be_err("smtp down")
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
`all_calls_to` sets a persistent stub; `next_call_to` queues one-shot answers.
|
|
289
|
+
`have_been_called` chains `.with(...)`, `.once`, `.times(n)`. Matchers include
|
|
290
|
+
`be_ok`, `be_err`, `be_just`, and `be_nothing`. Because effects only happen
|
|
291
|
+
through `Task`, a function's return type tells you whether it performs IO.
|
|
292
|
+
|
|
293
|
+
## Using Jade from Ruby
|
|
294
|
+
|
|
295
|
+
The gem is `jade-lang`; the library you `require` is still `jade`. A RubyGems
|
|
296
|
+
release is coming — for now, install from a path or a git ref:
|
|
297
|
+
|
|
298
|
+
```ruby
|
|
299
|
+
# Gemfile
|
|
300
|
+
gem 'jade-lang', path: '../jade'
|
|
301
|
+
# or: gem 'jade-lang', git: 'https://github.com/agustinrhcp/jade'
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Point it at your source, then `require` modules by name. Jade compiles each
|
|
305
|
+
`.jd` to `.jade/build/<module>.rb` on first require (and only when the source
|
|
306
|
+
is newer), then loads it:
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
require 'jade'
|
|
310
|
+
|
|
311
|
+
Jade.setup do |config|
|
|
312
|
+
config.source_root = 'src' # where your .jd files live
|
|
313
|
+
# config.build_dir = '.jade/build' (default)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
Jade.require('greeter')
|
|
317
|
+
|
|
318
|
+
Greeter.greet('Ada') # => "Hello, Ada"
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Existing Ruby calls into Jade, and Jade calls into Ruby through `uses` blocks.
|
|
322
|
+
It's plain Ruby on disk, so it sits inside a Rails app like any other file.
|
|
323
|
+
|
|
324
|
+
## If it doesn't work out
|
|
325
|
+
|
|
326
|
+
Run the compiler one last time, commit the generated `.rb`, and drop the `.jd`
|
|
327
|
+
files. The output is already plain Ruby — no rewrite, no migration. The
|
|
328
|
+
`jade-eject` skill automates removing the gem dependency, but it isn't required.
|
|
329
|
+
|
|
330
|
+
Worst case: you wrote Ruby with a nicer authoring layer for a while.
|
|
331
|
+
|
|
332
|
+
## Editors and agents
|
|
333
|
+
|
|
334
|
+
There's a language server — type errors, inferred types, and jump-to-definition
|
|
335
|
+
in any editor that speaks LSP. For tools that don't, `jade q` answers the same
|
|
336
|
+
questions as one-shot JSON (hover, definition, references, symbols).
|
|
337
|
+
|
|
338
|
+
In our experience coding agents like Claude Code and Cursor handle Jade well:
|
|
339
|
+
the syntax is close enough to the ML family (Elm, OCaml, Haskell) that models
|
|
340
|
+
have useful priors, and the generated Ruby gives them a second source of truth
|
|
341
|
+
to check against. No promises that holds for every model — it's just held up
|
|
342
|
+
for us so far.
|
|
343
|
+
|
|
344
|
+
## Tooling
|
|
345
|
+
|
|
346
|
+
A single `jade` binary fronts the toolchain:
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
jade fmt [-i|-c] [file] # format .jd source (stdin or file)
|
|
350
|
+
jade lsp # language server over stdio (hover, defn, refs, diagnostics)
|
|
351
|
+
jade q hover FILE:L:C # headless JSON queries — hover/symbols/defn/refs
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`jade fmt` is deterministic and idempotent; wire it into your editor or a
|
|
355
|
+
pre-commit hook.
|
|
356
|
+
|
|
357
|
+
## Standard library
|
|
358
|
+
|
|
359
|
+
`Basics`, `String`, `Char`, `List`, `Dict`, `Set`, `Tuple`, `Maybe`, `Result`,
|
|
360
|
+
`Task`, `Decode`, `Encode`, `Bytes`, `Calendar`, `Clock`. Stdlib operations
|
|
361
|
+
compile inline rather than through a runtime dispatch layer.
|
|
362
|
+
|
|
363
|
+
## Docs
|
|
364
|
+
|
|
365
|
+
- [docs/syntax.md](docs/syntax.md) — the full language tour
|
|
366
|
+
- [docs/interop.md](docs/interop.md) — the Ruby boundary: ports, decoding, what crosses
|
|
367
|
+
- [docs/json.md](docs/json.md) — `Decode` / `Encode`, by hand and auto-derived
|
|
368
|
+
- [docs/testing.md](docs/testing.md) — stubbing Tasks, the RSpec matchers
|
|
369
|
+
- [docs/stdlib.md](docs/stdlib.md) — module-by-module map
|
|
370
|
+
- [docs/lsp.md](docs/lsp.md) — language server and `jade q` setup
|
|
371
|
+
- [examples/](examples/) — runnable `.jd` files
|
|
372
|
+
|
|
373
|
+
## Status
|
|
374
|
+
|
|
375
|
+
Early and experimental — being tried out on small projects.
|
|
376
|
+
|
|
377
|
+
**In progress:** `Comparable` / `Show` derivation for user types, partial
|
|
378
|
+
record types in signatures, a stable REPL.
|
|
379
|
+
|
|
380
|
+
**Not great for:** throwaway scripts, libraries you ship to other Ruby
|
|
381
|
+
projects (they'd inherit the dependency), and performance-critical hot paths
|
|
382
|
+
(output is YJIT-friendly but unbenchmarked).
|
|
383
|
+
|
|
384
|
+
## License
|
|
385
|
+
|
|
386
|
+
[MIT](LICENSE).
|
data/exe/jade
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module AST
|
|
3
|
+
module Node
|
|
4
|
+
@@_next_id = 0
|
|
5
|
+
|
|
6
|
+
# Boilerplate added by Nodes.define alongside the AST-specific fields.
|
|
7
|
+
# Excluded from child traversal so we don't iterate the `range` Range
|
|
8
|
+
# as integers or descend into comment metadata.
|
|
9
|
+
BOILERPLATE_FIELDS = %i[
|
|
10
|
+
range symbol id
|
|
11
|
+
leading_comments trailing_comments dangling_comments
|
|
12
|
+
trailing_comma
|
|
13
|
+
].freeze
|
|
14
|
+
|
|
15
|
+
def self.next_id
|
|
16
|
+
@@_next_id += 1
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Deepest descendant whose range covers `offset`, or self if no child
|
|
20
|
+
# matches. Returns nil if this node's range is missing or doesn't
|
|
21
|
+
# cover the offset.
|
|
22
|
+
def find_at(offset)
|
|
23
|
+
find_at_path(offset).last
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Path of nested nodes from self down to the deepest descendant
|
|
27
|
+
# covering `offset`. Empty if self doesn't cover. Use this when you
|
|
28
|
+
# need to consult ancestors — e.g. hover on `String` inside
|
|
29
|
+
# `String.length(...)` needs the surrounding QualifiedAccess, not the
|
|
30
|
+
# raw ConstructorReference.
|
|
31
|
+
def find_at_path(offset)
|
|
32
|
+
return [] unless range&.cover?(offset)
|
|
33
|
+
|
|
34
|
+
(members - BOILERPLATE_FIELDS)
|
|
35
|
+
.flat_map { public_send(it) }
|
|
36
|
+
.flat_map { it.is_a?(Array) ? it : [it] }
|
|
37
|
+
.filter_map { it.is_a?(Node) ? it.find_at_path(offset) : nil }
|
|
38
|
+
.reject(&:empty?)
|
|
39
|
+
.first
|
|
40
|
+
.then { [self] + (it || []) }
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'jade/ast/node'
|
|
2
|
+
|
|
3
|
+
module Jade
|
|
4
|
+
module AST
|
|
5
|
+
module Nodes
|
|
6
|
+
def define(name, *fields)
|
|
7
|
+
Data.define(
|
|
8
|
+
*fields,
|
|
9
|
+
:range,
|
|
10
|
+
:symbol,
|
|
11
|
+
:id,
|
|
12
|
+
:leading_comments,
|
|
13
|
+
:trailing_comments,
|
|
14
|
+
:dangling_comments,
|
|
15
|
+
:trailing_comma,
|
|
16
|
+
:dictionaries
|
|
17
|
+
) {
|
|
18
|
+
include Node
|
|
19
|
+
|
|
20
|
+
define_method(:initialize) do |**kwargs|
|
|
21
|
+
kwargs[:symbol] ||= nil
|
|
22
|
+
kwargs[:id] ||= Node.next_id
|
|
23
|
+
kwargs[:leading_comments] ||= []
|
|
24
|
+
kwargs[:trailing_comments] ||= []
|
|
25
|
+
kwargs[:dangling_comments] ||= []
|
|
26
|
+
kwargs[:trailing_comma] ||= false
|
|
27
|
+
kwargs[:dictionaries] ||= []
|
|
28
|
+
super(**kwargs)
|
|
29
|
+
end
|
|
30
|
+
}
|
|
31
|
+
.then { const_set(name, it) }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Jade
|
|
2
|
+
module AST
|
|
3
|
+
module PrettyPrinter
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
def print(node, indent = 0)
|
|
7
|
+
prefix = ' ' * indent
|
|
8
|
+
|
|
9
|
+
case node
|
|
10
|
+
in AST::VariableReference(name:)
|
|
11
|
+
prefix + "Var(#{name})"
|
|
12
|
+
|
|
13
|
+
in AST::Assign(pattern:, expression:)
|
|
14
|
+
prefix + "VarBound(#{pattern} = " + print(expression, indent) + ")"
|
|
15
|
+
|
|
16
|
+
in AST::Literal(value:)
|
|
17
|
+
case value
|
|
18
|
+
in Integer | TrueClass | FalseClass
|
|
19
|
+
value.to_s
|
|
20
|
+
|
|
21
|
+
in String
|
|
22
|
+
"\"#{value}\""
|
|
23
|
+
end
|
|
24
|
+
in AST::FunctionCall(callee:, args:)
|
|
25
|
+
case callee
|
|
26
|
+
in AST::VariableReference(name:)
|
|
27
|
+
if is_infix?(name)
|
|
28
|
+
operator = name.delete_prefix('(').delete_suffix(')')
|
|
29
|
+
|
|
30
|
+
return prefix + "(#{print(args[0])} #{operator} #{print(args[1])})"
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
args
|
|
36
|
+
.map { print(it) }.join(', ')
|
|
37
|
+
.then { "(#{it})"}
|
|
38
|
+
.then { prefix + print(callee, indent) + it }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def is_infix?(name)
|
|
45
|
+
name.start_with?('(') &&
|
|
46
|
+
name.end_with?(')')
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|