From 96132b20f6efb8fab884195f7f5144dc87e20be1 Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Wed, 10 Aug 2022 23:02:27 +0900 Subject: [PATCH] initial commit --- .gitattributes | 2 + .github/workflows/rust.yml | 22 + .gitignore | 18 + Cargo.toml | 42 + LICENSE-APACHE | 176 ++ LICENSE-MIT | 17 + README.md | 184 ++ README_JA.md | 182 ++ TODO.md | 99 + assets/erg_logo.png | Bin 0 -> 63970 bytes assets/erg_logo.svg | 1 + assets/erg_logo_with_slogan.svg | 1 + assets/erg_logo_with_slogan_transparent.png | Bin 0 -> 139740 bytes assets/erg_logo_with_slogan_transparent.svg | 1 + benchmark/bm1.er | 5 + benchmark/bm1.js | 11 + benchmark/bm1.py | 6 + doc/EN/faq_general.md | 31 + doc/EN/faq_technical.md | 23 + doc/EN/improved_points.md | 48 + doc/EN/index.md | 25 + doc/EN/migration_from_py.md | 28 + doc/EN/syntax/00_basic.md | 120 + doc/EN/syntax/01_literal.md | 149 + doc/EN/syntax/02_name.md | 164 ++ doc/EN/syntax/03_declaration.md | 49 + doc/EN/syntax/04_function.md | 281 ++ doc/EN/syntax/05_builtin_funcs.md | 49 + doc/EN/syntax/06_operator.md | 29 + doc/EN/syntax/07_side_effect.md | 121 + doc/EN/syntax/08_procedure.md | 12 + doc/EN/syntax/09_builtin_procs.md | 14 + doc/EN/syntax/10_array.md | 40 + doc/EN/syntax/11_tuple.md | 117 + doc/EN/syntax/12_dict.md | 67 + doc/EN/syntax/13_record.md | 199 ++ doc/EN/syntax/14_set.md | 47 + doc/EN/syntax/15_type.md | 7 + doc/EN/syntax/type/01_type_system.md | 227 ++ doc/EN/syntax/type/02_basic.md | 154 + doc/EN/syntax/type/03_trait.md | 150 + doc/EN/syntax/type/04_class.md | 276 ++ doc/EN/syntax/type/05_inheritance.md | 253 ++ doc/EN/syntax/type/06_nst_vs_sst.md | 0 doc/EN/syntax/type/07_patch.md | 222 ++ doc/EN/syntax/type/08_value.md | 37 + doc/EN/syntax/type/09_attributive.md | 0 doc/EN/syntax/type/10_interval.md | 0 doc/EN/syntax/type/11_enum.md | 0 doc/EN/syntax/type/12_refinement.md | 75 + doc/EN/syntax/type/13_algebraic.md | 0 doc/EN/syntax/type/14_dependent.md | 74 + doc/EN/syntax/type/15_quantified.md | 280 ++ doc/EN/tips.md | 135 + doc/JA/API/consts.md | 13 + doc/JA/API/funcs.md | 121 + doc/JA/API/index.md | 0 doc/JA/API/modules/external/alstruct.md | 57 + doc/JA/API/modules/repl.md | 24 + doc/JA/API/modules/status.md | 6 + doc/JA/API/modules/unit.md | 73 + doc/JA/API/modules/unsound.md | 24 + doc/JA/API/operators.md | 64 + doc/JA/API/procs.md | 39 + doc/JA/API/special.md | 176 ++ doc/JA/API/types.md | 270 ++ doc/JA/API/types/classes/Array!(T).md | 4 + doc/JA/API/types/classes/Array(T).md | 3 + doc/JA/API/types/classes/ArrayWithLen(T,N).md | 34 + .../types/classes/ArrayWithMutLength!(T,N).md | 26 + doc/JA/API/types/classes/Class.md | 0 doc/JA/API/types/classes/Complex.md | 14 + doc/JA/API/types/classes/Dict!.md | 7 + doc/JA/API/types/classes/Either.md | 12 + doc/JA/API/types/classes/Float.md | 21 + doc/JA/API/types/classes/Function(N).md | 9 + doc/JA/API/types/classes/Inf.md | 7 + doc/JA/API/types/classes/Int.md | 10 + doc/JA/API/types/classes/IntRange.md | 19 + doc/JA/API/types/classes/Interval.md | 18 + doc/JA/API/types/classes/Iterator.md | 0 doc/JA/API/types/classes/Kind(N).md | 5 + doc/JA/API/types/classes/Matrix.md | 7 + doc/JA/API/types/classes/Module.md | 3 + doc/JA/API/types/classes/Nat.md | 18 + doc/JA/API/types/classes/Neg.md | 8 + doc/JA/API/types/classes/Never.md | 13 + doc/JA/API/types/classes/NonZero.md | 30 + doc/JA/API/types/classes/Object.md | 7 + doc/JA/API/types/classes/Operator.md | 7 + doc/JA/API/types/classes/Option.md | 21 + doc/JA/API/types/classes/Pos.md | 8 + doc/JA/API/types/classes/Ratio.md | 5 + doc/JA/API/types/classes/Record.md | 14 + doc/JA/API/types/classes/Result.md | 7 + doc/JA/API/types/classes/Str!.md | 3 + doc/JA/API/types/classes/Str.md | 9 + doc/JA/API/types/classes/StrWithLen.md | 0 doc/JA/API/types/classes/Subroutine.md | 19 + doc/JA/API/types/classes/Tensor.md | 24 + doc/JA/API/types/classes/TransCell(T).md | 12 + doc/JA/API/types/classes/Tuple.md | 27 + doc/JA/API/types/classes/Type.md | 0 doc/JA/API/types/classes/Vector.md | 3 + doc/JA/API/types/patches/BinOp.md | 7 + doc/JA/API/types/patches/UnaryOp.md | 7 + doc/JA/API/types/traits/Add(R,O).md | 33 + doc/JA/API/types/traits/Div(R,O).md | 9 + doc/JA/API/types/traits/Eq.md | 0 doc/JA/API/types/traits/Into.md | 11 + doc/JA/API/types/traits/Iterable.md | 0 doc/JA/API/types/traits/Num.md | 16 + doc/JA/API/types/traits/Ord.md | 0 doc/JA/API/types/traits/SafeDiv(R,O).md | 8 + doc/JA/API/types/traits/Sample.md | 31 + doc/JA/API/types/traits/Seq.md | 0 doc/JA/API/types/traits/Show.md | 0 doc/JA/API/types/traits/Unpack.md | 13 + doc/JA/compiler/TODO_hint.md | 5 + doc/JA/compiler/TODO_recov_suggest.md | 11 + doc/JA/compiler/TODO_warn.md | 5 + doc/JA/compiler/abandoned.md | 10 + doc/JA/compiler/architecture.md | 42 + doc/JA/compiler/errors.md | 131 + doc/JA/compiler/hir.md | 148 + doc/JA/compiler/index.md | 0 doc/JA/compiler/inference.md | 378 +++ doc/JA/compiler/memory_management.md | 31 + doc/JA/compiler/overview.md | 23 + doc/JA/compiler/parsing.md | 33 + doc/JA/compiler/query.md | 4 + doc/JA/compiler/refinement_subtyping.md | 156 + doc/JA/compiler/trait_method_resolving.md | 95 + doc/JA/compiler/transpile.md | 92 + doc/JA/compiler/ty_bound_resolving.md | 1 + doc/JA/compiler/type_var_normalization.md | 39 + doc/JA/compiler/unification.md | 34 + doc/JA/dev_guide/branches.md | 46 + doc/JA/dev_guide/doc_guideline.md | 12 + doc/JA/dev_guide/env.md | 12 + doc/JA/dev_guide/faq_syntax.md | 116 + doc/JA/dev_guide/features.md | 11 + doc/JA/dev_guide/index.md | 0 doc/JA/dev_guide/rust_code_guideline.md | 24 + doc/JA/dev_guide/terms.md | 839 ++++++ doc/JA/dev_guide/unify_terms.md | 88 + doc/JA/faq_general.md | 36 + doc/JA/faq_technical.md | 23 + doc/JA/improved_points.md | 48 + doc/JA/index.md | 25 + doc/JA/migration_from_py.md | 25 + doc/JA/python/bytecode_instructions.md | 96 + doc/JA/python/bytecode_specification.md | 71 + doc/JA/python/class_system.md | 94 + doc/JA/python/index.md | 0 doc/JA/python/python_vers.txt | 6 + doc/JA/syntax/00_basic.md | 118 + doc/JA/syntax/01_literal.md | 150 + doc/JA/syntax/02_name.md | 165 ++ doc/JA/syntax/03_declaration.md | 50 + doc/JA/syntax/04_function.md | 283 ++ doc/JA/syntax/05_builtin_funcs.md | 49 + doc/JA/syntax/06_operator.md | 29 + doc/JA/syntax/07_side_effect.md | 121 + doc/JA/syntax/08_procedure.md | 11 + doc/JA/syntax/09_builtin_procs.md | 13 + doc/JA/syntax/10_array.md | 40 + doc/JA/syntax/11_tuple.md | 117 + doc/JA/syntax/12_dict.md | 67 + doc/JA/syntax/13_record.md | 199 ++ doc/JA/syntax/14_set.md | 46 + doc/JA/syntax/15_type.md | 7 + doc/JA/syntax/16_iterator.md | 89 + doc/JA/syntax/17_mutability.md | 98 + doc/JA/syntax/18_memory_management.md | 110 + doc/JA/syntax/19_visibility.md | 191 ++ doc/JA/syntax/20_naming_rule.md | 50 + doc/JA/syntax/21_lambda.md | 94 + doc/JA/syntax/22_subroutine.md | 61 + doc/JA/syntax/23_scope.md | 96 + doc/JA/syntax/24_module.md | 42 + doc/JA/syntax/25_object_system.md | 82 + doc/JA/syntax/26_pattern_matching.md | 189 ++ doc/JA/syntax/27_comprehension.md | 55 + doc/JA/syntax/28_spread_syntax.md | 44 + doc/JA/syntax/29_decorator.md | 107 + doc/JA/syntax/30_error_handling.md | 109 + doc/JA/syntax/31_pipeline.md | 29 + doc/JA/syntax/32_integration_with_Python.md | 57 + doc/JA/syntax/33_package_system.md | 83 + doc/JA/syntax/34_generator.md | 32 + doc/JA/syntax/SUMMARY.md | 69 + doc/JA/syntax/container_ownership.md | 42 + doc/JA/syntax/grammar.txt | 77 + doc/JA/syntax/indexes.md | 452 +++ doc/JA/syntax/quick_tour.md | 267 ++ doc/JA/syntax/type/01_type_system.md | 226 ++ doc/JA/syntax/type/02_basic.md | 154 + doc/JA/syntax/type/03_trait.md | 150 + doc/JA/syntax/type/04_class.md | 277 ++ doc/JA/syntax/type/05_inheritance.md | 252 ++ doc/JA/syntax/type/06_nst_vs_sst.md | 41 + doc/JA/syntax/type/07_patch.md | 221 ++ doc/JA/syntax/type/08_value.md | 37 + doc/JA/syntax/type/09_attributive.md | 8 + doc/JA/syntax/type/10_interval.md | 31 + doc/JA/syntax/type/11_enum.md | 82 + doc/JA/syntax/type/12_refinement.md | 75 + doc/JA/syntax/type/13_algebraic.md | 84 + doc/JA/syntax/type/14_dependent.md | 74 + doc/JA/syntax/type/15_quantified.md | 281 ++ doc/JA/syntax/type/16_subtyping.md | 76 + doc/JA/syntax/type/17_type_casting.md | 72 + doc/JA/syntax/type/18_mut.md | 164 ++ doc/JA/syntax/type/advanced.md | 1 + doc/JA/syntax/type/advanced/GADTs.md | 66 + doc/JA/syntax/type/advanced/_rank2type.md | 142 + doc/JA/syntax/type/advanced/default_param.md | 28 + doc/JA/syntax/type/advanced/erasure.md | 43 + doc/JA/syntax/type/advanced/existential.md | 41 + doc/JA/syntax/type/advanced/keyword_param.md | 26 + doc/JA/syntax/type/advanced/kind.md | 149 + doc/JA/syntax/type/advanced/marker_trait.md | 31 + doc/JA/syntax/type/advanced/mut_struct.md | 40 + doc/JA/syntax/type/advanced/phantom.md | 57 + doc/JA/syntax/type/advanced/projection.md | 53 + .../advanced/quantified_dependent_type.md | 26 + doc/JA/syntax/type/advanced/rank2type.md | 25 + .../type/advanced/recursive_type_inferring.md | 50 + doc/JA/syntax/type/advanced/shared.md | 72 + doc/JA/syntax/type/advanced/type_inferring.md | 164 ++ .../syntax/type/advanced/type_inferring_v2.md | 149 + doc/JA/syntax/type/advanced/typeof.md | 65 + doc/JA/syntax/type/advanced/variance.md | 143 + doc/JA/syntax/type/advanced/widening.md | 92 + doc/JA/syntax/type/guard.md | 18 + doc/JA/syntax/type/newtype.md | 31 + doc/JA/syntax/type/option.md | 1 + doc/JA/syntax/type/overloading.md | 89 + doc/JA/syntax/type/special.md | 52 + doc/JA/tips.md | 135 + doc/JA/tools/build.md | 14 + doc/JA/tools/env.md | 7 + doc/JA/tools/fmt.md | 6 + doc/JA/tools/index.md | 0 doc/JA/tools/install.md | 10 + doc/JA/tools/pack.md | 100 + doc/JA/tools/repl.md | 13 + doc/JA/tools/test.md | 45 + examples/add.er | 13 + examples/array.er | 16 + examples/class.er | 17 + examples/control.er | 33 + examples/dependent.er | 16 + examples/dict.er | 7 + examples/enum.er | 40 + examples/fib.er | 6 + examples/helloworld.er | 7 + examples/list.er | 18 + examples/patch.er | 12 + examples/quantified.er | 12 + examples/rank2.er | 38 + examples/record.er | 21 + examples/slot.ts | 19 + examples/tuple.er | 8 + src/common/Cargo.toml | 18 + src/common/build.rs | 17 + src/common/cache.rs | 64 + src/common/codeobj.rs | 480 +++ src/common/color.rs | 9 + src/common/combinations.rs | 199 ++ src/common/config.rs | 219 ++ src/common/datetime.rs | 20 + src/common/deserialize.rs | 255 ++ src/common/dict.rs | 153 + src/common/error.rs | 444 +++ src/common/fxhash.rs | 143 + src/common/lazy.rs | 364 +++ src/common/lazy_buffer.rs | 70 + src/common/levenshtein.rs | 30 + src/common/lib.rs | 121 + src/common/macros.rs | 268 ++ src/common/opcode.rs | 298 ++ src/common/python_util.rs | 61 + src/common/rccell.rs | 71 + src/common/serialize.rs | 159 + src/common/set.rs | 178 ++ src/common/stdin.rs | 45 + src/common/str.rs | 117 + src/common/traits.rs | 414 +++ src/common/tsort.rs | 64 + src/common/ty.rs | 2575 +++++++++++++++++ src/common/value.rs | 448 +++ src/compiler/.gitignore | 2 + src/compiler/Cargo.toml | 23 + src/compiler/README.md | 3 + src/compiler/codegen.rs | 1181 ++++++++ src/compiler/compile.rs | 145 + src/compiler/effectcheck.rs | 186 ++ src/compiler/error.rs | 488 ++++ src/compiler/eval.rs | 450 +++ src/compiler/hir.rs | 829 ++++++ src/compiler/initialize.rs | 463 +++ src/compiler/lib.rs | 18 + src/compiler/lower.rs | 349 +++ src/compiler/main.rs | 28 + src/compiler/optimize.rs | 17 + src/compiler/ownercheck.rs | 177 ++ src/compiler/parser/.gitignore | 2 + src/compiler/parser/Cargo.toml | 21 + src/compiler/parser/README.md | 5 + src/compiler/parser/ast.rs | 1856 ++++++++++++ src/compiler/parser/desugar.rs | 152 + src/compiler/parser/error.rs | 104 + src/compiler/parser/lex.rs | 729 +++++ src/compiler/parser/lib.rs | 12 + src/compiler/parser/main.rs | 22 + src/compiler/parser/parse.rs | 1372 +++++++++ src/compiler/parser/tests/ast_example.txt | 26 + src/compiler/parser/tests/dependent.er | 4 + src/compiler/parser/tests/fib.er | 6 + src/compiler/parser/tests/hello_world.er | 1 + src/compiler/parser/tests/stack.er | 53 + src/compiler/parser/tests/test.rs | 146 + .../parser/tests/test1_basic_syntax.er | 20 + .../parser/tests/test2_advanced_syntax.er | 28 + src/compiler/parser/token.rs | 418 +++ src/compiler/table.rs | 2555 ++++++++++++++++ src/compiler/tests/dependent.er | 4 + src/compiler/tests/fib.er | 6 + src/compiler/tests/infer_arr.er | 6 + src/compiler/tests/side_effect.er | 9 + src/compiler/varinfo.rs | 155 + src/dummy.rs | 40 + src/e2wasm/Cargo.toml | 8 + src/e2wasm/README.md | 3 + src/e2wasm/main.rs | 0 src/e2wasm/opcode.rs | 183 ++ src/lib.rs | 3 + src/main.rs | 31 + src/packagemanager/README.md | 1 + src/std/prelude.er | 57 + tests/add.er | 5 + tests/fizzbuzz.er | 6 + tests/mut_array.er | 5 + tests/test.rs | 11 + 346 files changed, 36806 insertions(+) create mode 100644 .gitattributes create mode 100644 .github/workflows/rust.yml create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 README_JA.md create mode 100644 TODO.md create mode 100644 assets/erg_logo.png create mode 100644 assets/erg_logo.svg create mode 100644 assets/erg_logo_with_slogan.svg create mode 100644 assets/erg_logo_with_slogan_transparent.png create mode 100644 assets/erg_logo_with_slogan_transparent.svg create mode 100644 benchmark/bm1.er create mode 100644 benchmark/bm1.js create mode 100644 benchmark/bm1.py create mode 100644 doc/EN/faq_general.md create mode 100644 doc/EN/faq_technical.md create mode 100644 doc/EN/improved_points.md create mode 100644 doc/EN/index.md create mode 100644 doc/EN/migration_from_py.md create mode 100644 doc/EN/syntax/00_basic.md create mode 100644 doc/EN/syntax/01_literal.md create mode 100644 doc/EN/syntax/02_name.md create mode 100644 doc/EN/syntax/03_declaration.md create mode 100644 doc/EN/syntax/04_function.md create mode 100644 doc/EN/syntax/05_builtin_funcs.md create mode 100644 doc/EN/syntax/06_operator.md create mode 100644 doc/EN/syntax/07_side_effect.md create mode 100644 doc/EN/syntax/08_procedure.md create mode 100644 doc/EN/syntax/09_builtin_procs.md create mode 100644 doc/EN/syntax/10_array.md create mode 100644 doc/EN/syntax/11_tuple.md create mode 100644 doc/EN/syntax/12_dict.md create mode 100644 doc/EN/syntax/13_record.md create mode 100644 doc/EN/syntax/14_set.md create mode 100644 doc/EN/syntax/15_type.md create mode 100644 doc/EN/syntax/type/01_type_system.md create mode 100644 doc/EN/syntax/type/02_basic.md create mode 100644 doc/EN/syntax/type/03_trait.md create mode 100644 doc/EN/syntax/type/04_class.md create mode 100644 doc/EN/syntax/type/05_inheritance.md create mode 100644 doc/EN/syntax/type/06_nst_vs_sst.md create mode 100644 doc/EN/syntax/type/07_patch.md create mode 100644 doc/EN/syntax/type/08_value.md create mode 100644 doc/EN/syntax/type/09_attributive.md create mode 100644 doc/EN/syntax/type/10_interval.md create mode 100644 doc/EN/syntax/type/11_enum.md create mode 100644 doc/EN/syntax/type/12_refinement.md create mode 100644 doc/EN/syntax/type/13_algebraic.md create mode 100644 doc/EN/syntax/type/14_dependent.md create mode 100644 doc/EN/syntax/type/15_quantified.md create mode 100644 doc/EN/tips.md create mode 100644 doc/JA/API/consts.md create mode 100644 doc/JA/API/funcs.md create mode 100644 doc/JA/API/index.md create mode 100644 doc/JA/API/modules/external/alstruct.md create mode 100644 doc/JA/API/modules/repl.md create mode 100644 doc/JA/API/modules/status.md create mode 100644 doc/JA/API/modules/unit.md create mode 100644 doc/JA/API/modules/unsound.md create mode 100644 doc/JA/API/operators.md create mode 100644 doc/JA/API/procs.md create mode 100644 doc/JA/API/special.md create mode 100644 doc/JA/API/types.md create mode 100644 doc/JA/API/types/classes/Array!(T).md create mode 100644 doc/JA/API/types/classes/Array(T).md create mode 100644 doc/JA/API/types/classes/ArrayWithLen(T,N).md create mode 100644 doc/JA/API/types/classes/ArrayWithMutLength!(T,N).md create mode 100644 doc/JA/API/types/classes/Class.md create mode 100644 doc/JA/API/types/classes/Complex.md create mode 100644 doc/JA/API/types/classes/Dict!.md create mode 100644 doc/JA/API/types/classes/Either.md create mode 100644 doc/JA/API/types/classes/Float.md create mode 100644 doc/JA/API/types/classes/Function(N).md create mode 100644 doc/JA/API/types/classes/Inf.md create mode 100644 doc/JA/API/types/classes/Int.md create mode 100644 doc/JA/API/types/classes/IntRange.md create mode 100644 doc/JA/API/types/classes/Interval.md create mode 100644 doc/JA/API/types/classes/Iterator.md create mode 100644 doc/JA/API/types/classes/Kind(N).md create mode 100644 doc/JA/API/types/classes/Matrix.md create mode 100644 doc/JA/API/types/classes/Module.md create mode 100644 doc/JA/API/types/classes/Nat.md create mode 100644 doc/JA/API/types/classes/Neg.md create mode 100644 doc/JA/API/types/classes/Never.md create mode 100644 doc/JA/API/types/classes/NonZero.md create mode 100644 doc/JA/API/types/classes/Object.md create mode 100644 doc/JA/API/types/classes/Operator.md create mode 100644 doc/JA/API/types/classes/Option.md create mode 100644 doc/JA/API/types/classes/Pos.md create mode 100644 doc/JA/API/types/classes/Ratio.md create mode 100644 doc/JA/API/types/classes/Record.md create mode 100644 doc/JA/API/types/classes/Result.md create mode 100644 doc/JA/API/types/classes/Str!.md create mode 100644 doc/JA/API/types/classes/Str.md create mode 100644 doc/JA/API/types/classes/StrWithLen.md create mode 100644 doc/JA/API/types/classes/Subroutine.md create mode 100644 doc/JA/API/types/classes/Tensor.md create mode 100644 doc/JA/API/types/classes/TransCell(T).md create mode 100644 doc/JA/API/types/classes/Tuple.md create mode 100644 doc/JA/API/types/classes/Type.md create mode 100644 doc/JA/API/types/classes/Vector.md create mode 100644 doc/JA/API/types/patches/BinOp.md create mode 100644 doc/JA/API/types/patches/UnaryOp.md create mode 100644 doc/JA/API/types/traits/Add(R,O).md create mode 100644 doc/JA/API/types/traits/Div(R,O).md create mode 100644 doc/JA/API/types/traits/Eq.md create mode 100644 doc/JA/API/types/traits/Into.md create mode 100644 doc/JA/API/types/traits/Iterable.md create mode 100644 doc/JA/API/types/traits/Num.md create mode 100644 doc/JA/API/types/traits/Ord.md create mode 100644 doc/JA/API/types/traits/SafeDiv(R,O).md create mode 100644 doc/JA/API/types/traits/Sample.md create mode 100644 doc/JA/API/types/traits/Seq.md create mode 100644 doc/JA/API/types/traits/Show.md create mode 100644 doc/JA/API/types/traits/Unpack.md create mode 100644 doc/JA/compiler/TODO_hint.md create mode 100644 doc/JA/compiler/TODO_recov_suggest.md create mode 100644 doc/JA/compiler/TODO_warn.md create mode 100644 doc/JA/compiler/abandoned.md create mode 100644 doc/JA/compiler/architecture.md create mode 100644 doc/JA/compiler/errors.md create mode 100644 doc/JA/compiler/hir.md create mode 100644 doc/JA/compiler/index.md create mode 100644 doc/JA/compiler/inference.md create mode 100644 doc/JA/compiler/memory_management.md create mode 100644 doc/JA/compiler/overview.md create mode 100644 doc/JA/compiler/parsing.md create mode 100644 doc/JA/compiler/query.md create mode 100644 doc/JA/compiler/refinement_subtyping.md create mode 100644 doc/JA/compiler/trait_method_resolving.md create mode 100644 doc/JA/compiler/transpile.md create mode 100644 doc/JA/compiler/ty_bound_resolving.md create mode 100644 doc/JA/compiler/type_var_normalization.md create mode 100644 doc/JA/compiler/unification.md create mode 100644 doc/JA/dev_guide/branches.md create mode 100644 doc/JA/dev_guide/doc_guideline.md create mode 100644 doc/JA/dev_guide/env.md create mode 100644 doc/JA/dev_guide/faq_syntax.md create mode 100644 doc/JA/dev_guide/features.md create mode 100644 doc/JA/dev_guide/index.md create mode 100644 doc/JA/dev_guide/rust_code_guideline.md create mode 100644 doc/JA/dev_guide/terms.md create mode 100644 doc/JA/dev_guide/unify_terms.md create mode 100644 doc/JA/faq_general.md create mode 100644 doc/JA/faq_technical.md create mode 100644 doc/JA/improved_points.md create mode 100644 doc/JA/index.md create mode 100644 doc/JA/migration_from_py.md create mode 100644 doc/JA/python/bytecode_instructions.md create mode 100644 doc/JA/python/bytecode_specification.md create mode 100644 doc/JA/python/class_system.md create mode 100644 doc/JA/python/index.md create mode 100644 doc/JA/python/python_vers.txt create mode 100644 doc/JA/syntax/00_basic.md create mode 100644 doc/JA/syntax/01_literal.md create mode 100644 doc/JA/syntax/02_name.md create mode 100644 doc/JA/syntax/03_declaration.md create mode 100644 doc/JA/syntax/04_function.md create mode 100644 doc/JA/syntax/05_builtin_funcs.md create mode 100644 doc/JA/syntax/06_operator.md create mode 100644 doc/JA/syntax/07_side_effect.md create mode 100644 doc/JA/syntax/08_procedure.md create mode 100644 doc/JA/syntax/09_builtin_procs.md create mode 100644 doc/JA/syntax/10_array.md create mode 100644 doc/JA/syntax/11_tuple.md create mode 100644 doc/JA/syntax/12_dict.md create mode 100644 doc/JA/syntax/13_record.md create mode 100644 doc/JA/syntax/14_set.md create mode 100644 doc/JA/syntax/15_type.md create mode 100644 doc/JA/syntax/16_iterator.md create mode 100644 doc/JA/syntax/17_mutability.md create mode 100644 doc/JA/syntax/18_memory_management.md create mode 100644 doc/JA/syntax/19_visibility.md create mode 100644 doc/JA/syntax/20_naming_rule.md create mode 100644 doc/JA/syntax/21_lambda.md create mode 100644 doc/JA/syntax/22_subroutine.md create mode 100644 doc/JA/syntax/23_scope.md create mode 100644 doc/JA/syntax/24_module.md create mode 100644 doc/JA/syntax/25_object_system.md create mode 100644 doc/JA/syntax/26_pattern_matching.md create mode 100644 doc/JA/syntax/27_comprehension.md create mode 100644 doc/JA/syntax/28_spread_syntax.md create mode 100644 doc/JA/syntax/29_decorator.md create mode 100644 doc/JA/syntax/30_error_handling.md create mode 100644 doc/JA/syntax/31_pipeline.md create mode 100644 doc/JA/syntax/32_integration_with_Python.md create mode 100644 doc/JA/syntax/33_package_system.md create mode 100644 doc/JA/syntax/34_generator.md create mode 100644 doc/JA/syntax/SUMMARY.md create mode 100644 doc/JA/syntax/container_ownership.md create mode 100644 doc/JA/syntax/grammar.txt create mode 100644 doc/JA/syntax/indexes.md create mode 100644 doc/JA/syntax/quick_tour.md create mode 100644 doc/JA/syntax/type/01_type_system.md create mode 100644 doc/JA/syntax/type/02_basic.md create mode 100644 doc/JA/syntax/type/03_trait.md create mode 100644 doc/JA/syntax/type/04_class.md create mode 100644 doc/JA/syntax/type/05_inheritance.md create mode 100644 doc/JA/syntax/type/06_nst_vs_sst.md create mode 100644 doc/JA/syntax/type/07_patch.md create mode 100644 doc/JA/syntax/type/08_value.md create mode 100644 doc/JA/syntax/type/09_attributive.md create mode 100644 doc/JA/syntax/type/10_interval.md create mode 100644 doc/JA/syntax/type/11_enum.md create mode 100644 doc/JA/syntax/type/12_refinement.md create mode 100644 doc/JA/syntax/type/13_algebraic.md create mode 100644 doc/JA/syntax/type/14_dependent.md create mode 100644 doc/JA/syntax/type/15_quantified.md create mode 100644 doc/JA/syntax/type/16_subtyping.md create mode 100644 doc/JA/syntax/type/17_type_casting.md create mode 100644 doc/JA/syntax/type/18_mut.md create mode 100644 doc/JA/syntax/type/advanced.md create mode 100644 doc/JA/syntax/type/advanced/GADTs.md create mode 100644 doc/JA/syntax/type/advanced/_rank2type.md create mode 100644 doc/JA/syntax/type/advanced/default_param.md create mode 100644 doc/JA/syntax/type/advanced/erasure.md create mode 100644 doc/JA/syntax/type/advanced/existential.md create mode 100644 doc/JA/syntax/type/advanced/keyword_param.md create mode 100644 doc/JA/syntax/type/advanced/kind.md create mode 100644 doc/JA/syntax/type/advanced/marker_trait.md create mode 100644 doc/JA/syntax/type/advanced/mut_struct.md create mode 100644 doc/JA/syntax/type/advanced/phantom.md create mode 100644 doc/JA/syntax/type/advanced/projection.md create mode 100644 doc/JA/syntax/type/advanced/quantified_dependent_type.md create mode 100644 doc/JA/syntax/type/advanced/rank2type.md create mode 100644 doc/JA/syntax/type/advanced/recursive_type_inferring.md create mode 100644 doc/JA/syntax/type/advanced/shared.md create mode 100644 doc/JA/syntax/type/advanced/type_inferring.md create mode 100644 doc/JA/syntax/type/advanced/type_inferring_v2.md create mode 100644 doc/JA/syntax/type/advanced/typeof.md create mode 100644 doc/JA/syntax/type/advanced/variance.md create mode 100644 doc/JA/syntax/type/advanced/widening.md create mode 100644 doc/JA/syntax/type/guard.md create mode 100644 doc/JA/syntax/type/newtype.md create mode 100644 doc/JA/syntax/type/option.md create mode 100644 doc/JA/syntax/type/overloading.md create mode 100644 doc/JA/syntax/type/special.md create mode 100644 doc/JA/tips.md create mode 100644 doc/JA/tools/build.md create mode 100644 doc/JA/tools/env.md create mode 100644 doc/JA/tools/fmt.md create mode 100644 doc/JA/tools/index.md create mode 100644 doc/JA/tools/install.md create mode 100644 doc/JA/tools/pack.md create mode 100644 doc/JA/tools/repl.md create mode 100644 doc/JA/tools/test.md create mode 100644 examples/add.er create mode 100644 examples/array.er create mode 100644 examples/class.er create mode 100644 examples/control.er create mode 100644 examples/dependent.er create mode 100644 examples/dict.er create mode 100644 examples/enum.er create mode 100644 examples/fib.er create mode 100644 examples/helloworld.er create mode 100644 examples/list.er create mode 100644 examples/patch.er create mode 100644 examples/quantified.er create mode 100644 examples/rank2.er create mode 100644 examples/record.er create mode 100644 examples/slot.ts create mode 100644 examples/tuple.er create mode 100644 src/common/Cargo.toml create mode 100644 src/common/build.rs create mode 100644 src/common/cache.rs create mode 100644 src/common/codeobj.rs create mode 100644 src/common/color.rs create mode 100644 src/common/combinations.rs create mode 100644 src/common/config.rs create mode 100644 src/common/datetime.rs create mode 100644 src/common/deserialize.rs create mode 100644 src/common/dict.rs create mode 100644 src/common/error.rs create mode 100644 src/common/fxhash.rs create mode 100644 src/common/lazy.rs create mode 100644 src/common/lazy_buffer.rs create mode 100644 src/common/levenshtein.rs create mode 100644 src/common/lib.rs create mode 100644 src/common/macros.rs create mode 100644 src/common/opcode.rs create mode 100644 src/common/python_util.rs create mode 100644 src/common/rccell.rs create mode 100644 src/common/serialize.rs create mode 100644 src/common/set.rs create mode 100644 src/common/stdin.rs create mode 100644 src/common/str.rs create mode 100644 src/common/traits.rs create mode 100644 src/common/tsort.rs create mode 100644 src/common/ty.rs create mode 100644 src/common/value.rs create mode 100644 src/compiler/.gitignore create mode 100644 src/compiler/Cargo.toml create mode 100644 src/compiler/README.md create mode 100644 src/compiler/codegen.rs create mode 100644 src/compiler/compile.rs create mode 100644 src/compiler/effectcheck.rs create mode 100644 src/compiler/error.rs create mode 100644 src/compiler/eval.rs create mode 100644 src/compiler/hir.rs create mode 100644 src/compiler/initialize.rs create mode 100644 src/compiler/lib.rs create mode 100644 src/compiler/lower.rs create mode 100644 src/compiler/main.rs create mode 100644 src/compiler/optimize.rs create mode 100644 src/compiler/ownercheck.rs create mode 100644 src/compiler/parser/.gitignore create mode 100644 src/compiler/parser/Cargo.toml create mode 100644 src/compiler/parser/README.md create mode 100644 src/compiler/parser/ast.rs create mode 100644 src/compiler/parser/desugar.rs create mode 100644 src/compiler/parser/error.rs create mode 100644 src/compiler/parser/lex.rs create mode 100644 src/compiler/parser/lib.rs create mode 100644 src/compiler/parser/main.rs create mode 100644 src/compiler/parser/parse.rs create mode 100644 src/compiler/parser/tests/ast_example.txt create mode 100644 src/compiler/parser/tests/dependent.er create mode 100644 src/compiler/parser/tests/fib.er create mode 100644 src/compiler/parser/tests/hello_world.er create mode 100644 src/compiler/parser/tests/stack.er create mode 100644 src/compiler/parser/tests/test.rs create mode 100644 src/compiler/parser/tests/test1_basic_syntax.er create mode 100644 src/compiler/parser/tests/test2_advanced_syntax.er create mode 100644 src/compiler/parser/token.rs create mode 100644 src/compiler/table.rs create mode 100644 src/compiler/tests/dependent.er create mode 100644 src/compiler/tests/fib.er create mode 100644 src/compiler/tests/infer_arr.er create mode 100644 src/compiler/tests/side_effect.er create mode 100644 src/compiler/varinfo.rs create mode 100644 src/dummy.rs create mode 100644 src/e2wasm/Cargo.toml create mode 100644 src/e2wasm/README.md create mode 100644 src/e2wasm/main.rs create mode 100644 src/e2wasm/opcode.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/packagemanager/README.md create mode 100644 src/std/prelude.er create mode 100644 tests/add.er create mode 100644 tests/fizzbuzz.er create mode 100644 tests/mut_array.er create mode 100644 tests/test.rs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 00000000..7ae98f3b --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,22 @@ +name: Rust + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..df30ee0e --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk +*.pyc +/.vscode/ +/.VSCodeCounter/ +/.vs/ +/.DS_Store +/*/.DS_Store +/.idea/ +/timeit.dat \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..9d0678ac --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "erg" +version = "0.1.0" +description = "The Erg programming language" +authors = ["Shunsuke Shibayama "] +license = "MIT OR Apache-2.0" +edition = "2021" + +[features] +# when "debug" feature is turned on, that of the following crates will also be turned on. +debug = [ + "common/debug", + "parser/debug", + "compiler/debug", + #"vm/debug" +] +japanese = [ + "common/japanese", + "parser/japanese", + "compiler/japanese", + #"vm/japanese" +] + +[dependencies] +common = { path = "./src/common" } +parser = { path = "./src/compiler/parser" } +compiler = { path = "./src/compiler" } +# vm = { path = "./src/vm" } + +# [workspace] +# member = ["cm", "dyne"] + +# [profile.release] +# panic = 'abort' + +# [[bin]] +# name = "cm" +# path = "src/compiler/main.rs" + +# [[bin]] +# name = "dyne" +# path = "src/vm/main.rs" diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 00000000..29c22fda --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 00000000..969d061e --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..7b4cc487 --- /dev/null +++ b/README.md @@ -0,0 +1,184 @@ +# The Erg Programming Language + +
+ +
+ +
[Erg](https://mtshiba.github.io/TheErgBook) is a statically typed language that is Python compatible. + +

+ Build status + License: MIT & APACHE 2.0
+ English | 日本語 +

+ +## Erg can be recommended to a person that: + +* uses Python, but want Rust-like robustness and comfortable compiler support. +* and yet, doesn't need the verbose type specifications & memory management model like Rust. +* wants a simple and consistent language like ML. +* wants a practical general-purpose language with dependent/refinement types. +* wants a language like Scala that can be used both object-oriented and functional. + +## Features + +> Some features are not yet implemented. Please see [TODO.md](./TODO.md) for implementation status. + +1. Robustness + + Erg has a smart & powerful type system. For example, Erg can do null checking (Option type), division by zero and out-of-range addresses in arrays at compile time. + + ```python + rand = import "random" + + l = [1, 2, 3] + assert l in [Nat; 3] # type checking + assert l in [1..3; 3] # more detailed + l2 = l.push(rand.choice! 0..10) + assert l2 in [0..10; 4] + assert l2 + [3, 5, 7] in [0..10; 7] + # This causes an IndexError, Erg can detect it at compile time + l2[10] # IndexError: `l2` has 7 elements but was accessed the 10th element + + 2.times! do!: + print! "hello, ", end: "" + # => hello, hello, + -2.times! do!: + print! "hello, ", end: "" + # TypeError: `.times!` is a method of `Nat` (0 or more Int), not `Int` + + {Meter; Sec; meter; yard; sec; ...} = import "unit" + + velocity x: Meter, t: Sec = x / t + + v = velocity 3yard, 2sec # TypeError: the type of `x` was mismatched: expect `Meter`, found `Yard` + v = velocity 3meter, 2sec # v == 1.5 m/s + ``` + +2. Simplicity + + Erg consists of a very simple syntax, which can significantly reduce the amount of code compared to other languages. However, its functionality is not inferior to them. + + Since the type inference system is powerful, you can code like a dynamically typed language. + + ```python + fib 0 = 0 + fib 1 = 1 + fib n = fib(n - 1) + fib(n - 2) + assert fib(10) == 55 + ``` + + In Erg, there are very few things that are treated as special; there are no reserved words. + even for and while expressions are just one of the subroutines, so this is possible. + + ```python + loop! block = while! True, block + + # equals to `while! True, do! print! "hello"` + loop! do!: + print! "hello" + ``` + +3. Functional & Object-oriented + + Erg is a pure object-oriented language. Everything is an object; types, functions, and operators are all objects. On the other hand, Erg is also a functional language. + Erg requires some kinds of markers to be placed on code that causes side effects or changes internal state, which can localize the complexity of code. This will greatly improve the maintainability of your code. + + ```python + # Functional style (immutable), same as `.sorted()` in Python + immut_arr = [1, 3, 2] + assert immut_arr.sort() == [1, 2, 3] + # Object-oriented style (mutable) + mut_arr = ![1, 3, 2] + mut_arr.sort!() + assert mut_arr == [1, 2, 3] + i = !1 + i.update! old -> old + 1 + assert i == 2 + + # Functions cannot cause side effects + inc i: Int! = + i.update! old -> old + 1 + # SyntaxError: cannot call a procedural method in a function + # hint: only methods of mutable types can change the state of objects + + Counter! = Inherit Int! + Counter!. + new i: Int = Self!::__new__ !i + inc! ref! self = + self.update! old -> old + 1 + + c = Counter!.new 1 + c.inc!() + assert c == 2 + ``` + +4. Interoperability + + Erg is internally compatible with Python and can import the Python API at zero cost. + + ```python + # using built-in Python modules + math, time = pyimport "math", "time" + {sin; pi; ...} = math + # using an external Python module + Tqdm! = pyimport("tqdm").'tqdm' + + print! sin pi # 1.2246467991473532e-16 + for! Tqdm!.'__call__'(0..99), i => + time.sleep! 0.01 * i + ``` + +5. Readable Error Messages + + Erg emphasizes the readability of error messages; Erg is a programmer-friendly language, ~~unlike C++.~~ + + ```python + proc! x = + l = [1, 2, 3] + l.push!(x) + l + ``` + + ```console + Error[#12]: File example.er, line 3, in ::proc! + 2│ l = [1, 2, 3] + 3│ l.push!(x) + ^^^^^ + AttributeError: Array object has no attribute `.push!` + hint: in order to update the internal state of an object, make it mutable by using `!` operator + hint: `Array` has `push`, see https://erg-lang.org/docs/prelude/Array/##push for more information + hint: `Array!` has `push!`, see https://erg-lang.org/docs/prelude/Array!/##push! for more information + ``` + +## Installation + +### Installing by cargo (Rust package manager) + +```sh +cargo install erg +``` + +### Installing by an installer + +You can get an installer from Github releases. + +### Building from source + +Building from source code requires the Rust toolchain. + +```sh +git clone https://github.com/erg-lang/erg.git +cd erg +cargo build --release +``` + +## Contribution + +Contributions are always welcome! +If you have any questions, please feel free to ask them on the [Discord channel]. + +## License + +Erg is distributed under the terms of both the MIT license and the Apache License (Version 2.0). +See [LICENSE-APACHE](./LICENSE-APACHE), [LICENSE-MIT](./LICENSE-MIT) for details. diff --git a/README_JA.md b/README_JA.md new file mode 100644 index 00000000..df238e59 --- /dev/null +++ b/README_JA.md @@ -0,0 +1,182 @@ +# The Erg Programming Language + +
+ +
+ +
[Erg](https://mtshiba.github.io/TheErgBook)はPython互換の静的型付け言語です。 + +

+ Build status + License: MIT & APACHE 2.0
+ English | 日本語 +

+ +## Ergはこんな人におすすめです: + +* Pythonを使用しているが、Rustのような静的型付き言語で堅牢かつ快適にコーディングしたい +* しかし、煩雑な型定義やメモリ管理は避けたい +* MLのようにシンプルで一貫性のある言語を使いたい +* 依存型/篩型を持つ実用的な汎用言語を使いたい +* Scalaのように関数型とオブジェクト指向が高度に融合された言語を使いたい + +## 特徴 + +> いくつかの機能は未実装です。実装状況は[TODO.md](./TODO.md)を御覧ください。 + +1. 堅牢性 + + Ergは賢くパワフルな型システムを持っています。コンパイル時のnullチェック(Option型)はもちろん可能で、ゼロ除算や配列の範囲外アクセスまでもがコンパイル時に検出できます。 + + ```python + rand = import "random" + + l = [1, 2, 3] + assert l in [Int; 3] # 型チェック + assert l in [1..3; 3] # さらに詳細に + l2 = l.push(rand.choice! 0..10) + assert l2 in [0..10; 4] + assert l2 + [3, 5, 7] in [0..10; 7] + # これはIndexErrorを引き起こしますが、コンパイル時に検出できます + l2[10] # IndexError: `l2`は7つの要素を持っていますが、10番目の要素のアクセスしようとしています + + 2.times! do!: + print! "hello, ", end: "" + # => hello, hello, + -2.times! do!: + print! "hello,", end: "" + # TypeError: `.times!`は`Nat`(0以上のInt)のメソッドです、`Int`ではありません + + {Meter; Sec; meter; yard; sec; ...} = import "unit" + + velocity x: Meter, t: Sec = x / t + + v = velocity 3yard, 2sec # TypeError: `x`の型が適合しません。`Meter`を予期しましたが、`Yard`が渡されました + v = velocity 3meter, 2sec # v == 1.5 m/s + ``` + +2. 簡潔性 + + Ergはとてもシンプルな文法からなり、コードもシンプルに書き上げられます。しかし、その機能の豊富さは他の言語に劣りません。 + 型推論機構は非常に強力であり、まるで動的型付け言語かのように書くことができます。 + + ```python + fib 0 = 0 + fib 1 = 1 + fib n = fib(n - 1) + fib(n - 2) + assert fib(10) == 55 + ``` + + Ergでは特別扱いされる構文要素がとても少なく、例えば予約語が一つもありません。以下のような芸当も可能です。 + + ```python + loop! block = while! True, block + + # `while! True, do! print! "hello"`と同じです + loop! do!: + print! "hello" + ``` + +3. 関数型&オブジェクト指向 + + Ergは純粋なオブジェクト指向言語です。全てはオブジェクトであり、型、関数、演算子も例外ではありません。 + 一方、Ergは関数型言語でもあります。副作用を引き起こすコードには`!`を付けなくてはなりません。これは、副作用の局所化を意識させてくれます。 + + ```python + # immutableな関数型スタイル、Pythonの`.sorted()`と同じです + immut_arr = [1, 3, 2] + assert immut_arr.sort() == [1, 2, 3] + # mutableなオブジェクト指向スタイル + mut_arr = ![1, 3, 2] + mut_arr.sort!() + assert mut_arr == [1, 2, 3] + i = !1 + i.update! old -> old + 1 + assert i == 2 + + # 関数は副作用を起こせません + inc i: Int! = + i.update! old -> old + 1 + # SyntaxError: 関数の中でプロシージャルメソッドは呼び出せません + # ヒント: 可変型メソッドだけがオブジェクトの状態を変更できます + + Counter! = Inherit Int! + Counter!. + new i: Int = Self!::__new__ !i + inc! ref! self = + self.update! old -> old + 1 + + c = Counter!.new 1 + c.inc!() + assert c == 2 + ``` + +4. 相互運用性 + + Ergは内部的にPythonと互換性があり、PythonのAPIをゼロコストで呼び出すことが出来ます。 + + ```python + # Pythonのビルトインモジュールを使います + math, time = pyimport "math", "time" + {sin; pi; ...} = math + # Pythonの外部モジュールを使います + Tqdm! = pyimport("tqdm").'tqdm' + + print! sin pi # 1.2246467991473532e-16 + for! Tqdm!.'__call__'(0..99), i => + time.sleep! 0.01 * i + ``` + +5. 読みやすいエラーメッセージ + + Ergはエラーメッセージの読みやすさを重視しています。Ergはプログラマに寄り添う言語であり、~~C++のように~~訳のわからない呪文を吐いたりはしません。 + + ```python + proc! x = + l = [1, 2, 3] + l.push!(x) + l + ``` + + ```console + Error[#12]: ファイル example.er, 3行目, ::proc! + 2│ l = [1, 2, 3] + 3│ l.push!(x) + ^^^^^ + AttributeError: Arrayオブジェクトは`.push!`という属性を持っていません + ヒント: オブジェクトの内部状態を変更したい場合は、`!`演算子を使って可変化してください + ヒント: `Array`は`push`メソッドを持っています、詳しくは https://erg-lang.org/docs/prelude/Array/##push を参照してください + ヒント: `Array!`は`push!`メソッドを持っています、詳しくは https://erg-lang.org/docs/prelude/Array!/##push! を参照してください + ``` + +## インストール + +### cargo(Rustパッケージマネージャ)によるインストール + +```sh +cargo install erg +``` + +### インストーラによるインストール + +GitHub releaseからインストーラを入手できます。 + +### ソースコードからのビルド + +ソースコードからのビルドにはRustツールチェインが必要です。 + +```sh +git clone https://github.com/erg-lang/erg.git +cd erg +cargo build --release +``` + +## コントリビューション + +コントリビューション(プロジェクトへの貢献、協力)はいつでも歓迎しています! +何かわからないことがあれば、[discord channel]で気軽に質問してください。 + +## ライセンス + +Ergは、MITライセンスとApache2.0ライセンスのデュアルライセンスで配布されています。 +詳しくは[LICENSE-APACHE](./LICENSE-APACHE), [LICENSE-MIT](./LICENSE-MIT)をご覧ください。 diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..fc6c91d4 --- /dev/null +++ b/TODO.md @@ -0,0 +1,99 @@ +# TODOs + +* [ ] Implement the specification + * [x] Control flow + * [x] if/if! + * [x] match/match! + * [x] for! + * [x] while! + * [ ] operator + * [x] + (binary/unary) + * [x] - (binary/unary) + * [x] * + * [x] / + * [x] ** (power) + * [x] % (modulo) + * [x] comparison + * [x] ! (mutation) + * [ ] Pattern-matching + * [x] Variable Pattern + * [x] Literal Pattern + * [x] Array Pattern + * [ ] Tuple Pattern + * [ ] Record Pattern + * [ ] Data Type Pattern + * [ ] Refinement Pattern + * [x] Array literal + * [ ] Record literal + * [ ] Set literal + * [ ] Dict literal + * [ ] Tuple literal + * [x] Function definition + * [x] Procedure definition + * [ ] Type definition + * [ ] Class definition + * [ ] Trait definition + * [ ] Structural trait definition + * [ ] Patch definition + * [ ] Glue Patch definition + * [ ] Range object + * [ ] Decorator + * [ ] Comprehension + * [ ] Array + * [ ] Dict + * [ ] Set + * [ ] Tuple + * [ ] Pipeline operator + * [ ] ? operator + * [ ] Multi-line string + * [ ] String interpolation + * [ ] Multi-line comment +* [ ] Complete the type inference system + * [x] Type variable + * [x] Dependent type variable + * [ ] Polymorphic type variable + * [ ] Mutable type + * [x] Dependent mutable method + * [x] Projection type + * [ ] Polymorphic projection type + * [x] Subtyping + * [x] Refinement subtyping + * [x] Nominal subtyping + * [ ] Module system + * [ ] Recursive module + * [ ] Visibility check + * [x] Patching + * [ ] Rank-2 type +* [ ] Implement a side-effect checker + * [x] procedure call + * [ ] mutable type +* [x] Implement reference types (for methods) +* [ ] Implement a ownership checker + * [x] Implement a move checker + * [x] Implement a borrow checker + * [ ] Implement a cycle-reference detector +* [ ] Implement a compiletime evaluator + * [ ] Compiletime operator + * [ ] Compiletime function +* [ ] Maintain unit tests +* [ ] Implement a Python parser +* [ ] Make code readable + * [ ] Add docs comments to every functions + * [ ] Replace Parser (to more elegant one) +* [ ] Make error messages more readable + * [ ] Add hints (include a URL with detailed information) + * [ ] Multiple error points indication + * [ ] Support for languages other than English + * [x] Japanese +* [ ] Develop the development environment + * [ ] Implement LSP (Language Server Protocol) + * [ ] Implement a syntax highlighter (REPL/debugger built-in) + * [ ] Implement a package manager (`pack` subcommand) + * [ ] Implement a virtual environment manager (`env` subcommand) + * [ ] Prepare an installer for each platform + * [ ] Implement a compiling server +* [ ] Maintain documentations + * [ ] I18n + * [ ] Write educational materials to learn Erg while creating applications (e.g. CLI chess game -> GUI chess game, calculator -> toy language) +* [ ] Develop Dyne (CPython compatible VM) +* [ ] Develop WebAssembly backend diff --git a/assets/erg_logo.png b/assets/erg_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..882f329e52a0380dc75cb9e81eac0d369c54ffc3 GIT binary patch literal 63970 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 literal 0 HcmV?d00001 diff --git a/assets/erg_logo.svg b/assets/erg_logo.svg new file mode 100644 index 00000000..810c3ac7 --- /dev/null +++ b/assets/erg_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/erg_logo_with_slogan.svg b/assets/erg_logo_with_slogan.svg new file mode 100644 index 00000000..2fdbbe76 --- /dev/null +++ b/assets/erg_logo_with_slogan.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/erg_logo_with_slogan_transparent.png b/assets/erg_logo_with_slogan_transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..f2d725d1308a82ee99ef49bf26f4d62eb1ccf867 GIT binary patch literal 139740 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

xz7e|v(Cu_qnw=}Z&R!ilsy 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 \ No newline at end of file diff --git a/benchmark/bm1.er b/benchmark/bm1.er new file mode 100644 index 00000000..530fa08c --- /dev/null +++ b/benchmark/bm1.er @@ -0,0 +1,5 @@ +fib 0 = 0 +fib 1 = 1 +fib n = fib(n-1) + fib(n-2) + +print! fib 20 diff --git a/benchmark/bm1.js b/benchmark/bm1.js new file mode 100644 index 00000000..e9698c34 --- /dev/null +++ b/benchmark/bm1.js @@ -0,0 +1,11 @@ +function fib(n) { + if (n == 0) { + 0 + } else if (n == 1) { + 1 + } else { + fib(n - 1) + fib(n - 2) + } +} + +console.log(fib(20)) diff --git a/benchmark/bm1.py b/benchmark/bm1.py new file mode 100644 index 00000000..d686a9be --- /dev/null +++ b/benchmark/bm1.py @@ -0,0 +1,6 @@ +def fib(n): + if n == 0: return 0 + elif n == 1: return 1 + else: return fib(n-1) + fib(n-2) + +print(fib(20)) diff --git a/doc/EN/faq_general.md b/doc/EN/faq_general.md new file mode 100644 index 00000000..432d3cdc --- /dev/null +++ b/doc/EN/faq_general.md @@ -0,0 +1,31 @@ +# Erg FAQ + +This FAQ is intended for the general Erg beginner. +For individual (common) technical issues, please refer to [here](./faq_technical.md) for individual (common) technical issues, and +[Here](./dev_guide/faq_syntax.md) for more information. + +## What does it mean that Erg is a Python compatible language? + +~~A: Erg's executable system, EVM (Erg VirtualMachine), executes Erg bytecode, which is an extension of Python bytecode. It introduces a static typing system and other features into the Python bytecode (such as introducing arguments to instructions that do not take arguments, and implementing unique instructions in the free numbers). This allows Erg to call Python code seamlessly and execute it fast.~~ + +A: Erg code is transpiled into Python bytecode. That is, it runs on the same interpreter as Python. Originally, we planned to develop a Cpython-compatible interpreter, and to combine it with the compiler to form "Erg". However, since the development of the processing system has lagged far behind that of the compiler, we have decided to release only the compiler in advance (But the interpreter is still under development). + +## What languages have influenced Erg? + +We have been influenced by more languages than we can count on both hands, but Python, Rust, Nim, and Haskell have been the strongest influences. +We inherited many semantics from Python, expression-oriented and trait from Rust, procedures from Nim, and functional programming-related features from Haskell. + +## Languages that can call Python include Julia. Why did you create Erg? + +A: One of the motivations for Erg's design was to have a language that is easy to use, yet has a powerful type system. That is, a language with type inference, Kind, dependent types, etc. +Julia can be typed, but it is really a dynamically typed language and does not have the compile-time error detection benefits of statically typed languages. + +## Erg supports multiple styles of programming, including functional and object-oriented programming. Isn't this contrary to Python's "There should be one --and preferably only one-- obvious way to do it."? + +A: In Erg, the term is taken in a more narrow context. For example, there are generally no aliases in the Erg API; Erg is "only one way" in this context. +In a larger context, such as FP or OOP, having only one way of doing things is not necessarily a convenience. +For example, JavaScript has several libraries to help create immutable programs, and C has several libraries for garbage collection. +However, having multiple libraries for even such basic features not only takes time to select, but also creates significant difficulties in integrating code that uses different libraries. +Even in Haskell, a purely functional language, there are libraries that support OOP. +If programmers don't have some stuffs, they will create them on their own. So, we think it would be better to provide them as a standard. +This also fits with Python's "Battery included" concept. diff --git a/doc/EN/faq_technical.md b/doc/EN/faq_technical.md new file mode 100644 index 00000000..ffbffdb4 --- /dev/null +++ b/doc/EN/faq_technical.md @@ -0,0 +1,23 @@ +# Technical FAQ + +This section answers technical questions about using the Erg language. In other words, it contains questions that begin with What or Which, and questions that can be answered with Yes/No. + +For more information on how the grammar was determined, see [here](./dev_guide/faq_syntax.md) for the underlying syntax decisions, and [here](./dev_guide/../faq_general.md). + +## Is there an exception mechanism in Erg? + +A: No. Erg uses the `Result` type instead. See [here](./dev_guide/faq_syntax.md) for why Erg does not have an exception mechanism. + +## Does Erg have a type equivalent to TypeScript's `Any`? + +A: No, there is not. All objects belong to at least the `Object` class, but this type only provides a minimal set of attributes, so you can't do whatever you want with it like you can with Any. +The `Object` class is converted to the desired type through dynamic inspection by `match`, etc. It is the same kind of `Object` in Java and other languages. +In the Erg world, there is no chaos and hopelessness like in TypeScript, where the API definition is ``Any''. + +## What is the difference between Never, {}, None, (), NotImplemented, and Ellipsis? + +A: `Never` is an "impossible" type. A subroutine that produces a runtime error has `Never` (or a merger type of `Never`) as its return type. The program will stop as soon as it detects this. Although the `Never` type is by definition also a subclass of all types, `Never` type objects never appear in Erg code and are never created. `{}` is equivalent to `Never`. +`Ellipsis` is an object that represents an ellipsis, and comes from Python. +`NotImplemented` is also from Python. It is used as a marker for not implemented, but Erg prefers the `todo` function which produces an error. +`None` is an instance of `NoneType`. It is often used with the `Option` type. +`()` is a unit type and an instance of itself. It is used when you want to return a "meaningless value" such as the return value of a procedure. diff --git a/doc/EN/improved_points.md b/doc/EN/improved_points.md new file mode 100644 index 00000000..dc8f71e0 --- /dev/null +++ b/doc/EN/improved_points.md @@ -0,0 +1,48 @@ +# Improvements from Python + +## Perform static analysis (static type checking, variable and property checking) + +The benefit of static type checking cannot be emphasized enough now, but checking for the existence of variables and properties is also a part that comes into play quite a bit. + +## Strict scope handling + +In Python, statements do not have scopes. +Therefore, variables defined in a `for` or `if` have outside effects. You cannot name variables casually. + +```python +for i in range(10): + x = 1 + print(i + x) +print(x) # 1 +``` + +In Erg, all blocks have scope and are completely isolated. + +## Clear distinction between mutable and immutable objects + +Python is not clear on the distinction between mutable and immutable / heap and value objects, so you have to keep in mind that tuples are immutable but lists are mutable... You need to keep in mind that tuples are immutable, but lists are mutable... and so on. +Also, if you want to make your own classes immutable, you have to go through a tedious process. + +```python +# Can you believe this code is valid for the past versions of Python? +i = 256 +assert i is 256 +i = 257 +assert i is not 257 +``` + +## Traits + +Just like Java's interface, you can do contract-based programming. + +Python also has ABC (Abstract Base Class), but this kind of structure works best with static typing. + +## Resolve dependencies statically + +This prevents the annoying experience of running a program for a long time and then running it with an error due to missing modules. + +## Built-in package manager + +Reproducible builds with a standardized directory structure and build files. +Lock file generation and version control are of course provided. +There is no need to choice or mix anaconda, pyenv, poetry, etc. for each project. diff --git a/doc/EN/index.md b/doc/EN/index.md new file mode 100644 index 00000000..0dab9ad3 --- /dev/null +++ b/doc/EN/index.md @@ -0,0 +1,25 @@ +# Index + +## [API/](./API/index.md) + + This section describes the specifications of subroutines, types, constants, etc. provided by Erg's built-in or standard libraries. + +## [compiler/](./compiler/index.md) + + Describes the design of the Erg compiler (Centimetre). + +## [dev_guide/](./dev_guide/index.md) + + It explains the development policy of the project, how to make contributions, etc. + +## [python/](./python/index.md) + + The knowledge of Python required to develop Erg is explained. + +## [syntax/](./syntax/00_basic.md) + + The syntax of Erg is explained. + +## [tools/](./tools/index.md) + + Explains how to use Erg's peripheral tools and command options. diff --git a/doc/EN/migration_from_py.md b/doc/EN/migration_from_py.md new file mode 100644 index 00000000..f039b9ca --- /dev/null +++ b/doc/EN/migration_from_py.md @@ -0,0 +1,28 @@ +# Tips on migrating from Python to Erg + +## Want to convert a string to an int, etc + +Use the `parse` method of the `Str` class. It returns a `Result` type. + +```python +# Python +s: str +i: int = int(s) +``` + +```erg +# Erg +s: Str +res: Result(Int, IntParseError) = s.parse Int +i: Int = res.unwrap() +f: Result(Float, FloatParseError) = s.parse Float +``` + +You can also use the `try_from` method. + +```erg +# Erg +s: Str +i: Int = Int.try_from(s).unwrap() +f: Float = Float.try_from(s).unwrap() +``` diff --git a/doc/EN/syntax/00_basic.md b/doc/EN/syntax/00_basic.md new file mode 100644 index 00000000..f140cb02 --- /dev/null +++ b/doc/EN/syntax/00_basic.md @@ -0,0 +1,120 @@ +# Basics + +> __Info__: This document is incomplete. It has not been proofread (style, correct links, mistranslation, etc.). Also, Erg's syntax may be change destructively during version 0.*, and the documentation may not have been updated accordingly. Please be aware of this beforehand. +> If you find any errors in this document, please report then to [here form](https://forms.gle/HtLYRfYzWCAaeTGb6) or [GitHub repo](https://github.com/mtshiba/TheErgBook/issues/new). We would appreciate your suggestions. +> +> [The Erg book original version (Japanese)](http://mtshiba.me/TheErgBook/) + +This document describes the basic syntax of Erg. The [Standard API](./API/index.md) and [internal documents for Erg contributors](./dev_guide/index.md) are located in another directory. + +## Hello, World! + +First, let's do "Hello World". + +```erg +print!("Hello, World!") +``` + +This is almost identical to Python and other languages in the same family. The most striking feature is the `!`, the meaning of which will be explained later. +In Erg, parentheses `()` can be omitted unless there is some confusion in interpretation. +The omission of parentheses is similar to Ruby, but it is not possible to omit parentheses that can be interpreted in more than one way. + +```erg +print! "Hello, World!" # OK +print! "Hello,", "World!" # OK +print!() # OK +print! # OK, but this does not mean to call, simply to get `print!` as a callable object + +print! f x # OK, interpreted as `print!(f(x))` +print!(f(x, y)) # OK +print! f(x, y) # OK +print! f(x, g y) # OK +print! f x, y # NG, can be taken to mean either `print!(f(x), y)` or `print!(f(x, y))` print! +print!(f x, y) # NG, can be taken to mean either `print!(f(x), y)` or `print!(f(x, y))` +print! f(x, g y, z) # NG, can be taken to mean either `print!(x, g(y), z)` or `print!(x, g(y, z))` +``` + +## Scripts + +Erg code is called a script. Scripts can be saved and executed in file format (.er). + +## Comments + +The code after `#` is ignored as a comment. Use this to explain the intent of the code or to temporarily disable the code. + +```erg +# Comment +# `#` and after are ignored until a new line is inserted +#[ +Multi-line comment +Treated as a comment all the way up to the corresponding `]#` +]# +``` + +## Expressions, separators + +A script is a series of expressions. An expression is something that can be calculated or evaluated, and in Erg almost everything is an expression. +Each expression is separated by a separator - either a new line or a semicolon `;`-. +Erg scripts are basically evaluated from left to right, top to bottom. + +```erg +n = 1 # assignment expression +f(1, 2) # function-call expression +1 + 1 # operator-call expression +f(1, 2); 1 + 1 +``` + +As shown below, there is a syntax called instant block that takes the last expression evaluated in the block as the value of the variable. +This differs from a function with no arguments, which does not add `()`. Note that instant blocks are evaluated only once on the fly. + +```erg +i = + x = 1 + x + 1 +assert i == 2 +``` + +This cannot be accomplished with a semicolon (`;`). + +```erg +i = (x = 1; x + 1) # SyntaxError: cannot use `;` in parentheses +``` + +## Indentation + +Erg, like Python, uses indentation to represent blocks. There are five operators (special forms) that trigger the start of a block: `=`, `->`, `=>`, `do`, and `do!` (In addition, `:` and `|`, although not operators, also produce indentation). The meanings of each are described later. + +```erg +f x, y = + x + y + +for! 0..9, i => + print! + +for! 0..9, i => + print! i; print! i + +ans = match x: + 0 -> "zero" + _: 0..9 -> "1 dight" + _: 10..99 -> "2 dights" + _ -> "unknown" +``` + +If a line is too long, it can be broken using `\`. + +```erg +# this does not means `x + y + z` but means `x; +y; +z` +x ++ y ++ z + +# this means `x + y + z` +x \ ++ y \ ++ z +``` + +

diff --git a/doc/EN/syntax/01_literal.md b/doc/EN/syntax/01_literal.md new file mode 100644 index 00000000..def666eb --- /dev/null +++ b/doc/EN/syntax/01_literal.md @@ -0,0 +1,149 @@ +# Literal + +## Basic Literals + +### Int Literal + +```erg +0, -0, 1, -1, 2, -2, 3, -3, ... +``` + +### Ratio Literal + +```erg +0.00, -0.0, 0.1, 400.104, ... +``` + +If a `Ratio` literal has an integer or decimal part of `0`, you can omit the `0`. + +```erg +assert 1.0 == 1. +assert 0.5 == 0.5 +``` + +> __Note__: This function `assert` was used to show that `1.0` and `1.` are equal. +Subsequent documents may use `assert` to indicate that the results are equal. + +### Str Literal + +Any Unicode-representable string can be used. +Unlike Python, quotation marks cannot be enclosed in `'`. If you want to use `"` in a string, use `\"`. + +```erg +"", "a", "abc", "111", "1# 3f2-3*8$", "こんにちは", "السَّلَامُ عَلَيْكُمْ", ... +``` + +`{}` allows you to embed expressions in strings. This is called string interpolation. +If you want to output `{`, `}` itself, use `\{`, `\}`. + +```erg +assert "1 + 1 is 2" == "{1} + {1} is {1+1}" +s = "1+1" +assert "\{1+1}\" == "\{{s}\}" +``` + +### Exponential Literal + +This is a literal representing exponential notation often used in academic calculations. It is an instance of type ``Ratio``. +The notation is the same as in Python. + +```erg +1e-34, 0.4e-10, 2.455+e5, 245e5, 25E5, ... +``` + +```erg +assert 1e-10 == 0.0000000001 +``` + +## Compound Literals + +Each of these literals has its own documentation describing them separately, so please refer to that documentation for details. + +### [Array Literal](./10_array.md) + +```erg +[], [1], [1, 2, 3], ["1", "2",], [1, "1", True, [1]], ... +``` + +### [Dict Literal](./11_dict.md) + +```erg +{:}, {"one": 1}, {"one": 1, "two": 2}, {"1": 1, "2": 2}, {1: "1", 2: True, "three": [1]}, ... +``` + +### [Tuple Literal](./12_tuple.md) + +```erg +(), (1, 2, 3), (1, "hello", True), ... +``` + +### [Record Literal](./13_record.md) + +```erg +{=}, {one = 1}, {one = 1; two = 2}, {.name = "John"; .age = 12}, {.name = Str; .age = Nat}, ... +``` + +### [Set Literal](./14_set.md) + +```erg +{}, {1}, {1, 2, 3}, {"1", "2", "1"}, {1, "1", True, [1]} ... +``` + +As a difference from `Array` literals, duplicate elements are removed in `Set`. + +```erg +assert {1, 2, 1} == {1, 2} +``` + +### What looks like a literal but isn't + +## Boolean Object + +```erg +True, False +``` + +### None Object + +```erg +None +``` + +## Range Object + +```erg +assert 0..5 == {1, 2, 3, 4, 5} +assert 0..10 in 5 +assert 0..<10 notin 10 +assert 0..9 == 0..<10 +``` + +## Float Object + +```erg +assert 0.0f64 == 0 +assert 0.0f32 == 0.0f64 +``` + +Float objects are constructed by multiplying a `Ratio` object by `f64`, which is a `Float 64` unit object. + +## Complex Object + +```erg +1+2im, 0.4-1.2im, 0im, im +``` + +A `Complex` object is simply an arithmetic combination of an imaginary unit object, `im`. + +## *-less multiplication + +In Erg, you can omit the `*` to indicate multiplication as long as there is no confusion in interpretation. However, the combined strength of the operators is set stronger than `*`. + +```erg +# same as `assert (1*m) / (1*s) == 1*(m/s)` +assert 1m / 1s == 1 (m/s) +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/02_name.md b/doc/EN/syntax/02_name.md new file mode 100644 index 00000000..d0b6e511 --- /dev/null +++ b/doc/EN/syntax/02_name.md @@ -0,0 +1,164 @@ +# Variable + +Variables are a type of algebra; algebra in Erg - sometimes simply referred to as variable if there is no confusion - refers to the feature to name objects and make them referable from elsewhere in the code. + +A variable is defined as follows. +The `n` part is called the variable name (or identifier), `=` is the assignment operator, and the `1` part is the assigned value. + +```erg +n = 1 +``` + +The `n` defined in this way can thereafter be used as a variable to denote the integer object `1`. This system is called assignment (or binding). +We have just said that `1` is an object. We will discuss what an object is later, but for now we will assume that it is something that can be assigned to, i.e., on the right side of the assignment operator (`=`, etc.). + +If you want to specify the "type" of a variable, do the following. The type is roughly the set to which an object belongs, as will be explained later. +Here we specify that `n` is a natural number (`Nat`) type. + +```erg +n: Nat = 1 +``` + +Note that, unlike other languages, multiple assignments are not allowed. + +```erg +# NG +l1 = l2 = [1, 2, 3] # SyntaxError: multiple assignment not allowed +# OK +l1 = [1, 2, 3] +l2 = l1.clone() +``` + +It is also not possible to reassign to a variable. The syntax that can be used instead, to hold mutable states, are described later. + +```erg +i = 1 +i = i + 1 # AssignError: cannot assign twice +``` + +You can define a variable with the same name in the inner scope, but you are only covering it over, not destructively rewriting its value. If you go back to the outer scope, the value will return as well. +Note that this is a different behavior than the Python "statement" scope. +This kind of functionality is generally referred to as shadowing. However, unlike shadowing in other languages, you cannot shadow in the same scope. + +```erg +x = 0 +# x = 1 # AssignError: cannot assign twice +if x.is_zero(), do: + x = 1 # different from outer x with same name + assert x == 1 +assert x == 0 +``` + +The following may seem possible at first glance, but it is still not possible. This is a design decision, not a technical constraint. + +```erg +x = 0 +if x.is_zero(), do: + x = x + 1 # NameError: cannot define variables refer to variables with the same name + assert x == 1 +assert x == 0 +``` + +## Constants + +Constants are also a type of algebra. If you start an identifier with a capital letter, it is treated as a constant. They are called constants because once defined, they do not change. +The `N` part is called the constant name (or identifier). Otherwise, it is the same as a variable. + +```erg +N = 0 +if True, do: + N = 1 # AssignError: constants cannot be shadowed + pass() +``` + +Constants are immutable beyond the defined scope. They cannot be shadowed. Because of this property, constants can be used in pattern matching. Pattern matching is explained later. + +For example, constants are used for mathematical constants, information about external resources, and other immutable values. + +It is common practice to use all-caps (style in which all letters are capitalized) for identifiers of objects other than [types](./type/01_type_system.md). + +```erg +PI = 3.141592653589793 +URL = "https://example.com" +CHOICES = ["a", "b", "c"] +``` + +```erg +PI = 3.141592653589793 +match! x: + PI => print! "π" + other => print! "other" +``` + +The above code prints `π` when `x` is `3.141592653589793`. If `x` is changed to any other number, it prints `other`. + +Some objects cannot be bound as constants. Mutable objects, for example. Mutable objects are objects whose states can be changed, as described in detail later. +This is because of the rule that only constant expressions can be assigned to constants. Constant expressions are also discussed later. + +```erg +X = 1 # OK +X = !1 # TypeError: cannot define Int! object as a constant +``` + +## Delete an Variable + +You can delete an variable by using the `Del` function. All other variables that depend on the variable (that is, that refer directly to the value of the variable) are also removed. + +```erg +x = 1 +y = 2 +Z = 3 +f a = x + a + +assert f(2) == 3 +Del x +Del y, Z + +f(2) # NameError: f is not defined (deleted in line 6) +``` + +Note that `Del` can only delete variables defined in the user-defined module. Built-in constants such as `True` cannot be deleted. + +```erg +Del True # TypeError: cannot delete built-in constants +Del print! # TypeError: cannot delete built-in variables +``` + +## Appendix: Assignment and Equivalence + +Note that `x == a` is not necessarily true when `x = a`. An example is `Float.NaN`. This is the formal specification of floating-point numbers as defined by IEEE 754. + +```erg +x = Float.NaN +assert x ! = NaN +assert x ! = x +``` + +There are other objects for which no equivalence relation is defined in the first place. + +```erg +f = x -> x**2 + 2x + 1 +g = x -> (x + 1)**2 +f == g # TypeError: cannot compare function objects + +C = Class {i: Int} +D = Class {i: Int} +C == D # TypeError: cannot compare class objects +``` + +Strictly speaking, `=` does not assign the right-hand side value directly to the left-hand side identifier. +In the case of function and class objects, "modification" such as giving variable name information to the object is performed. However, this is not the case for structural types. + +```erg +f x = x +print! f # +g x = x + 1 +print! g # + +C = Class {i: Int} +print! C # +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/03_declaration.md b/doc/EN/syntax/03_declaration.md new file mode 100644 index 00000000..8d26675b --- /dev/null +++ b/doc/EN/syntax/03_declaration.md @@ -0,0 +1,49 @@ +# Declaration + +Declaration is the syntax for specifying the type of variable to be used. +Declarations can be made anywhere in the code, but declarations alone do not refer to the variables. They must be initialized. +After the assignment, the declaration can be checked to ensure that the type is compatible with the object to which it is assigned. + +```erg +i: Int +# Can be declared at the same time as the assignment, like i: Int = 2 +i = 2 +i: Num +i: Nat +i: -2..2 +i: {2} +``` + +Declaration after assignment is similar to type checking by `assert`, but has the feature that it is checked at compile time. +Type checking by `assert` at runtime can be checked for "may be type Foo", but type checking by `:` at compile time is strict: if the type is not determined to be "type Foo", it will not pass the check and an error will occur. + +```erg +i = (-1..10).sample! +assert i in Nat # this may pass +i: Int # this will pass +i: Nat # this will not pass (-1 is not an element of Nat) +``` + +Functions can be declared in four different ways. None of them can omit the return type. + +```erg +f(x: Int, y: Int): Int +f(Int, Int): Int +f: (x: Int, y: Int) -> Int +f: (Int, Int) -> Int +``` + +If you declare the argument names explicitly, a type error will result if the names are different at definition time. If you want to give the argument names arbitrary names, you can declare them in the second or fourth way. In that case, only the method name and its type will be seen by type checking. + +```erg +T = Trait { + .f(x: Int, y: Int): Int +} + +C = Class(U, Impl: T) +C.f(a: Int, b: Int): Int = ... # TypeError: `.f` must be type of `(x: Int, y: Int) -> Int`, not `(a: Int, b: Int) -> Int` +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/04_function.md b/doc/EN/syntax/04_function.md new file mode 100644 index 00000000..cc94e4b6 --- /dev/null +++ b/doc/EN/syntax/04_function.md @@ -0,0 +1,281 @@ +# Function + +A function is a block that takes an "argument", processes it, and returns it as a "return value". It is defined as follows. + +```erg +add x, y = x + y +# or +add(x, y) = x + y +``` + +The names specified after a function name are called parameters. +In contrast, the objects passed to a function are called arguments. +The function `add` is a function that takes `x` and `y` as parameters and returns the sum of them, `x + y`. +The defined function can be called (applied/invoked) as follows. + +```erg +add 1, 2 +# or +add(1, 2) +``` + +## Colon application style + +Functions are invoked like `f x, y, ...`, but if there are too many arguments for a single line, they can be applied using `:` (colon). + +```erg +f some_long_name_variable_1 + some_long_name_variable_2, some_long_name_variable_3 * some_long_name_variable_4 +``` + +```erg +f some_long_name_variable_1 + some_long_name_variable_2: + some_long_name_variable_3 * some_long_name_variable_4 +``` + +```erg +f: + some_long_name_variable_1 + some_long_name_variable_2 + some_long_name_variable_3 * some_long_name_variable_4 +``` + +All three codes above mean the same thing. This style is also useful when using `if` functions, for example. + +```erg +result = if Bool.sample!(): + do: + log "True was chosen" + 1 + do: + log "False was chosen" + 0 +``` + +After `:`, no code other than comments may be written, and must always be on a new line. + +## Keyword Arguments + +If a function is defined with a large number of parameters, there is a danger of passing the arguments in the wrong order. +In such cases, it is safe to call the function using keyword arguments. + +```erg +f x, y, z, w, v, u: Int = ... +``` + +The functions defined above have many arguments and are arranged in a confusing order. You should not create such a function, but you may encounter such code when using code written by others. Therefore, we use keyword arguments. If you use keyword arguments, the values are passed from the name to the correct argument, even if they are in the wrong order. + +```erg +f u: 6, v: 5, w: 4, x: 1, y: 2, z: 3 +``` + +Note that keyword arguments and a new line immediately after the `:` are considered a colon-call style. + +```erg +# means `f(x: y)` +f x: y + +# means `f(x, y)` +f x: + y +``` + +## Default parameters + +Default parameters are used when some parameters are mostly fixed and you want to be able to omit them. + +Default parameters are specified by `|=`(or-assign operator). If `base` is not specified, assign `math.E` to `base`. + +```erg +math_log x: Ratio, base |= math.E = ... + +assert math_log(100, 10) == 2 +assert math_log(100) == math_log(100, math.E) +``` + +Note that there is a distinction between specifying no argument and assigning `None`. + +```erg +p! x |= 0 = print! +p!(2) # 2 +p!() # 0 +p!(None) # None +``` + +Can also be used with type specification and patterns. + +```erg +math_log x, base: Ratio |= math.E = ... +f [x, y] |= [1, 2] = ... +``` + +However, within the default arguments, it is not possible to call the procedures (described later) or assign mutable objects. + +```erg +f x |= p! 1 = ... # NG +``` + +Also, the argument just defined cannot be used as the value passed to the default argument. + +```erg +f x |= 1, y |= x = ... # NG +``` + +## Variable-length arguments + +The `log` function, which outputs a log (record) of its arguments, can take any number of arguments. + +```erg +log "Hello", "World", "!" # Hello World ! +``` + +To define such a function, add `...` to a parameter. This way, the function receives arguments as variable-length tuples. + +```erg +f ...x = + for x, i -> + log i + +# x == [1, 2, 3, 4, 5] +f 1, 2, 3, 4, 5 +``` + +## Function definition with multiple patterns + +```erg +fib n: Nat = + match n: + 0 -> 0 + 1 -> 1 + n -> fib(n - 1) + fib(n - 2) +``` + +Functions like the one above, where `match` appears directly under the definition, can be rewritten as follows. + +```erg +fib 0 = 0 +fib 1 = 1 +fib(n: Nat): Nat = fib(n - 1) + fib(n - 2) +``` + +Note that a function definition with multiple patterns is not so-called overloading (multiple definition); a function has only a single definition. In the example above, `n` must be of the same type as `0` or `1`. Also, as with `match`, pattern matching is done from top to bottom. + +If instances of different classes are mixed, the last definition must specify that the function argument is of type `Or`. + +```erg +f "aa" = ... +f 1 = ... +# `f x = ... ` is invalid +f x: Int or Str = ... +``` + +Also, like `match`, it must be exhaustive. For emergency, you can use `fib _ = not_implemented()`. + +```erg +fib 0 = 0 +fib 1 = 1 +# PatternError: pattern of fib's parameter is not exhaustive +``` + +## Recursive functions + +A recursive function is a function that includes itself in its definition. + +As a simple example, let us define a function `factorial` that performs a factorial calculation. Factorial is a computation that "multiplies all positive numbers less than or equal to". +The factorial of 5 is `5*4*3*2*1 == 120`. + +```erg +factorial 0 = 1 +factorial 1 = 1 +factorial(n: Nat): Nat = n * factorial(n - 1) +``` + +First, from the definition of factorial, the factorial of 0 and 1 are both 1. +In turn, the factorial of 2 is `2*1 == 2`, the factorial of 3 is `3*2*1 == 6`, and the factorial of 4 is `4*3*2*1 == 24`. +If we look closely, we can see that the factorial of a number n is the factorial of the preceding number n-1 multiplied by n. +Putting this into code, we get `n * factorial(n - 1)`. +Since the definition of `factorial` contains itself, `factorial` is a recursive function. + +As a reminder, if you do not add a type specification, it is inferred like this. + +```erg +factorial: |T <: Sub(Int, T) and Mul(Int, Int) and Eq(Int)| T -> Int +factorial 0 = 1 +factorial 1 = 1 +factorial n = n * factorial(n - 1) +``` + +However, even if you can reason about it, you should explicitly specify the type of the recursive function. In the example above, a code like ``factorial(-1)`` would work, but + +```erg +factorial(-1) == -1 * factorial(-2) == -1 * -2 * factorial(-3) == ... +``` + +and this computation does not stop. Recursive functions must carefully define the range of values or you may end up in an infinite loop. +So the type specification also helps to avoid accepting unexpected values. + +## Compile-time functions + +A function name begins with an uppercase letter to indicate a compile-time function. User-defined compile-time functions must have all arguments as constants and must specify their types. +Compile-time functions are limited in what they can do. Only constant expressions can be used in compile-time functions, i.e., only some operators (such as quadrature, comparison, and type construction operations) and compile-time functions. Arguments to be passed must also be constant expressions. +In return, the advantage is that the computation can be done at compile time. + +```erg +Add(X, Y: Nat): Nat = X + Y +assert Add(1, 2) == 3 + +Factorial 0 = 1 +Factorial(X: Nat): Nat = X * Factorial(X - 1) +assert Factorial(10) == 3628800 + +math = import "math" +Sin X = math.sin X # ConstantError: this function is not computable at compile time +``` + +Compile-time functions are also used in polymorphic type definitions. + +```erg +Option T: Type = T or NoneType +Option: Type -> Type +``` + +## Appendix: Function Comparison + +Erg does not define `==` for functions. This is because there is no structural equivalence algorithm for functions in general. + +```erg +f = x: Int -> (x + 1)**2 +g = x: Int -> x**2 + 2x + 1 + +assert f == g # TypeError: cannot compare functions +``` + +Although `f` and `g` always return the same result, it is extremely difficult to make that determination. We have to teach algebra to the compiler. +So Erg gives up on function comparisons entirely, and `(x -> x) == (x -> x)` also results in a compile error. This is a different specification from Python and should be noted. + +```python +# Python, weird example +f = lambda x: x +assert f == f +assert (lambda x: x) ! = (lambda x: x) +``` + +## Appendix2: ()-completion + +```erg +f x: Object = ... +# will be completed to +f(x: Object) = ... + +f a +# will be completed to +f(a) + +f a, b # TypeError: f() takes 1 positional argument but 2 were given +f(a, b) # TypeError: f() takes 1 positional argument but 2 were given +f((a, b)) # OK +``` + +The function type `T -> U` is actually the syntax sugar of `(T,) -> U`. + +

+ Previous | Next +

diff --git a/doc/EN/syntax/05_builtin_funcs.md b/doc/EN/syntax/05_builtin_funcs.md new file mode 100644 index 00000000..dc88e7ba --- /dev/null +++ b/doc/EN/syntax/05_builtin_funcs.md @@ -0,0 +1,49 @@ +# Built-in functions + +## if + +`if` is a function that changes processing depending on a condition. + +```erg +result: Option Int = if! Bool.sample!(), do: + log "True was chosen" + 1 +print! result # None (or 1) +``` + +`.sample!()` returns a random set of values. If the return value is true, `print! "True"` is executed. +You can also specify what to do if the condition is false; the second do block is called the else block. + +```erg +result: Nat = if Bool.sample!(): + do: + log "True was chosen" + 1 + do: + log "False was chosen" + 0 +print! result # 1 (or 0) +``` + +If the process is a single line, you can omit indentation. + +```erg +result = if Bool.sample!(): + do 1 + do 0 +``` + +## for + +You can use `for` to write a repeating process. + +```erg +match_s(ss: Iterator(Str), pat: Pattern): Option Str = + for ss, s -> + if pat.match(s).is_some(): + break s +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/06_operator.md b/doc/EN/syntax/06_operator.md new file mode 100644 index 00000000..abe81367 --- /dev/null +++ b/doc/EN/syntax/06_operator.md @@ -0,0 +1,29 @@ +# operator + +Operators are symbols that represent operations. Operands are things to the (left) right of an operator. + +Operators are a kind of function, and thus are themselves first-class objects that can be bound to variables. When binding, it is necessary to enclose it with ``. +For `+` (and `-`), there are both unary and binary operators, so `_+_`(binary operation)/`+_`(unary operation ) must be specified. + +``` erg +add = `+` # SyntaxError: specify `_+_` or `+_` +add=`_+_` +assert f(1, 2) == 3 +assert f("a", "b") == "ab" + +g = `*` # OK, this is binary only +assert g(1, 2) == 2 +``` + +Some fundamental operators, called special forms, cannot be bound. + +``` erg +def = `=` # SyntaxError: cannot bind `=` operator, this is a special form +# NG: def x, 1 +function = `->` # SyntaxError: cannot bind `->` operator, this is a special form +# NG: function x, x + 1 +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/07_side_effect.md b/doc/EN/syntax/07_side_effect.md new file mode 100644 index 00000000..85ea41c1 --- /dev/null +++ b/doc/EN/syntax/07_side_effect.md @@ -0,0 +1,121 @@ +# Side effects and procedures + +We have been neglecting to explain the meaning of the `!`, but now its meaning will finally be revealed. This `!` indicates that this object is a "procedure" with a "side-effect". A procedure is a function with a side-effect. + +```erg +f x = print! x # EffectError: functions cannot be assigned objects with side effects +# hint: change the name to 'f!' +``` + +The above code will result in a compile error. This is because you are using a procedure in a function. In such a case, you must define it as a procedure. + +```erg +p! x = print! x +``` + +`p!`, `q!`, ... are typical variable names for procedures. +Procedures defined in this way also cannot be used within a function, so side-effects are completely isolated. + +## Methods + +Functions and procedures each can be methods. Functional methods can only take immutable references to `self`, while procedural methods can take mutable references to `self`. +The `self` is a special parameter, which in the context of a method refers to the calling object itself. The reference `self` cannot be assigned to any other variable. + +```erg +C!. + method ref self = + x = self # OwnershipError: cannot move out 'self' + x +``` + +Procedural methods can also take [ownership]() of `self`. Remove `ref` or `ref!` from the method definition. + +```erg +n = 1 +s = n.into(Str) # '1' +n # ValueError: n was moved by .into (line 2) +``` + +Only one procedural methods can have a mutable reference at any given time. In addition, while a mutable reference is taken, no more mutable reference can be taken from the original object. In this sense, `ref!` causes a side-effect on `self`. + +Note, however, that it is possible to create (immutable/mutable) references from mutable references. This allows recursion and `print!` of `self` in procedural methods. + +```erg +T -> T # OK (move) +T -> Ref T # OK (move) +T => Ref! T # OK (only once) +Ref T -> T # NG +Ref T -> Ref T # OK +Ref T => Ref! +T -> Ref T # NG +T -> Ref T # OK +T => Ref! +``` + +## Appendix: Strict definition of side-effects + +The rules for whether a code has a side-effect or not are not immediately understandable. +Until you can understand them, we recommend that you leave it to the compiler to define them as functions for the time being, and if an error occurs, add `!` to treat them as procedures. +However, for those who want to understand the exact specifications of the language, the following is a more detailed explanation of side-effects. + +First, it must be stated that the equivalence of return values is irrelevant with respect to side effects in Erg. +There are procedures that for any given `x` will result in `p!(x) == p!(x)` (e.g. always return `None`), and there are functions that will result in `f(x) ! = f(x)`. + +An example of the former is `print!`, and an example of the latter is the following function. + +```erg +nan _ = Float.NaN +assert nan(1) ! = nan(1) +``` + +There are also objects, such as classes, for which equivalence determination itself is not possible. + +```erg +T = Structural {i = Int} +U = Structural {i = Int} +assert T == U + +C = Class {i = Int} +D = Class {i = Int} +assert C == D # TypeError: cannot compare classes +``` + +Back to the point: the precise definition of "side-effect" in Erg is + +* Accessing mutable external information. + +"External" generally refers to the outer scope; computer resources that Erg cannot touch and pre-/post-execution information are not included in "external". "Access" includes reading as well as writing. + +As an example, consider the `print!` procedure. At first glance, `print!` does not seem to rewrite any variables. But if it were a function, it could rewrite outer variables, for example, with code like this: + +```erg +camera = import "some_camera_module" +ocr = import "some_ocr_module" + +n = 0 +_ = + f x = print x # Suppose we could use print as a function + f(3.141592) +cam = camera.new() # camera faces PC display +image = cam.shot!() +n = ocr.read_num(image) # n = 3.141592 +``` + +Think of the `camera` module as an external library providing an API for a certain camera product, and `ocr` as a library for OCR (optical character recognition). +The direct side-effect is caused by `cam.shot!()`, but obviously that information is leaked from `f`. Therefore, `print!` cannot be a function by nature. + +Nevertheless, there may be cases where you want to temporarily check a value in a function and do not want to add `!` in the related function just for that purpose. In such cases, the `log` function can be used. +`log` prints the value after the entire code has been executed. In this way, side-effects are not propagated. + +```erg +log "this will be printed after execution" +print! "this will be printed immediately" +# this will be printed immediately +# this will be printed after execution +``` + +If there is no feedback to the program, or in other words, if no external object can use the internal information, then the "leakage" of the information may be allowed. It is only necessary that the information not be "propagated". + +

+ Previous | Next +

diff --git a/doc/EN/syntax/08_procedure.md b/doc/EN/syntax/08_procedure.md new file mode 100644 index 00000000..44363e46 --- /dev/null +++ b/doc/EN/syntax/08_procedure.md @@ -0,0 +1,12 @@ +# Procedures + +Procedures are necessary when dealing with mutable objects, but having a mutable object as an argument does not necessarily make it a procedure. +Here is a function takes a mutable object (not procedure). + +```erg +peek_str s: Str! = log s +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/09_builtin_procs.md b/doc/EN/syntax/09_builtin_procs.md new file mode 100644 index 00000000..b4194c3c --- /dev/null +++ b/doc/EN/syntax/09_builtin_procs.md @@ -0,0 +1,14 @@ +# Built-in procedure + +## id! + +Returns the unique identification number of the object. +Although in pure Erg semantics no difference can be found between objects with the same structure, in practice objects have different locations in memory. +`id!` returns a number representing this position. + +```erg +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/10_array.md b/doc/EN/syntax/10_array.md new file mode 100644 index 00000000..ca31e22f --- /dev/null +++ b/doc/EN/syntax/10_array.md @@ -0,0 +1,40 @@ +# Array + +Arrays are the most basic __collection (aggregate)__. +A collection is an object that can hold multiple objects inside it. + +```erg +a = [1, 2, 3] +a: [Int; 3] # Type specification: number after semicolon is the number of elements +# Can be omitted if the number of elements is not known +a: [Int] + +mut_a = [!1, !2, !3] +mut_a[0].inc!() +assert mut_a == [2, 2, 3] +``` + +## Slice + +An array can also have multiple values taken out at once. This is called slicing. + +```erg +l = [1, 2, 3, 4] +# Same as l[1:3] in Python +assert l[1.. <3] == [2, 3] +assert l[1..2] == [2, 3] +# Same as l[1] +assert l[1..1] == [2] +# Same as l[::2] in Python +assert l[..].step(2) == [2, 4] +``` + +The object obtained by slicing is an (immutable) copy to an array. + +```erg +print! Typeof l[1..2] # [Int; 4] +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/11_tuple.md b/doc/EN/syntax/11_tuple.md new file mode 100644 index 00000000..296ca2bd --- /dev/null +++ b/doc/EN/syntax/11_tuple.md @@ -0,0 +1,117 @@ +# Tuple + +Tuples are similar to arrays, but can hold objects of different types. +Such a collection is called an unequal collection. In contrast, homogeneous collections include arrays, sets, etc. + +```erg +t = (1, True, "a") +(i, b, s) = t +assert(i == 1 and b == True and s == "a") +``` + +The tuple `t` can retrieve the nth element in the form `t.n`; note that unlike Python, it is not `t[n]`. +This is because accessing tuple elements is more like an attribute (the existence of the element is checked at compile time, and the type can change depending on `n`) than a method (an array's `[]` is a method). + +```erg +assert t.0 == 1 +assert t.1 == True +assert t.2 == "a" +``` + +Parentheses `()` are optional when not nested. + +```erg +t = 1, True, "a" +i, b, s = t +``` + +Tuples can hold objects of different types, so they cannot be iterated like arrays. + +```erg +t: ({1}, {2}, {3}) = (1, 2, 3) +(1, 2, 3).iter().map(x -> x + 1) # TypeError: type ({1}, {2}, {3}) has no method `.iter()` +# If all types are the same, they can be represented by `(T; n)` like arrays, but this still does not allow iteration +t: (Int; 3) = (1, 2, 3) +assert (Int; 3) == (Int, Int, Int) +``` + +However, nonhomogeneous collections (such as tuples) can be converted to homogeneous collections (such as arrays) by upcasting, intersecting, and so on. +This is called equalization. + +```erg +(Int, Bool, Str) can be [T; 3] where T :> Int, T :> Bool, T :> Str +``` + +```erg +t: (Int, Bool, Str) = (1, True, "a") # non-homogenous +a: [Int or Bool or Str; 3] = [1, True, "a"] # homogenous +_a: [Show; 3] = [1, True, "a"] # homogenous +_a.iter().map(x -> log x) # OK +t.try_into([Show; 3])? .iter().map(x -> log x) # OK +``` + +## Unit + +A tuple with zero elements is called a __unit__. A unit is a value, but also refers to its own type. + +```erg +unit = () +(): () +``` + +Unit is a superclass of all element 0 tuples. + +```erg +() > (Int; 0) +() > (Str; 0) +``` + +The use of this object is for procedures with no arguments and no return value, etc. Erg subroutines must have arguments and a return value. However, in some cases, such as a procedure, there may be no meaningful arguments or return value, only side effects. In such cases, we use units as "meaningless, formal values. + +```erg +# ↓ Actually, this parenthesis is a unit +p!() =. + # `print!` does not return a meaningful value + print! "Hello, world!" +p!: () => () +``` + +However, Python tends to use `None` instead of units in such cases. +In Erg, you should use `()` when you are sure from the beginning that the operation will not return a meaningful value, such as in a procedure, and return `None` when there is a possibility that the operation will fail and you will get nothing, such as when retrieving an element. + +## Arguments and Tuple + +Actually, all of Erg's `Callable` objects are one argument and one return value; a subroutine that takes N arguments was just receiving "one tuple with N elements" as an argument. + +```erg +# f x = ... is implicitly assumed to be f(x) = ... is considered to be +f x = x +assert f(1) == 1 +f(1, 2, 3) # ArgumentError: f takes 1 positional argument but 3 were given +# ArgumentError: f takes 1 positional argument but 3 were given +g x: Int, . . y: Int = y +assert (2, 3) == g 1, 2, 3 +``` + +This also explains the function type. + +```erg +assert f in T: {(T,) -> T | T} +assert g in {(Int, ... (Int; N)) -> (Int; N) | N: Nat} +``` + +To be precise, the function's input is not a tuple but a "Named tuple with default attributes". This is a special tuple that can only be used in function arguments, can be named like a record, and can have a default value. + +```erg +f(x: Int, y=0) = x + y +f: (Int, y=Int) -> Int + +f(x=0, y=1) +f(y=1, x=0) +f(x=0) +f(0) +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/12_dict.md b/doc/EN/syntax/12_dict.md new file mode 100644 index 00000000..86ae328a --- /dev/null +++ b/doc/EN/syntax/12_dict.md @@ -0,0 +1,67 @@ +# Dict + +Dict is a collection of key/value pairs. + +```erg +ids = {"Alice": 145, "Bob": 214, "Charlie": 301} +assert ids["Alice"] == 145 +``` + +The key does not have to be a string if it is a `Hash` object. + +```erg +# deprecated to use a range object as a key (confused with slice) +r = {1..3: "1~3", 4..6: "4~6", 7..9: "7~9"} +assert r[1..3] == "1~3" +l = {[]: "empty", [1]: "1"} +assert l[[]] == "empty" +``` + +Order does not matter for Dict. It also cannot have duplicate elements. In this respect, Dict is similar to Set. +You could say that a Dict is a Set with a key. + +```erg +{"Alice": 145, "Bob": 214, "Charlie": 301} == {"Alice": 145, "Charlie": 301, "Bob": 214} +``` + +When generating a dict from a dict literal, it is checked for duplicate keys. +Any duplicates will result in a compile error. + +```erg +{"Alice": 145, "Alice": 1} # KeyError: Duplicate key "Alice" +``` + +Empty Dict is created with `{:}`. Note that `{}` denotes an empty set. + +```erg +mut_dict = !{:} +mut_dict.insert! "Alice", 145 +mut_dict.insert! "Bob", 214 +assert mut_dict["Alice"] == 145 +``` + +## Heterogeneous Dict + +There need not be a single key/value type. Such a dictionary is called a __heterogenous dict_. + +```erg +d: {Str: Int, Int: Str} = {"a": 1, 1: "a"} +assert d["a"] == 1 +assert d[1] == "a" +``` + +However, it is not possible to assign values of the same type to keys of different types, or values of different types to keys of the same type. +In such cases, use the type Or instead. + +```erg +invalid1 = {1: "a", "a": "b"} +invalid2 = {1: "a", 2: 2} + +# Erg type inference does not infer Or type, so type specification is required +valid1: {Int or Str: Str} = {1: "a", "a": "b"} +valid2: {Int: Int or Str} = {1: "a", 2: 2} +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/13_record.md b/doc/EN/syntax/13_record.md new file mode 100644 index 00000000..eaf63c27 --- /dev/null +++ b/doc/EN/syntax/13_record.md @@ -0,0 +1,199 @@ +# Record + +A record is a collection that combines the properties of a Dict accessed by key and a tuple whose access is inspected at compile time. +If you know JavaScript, think of it as a (more enhanced) kind of object literal notation. + +```erg +john = {.name = "John"; .age = 21} + +assert john.name == "John" +assert john.age == 21 +assert john in {.name = Str; .age = Nat} +john["name"] # Error: john is not subscribable +``` + +The `.name` and `.age` parts are called attributes, and the `"John"` and `21` parts are called attribute values. + +The difference from JavaScript object literals is that they are not accessible as strings. That is, attributes are not just strings. +This is because access to the value is determined at compile-time, and because dictionaries and records are different things. In other words, `{"name": "John"}` is a Dict and `{name = "John"}` is a record. +So how should we use dictionaries and records? +In general, we recommend using records. Records have the advantages of being checked at compile-time for the existence of elements and of being able to specify __visibility_. +Specifying visibility is equivalent to specifying public/private in Java and other languages. For details, see [visibility](. /15_visibility.md) for details. + +```erg +a = {x = 1; .y = x + 1} +a.x # AttributeError: x is private +# Hint: declare as `.x`. +assert a.y == 2 +``` + +The above example may seem strange to someone familiar with JavaScript, but simply declaring `x` makes it inaccessible from the outside. `. `. `. + +You can also explicitly specify the type of an attribute. + +```erg +anonymous = { + .name: Option! Str = ! + .age = 20 +} +anonymous.name.set! "John" +``` + +A record can also have the method. + +```erg +o = { + .i = !0 + .inc! ref! self = self.i.inc!() +} + +assert o.i == 0 +o.inc!() +assert o.i == 1 +``` + +There is a notable syntax with respect to records. When all the attribute values of a record are classes (not structural types), the record itself behaves as a type with its own attributes as required attributes. +Such a type is called a record type. See the section [Record] for more details. + +```erg +# record +john = {.name = "John"} +# record type +john: {.name = Str} +Named = {.name = Str} +john: Named + +greet! n: Named = + print! "Hello, I am {n.name}" +john # "Hello, I am John" print! + +Named.name # Str +``` + +## Deconstructing a record + +Records can be deconstructed as follows. + +```erg +record = {x = 1; y = 2} +{x = a; y = b} = record +assert a == 1 +assert b == 2 + +point = {x = 2; y = 3; z = 4} +match point: + {x = 0; y = 0; z = 0} -> "origin" + {x = _; y = 0; z = 0} -> "on the x axis" + {x = 0; ...} -> "x = 0" + {x = x; y = y; z = z} -> "({x}, {y}, {z})" +``` + +`x = ...` can also be abbreviated to `x` when there is a variable with the same name as the attribute, for example, `x = x` or `x = .x` to `x`, and `.x = .x` or `.x = x` to `.x`. +However, when there is only one attribute, it must be followed by `;` to distinguish it from a set. + +```erg +x = 1 +y = 2 +xy = {x; y} +a = 1 +b = 2 +ab = {.a; .b} +assert ab.a == 1 +assert ab.b == 2 + +record = {x;} +tuple = {x} +assert tuple.1 == 1 +``` + +This syntax can be used to deconstructed a record and assign it to a variable. + +```erg +# same as `{x = x; y = y} = xy` +{x; y} = xy +assert x == 1 +assert y == 2 +# same as `{.a = a; .b = b} = ab` +{a; b} = ab +assert a == 1 +assert b == 2 +``` + +## Empty Record + +An empty record is represented by `{=}`. An empty record is also its own class, like Unit. + +```erg +empty_record = {=} +empty_record: {=} +# Object: Type = {=} +empty_record: Object +empty_record: Structural {=} +{x = 3; y = 5}: Structural {=} +``` + +An empty record is different from an empty Dict `{:}` or empty set `{}`. In particular, note that it is the opposite of `{}` in meaning (in Python, `{}` is an empty dictionary, while in Erg it is `!{:}` in Erg). +As an enumerated type, `{}` is an empty type that contains nothing in its elements. The `Never` type is a classification of this type. +Conversely, the record class `{=}` has no required instance attribute, so all objects are elements of it. An `Object` is an alias of this. +An `Object` (a patch of `Object`) is an element of `. __sizeof__` and other very basic provided methods. + +```erg +AnyPatch = Patch Structural {=} + . __sizeof__ self = ... + .clone self = ... + ... +Never = Class {} +``` + +Note that no other type or class can be structurally equivalent to the `{}`, `Never` type, and it is an error if the user defines a type with `{}`, `Class {}` on the right side. +This means that, for example, `1..10 or -10. -1`, but `1..10 and -10... -1`. `-1` when it should be `1..10 or -10...-1`, for example. +Also, if you define a type (such as `Int and Str`) that results in a composition `Object`, you will be warned to simply set it to `Object`. + +## Instant Block + +Erg has another syntax, instant block, which simply returns the last value evaluated. Attributes cannot be retained. + +```erg +x = + x = 1 + y = x + 1 + y ** 3 +assert x == 8 + +y = + .x = 1 # SyntaxError: cannot define an attribute in an entity block +``` + +## Data Class + +A bare record (a record generated by a record literal) must be defined directly in the instance if you try to implement a method on its own. +This is inefficient, and as the number of attributes increases, error messages and the like become difficult to see and use. + +```erg +john = { + name = "John Smith" + age = !20 + .greet! ref self = print! "Hello, my name is {self::name} and I am {self::age} years old." + .inc_age! ref! self = self::age.update! x -> x + 1 +} +john + 1 +# TypeError: + is not implemented for {name = Str; age = Int; .greet! = Ref(Self). () => None; inc_age! = Ref! () => None}, Int +``` + +So, in such a case, you can inherit a record class. Such a class is called a data class. +This is described in [class](. /type/04_class.md). + +```erg +Person = Inherit {name = Str; age = Nat} +Person. + greet! ref self = print! "Hello, my name is {self::name} and I am {self::age} years old." + inc_age! ref! self = self::age.update! x -> x + 1 + +john = Person.new {name = "John Smith"; age = 20} +john + 1 +# TypeError: + is not implemented for Person, Int +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/14_set.md b/doc/EN/syntax/14_set.md new file mode 100644 index 00000000..272faa68 --- /dev/null +++ b/doc/EN/syntax/14_set.md @@ -0,0 +1,47 @@ +# Set + +A set is an unordered array with no duplicates. + +```erg +assert Set.from([1, 2, 3, 2, 1]) == {1, 2, 3} +assert {1, 2} == {1, 1, 2} # duplicates are automatically removed +assert {1, 2} == {2, 1} +``` + +Sets can perform mathematical set operations. + +```erg +assert 1 in {1, 2, 3} +assert not 1 in {} +assert {1} or {2} == {1, 2} +assert {1, 2} and {2, 3} == {2} +assert {1, 2} not {2} == {1} +``` + +A set is a homogenous collection. Objects of different classes must be made equal in order to coexist. + +```erg +s1 = {"a", 1, "b", -1} # TypeError +s2: {Int or Str} = {"a", 1, "b", -1} +``` + +## Set as Type + +Sets can also be treated as types. Such a type is called an __Enum type_. + +```erg +i: {1, 2, 3} = 1 +assert i in {1, 2, 3} +``` + +The elements of the set are directly the elements of the type. +Note that the set itself is different. + +```erg +mut_set = {1, 2, 3}.into {Int; !3} +mut_set.insert!(4) +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/15_type.md b/doc/EN/syntax/15_type.md new file mode 100644 index 00000000..97b8bad7 --- /dev/null +++ b/doc/EN/syntax/15_type.md @@ -0,0 +1,7 @@ +# types + +Types are a very important feature in Erg, so we have a [dedicated section](./type/01_type_system.md). Please see there. + +

+ Previous | Next +

diff --git a/doc/EN/syntax/type/01_type_system.md b/doc/EN/syntax/type/01_type_system.md new file mode 100644 index 00000000..07c1c612 --- /dev/null +++ b/doc/EN/syntax/type/01_type_system.md @@ -0,0 +1,227 @@ +# Erg's Type System + +The following is a brief description of Erg's type system. Details are explained in other sections. + +## How to define + +One of the unique features of Erg is that there is not much difference in syntax between (normal) variable, function (subroutine), and type (Kind) definitions. All are defined according to the syntax of normal variable and function definitions. + +```erg +f i: Int = i + 1 +f # +f(1) # 2 +f.method self = ... # SyntaxError: cannot define a method to a subroutine + +T I: Int = {...} +T # +T(1) # Type T(1) +T.method self = ... +D = Class {private = Int; .public = Int} +D # +o1 = {private = 1; .public = 2} # o1 is an object that does not belong to any class +o2 = D.new {private = 1; .public = 2} # o2 is an instance of D +o2 = D.new {.public = 2} # InitializationError: class 'D' requires attribute 'private'(: Int) but not defined +``` + +## Classification + +All objects in Erg are strongly typed. +The top-level type is `{=}`, which implements `__repr__`, `__hash__`, `clone`, etc. (not required methods, and these attributes cannot be overridden). +Erg's type system incorporates structural subtyping (SST). The types typed by this system are called Structural types. +There are three major types of structural types: Attributive (attribute type), Refinement (sieve type), and Algebraic (algebraic type). + +| Record | Enum | Interval | Union | Intersection | Diff | +| --------- | ----------- | ---------- | -------------- | ----------- | ------------ | ------------ | +| kind | Attributive | Refinement | Refinement | Algebraic | Algebraic | Algebraic | Algebraic | Algebraic +| generator | record | set | range operator | or operator | and operator | not operator + +Nominal subtyping (NST) can also be used, and the conversion of an SST type to an NST type is called nominalization of the type. The resulting type is called a nominal type. +In Erg, the nominal types are classes and traits. When we simply say class/trait, we often mean record class/trait. + +| Type | Abstraction | Subtyping procedure | +| --- | -------------- | ---------------- | ------------------- | +| NST | NominalType | Trait | Inheritance | +| SST | StructuralType | Structural Trait | (Implicit) + +The type for the entire nominal type (`NominalType`) and the type for the entire structural type (`StructuralType`) are subtypes of the type for the entire type (`Type`). + +Erg can pass arguments (type arguments) to the type definition. An `Option`, `Array`, etc. with type arguments are called a polynomial kind. These are not themselves types, but they become types by applying arguments. Types such as `Int`, `Str`, etc., which have no arguments, are called simple types (scalar types). + +A type can be regarded as a set, and there is an inclusion relation. For example, `Num` contains `Add`, `Sub`, etc., and `Int` contains `Nat`. +The upper class of all classes is `Object == Class {:}` and the lower class of all types is `Never == Class {}`. This is described below. + +## Types + +A type like `Array T` can be regarded as a function of type `Type -> Type` that takes type `T` as an argument and returns type `Array T` (also called Kind in type theory). Types like `Array T` are specifically called polymorphic types, and `Array` itself is called unary Kind. + +The type of a function whose argument and return types are known is denoted as `(T, U) -> V`. If you want to specify an entire two-argument function of the same type, you can use `|T| (T, T) -> T`, and if you want to specify an entire N-argument function, you can use `Func N`. However, the `Func N` type has no information about the number of arguments or their types, so all return values are of type `Obj` when called. + +The `Proc` type is denoted as `() => Int` and so on. Also, the name of the `Proc` type instance must end with `!` at the end. + +A `Method` type is a function/procedure whose first argument is the object `self` to which it belongs (by reference). For dependent types, you can also specify the type of yourself after the method is applied. This is `T!(!N)` type and `T!(N ~> N-1). () => Int` and so on. + +Erg's array (Array) is what Python calls a list. `[Int; 3]` is an array class that contains three objects of type `Int`. + +> __Note__: `(Type; N)` is both a type and a value, so it can be used like this. +> +> ```erg. +> Types = (Int, Str, Bool) +> +> for! Types, T => +> print! T +> # Int Str Bool +> a: Types = (1, "aaa", True) +> ``` + +```erg +pop|T, N|(l: [T; N]): ([T; N-1], T) = + [...l, last] = l + (l, last) + +lpop|T, N|(l: [T; N]): (T, [T; N-1]) = + [first, ...l] = l + (first, l) +``` + +A type ends with `!` can be rewritten internal structure. For example, the `[T; !N]` class is a dynamic array. +To create an object of type `T!` from an object of type `T`, use the unary operator `!`. + +```erg +i: Int! = !1 +i.update! i -> i + 1 +assert i == 2 +arr = [1, 2, 3] +arr.push! 4 # ImplError: +mut_arr = [1, 2, 3].into [Int; !3] +mut_arr.push4 +assert mut_arr == [1, 2, 3, 4]. +``` + +## Type Definitions + +Types are defined as follows. + +```erg +Point2D = {.x = Int; .y = Int} +``` + +Note that if `.` is omitted from a variable, it becomes a private variable used within the type. However, this is also a required attribute. +Since types are also objects, there are attributes on the types themselves. Such attributes are called type attributes. In the case of a class, they are also called class attributes. + +## Data type + +As mentioned earlier, a "type" in Erg roughly means a set of objects. + +The following is a definition of the `Add` type, which requires `+` (the middle operator). `R, O` are the so-called type parameters, which can be a true type (class) such as `Int` or `Str`. In other languages, type parameters are given a special notation (generics, templates, etc.), but in Erg they can be defined just like normal parameters. +Type parameters can also be used for types other than type objects. For example, the array type `[Int; 3]` is a syntax sugar for `Array Int, 3`. If the type implementations overlap, the user must explicitly choose one. + +```erg +Add R, O = Trait { + . `_+_` = Self.(R) -> O +} +``` + +.`_+_` is an abbreviation for Add.`_+_`. The prefix operator .`+_` is a method of type `Num`. + +```erg +Num = Add and Sub and Mul and Eq +NumImpl = Patch Num +NumImpl. + `+_`(self): Self = self + ... +``` + +Polymorphic types can be treated like functions. They can be monomorphic by specifying them as `Mul Int, Str`, etc. (in many cases, they are inferred with real arguments without specifying them). + +```erg +1 + 1 +`_+_` 1, 1 +Nat.`_+_` 1, 1 +Int.`_+_` 1, 1 +``` + +The top four lines return the same result (to be exact, the bottom one returns `Int`), but it is common to use the top one. +```Ratio.`_+_`(1, 1)``` will return `2.0` without error. +This is because `Int <: Ratio`, so `1` is downcast to `Ratio`. +But this is not cast. + +```erg +i = 1 +if i: # TypeError: i: Int cannot be cast to Bool, use Int.is_zero() instead. + log "a" + log "b" +``` + +This is because `Bool <: Int` (`True == 1`, `False == 0`). Casts to subtypes generally require validation. + +## Type Inference System + +Erg uses static duck typing, so there is little need to explicitly specify the type. + +```erg +f x, y = x + y +``` + +In the case of the code above, the type with `+`, i.e., `Add` is automatically inferred; Erg first infers the smallest type. If `f 0, 1`, it will infer `f x: {0}, y: {1}`, if `n: Nat; f n, 1`, it will infer `f x: Nat, y: {1}`. After minimization, the type is increased until an implementation is found. In the case of `{0}, {1}`, `Nat` is monomorphic to `Nat` since `Nat` is the smallest type with a `+` implementation. +If `{0}, {-1}`, it is monomorphic to `Int` since it does not match `Nat`. If there is no relationship between subtypes and supertypes, the one with the lowest concentration (number of instances) (or even fewer arguments in the case of polymorphic types) is tried first. +`{0}` and `{1}` are enumerated types that are partial types such as `Int` and `Nat`. +Enumerated types, for example, can be given names and request/implementation methods. In namespaces that have access to that type, objects that satisfy the request can use the implementation method. + +```erg +Binary = Patch {0, 1} +Binary. + # self contains an instance. In this example, either 0 or 1. + # If you want to rewrite self, you must append ! must be added to the type name and method name. + is_zero(self) = match self: + 0 -> True + 1 -> False # You can also use _ -> False + is_one(self) = not self.is_zero() + to_bool(self) = match self: + 0 -> False + 1 -> True +``` + +Thereafter, the code `0.to_bool()` is possible (although `0 as Bool == False` is defined built-in). +Here is an example of a type that can actually rewrite `self` as shown in the code. + +```erg +Binary! = Patch {0, 1}! +Binary! + switch! ref! self = match! self: + 0 => self = 1 + 1 => self = 0 + +b = !1 +b.switch!() +print! b # => 0 +``` + +## Structure type (anonymous type) + +```erg +Binary = {0, 1} +``` + +`Binary` in the above code is a type whose elements are `0` and `1`. It is also a subtype of the `Int` type, which has both `0` and `1`. +An object like `{}` is itself a type and can be used with or without assignment to a variable as above. +Such types are called structural types. When we want to emphasize its use as the latter in contrast to a class (named type), it is also called an unnamed type. A structural type such as `{0, 1}` is called an enumerated type, and there are also interval types, record types, and so on. + +### Type Identity + +The following cannot be specified. For example, you cannot specify `Int` and `Int` and `Int` and `Int` and `Int` and `Int`. +For example, `Int` and `Str` are both `Add`, but `Int` and `Str` cannot be added. + +```erg +add l: Add, r: Add = + l + r # TypeError: there is no implementation of `_+_`: |T, U <: Add| (T, U) -> +``` + +Also, the types `A` and `B` below are not considered the same type. However, the type `O` is considered to match. + +```erg +... |R1; R2; O; A <: Add(R1, O); B <: Add(R2, O)| +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/type/02_basic.md b/doc/EN/syntax/type/02_basic.md new file mode 100644 index 00000000..89170130 --- /dev/null +++ b/doc/EN/syntax/type/02_basic.md @@ -0,0 +1,154 @@ +# Basic syntax for types + +## Type specification + +In Erg, the type of a variable can be specified after `:` as follows. This can be done at the same time as an assignment. + +```erg +i: Int # Declare the variable i to be of type Int +i: Int = 1 +j = 1 # type specification can be omitted +``` + +For simple variable assignments, most type specifications can be omitted. +Type specifications are more useful when defining subroutines and types. + +```erg +# Type specification for parameters +f x, y: Array Int = ... +T X, Y: Array Int = ... +``` + +Note that in the above case, `x, y` are both `Array Int`. + +```erg +# The value of a capital variable must be a constant expression +f X: Int = X +``` + +Alternatively, if you don't need complete information about the type argument, you can omit it with `_`. + +```erg +g v: [T; _] = ... +``` + +Note, however, `_` at a type specification implies `Object`. + +```erg +f x: _, y: Int = x + y # TypeError: + is not implemented between Object and Int +``` + +## Subtype specification + +In addition to the `:` (type declaration operator), Erg also allows you to specify the relationship between types by using `<:` (partial type declaration operator). +The left side of `<:` can only specify a class. Use `Subtypeof` or similar operators to compare structural types. + +This is also often used when defining subroutines or types, rather than simply specifying variables. + +```erg +# Subtype specification of an argument +f X <: T = ... + +# Subtype specification of the required attribute (.Iterator attribute is required to be a subtype of type Iterator) +Iterable T = Trait { + .Iterator = {Iterator} # {Iterator} == {I: Type | I <: Iterator} + .iter = Self.() -> Self.Iterator T + ... +} +``` + +You can also use a subtype specification when defining a class to statically check whether the class is a subtype of the specified type. + +```erg +# Class C is a subtype of Show +C = Class Object, Impl: Show +C.show self = ... # Show's required attributes. +``` + +You can also specify a subtype only in specific cases. + +```erg +K T: Eq +K Int <: Show and Eq +K T = Class Object +K(T). + `==` self, other = ... +K(Int). + show self = ... +``` + +Subtype specification is recommended when implementing structural types. +This is because, due to the nature of structural subtyping, typo or type specification errors will not cause errors when implementing required attributes. + +```erg +C = Class Object +C.shoe self = ... # Show is not implemented due to Typo (it is considered just a unique method). +``` + +## Attribute definitions + +Attributes can be defined for traits and classes only in modules. + +```erg +C = Class() +C.pub_attr = "this is public" +C::private_attr = "this is private" + +c = C.new() +assert c.pub_attr == "this is public" +``` + +The syntax for defining a batch definition is called a batch definition, in which a newline is added after `C.` or `C::` and the definitions are grouped together below the indentation. + +```erg +C = Class() +C.pub1 = ... +C.pub2 = ... +C::priv1 = ... +C::priv2 = ... +# is equivalent to +C = Class() +C. + pub1 = ... + C. pub2 = ... +C:: + priv1 = ... + priv2 = ... +``` + +## Aliasing + +Types can be aliased. This allows long types, such as record types, to be shortened. + +```erg +Id = Int +Point3D = {x = Int; y = Int; z = Int} +IorS = Int or Str +Vector = Array Int +``` + +Also, when displaying errors, the compiler will use aliases for composite types (in the above example, right-hand-side types other than the first) if they are defined. + +However, only one alias of the same type is allowed per module, and multiple aliases will result in a warning. +This means that types with different purposes should be defined as separate types. +The purpose is also to prevent adding aliases on top of types that already have aliases. + +```erg +Id = Int +UserId = Int # TypeWarning: duplicate aliases: Id and UserId + +Ids = Array Id +Ints = Array Int # TypeWarning: duplicate aliases: Isd and Ints + +IorS = Int or Str +IorSorB = IorS or Bool +IorSorB_ = Int or Str or Bool # TypeWarning: duplicate aliases: IorSorB and IorSorB_ + +Point2D = {x = Int; y = Int} +Point3D = {.... Point2D; z = Int} +Point = {x = Int; y = Int; z = Int} # TypeWarning: duplicate aliases: Point3D and Point +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/type/03_trait.md b/doc/EN/syntax/type/03_trait.md new file mode 100644 index 00000000..503d641e --- /dev/null +++ b/doc/EN/syntax/type/03_trait.md @@ -0,0 +1,150 @@ +# Trait + +Trait is a nominal type that adds a type attribute requirement to record types. +It is similar to the Abstract Base Class (ABC) in Python, but with the distinction of being able to perform algebraic operations. + +```erg +Norm = Trait {.x = Int; .y = Int; .norm = Self.() -> Int} +``` + +Trait does not distinguish between attributes and methods. + +Note that traits can only be declared, not implemented (implementation is achieved by a feature called patching, which will be discussed later). +Traits can be checked for implementation in a class by specifying a partial type. + +```erg +Point2D <: Norm +Point2D = Class {.x = Int; .y = Int} +Point2D.norm self = self.x**2 + self.y**2 +``` + +Error if the required attributes are not implemented. + +```erg +Point2D <: Norm # TypeError: Point2D is not a subtype of Norm +Point2D = Class {.x = Int; .y = Int} +``` + +Traits, like structural types, can apply operations such as composition, substitution, and elimination (e.g. `T and U`). The resulting trace is called an instant trace. + +```erg +T = Trait {.x = Int} +U = Trait {.y = Int} +V = Trait {.x = Int; y: Int} +assert Structural(T and U) == Structural V +assert Structural(V not U) == Structural T +W = Trait {.x = Ratio} +assert Structural(W) ! = Structural(T) +assert Structural(W) == Structural(T.replace {.x = Ratio}) +``` + +Trait is also a type, so it can be used for normal type specification. + +```erg +points: [Norm; 2] = [Point2D::new(1, 2), Point2D::new(3, 4)] +assert points.iter().map(x -> x.norm()).collect(Array) == [5, 25]. +``` + +## Trait inclusion + +The expansion operator `...` allows you to define a trace that contains a certain trace as a supertype. This is called the __subsumption__ of a trace. +In the example below, `BinAddSub` subsumes `BinAdd` and `BinSub`. +This corresponds to Inheritance in a class, but unlike Inheritance, multiple base types can be combined using `and`. Traits that are partially excluded by `not` are also allowed. + +```erg +Add R, O = Trait { + . `_+_` = Self.(R) -> O +} +BinAdd = Subsume Add(Self, Self.AddO), { + .AddO = Type +} +Sub R, O = Trait { + . `_-_` = Self.(R) -> O +} +BinSub = Subsume Sub(Self, Self.SubO), { + .SubO = Type +} +BinAddSub = Subsume BinAdd(Self, Self.AddO) and BinSub(Self, Self.SubO) +``` + +## Structural Traits + +Traits can be structured. + +```erg +SAdd = Structural Trait { + . `_+_` = Self.(Self) -> Self +} +# |A <: SAdd| cannot be omitted +add|A <: SAdd| x, y: A = x.`_+_` y + +C = Class {i = Int} +C. + new i = Self.__new__ {i;} + `_+_` self, other: Self = Self.new {i = self::i + other::i} + +assert add(C.new(1), C.new(2)) == C.new(3) +``` + +Nominal traits cannot be used simply by implementing a request method, but must be explicitly declared to have been implemented. +In the following example, `add` cannot be used with an argument of type `C` because there is no explicit declaration of implementation. It must be `C = Class {i = Int}, Impl: Add`. + +```erg +Add = Trait { + . `_+_` = Self.(Self) -> Self. +} +# |A <: Add| can be omitted +add|A <: Add| x, y: A = x.`_+_` y + +C = Class {i = Int} +C. + new i = Self.__new__ {i;} + `_+_` self, other: Self = Self.new {i = self::i + other::i} + +add C.new(1), C.new(2) # TypeError: C is not a subclass of Add +# hint: inherit or patch 'Add' +``` + +Structural traits do not need to be declared for this implementation, but instead type inference does not work. Type specification is required for use. + +## Polymorphic Traits + +Traits can take parameters. This is the same as for polymorphic types. + +```erg +Mapper T: Type = Trait { + .mapIter = {Iterator} + .map = Self(T). (T -> U) -> Self.MapIter U +} + +# ArrayIterator <: Mapper +# ArrayIterator.MapIter == ArrayMapper +# [1, 2, 3].iter(): ArrayIterator Int +# [1, 2, 3].iter().map(x -> "{x}"): ArrayMapper Str +assert [1, 2, 3].iter().map(x -> "{x}").collect(Array) == ["1", "2", "3"]. +``` + +## Override in Trait + +Derived traits can override the type definitions of the base trait. +In this case, the type of the overriding method must be a subtype of the base method type. + +```erg +# `Self.(R) -> O` is a subtype of ``Self.(R) -> O or Panic +Div R, O: Type = Trait { + . `/` = Self.(R) -> O or Panic +} +SafeDiv R, O = Subsume Div, { + @Override + . `/` = Self.(R) -> O +} +``` + +## Appendix: Differences from Rust traits + +Erg's trait is faithful to the one proposed by [Schärli et al.](https://www.ptidej.net/courses/ift6251/fall06/presentations/061122/061122.doc.pdf). +In order to allow algebraic operations, traits are designed to be unable to have method implementations directory, but can be patched if necessary. + +

+ Previous | Next +

diff --git a/doc/EN/syntax/type/04_class.md b/doc/EN/syntax/type/04_class.md new file mode 100644 index 00000000..daa0d7fb --- /dev/null +++ b/doc/EN/syntax/type/04_class.md @@ -0,0 +1,276 @@ +# Class + +A class in Erg is roughly a type that can create its own elements (instances). +Here is an example of a simple class. + +```erg +Person = Class {.name = Str; .age = Nat} +Person. + new name, str = Self::__new__ {.name = name; .age = age} + +john = Person.new "John Smith", 25 +print! john # +print! classof(john) # Person +``` + +The type given to `Class` is called the requirement type (in this case `{.name = Str; .age = Nat}`). +Instances can be created with `::__new__ { = ; ...}` can be created with. +`{.name = "John Smith"; .age = 25}` is just a record, but it is converted to a `Person` instance by passing `Person.new`. +The subroutine that creates such an instance is called a constructor. +In the class above, the `.new` method is defined so that field names, etc. can be omitted. + +## Instance and class attributes + +In Python and other languages, instance attributes are often defined on the block side as follows, but note that such writing has a different meaning in Erg. + +```python +# Python +class Person: + name: str + age: int +``` + +```erg +# In Erg, this notation implies the declaration of a class attribute (not an instance attribute) +Person = Class() +Person. + name: Str + age: Int +``` + +```erg +# Erg code for the Python code above +Person = Class { + .name = Str + .age = Nat +} +``` + +Element attributes (attributes defined in a record) and type attributes (also called instance/class attributes, especially in the case of classes) are completely different things. Type attributes are attributes of the type itself. An element of a type refers to a type attribute when it does not have the desired attribute in itself. An element attribute is a unique attribute directly possessed by the element. +Why is this distinction made? If all attributes were element attributes, it would be inefficient to duplicate and initialize all attributes when the object is created. +In addition, dividing the attributes in this way clarifies roles such as "this attribute is shared" and "this attribute is held separately". + +The example below illustrates this. The attribute `species` is common to all instances, so it is more natural to use it as a class attribute. However, the attribute `name` should be an instance attribute because each instance should have it individually. + +```erg +Person = Class {name = Str} +Person:: + species = "human" +Person. + describe() = + log "species: {species}" + greet self = + log "Hello, My name is {self::name}." + +Person.describe() # species: human +Person.greet() # TypeError: unbound method Person.greet needs an argument + +john = Person.new {name = "John"} +john.describe() # species: human +john.greet() # Hello, My name is John. + +alice = Person.new {name = "Alice"} +alice.describe() # species: human +alice.greet() # Hello, My name is Alice. +``` + +Incidentally, if an instance attribute and a type attribute have the same name and the same type, a compile error occurs. This is to avoid confusion. + +```erg +C = Class {.i = Int} +C.i = 1 # AttributeError: `.i` is already defined in instance fields +``` + +## Class, Type + +Note that the class and type of `1` are different. +There is only one class `Int` that is the generator of `1`. You can get the class to which an object belongs by `classof(obj)` or `obj.__class__`. +In contrast, there are countless types of `1`. For example, `{1}, {0, 1}, 0..12, Nat, Int, Num`. +However, the smallest type can be defined as a single type, in this case `{1}`. The type to which an object belongs can be obtained with `Typeof(obj)`. This is a compile-time function. +Objects can use patch methods as well as class methods. +Erg does not allow you to add class methods, but you can use [patch](./07_patch.md) to extend a class. + +You can also inherit from existing classes ([Inheritable](./../27_decorator.md/#inheritable) class). +You can create an inherited class by using `Inherit`. The type on the left-hand side is called the derived class, and the argument type of `Inherit` on the right-hand side is called the base class (inherited class). + +```erg +MyStr = Inherit Str +# other: You can use MyStr if you set ``other: Str''. +MyStr.`-` self, other: Str = self.replace other, "" + +abc = MyStr.new("abc") +# Comparison here gets an upcast +assert abc - "b" == "ac" +``` + +Unlike Python, the defined Erg classes are `final` (non-inheritable) by default. +To make a class inheritable, an `Inheritable` decorator must be attached to the class. +Str` is one of the inheritable classes. + +```erg +MyStr = Inherit Str # OK +MyStr2 = Inherit MyStr # NG + +@Inheritable +InheritableMyStr = Inherit Str +MyStr3 = Inherit InheritableMyStr # OK +``` + +`Inherit Object` and `Class()` are almost equivalent in practice. The latter is generally used. + +Classes have a different equivalence checking mechanism than types. +Types are equivalence tested based on their structure. + +```erg +Person = {.name = Str; .age = Nat} +Human = {.name = Str; .age = Nat} + +assert Person == Human +``` + +class has no equivalence relation defined. + +```erg +Person = Class {.name = Str; .age = Nat} +Human = Class {.name = Str; .age = Nat} + +Person == Human # TypeError: cannot compare classes +``` + +## Difference from structural types + +We said that a class is a type that can generate its own elements, but that is not a strict description. In fact, a record type + patch can do the same thing. + +```erg +Person = {.name = Str; .age = Nat} +PersonImpl = Patch Person +PersonImpl. + new name, age = {.name; .age} + +john = Person.new("John Smith", 25) +``` + +There are four advantages to using classes. +The first is that the constructor is validity checked, the second is that it is more performant, the third is that you can use notational subtypes (NSTs), and the fourth is that you can inherit and override. + +We saw earlier that record type + patch can also define a constructor (of sorts), but this is of course not a legitimate constructor. This is of course not a legitimate constructor, because it can return a completely unrelated object even if it calls itself `.new`. In the case of a class, `.new` is statically checked to see if it produces an object that satisfies the requirements. + +~ + +Type checking for classes is simply a matter of checking the object's `. __class__` attribute of the object. So it is fast to check if an object belongs to a type. + +~ + +Erg enables NSTs in classes; the advantages of NSTs include robustness. +When writing large programs, it is often the case that the structure of an object is coincidentally matched. + +```erg +Dog = {.name = Str; .age = Nat} +DogImpl = Patch Dog +DogImpl.bark = log "Yelp!" +... +Person = {.name = Str; .age = Nat} +PersonImpl = Patch Person +PersonImpl.greet self = log "Hello, my name is {self.name}." + +john = {.name = "John Smith"; .age = 20} +john.bark() # "Yelp!" +``` + +The structure of `Dog` and `Person` is exactly the same, but it is obviously nonsense to allow animals to greet and humans to bark. +The former is impossible, so it is safer to make it inapplicable. In such cases, it is better to use classes. + +```erg +Dog = Class {.name = Str; .age = Nat} +Dog.bark = log "Yelp!" +... +Person = Class {.name = Str; .age = Nat} +Person.greet self = log "Hello, my name is {self.name}." + +john = Person.new {.name = "John Smith"; .age = 20} +john.bark() # TypeError: `Person` object has no method `.bark`. +``` + +Another feature is that the type attributes added by the patch are virtual and are not held as entities by the implementing class. +That is, `T.x`, `T.bar` are objects that can be accessed (compile-time bound) by types compatible with `{i = Int}`, and are not defined in `{i = Int}` or `C`. +In contrast, class attributes are held by the class itself. Therefore, they cannot be accessed by classes that are not in an inheritance relationship, even if they have the same structure. + +```erg +C = Class {i = Int} +C. + foo self = ... +print! dir(C) # ["foo", ...]. + +T = Patch {i = Int} +T. + x = 1 + bar self = ... +print! dir(T) # ["bar", "x", ...]. +assert T.x == 1 +assert {i = 1}.x == 1 +print! T.bar # +{i = Int}.bar # TypeError: Record({i = Int}) has no method `.bar`. +C.bar # TypeError: C has no method `.bar` print! +print! {i = 1}.bar # +C.new({i = 1}).bar # +``` + +## Difference from data class + +There are two types of classes: regular classes, which are record classes through `Class`, and data classes, which inherit (`Inherit`) from record classes. +The data class inherits the functionality of the record class and has features such as decomposition assignment, `==` and `hash` implemented by default, etc. On the other hand, the data class has its own equivalence relation and format display. +On the other hand, if you want to define your own equivalence relations or formatting displays, you should use the normal class. + +```erg +C = Class {i = Int} +c = C.new {i = 1} +d = C.new {i = 2} +print! c # +c == d # TypeError: `==` is not implemented for `C` + +D = Inherit {i = Int} +e = D.new {i = 1} +f = D.new {i = 2} +print! e # D{i = 1} +assert e ! = f +``` + +## Enum Class + +To facilitate defining classes of type `Or`, an `Enum` is provided. + +```erg +X = Class() +Y = Class() +XorY = Enum X, Y +``` + +Each type can be accessed as `XorY.X`, `XorY.Y` and the constructor can be obtained as `XorY.cons(X)`. +`.cons` is a method that takes a class and returns its constructor. + +```erg +x1 = XorY.new X.new() +x2 = XorY.cons(X)() +assert x1 == x2 +``` + +## Class Relationships + +A class is a subtype of a requirement type. methods (including patch methods) of the requirement type can be used in the class. + +```erg +T = Trait ... +T.foo: Foo +C = Class(... , impl: T) +C. + foo = foo + bar x = ... +assert C < T +assert C.foo == foo +assert not T < C +T.foo # AttributeError +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/type/05_inheritance.md b/doc/EN/syntax/type/05_inheritance.md new file mode 100644 index 00000000..5edf3b04 --- /dev/null +++ b/doc/EN/syntax/type/05_inheritance.md @@ -0,0 +1,253 @@ +# Inheritance + +Inheritance allows you to define a new class that adds functionality or specialization to an existing class. +Inheritance is similar to inclusion in a trace. The inherited class becomes a subtype of the original class. + +```erg +NewInt = Inherit Int +NewInt.plus1 self = self + 1 + +assert NewInt.new(1).plus1() == 2 +assert NewInt.new(1) + NewInt.new(1) == 2 +``` + +If you want the newly defined class to be inheritable, you must give it the `Inheritable` decorator. + +You can specify an optional argument `additional` to allow the class to have additional instance attributes, but only if the class is a value class. However, you cannot add instance attributes if the class is a value class. + +```erg +@Inheritable +Person = Class {name = Str} +Student = Inherit Person, additional: {id = Int} + +john = Person.new {name = "John"} +alice = Student.new {name = "Alice", id = 123} + +MailAddress = Inherit Str, additional: {owner = Str} # TypeError: instance variables cannot be added to a value class +``` + +Erg is exceptionally designed not to allow inheritance of type ``Never``. Erg is exceptionally designed not to allow inheritance of `Never` type, because `Never` is a unique class that can never be instantiated. + +## Inheritance of Enumerated Classes + +[Or type](./13_algebraic.md) can also be inherited. In this case, you can remove any of the choices (multiple choices are possible with `or`) by specifying the optional argument `Excluding`. +No additional choices can be added. The class to which you add an option is not a subtype of the original class. + +```erg +Number = Class Int or Float or Complex +Number.abs(self): Float = + match self: + i: Int -> i.abs().into Float + f: Float -> f.abs() + c: Complex -> c.abs().into Float + +# c: Complex cannot appear in match choices +RealNumber = Inherit Number, Excluding: Complex +``` + +Similarly, [sieve type](. /12_refinement.md) can also be specified. + +```erg +Months = Class 0..12 +MonthsNot31Days = Inherit Months, Excluding: {1, 3, 5, 7, 8, 10, 12} + +StrMoreThan3 = Class StrWithLen N | N >= 3 +StrMoreThan4 = Inherit StrMoreThan3, Excluding: StrWithLen N | N == 3 +``` + +## Overriding + +The class is the same as the patch in that new methods can be added to the original type, but the class can be further "overridden". +This overriding is called override. To override, three conditions must be met. +First, the override must have an `Override` decorator because by default it will result in an error. +In addition, the override cannot change the type of the method. It must be a subtype of the original type. +And if you override a method that is referenced by another method, you must also override all referenced methods. + +Why is this condition necessary? It is because overriding does not merely change the behavior of one method, but may affect the behavior of another method. + +Let's start with the first condition. This condition is to prevent "accidental overrides. +In other words, the `Override` decorator must be used to prevent the name of a newly defined method in a derived class from conflicting with the name of the base class. + +Next, consider the second condition. This is for type consistency. Since the derived class is a subtype of the base class, its behavior must also be compatible with that of the base class. + +Finally, consider the third condition. This condition is unique to Erg and not often found in other object-oriented languages, again for safety. Let's look at what could go wrong if this were not the case. + +```erg +# Bad example +@Inheritable +Base! = Class {x = Int!} +Base! + f! ref! self = + print! self::x + self.g!() + g! ref! self = self::x.update! x -> x + 1 + +Inherited! = Inherit Base! +Inherited! + @Override + g! ref! self = self.f!() # InfiniteRecursionWarning: This code falls into an infinite loop + # OverrideError: method `.g` is referenced by `.f` but not overridden +``` + +In the inherited class `Inherited!`, the `.g!` method is overridden to transfer processing to `.f!`. However, the `.f!` method in the base class transfers its processing to `.g!`, resulting in an infinite loop. `.f` was a problem-free method in the `Base!` class, but it was used in an unexpected way by the override, and it was broken. + +Erg has built this rule into the specification. + +```erg +# OK. +@Inheritable +Base! = Class {x = Int!} +Base! + f! ref! self = + print! self::x + self.g!() + g! ref! self = self::x.update! x -> x + 1 + +Inherited! = Inherit Base! +Inherited! + @Override + f! ref! self = + print! self::x + self::x.update! x -> x + 1 + @Override + g! ref! self = self.f!() +``` + +However, this specification does not completely solve the override problem. However, this specification does not completely solve the override problem, since the compiler cannot detect if the override fixes the problem. +It is the responsibility of the programmer creating the derived class to correct the effects of the override. Whenever possible, try to define an alias method. + +### Replacing Traits (or what looks like it) + +Although it is not possible to replace traits at inheritance time, there are examples that appear to do so. + +For example, `Int`, a subtype of `Real` (which implements `Add()`), appears to reimplement `Add()`. + +```erg +Int = Class ... , Impl: Add() and ... +``` + +But in fact `Add()` in `Real` stands for `Add(Real, Real)`, and in `Int` it is just overwritten by `Add(Int, Int)`. +They are two different traits (`Add` is a [covariate](. /advanced/variance.md), so `Add(Real, Real) :> Add(Int, Int)`). + +## Multiple Inheritance + +Erg does not allow intersection, diff, and complement between normal classes. + +```erg +Int and Str # TypeError: cannot unite classes +``` + +This rule prevents inheritance from multiple classes, i.e., multiple inheritance. + +```erg +IntAndStr = Inherit Int and Str # SyntaxError: multiple inheritance of classes is not allowed +``` + +However, multiple inherited Python classes can be used. + +## Multi-layer (multi-level) Inheritance + +Erg inheritance also prohibits multi-layer inheritance. That is, you cannot define a class that inherits from another class. +Inheritable classes that inherit from an `Object` may exceptionally inherit. + +Also in this case, Python's multi-layered inherited classes can be used. + +## Rewriting Inherited Attributes + +Erg does not allow rewriting the attributes inherited from the base class. This has two implications. + +The first is an update operation on the inherited source class attribute. It cannot be reassigned, nor can it be updated by the `.update!` method, for example. + +Overriding is different from rewriting because it is an operation to override with a more specialized method. Overrides must also be replaced by compatible types. + +```erg +@Inheritable +Base! = Class {.pub = !Int; pri = !Int} +Base! + var = !1 + inc_pub! ref! self = self.pub.update! p -> p + 1 + +Inherited! = Inherit Base! +Inherited! + var.update! v -> v + 1 + # TypeError: can't update base class variables + @Override + inc_pub! ref! self = self.pub + 1 + # OverrideError: `.inc_pub!` must be subtype of `Self! () => ()` +``` + +The second is an update operation on the (variable) instance attribute of the inherited source. This is also prohibited. Instance attributes of the base class may only be updated from methods provided by the base class. +Regardless of the visibility of the attribute, it cannot be updated directly. However, they can be read. + +```erg +@Inheritable +Base! = Class {.pub = !Int; pri = !Int} +Base! + inc_pub! ref! self = self.pub.update! p -> p + 1 + inc_pri! ref! self = self::pri.update! p -> p + 1 + +self = self.pub.update! +Inherited! + # OK + add2_pub! ref! self = + self.inc_pub!() + self.inc_pub!() + # NG, `Child` cannot touch `self.pub` and `self::pri`. + add2_pub! ref! self = + self.pub.update! p -> p + 2 +``` + +After all, Erg inheritance can only add new attributes and override base class methods. + +## Usage of Inheritance + +While inheritance is a powerful feature when used correctly, it also has the drawback that it tends to complicate class dependencies, especially when multiple or multi-layer inheritance is used. Complicated dependencies can reduce code maintainability. +The reason Erg prohibits multiple and multi-layer inheritance is to reduce this risk, and the class patch feature was introduced to reduce the complexity of dependencies while retaining the "add functionality" aspect of inheritance. + +So, conversely, where should inheritance be used? One indicator is when "semantic subtypes of the base class are desired. +Erg allows the type system to automatically do part of the subtype determination (e.g., Nat, where Int is greater than or equal to 0). +However, for example, it is difficult to create a "string type representing a valid e-mail address" relying solely on Erg's type system. You should probably perform validation on a normal string. Then, we would like to add some kind of "warrant" to the string object that has passed validation. That is the equivalent of downcasting to an inherited class. Downcasting a `Str object` to `ValidMailAddressStr` is a one-to-one correspondence with validating that the string is in the correct email address format. + +```erg +ValidMailAddressStr = Inherit Str +ValidMailAddressStr. + init s: Str = + validate s # mail-address validation + Self.new s + +s1 = "invalid mail address" +s2 = "foo@gmail.com" +_ = ValidMailAddressStr.init s1 # panic: invalid mail address +valid = ValidMailAddressStr.init s2 +valid: ValidMailAddressStr # assurance that it is in the correct email address format +``` + +Another indicator is when you want to achieve a nominal polymorphism. +For example, the `greet!` procedure defined below will accept any object of type `Named`. +But obviously it is wrong to apply a `Dog` type object. So we will use the `Person` class for the argument type. +This way, only `Person` objects, classes that inherit from them, and `Student` objects will be accepted as arguments. +This is more conservative and avoids unnecessarily assuming too much responsibility. + +```erg +Named = {name = Str; ...} +Dog = Class {name = Str; breed = Str} +Person = Class {name = Str} +Student = Inherit Person, additional: {id = Int} +structural_greet! person: Named = + print! "Hello, my name is {person::name}." +greet! person: Person = + print! "Hello, my name is {person::name}." + +max = Dog.new {name = "Max", breed = "Labrador"} +john = Person.new {name = "John"} +alice = Student.new {name = "Alice", id = 123} + +structural_greet! max # Hello, my name is Max. +structural_greet! john # Hello, my name is John. +greet! alice # Hello, my name is Alice. +greet! max # TypeError: +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/type/06_nst_vs_sst.md b/doc/EN/syntax/type/06_nst_vs_sst.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/EN/syntax/type/07_patch.md b/doc/EN/syntax/type/07_patch.md new file mode 100644 index 00000000..f4d992bd --- /dev/null +++ b/doc/EN/syntax/type/07_patch.md @@ -0,0 +1,222 @@ +# Patch + +Erg does not allow modification of existing types and classes. +This means, it is not possible to define additional methods in a class, nor to perform specialization (a language feature that monomorphizes a polymorphically declared type and defines a dedicated method, as in C++). +However, there are many situations where you may want to add feature to an existing type or class, and there is a function called "patching" that allows you to do this. + +```erg +StrReverse = Patch Str +StrReverse. + reverse self = self.iter().rev().collect(Str) + +assert "abc".reverse() == "cba" +``` + +The name of the patch should be a straightforward description of the primary functionality to be added. +This way, objects of the type being patched (`Str`) can use the methods of the patch (`StrReverse`). +In fact, built-in method `.reverse` is not a method of `Str`, but a method added to `StrRReverse`. + +However, patch methods have lower precedence than methods of the nominal type (class/trait) and cannot override methods of existing types. + +```erg +StrangeInt = Patch Int +StrangeInt. + `_+_` = Int.`_-_` # AssignError: . `_+_` is already defined in Int +``` + +If you want to override, you must inherit from the class. +However, it is basically recommended not to override and to define a method with a different name. +Overriding is not very easy to do because of some safety restrictions. + +```erg +StrangeInt = Inherit Int +StrangeInt. + # Overriding methods must be given Override decorators. + # In addition, you need to override all Int methods that depend on Int.`_+_`. + @Override + `_+_` = Super.`_-_` # OverrideError: Int.`_+_` is referenced by ... ````` , so these methods must also be overridden +``` + +## Selecting Patches + +Patches can be defined for a single type, and can be grouped together. + +```erg +# foo.er + +StrReverse = Patch(Str) +StrReverse. + reverse self = ... +StrMultiReplace = Patch(Str) +StrMultiReverse. + multi_replace self, pattern_and_targets: [(Pattern, Str)] = ... +StrToCamelCase = Patch(Str) +StrToCamelCase. + to_camel_case self = ... +StrToKebabCase = Patch(Str) +StrToKebabCase. + to_kebab_case self = ... + +StrBoosterPack = StrReverse and StrMultiReplace and StrToCamelCase and StrToKebabCase +StrBoosterPack = StrReverse and StrMultiReplace and StrToCamelCase and StrToKebabCase +``` + +```erg +{StrBoosterPack; ...} = import "foo" + +assert "abc".reverse() == "cba" +assert "abc".multi_replace([("a", "A"), ("b", "B")]) == "ABc" +assert "to camel case".to_camel_case() == "toCamelCase" +assert "to kebab case".to_kebab_case() == "to-kebab-case" +``` + +If multiple patches are defined, some of them may result in duplicate implementations. + +```erg +# foo.er + +StrReverse = Patch(Str) +StrReverse. + reverse self = ... +# more efficient implementation +StrReverseMk2 = Patch(Str) +StrReverseMk2. + reverse self = ... + +"hello".reverse() # PatchSelectionError: multiple choices of `.reverse`: StrReverse, StrReverseMk2 +``` + +In such a case, you can make it unique by using the __related function__ form instead of the method form. + +```erg +assert StrReverseMk2.reverse("hello") == "olleh" +``` + +You can also make it unique by selectively importing. + +```erg +{StrReverseMk2; ...} = import "foo" + +assert "hello".reverse() == "olleh" +``` + +## Glue Patch + +Patches can also relate types to each other. The `StrReverse` patch relates `Str` and `Reverse`. +Such a patch is called a __glue patch__. +Because `Str` is a built-in type, a glue patch is necessary for users to retrofit traits. + +```erg +Reverse = Trait { + .reverse = Self.() -> Self +} + +StrReverse = Patch Str, Impl: Reverse +StrReverse. + reverse self = + self.iter().rev().collect(Str) +``` + +Only one glue patch can be defined per type/trait pair. +This is because if multiple glue patches were "visible" at the same time, it would not be possible to uniquely determine which implementation to choose. +However, you can swap patches when moving to another scope (module). + +```erg +NumericStr = Inherit Str +NumericStr. + ... + +NumStrRev = Patch NumericStr, Impl: Reverse +NumStrRev. + ... +# DuplicatePatchError: NumericStr is already associated with `Reverse` +# hint: `Str` (superclass of `NumericStr`) is associated with `Reverse` by `StrReverse` +``` + +## Appendix: Relationship to Rust's Trait + +Erg patches are the equivalent of Rust's (retrofitted) `impl` blocks. + +```rust +// Rust +trait Reverse { + fn reverse(self) -> Self; +} + +impl Reverse for String { + fn reverse(self) -> Self { + self.chars().rev().collect() + } +} +``` + +You could say that Rust's traits are features of Erg's traits and patches. This makes Rust's traits sound more convenient, but that is not necessarily the case. + +```erg +# Erg +Reverse = Trait { + .reverse = Self.() -> Self +} + +StrReverse = Patch(Str, Impl: Reverse) +StrReverse. + reverse self = + self.iter().rev().collect(Str) +``` + +Because the `impl` block is objectized as a patch in Erg, selective inclusion is possible when importing from other modules. As a side-effect, it also allows implementation of external traits to external structures. +Also, syntaxes such as `dyn trait` and `impl trait` are no longer required by the structure type. + +```erg +# Erg +reversible: [Reverse; 2] = [[1, 2, 3], "hello"] + +iter|T|(i: Iterable T): Iterator T = i.iter() +``` + +```rust +// Rust +let reversible: [Box; 2] = [Box::new([1, 2, 3]), Box::new("hello")]; + +fn iter(i: I) -> impl Iterator where I: IntoIterator { + i.into_iter() +} +``` + +## For-All Patch + +A patch can be defined not only for one specific type, but also for "function types in general" and so on. +In this case, the term to which the degree of freedom is to be given is given as an argument (in the case below, `T: Type`). A patch defined in this way is called an all-symmetric patch. +As you can see, an all-symmetric patch is precisely a function that returns a patch, but it can also be considered a patch in its own right. + +```erg +FnType T: Type = Patch(T -> T) +FnType(T). + type = T + +assert (Int -> Int).type == Int +``` + +## Structural Patch + +In addition, patches can be defined for any type that satisfies a certain structure. +However, this has a lower priority than nominal patches and class methods. + +Careful design should be used when defining structural patches, as some properties are lost by extension, such as the following. + +```erg +# This should not be `Structural` +Norm = Structural Patch {x = Int; y = Int} +Norm. + norm self = self::x**2 + self::y**2 + +Point2D = Class {x = Int; y = Int} +assert Point2D.new({x = 1; y = 2}).norm() == 5 + +Point3D = Class {x = Int; y = Int; z = Int} +assert Point3D.new({x = 1; y = 2; z = 3}).norm() == 14 # AssertionError: +``` + +

+ Previous | Next +

diff --git a/doc/EN/syntax/type/08_value.md b/doc/EN/syntax/type/08_value.md new file mode 100644 index 00000000..21db678b --- /dev/null +++ b/doc/EN/syntax/type/08_value.md @@ -0,0 +1,37 @@ +# Value Type + +Value types are Erg built-in types that can be evaluated at compile time, specifically: + +``` erg +Value = ( + Int + or Nat + or Ratio + or Float + or Complex + or Bool + or Str + or NoneType + or Array Const + or Tuple Const + or Set Const + or ConstFunc(Const, _) + or ConstProc(Const, _) + or ConstMethod(Const, _) +) +``` + +Value-type objects, constants, and compile-time subroutines applied to them are called __constant expressions__. + +``` erg +1, 1.0, 1+2im, True, None, "aaa", [1, 2, 3], Fib(12) +``` + +Be careful with subroutines. Subroutines may or may not be value types. +Since the substance of a subroutine is just a pointer, it can be treated as a value [1](#1), but when compiling something that is not a subroutine cannot be used in a constant context. is not a value type because it doesn't make much sense. + +Types classified as value types may be added in the future. + +--- + +1 The term "value type" in Erg differs from the definition in other languages. There is no concept of memory within pure Erg semantics, and it is incorrect to state that it is a value type because it is placed on the stack, or that it is not a value type because it is actually a pointer. A value type only means that it is a `Value` type or its subtypes. [↩](#f1) diff --git a/doc/EN/syntax/type/09_attributive.md b/doc/EN/syntax/type/09_attributive.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/EN/syntax/type/10_interval.md b/doc/EN/syntax/type/10_interval.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/EN/syntax/type/11_enum.md b/doc/EN/syntax/type/11_enum.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/EN/syntax/type/12_refinement.md b/doc/EN/syntax/type/12_refinement.md new file mode 100644 index 00000000..2aac3db8 --- /dev/null +++ b/doc/EN/syntax/type/12_refinement.md @@ -0,0 +1,75 @@ +# Refinement Type + +Refinement type is a type constrained by a predicate expression. Enumeration types and interval types are syntax sugar of refinement types. + +The standard form of a refinement type is `{Elem: Type | (Pred)*}`. This means that the type is a type whose elements are `Elem` satisfying `Pred`. +The type that can be used for the sifting type is [Const type](./advanced/const.md) only. + +```erg +Nat = 0.. _ +Odd = {N: Int | N % 2 == 1} +Char = StrWithLen 1 +# StrWithLen 1 == {_: StrWithLen N | N == 1} +[Int; 3] == {_: Array Int, N | N == 3} +Array3OrMore == {A: Array _, N | N >= 3} +``` + +When there are multiple preds, they can be separated by `;` or `and` or `or`. `;` and `and` mean the same thing. + +The elements of `Odd` are `1, 3, 5, 7, 9, ...`. +It is called a refinement type because it is a type whose elements are part of an existing type as if it were a refinement. + +The `Pred` is called a (left-hand side) predicate expression. Like assignment expressions, it does not return a meaningful value, and only a pattern can be placed on the left-hand side. +That is, expressions such as `X**2 - 5X + 6 == 0` cannot be used as refinement-type predicate expressions. In this respect, it differs from a right-hand-side predicate expression. + +```erg +{X: Int | X**2 - 5X + 6 == 0} # SyntaxError: the predicate form is invalid. Only names can be on the left-hand side +``` + +If you know how to solve quadratic equations, you would expect the above refinement form to be equivalent to `{2, 3}`. +However, the Erg compiler has very little knowledge of algebra, so it cannot solve the predicate on the right. + +## Smart Cast + +It's nice that you defined `Odd`, but as it is, it doesn't look like it can be used much outside of literals. To promote an odd number in a normal `Int` object to `Odd`, i.e., to downcast an `Int` to `Odd`, you need to pass the constructor of `Odd`. +For refinement types, the normal constructor `.new` may panic, and there is an auxiliary constructor called `.try_new` that returns a `Result` type. + +```erg +i = Odd.new (0..10).sample!() +i: Odd # or Panic +``` + +It can also be used as a type specification in `match`. + +```erg +# i: 0..10 +i = (0..10).sample! +match i: + o: Odd -> + log "i: Odd" + n: Nat -> # 0..10 < Nat + log "i: Nat" +``` + +However, Erg cannot currently make sub-decisions such as `Even` because it was not `Odd`, etc. + +## Enumerated, Interval and Sift Types + +The enumerative/interval types introduced before are syntax sugar of the refinement type. +`{a, b, ...}` is `{I: Typeof(a) | I == a or I == b or ... }`, and `a..b` is desugarized to `{I: Typeof(a) | I >= a and I <= b}`. + +```erg +{1, 2} == {I: Int | I == 1 or I == 2} +1..10 == {I: Int | I >= 1 and I <= 10} +1... <10 == {I: Int | I >= 1 and I < 10} +``` + +## Refinement pattern + +Just as `_: {X}` can be rewritten as `X` (constant pattern), `_: {X: T | Pred}` can be rewritten as `X: T | Pred`. + +```erg +# method `.m` is defined for arrays of length 3 or greater +Array(T, N | N >= 3) + .m(&self) = ... +``` diff --git a/doc/EN/syntax/type/13_algebraic.md b/doc/EN/syntax/type/13_algebraic.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/EN/syntax/type/14_dependent.md b/doc/EN/syntax/type/14_dependent.md new file mode 100644 index 00000000..29ebd717 --- /dev/null +++ b/doc/EN/syntax/type/14_dependent.md @@ -0,0 +1,74 @@ +# Dependent Types + +Dependent types are one of the most important features of Erg. +Dependent types are types that take values as arguments. Normal polymorphic types can take only types as arguments, but dependent types loosen that restriction. + +Dependent types, such as `[T; N]`(`Array(T, N)`), are equivalent. +This type depends not only on the type `T` of the contents, but also on the number `N` of the contents. `N` contains objects of type `Nat`. + +```erg +a1 = [1, 2, 3]. +assert a1 in [Nat; 3]. +a2 = [4, 5, 6, 7] +assert a1 in [Nat; 4]. +assert a1 + a2 in [Nat; 7]. +``` + +If the type object passed as a function argument is related to a return type, write the following + +```erg +narray: |N: Nat| {N} -> [{N}; N] +narray(N: Nat): [N; N] = [N; N] +assert narray(3) == [3, 3, 3]. +``` + +When defining a dependent type, all type arguments must be constants. + +Dependent types already exist in some languages, but Erg has the unique feature of allowing you to define procedural methods on dependent types. + +```erg +x = 1 +f x = + print! f::x, module::x + +# Phantom types have an attribute called Phantom that has the same value as the type argument +T X: Int = Class Impl: Phantom X +T(X). + x self = self::Phantom + +T(1).x() # 1 +``` + +Type arguments of variable-dependent types can be transitioned by applying methods. +Transitions are specified with `~>`. + +```erg +# Note that `Id` is an immutable type and cannot be transitioned. +VM!(State: {"stopped", "running"}! |= _, Id: Nat |= _) = Class(... State). +VM!(). + # Variables that do not change can be omitted by passing `_`. + start! ref! self("stopped" ~> "running") = + self.initialize_something! + self::set_phantom!("running") + +# You can also cut out each type argument (only within the defined module) +VM!.new() = VM!(!" stopped", 1).new() +VM!("running" ~> "running").stop! ref! self = + self.close_something!() + self::set_phantom!("stopped")) + +vm = VM!.new() +vm.start!() +vm.stop!() +vm.stop!() # TypeError: VM! stopped", 1) doesn't have .stop! +# TypeError: VM! running", 1) has .stop! +``` + +You can also create dependent types by incorporating or inheriting from existing types. + +```erg +MyArray(T, N) = Inherit [T; N]. + +# type of self: Self(T, N) in conjunction with .array! +MyStruct!(T, N: Nat!) = Class {.array: [T; !N]} +``` diff --git a/doc/EN/syntax/type/15_quantified.md b/doc/EN/syntax/type/15_quantified.md new file mode 100644 index 00000000..368aaaf5 --- /dev/null +++ b/doc/EN/syntax/type/15_quantified.md @@ -0,0 +1,280 @@ +# Type Variable, quantified type + +A type variable is a variable used, for example, to specify the type of subroutine arguments, and its type is arbitrary (not monomorphic). +First, as motivation for introducing type variables, consider the `id` function, which returns input as is. + +```erg +id x: Int = x +``` + +The `id` function that returns the input as is is defined for the type `Int`, but this function can obviously be defined for any type. +Let's use `Object` for the largest class. + +```erg +id x: Object = x + +i = id 1 +s = id "foo" +b = id True +``` + +Sure, it now accepts arbitrary types, but there is one problem: the return type is expanded to `Object`. The return type is expanded to `Object`. +I would like to see the return type `Int` if the input is of type `Int`, and `Str` if it is of type `Str`. + +```erg +print! id 1 # +id(1) + 1 # TypeError: cannot add `Object` and `Int +``` + +To ensure that the type of the input is the same as the type of the return value, use a __type variable__. +Type variables are declared in `||`(type variable list). + +```erg +id|T: Type| x: T = x +assert id(1) == 1 +assert id("foo") == "foo" +assert id(True) == True +``` + +This is called the __universal quantification (universalization)__ of the function. There are minor differences, but it corresponds to the function called generics in other languages. A universalized function is called a __polymorphic function__. +Defining a polymorphic function is like defining a function of the same form for all types (Erg prohibits overloading, so the code below cannot really be written). + +```erg +id|T: Type| x: T = x +# pseudo code +id x: Int = x +id x: Str = x +id x: Bool = x +id x: Ratio = x +id x: NoneType = x +... +``` + +Also, the type variable `T` can be inferred to be of type `Type` since it is used in the type specification. So `|T: Type|` can simply be abbreviated to `|T|`. +You can also omit `|T, N| foo: [T; N]` if it can be inferred to be other than a type object (`T: Type, N: Nat`). + +You can also provide constraints if the type is too large for an arbitrary type. +Constraints also have advantages, for example, a subtype specification allows certain methods to be used. + +```erg +# T <: Add +# => T is a subclass of Add +# => can do addition +add|T <: Add| l: T, r: T = l + r +``` + +In this example, `T` is required to be a subclass of type `Add`, and the actual types of `l` and `r` to be assigned must be the same. +In this case, `T` is satisfied by `Int`, `Ratio`, etc. So, the addition of `Int` and `Str`, for example, is not defined and is therefore rejected. + +You can also type it like this. + +```erg +f| + Y, Z: Type + X <: Add Y, O1 + O1 <: Add Z, O2 + O2 <: Add X, _ +| x: X, y: Y, z: Z = + x + y + z + x +``` + +If the annotation list is long, you may want to pre-declare it. + +```erg +f: |Y, Z: Type, X <: Add(Y, O1), O1 <: Add(Z, O2), O2 <: Add(X, O3)| (X, Y, Z) -> O3 +f|X, Y, Z| x: X, y: Y, z: Z = + x + y + z + x +``` + +Unlike many languages with generics, all declared type variables must be used either in the temporary argument list (the `x: X, y: Y, z: Z` part) or in the arguments of other type variables. +This is a requirement from Erg's language design that all type variables are inferrable from real arguments. +So information that cannot be inferred, such as the return type, is passed from real arguments; Erg allows types to be passed from real arguments. + +```erg +Iterator T = Trait { + # Passing return types from arguments. + # .collect: |K: Type -> Type| Self(T). ({K}) -> K(T) + .collect(self(T), K: Type -> Type): K(T) = ... + ... +} + +it = [1, 2, 3].iter().map i -> i + 1 +it.collect(Array) # [2, 3, 4]. +``` + +Type variables can only be declared during `||`. However, once declared, they can be used anywhere until they exit scope. + +```erg +f|X|(x: X): () = + y: X = x.clone() + log X.__name__ + log X + +f 1 +# Int +# +``` + +You can also explicitly monophasize at the time of use as follows + +```erg +f: Int -> Int = id|Int| +``` + +In that case, the specified type takes precedence over the type of the actual argument (failure to match will result in a type error that the type of the actual argument is wrong). +That is, if the actual object passed can be converted to the specified type, it will be converted; otherwise, a compile error will result. + +```erg +assert id(1) == 1 +assert id|Int|(1) in Int +assert id|Ratio|(1) in Ratio +# You can also use keyword arguments +assert id|T: Int|(1) == 1 +id|Int|("str") # TypeError: id|Int| is type `Int -> Int` but got Str +``` + +When this syntax is batting against comprehensions, you need to enclose it in `()`. + +```erg +# {id|Int| x | x <- 1..10} would be interpreted as {id | ...} will be interpreted as. +{(id|Int| x) | x <- 1..10} +``` + +A type variable cannot be declared with the same name as a type that already exists. This is because all type variables are constants. + +```erg +I: Type +# ↓ invalid type variable, already exists +f|I: Type| ... = ... +``` + +## Type arguments in method definitions + +Type arguments on the left-hand side are treated as bound variables by default. + +```erg +K(T: Type, N: Nat) = ... +K(T, N). + foo(x) = ... +``` + +Using another type variable name will result in a warning. + +```erg +K(T: Type, N: Nat) = ... +K(U, M). # Warning: K's type variable names are 'T' and 'N' + foo(x) = ... +``` + +Constants are the same in all namespaces since their definition, so of course they cannot be used for type variable names. + +```erg +N = 1 +K(N: Nat) = ... # NameError: N is already defined + +L(M: Nat) = ... +# Defined only if M == N == 1 +L(N). + foo(self, x) = ... +# defined for any M: Nat +L(M). + .bar(self, x) = ... +``` + +You cannot have multiple definitions for each type argument, but you can define methods with the same name because there is no relationship between dependent types that are not assigned type arguments (non-primitive-kind) and dependent types that are assigned (primitive-kind). + +```erg +K(I: Int) = ... +K. + # K is not a true type (atomic Kind), so we cannot define a method + # This is not a method (more like a static method) + foo(x) = ... +K(0). + foo(self, x): Nat = ... +``` + +## All symmetric types + +The `id` function defined in the previous section is a function that can be of any type. So what is the type of the `id` function itself? + +```erg +print! classof(id) # |T: Type| T -> T +``` + +We get a type `|T: Type| T -> T`. This is called a __closed universal quantified type/universal type__, which is `['a. ...]'` in ML, and `forall t. ...` in Haskell. Why the adjective "closed" is used is discussed below. + +There is a restriction on the closed universal quantified type: only subroutine types can be made universal quantified, i.e., only subroutine types can be placed in the left clause. But this is sufficient, since subroutines are the most basic control structure in Erg, so when we say "I want to handle arbitrary X," i.e., I want a subroutine that can handle arbitrary X. So, the quantified type has the same meaning as the polymorphic function type. From now on, this type is basically called polymorphic function type. + +Like anonymous functions, polymorphic types have arbitrary type variable names, but they all have the same value. + +```erg +assert (|T: Type| T -> T) == (|U: Type| U -> U) +``` + +The equality is satisfied when there is an alpha equivalence, as in the lambda calculus. Since there are some restrictions on operations on types, equivalence determination is always possible (if we don't consider the stoppage property). + +## Subtyping of Polymorphic Function Types + +A polymorphic function type can be any function type. This means that there is a subtype relationship with any function type. Let's look at this relationship in detail. + +A type in which the type variable is defined on the left-hand side and used on the right-hand side, such as `OpenFn T: Type = T -> T`, is called an __open universal type__. +In contrast, a type in which type variables are defined and used on the right-hand side, such as `ClosedFn = |T: Type| T -> T`, is called a __closed universal type__. + +An open universal type is a supertype of all isomorphic "true" types. In contrast, a closed universal type is a subtype of all isomorphic true types. + +```erg +(|T: Type| T -> T) < (Int -> Int) < (T -> T) +``` + +You may remember that closed ones are smaller/open ones are larger. +But why is this so? For a better understanding, let's consider an instance of each. + +```erg +# id: |T: Type| T -> T +id|T|(x: T): T = x + +# iid: Int -> Int +iid(x: Int): Int = x + +# return arbitrary function as is +id_arbitrary_fn|T|(f1: T -> T): (T -> T) = f +# id_arbitrary_fn(id) == id +# id_arbitrary_fn(iid) == iid + +# return the poly correlation number as it is +id_poly_fn(f2: (|T| T -> T)): (|T| T -> T) = f +# id_poly_fn(id) == id +id_poly_fn(iid) # TypeError + +# Return Int type function as is +id_int_fn(f3: Int -> Int): (Int -> Int) = f +# id_int_fn(id) == id|Int| +# id_int_fn(iid) == iid +``` + +Since `id`, which is of type `|T: Type| T -> T`, can be assigned to a parameter `f3` of type `Int -> Int`, we may consider `(|T| T -> T) < (Int -> Int)`. +Conversely, `iid`, which is of type `Int -> Int`, cannot be assigned to parameter `f2` of type `(|T| T -> T)`, but it can be assigned to parameter `f1` of type `T -> T`, so `(Int -> Int) < (T -> T)`. +Therefore, it is indeed `(|T| T -> T) < (Int -> Int) < (T -> T)`. + +## Quantified Types and Dependent Types + +What is the relationship between dependent types and quantified types (polymorphic function types) and what is the difference between them? +We can say that a dependent type is a type that takes arguments, and an quantified type is a type that gives arbitrariness to the arguments. + +The important point is that there are no type arguments in the closed, polymorphic type itself. For example, the polymorphic function type `|T| T -> T` is a type that takes a polymorphic function __only__, and its definition is closed. You cannot define methods, etc. using its type argument `T`. + +In Erg, the type itself is also a value, so types that take arguments, such as function types, will probably be dependent types. In other words, polymorphic function types are both a quantified type and a dependent type. + +```erg +PolyFn = Patch(|T| T -> T) +PolyFn. + type self = T # NameError: cannot find 'T' +DepFn T = Patch(T -> T) +DepFn. + type self = + log "by DepFn" + T + +assert (Int -> Int).type() == Int # by DepFn +assert DepFn(Int).type() == Int # by DepFn +``` diff --git a/doc/EN/tips.md b/doc/EN/tips.md new file mode 100644 index 00000000..99dc5709 --- /dev/null +++ b/doc/EN/tips.md @@ -0,0 +1,135 @@ +# Tips + +## Want to change the language in which errors are displayed + +Please download Erg for your language. +However, external libraries may not support multiple languages. + +## Want to change only certain attributes of a record + +```erg +record: {.name = Str; .age = Nat; .height = CentiMeter} +{height; rest; ...} = record +mut_record = {.height = !height; ...rest} +``` + +## Want to shadow variables + +Shadowing in the same scope is not possible with Erg. However, you can redefine them if the scope changes (This is a syntax called instance block). + +````erg +## Get a T!-type object and finally assign it to a variable as type T +x: T = + x: T! = foo() + x.bar!() + x.freeze() +```` + +## Want to reuse a final class (non-inheritable class) somehow + +You can create a wrapper class. This is a so-called composition pattern. + +```erg +FinalWrapper = Class {inner = FinalClass} +FinalWrapper. + method self = + self::inner.method() + ... +``` + +## Want to use an enumerated type that is not a string + +You can define a traditional enumerated type (algebraic data type) commonly found in other languages as follows +If you implement `Singleton`, classes and instances are identical. +Also, if you use `Enum`, the type of choice is automatically defined as a redirect attribute. + +```erg +Ok = Class Impl: Singleton +Err = Class Impl: Singleton +ErrWithInfo = Inherit {info = Str} +Status = Enum Ok, Err, ErrWithInfo +stat: Status = Status.cons(ErrWithInfo) {info = "error caused by ..."} +match! stat: + Status.Ok -> ... + Status.Err -> ... + Status.ErrWithInfo.{info;} -> ... +``` + +```erg +Status = Enum Ok, Err, ErrWithInfo +# is equivalent to +Status = Class Ok or Err or ErrWithInfo +Status. + Ok = Ok + Err = Err + ErrWithInfo = ErrWithInfo +``` + +## I want to enumerate at the beginning of 1 + +method 1: + +```erg +arr = [...] +for! arr.iter().enumerate(start: 1), i => + ... +``` + +method 2: + +```erg +arr = [...] +for! arr.iter().zip(1...) , i => + ... +``` + +## Want to test a (white box) non-public API + +The private API in `foo.er` is specially accessible in the module `foo.test.er`. +The `foo.test.er` module cannot be imported, so it remains hidden. + +```erg +# foo.er +private x = ... +``` + +```erg +# foo.test.er +foo = import "foo" + +@Test +'testing private' x = + ... + y = foo::private x + ... +``` + +## Want to define a (variable) attribute that is read-only from the outside + +You can make the attribute private and define a getter. + +```erg +C = Class {v = Int!} +C:: + inc_v!(ref! self) = self::v.inc!() + ... +C. + get_v(ref self): Int = self::v.freeze() + ... +``` + +## Want the argument names to be identified on the type system + +You can receive arguments by record. + +```erg +Point = {x = Int; y = Int} + +norm: Point -> Int +norm({x: Int; y: Int}): Int = x**2 + y**2 +assert norm({x = 1; y = 2}) == norm({y = 2; x = 1}) +``` + +## Want to stop warnings + +There is no option in Erg to stop warnings (this is by design). Please rewrite your code. diff --git a/doc/JA/API/consts.md b/doc/JA/API/consts.md new file mode 100644 index 00000000..60453cd8 --- /dev/null +++ b/doc/JA/API/consts.md @@ -0,0 +1,13 @@ +# 組み込み定数 + +## True + +## False + +## None + +## Ellipsis + +## NotImplemented + +## Inf diff --git a/doc/JA/API/funcs.md b/doc/JA/API/funcs.md new file mode 100644 index 00000000..3c99f88d --- /dev/null +++ b/doc/JA/API/funcs.md @@ -0,0 +1,121 @@ +# 関数 + +## 基本関数 + +### if|T; U|(cond: Bool, then: T, else: U) -> T or U + +### map|T; U|(i: Iterable T, f: T -> U) -> Map U + +Pythonとは引数の順番が逆なので注意。 + +### log(x: Object, type: LogType = Info) -> None + +`x`をデバッグ表示でログに残す。ログは、実行が終了した後にまとめて表示される。 +絵文字対応ターミナルでは`type`に応じてプレフィックスがつく。 + +* type == Info: 💬 +* type == Ok: ✅ +* type == Warn: ⚠️ +* type == Hint: 💡 + +### panic(msg: Str) -> Panic + +msgを表示して停止する。 +絵文字対応ターミナルでは🚨がプレフィックスに付く。 + +### discard|T|(x: ...T) -> NoneType + +`x`を捨てる。戻り値を使用しないときなどに使う。`del`とは違い、変数`x`を参照できなくするわけではない。 + +```erg +p! x = + # q!は何らかのNoneや()でない値を返すとする + # 要らない場合は`discard`を使う + discard q!(x) + f x + +discard True +assert True # OK +``` + +### import(path: Path) -> Module or CompilerPanic + +モジュールをインポートする。モジュールが見つからない場合、コンパイルエラーを送出する。 + +### eval(code: Str) -> Object + +codeをコードとして評価し返す。 + +### classof(object: Object) -> Class + +`object`のクラスを返す。 +ただしクラスは比較できないため、インスタンス判定がしたい場合は`classof(object) == Class`ではなく`object in Class`を使う。 +コンパイル時に決定される構造型は`Typeof`で得られる。 + +## Iterator, Array生成系 + +### repeat|T|(x: T) -> RepeatIterator T + +```erg +rep = repeat 1 # Repeater(1) +for! rep, i => + print! i +# 1 1 1 1 1 ... +``` + +### dup|T; N|(x: T, N: Nat) -> [T; N] + +```erg +[a, b, c] = dup new(), 3 +print! a # +print! a == b # False +``` + +### cycle|T|(it: Iterable T) -> CycleIterator T + +```erg +cycle([0, 1]).take 4 # [0, 1, 0, 1] +cycle("hello").take 3 # "hellohellohello" +``` + +## 定数式関数 + +### Class + +クラスを新しく生成する。`Inherit`とは違い、`Class`を通すとベース型からは独立し、メソッドは失われる。 +比較もできなくなるが、パターンマッチなどは行える。 + +```erg +C = Class {i = Int} +NewInt = Class Int +Months = Class 1..12 +jan = Months.new(1) +jan + Months.new(2) # TypeError: `+` is not implemented for 'Months' +match jan: + 1 -> log "January" + _ -> log "Other" +``` + +第二引数のImplは実装するトレイトである。 + +### Inherit + +クラスを継承する。基底クラスのメソッドをそのまま使用できる。 + +### Trait + +トレイトを新しく生成する。現在のところ、指定できるのはレコード型のみ。 + +### Typeof + +引数の型を返す。実行時のクラスを得たい場合は`classof`を使う。 +型指定に使うとWarningが出る。 + +```erg +x: Typeof i = ... +# TypeWarning: Typeof(i) == Int, please replace it +``` + +### Deprecated + +デコレータとして使用する。型や関数が非推奨であると警告する。 diff --git a/doc/JA/API/index.md b/doc/JA/API/index.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/API/modules/external/alstruct.md b/doc/JA/API/modules/external/alstruct.md new file mode 100644 index 00000000..7542d9f3 --- /dev/null +++ b/doc/JA/API/modules/external/alstruct.md @@ -0,0 +1,57 @@ +# alstruct + +代数的構造を表すトレイトや、それにかかるパッチを提供するモジュール。 + +* member + +## BinOp + +```erg +BinOp Op: Kind 2 = Subsume Op(Self, Self.ReturnTypeOf Op), Additional: { + .ReturnTypeof = TraitType -> Type +} + +Nat <: BinOp Add +assert Nat.ReturnTypeof(Add) == Nat +assert Nat.ReturnTypeof(Sub) == Int +assert Nat.ReturnTypeof(Mul) == Nat +assert Nat.ReturnTypeof(Div) == Positive Ratio +``` + +## SemiGroup + +```erg +SemiGroup Op: Kind 2 = Op(Self, Self) + +IntIsSemiGroupAdd = Patch Int, Impl=SemiGroup Add + +Int <: SemiGroup Add +``` + +## Functor + +```erg +## * Identity law: x.map(id) == x +## * Composition law: x.map(f).map(g) == x.map(f.then g) +Functor = Trait { + .map|T, U: Type| = (Self(T), T -> U) -> Self U +} +``` + +## Applicative + +```erg +## * Identity law: x.app(X.pure(id)) == x +Applicative = Subsume Functor, Additional: { + .pure|T: Type| = T -> Self T + .app|T, U: Type| = (Self(T), Self(T -> U)) -> Self U +} +``` + +## Monad + +```erg +Monad = Subsume Applicative, Additional: { + .bind|T, U: Type| = (Self(T), T -> Self U) -> Self U +} +``` diff --git a/doc/JA/API/modules/repl.md b/doc/JA/API/modules/repl.md new file mode 100644 index 00000000..f2ee4894 --- /dev/null +++ b/doc/JA/API/modules/repl.md @@ -0,0 +1,24 @@ +# module `repl` + +provides REPL(Read-Eval-Print-Loop)-related APIs. + +## functions + +* `gui_help` + +オブジェクトに関する情報をブラウザで表示する。オフラインでも使用可能。 + +## types + +### Guess = Object + +#### methods + +* `.guess` + +与えられた引数と戻り値から、関数を推測する。 + +```erg +1.guess((1,), 2) # +[1, 2].guess((3, 4), [1, 2, 3, 4]) # +``` diff --git a/doc/JA/API/modules/status.md b/doc/JA/API/modules/status.md new file mode 100644 index 00000000..345da026 --- /dev/null +++ b/doc/JA/API/modules/status.md @@ -0,0 +1,6 @@ +# module `status` + +状態を表す型が定義されています。状況に応じて選択肢を外したりして使用してください。 + +* ExecResult = {"success", "warning", "failure", "fatal", "unknown"} +* ExecStatus = {"ready", "running", "sleeping", "plague", "completed", "terminated"} diff --git a/doc/JA/API/modules/unit.md b/doc/JA/API/modules/unit.md new file mode 100644 index 00000000..fa14eac6 --- /dev/null +++ b/doc/JA/API/modules/unit.md @@ -0,0 +1,73 @@ +# module `unit` + +`unit`モジュールは数値計算でよく使われる単位を型として定義したモジュールです。 +Ergの数値型は`Nat`, `Int`, `Ratio`などがあります。しかしこれらの型は「何を意味する数値なのか」という情報を持っておらず、メートルとヤード同士の足し算などといったナンセンスな計算を行えてしまいます。 +`unit`モジュールを使うことにより、単位の違う数値を関数に渡すといったミスを防げます。 +このようなミスは実際に起っており、[単位系の取り間違いで火星探査機が行方不明](http://www.sydrose.com/case100/287/)になるなど、深刻なバグを引き起こしかねません。 +数値計算を行う上でコードの堅牢性を高めたいならばこのモジュールを使用しておくべきです。 + +```erg +{*} = import "unit" + +x = 6m # `x = Meter.new(6)`と等価 +t = 3s # `t = Sec.new(3)`と等価 +# m/sは速度の単位オブジェクトで、Velocity型 +print! x/t # 2m/s +print! x + 4m # 10m +print! x + 2s # TypeError: `+`(Meter, Sec) is not implemented +``` + +`m`, `s`, `m/s`というオブジェクトは単位オブジェクトと呼ばれます。それ自体で1m, 1s, 1m/sという意味を持ちます。`m/s`はmとsの合成によって生まれた単位オブジェクトといえます。 + +unitでは以下の単位を型として定義しています。SI(国際単位系)と呼ばれるものです。 + +* 長さ:Meter(単位定数: m) +* 質量:KiloGram(単位定数: kg, g = 0.001kg) +* 時間:Sec (分、時間、日、年などはSecから生成されたminute, hour, day, yearなどの定数がある) +* 電流: Amper(単位定数: a) +* 温度: Kelvin(単位定数: k, Fahren, Celsius型もあり、相互変換可能) +* 物質量: Mol(単位定数: mol) +* 光度: Candela(単位定数: cd) + +また、`Unit1`, `UnitMul`, `UnitDiv`という型が定義されており、これを使用して基本型を合成し新しい単位を作成する事が可能です。 +例えば、振動数の単位ヘルツ(hertz)は振動周期(秒)の逆数で定義されているので、`UnitDiv(Unit1, Sec)`です。 +この型を意味のある型とみなしたい(専用のメソッドを加えたい、など)ときは、[パッチ](./../../syntax/type/07_patch.md)を作成すると良いでしょう。 + +```erg +Hertz = Patch UnitDiv(Unit1, Sec) +SquareMeter = Patch UnitMul(Meter, Meter) +``` + +補助単位も予めいくつか定義されています。 + +* 振動数: Hertz(hz) +* 力: Newton(newton) +* エネルギー: Joule(j) +* 仕事率: Watt(w) +* 電位: Volt(v) +* 電気抵抗: Ohm(ohm) +* 速度: Velocity(m/s) +* 面積: SquareMeter(m**2) +* 体積: CubicMeter(m**3) (litre = 10e-3 m**3) +* 角度: Degree(deg) (rad = 180/pi deg) +* 長さ: Feet, Yard, Inch, Mile, Ly, Au, Angstrom +* 重さ: Pond + +また、接頭辞も定義しています。 + +* Femto = 1e-15 +* Pico = 1e-12 +* Nano = 1e-9 +* Micro = 1e-6 +* Milli = 1e-3 +* Centi = 1e-2 +* Deci = 1e-1 +* Hecto = 1e+2 +* Kilo = 1e+3 +* Mega = 1e+6 +* Giga = 1e+9 +* Tera = 1e+12 +* Peta = 1e+15 +* Exa = 1e+18 + +※名前の由来に反して、Ergでは基本的にMKS単位系を採用しています。CGS単位系のunitモジュールが欲しい場合は、外部ライブラリ([cgs](https://github.com/mtshiba/cgs)等)を使用してください。 diff --git a/doc/JA/API/modules/unsound.md b/doc/JA/API/modules/unsound.md new file mode 100644 index 00000000..59bc56b7 --- /dev/null +++ b/doc/JA/API/modules/unsound.md @@ -0,0 +1,24 @@ +# module `unsound` + +Provides APIs perform unsound and unsafe operations that cannot be guaranteed safe in Erg's type system. + +## `unsafe!` + +Executes a `Unsafe` procedure. Just like Rust, `Unsafe` APIs cannot be called directly, but are all passed as higher-order functions to this procedure. + +```erg +unsound = import "unsound" + +i = unsound.unsafe! do!: + # convert `Result Int` to `Int` + unsound.transmute input!().try_into(Int), Int +``` + +## transmute + +第1引数のオブジェクトを第2引数の型へ変換します。型チェックは行われません。 +この関数は型システムの型安全性を損ないます。使用の際はバリデーション等を行ってください。 + +## auto_transmute + +`transmute`とは違い、期待される型に自動で変換します。Ocamlの`Obj.magic`と同じ働きをします。 diff --git a/doc/JA/API/operators.md b/doc/JA/API/operators.md new file mode 100644 index 00000000..66dbade4 --- /dev/null +++ b/doc/JA/API/operators.md @@ -0,0 +1,64 @@ +# 演算子 + +## 中置演算子 + +### `_+_`|R; O; A <: Add(R, O)|(x: A, y: R) -> O + +加算を実行する。 + +### `_-_`|R; O; S <: Sub(R, O)|(x: S, y: R) -> O + +減算を実行する。 + +### `*`|R; O; M <: Mul R, O|(x: M, y: R) -> O + +乗算を実行する。 + +### `/`|R; O; D <: Div(R, O)|(x: D, y: R) -> O + +除算を実行する。 + +## 中置アルファベット演算子 + +### `and`(x: Bool, y: Bool) -> Bool + +and演算を実行する。 + +### `or`(x: Bool, y: Bool) -> Bool + +and演算を実行する。 + +## 前置演算子 + +### `+_`|T <: Num|(x: T) -> T + +デフォルトではidと同じ。 + +### `-_`|T <: Num|(x: T) -> T.Neg + +例えば、Nat.`-`: Nat -> Negとなり、戻り値が違う。 + +### `!`|T <: Immut|(x: T) -> `T!` + +不変オブジェクトから可変オブジェクトを生成する。 +この演算子自体はProceduralではなく、関数内でも使える。 + +### `..`|T <: Ord|(x: T) -> Range T + +x終わりで下界のないRangeオブジェクトを生成する。 +x..xはイテレータとしてxのみ返す。 + +### `..<`|T <: Ord|(x: T) -> Range T + +x.. Range T + +x始まりで上界のないRangeオブジェクトを生成する。 + +### |T <: Ord|(x: T)`<..` -> Range T diff --git a/doc/JA/API/procs.md b/doc/JA/API/procs.md new file mode 100644 index 00000000..852369c9 --- /dev/null +++ b/doc/JA/API/procs.md @@ -0,0 +1,39 @@ +# プロシージャ + +## print! + +```erg +print!(x) -> NoneType +``` + + xを改行ありで返す。 + +## debug! + +```erg +debug!(x, type = Info) -> NoneType +``` + +xを改行ありでデバッグ表示(ファイル名、行数、変数の場合変数名が一緒に表示される)する。リリースモードでは除去される。 +絵文字対応ターミナルではtypeに応じてプレフィックスが付く。 + +* type == Info: 💬 +* type == Ok: ✅ +* type == Warn: ⚠️ +* type == Hint: 💡 + +## for! i: Iterable T, block: T => NoneType + +blockの動作でイテレータを走査する。 + +## while! cond: Bool!, block: () => NoneType + +condがTrueの間、blockを実行する。 + +## Lineno!() -> Nat + +## Filename!() -> Str + +## Namespace!() -> Str + +## Module!() -> Module diff --git a/doc/JA/API/special.md b/doc/JA/API/special.md new file mode 100644 index 00000000..8ded9cc0 --- /dev/null +++ b/doc/JA/API/special.md @@ -0,0 +1,176 @@ + +# 特殊形式(Special form) + +特殊形式は、Ergの型システムでは表現ができない演算子、サブルーチン(のようなもの)である。``で囲っているが、実際は捕捉できない。 +また、`Pattern`や`Body`, `Conv`といった型が便宜上登場するが、そのような型が存在するわけではない。その意味もコンテクストによって異なる。 + +## `=`(pat: Pattern, body: Body) -> NoneType + +bodyをpatに変数として代入する。同じスコープにすでに変数が存在する場合と、patにマッチしなかった場合にエラーを送出する。 +また、レコードの属性定義やデフォルト引数にも使われる。 + +```erg +record = {i = 1; j = 2} +f(x: Int, y = 2) = ... +``` + +bodyが型か関数であるときに`=`は特殊な振る舞いをする。 +左辺の変数名を右辺のオブジェクトに埋め込むのである。 + +```erg +print! Class() # > +print! x: Int -> x + 1 # > +C = Class() +print! c # +f = x: Int -> x + 1 +print! f # +g x: Int = x + 1 +print! g # +K X: Int = Class(...) +print! K # +L = X: Int -> Class(...) +print! L # +``` + +`=`演算子は、戻り値が「未定義」である。 +多重代入、関数中での`=`は文法エラーとなる。 + +```erg +i = j = 1 # SyntaxError: multiple assignments are not allowed +print!(x=1) # SyntaxError: cannot use `=` in function arguments +# hint: did you mean keyword arguments (`x: 1`)? +if True, do: + i = 0 # SyntaxError: A block cannot be terminated by an assignment expression +``` + +## `->`(pat: Pattern, body: Body) -> Func + +無名関数、関数型を生成する。 + +## `=>`(pat: Pattern, body: Body) -> Proc + +無名プロシージャ、プロシージャ型を生成する。 + +## `:`(subject, T) + +subjectがTに合致しているか判定する。合致していない場合はコンパイルエラーを送出する。 + +```erg +a: Int +f x: Int, y: Int = x / y +``` + +また、`:`適用スタイルにも使われる。 + +```erg +f x: + y + z +``` + +`:`も`=`と同じく演算の結果が未定義である。 + +```erg +_ = x: Int # SyntaxError: +print!(x: Int) # SyntaxError: +``` + +## `.`(obj, attr) + +objの属性を読み込む。 +`x.[y, z]`とすると、xのyとzという属性を配列にして返す。 + +## `|>`(obj, c: Callable) + +`c(obj)`を実行する。`x + y |>.foo()`は`(x + y).foo()`と同じ。 + +### (x: Option T)`?` -> T | T + +後置演算子。`x.unwrap()`を呼び出し、エラーの場合はその場で`return`する。 + +## match(obj, ...lambdas: Lambda) + +objについて、パターンにマッチしたlambdaを実行する。 + +```erg +match [1, 2, 3]: + (l: Int) -> log "this is type of Int" + [[a], b] -> log a, b + [...a] -> log a +# (1, 2, 3) +``` + +## del(x: ...T) -> NoneType | T + +変数`x`を削除する。ただし組み込みのオブジェクトは削除できない。 + +```erg +a = 1 +del a # OK + +del True # SyntaxError: cannot delete a built-in object +``` + +## do(body: Body) -> Func + +引数なしの無名関数を生成する。`() ->`の糖衣構文。 + +## do!(body: Body) -> Proc + +引数なしの無名プロシージャを生成する。`() =>`の糖衣構文。 + +## `else`(l, r) -> Choice + +Choiceオブジェクトという2つ組のタプルのような構造体を生成する。 +`l, r`は遅延評価される。すなわち、`.get_then`または`.get_else`が呼ばれたとき初めて式が評価される。 + +```erg +choice = 1 else 2 +assert choice.get_then() == 1 +assert choice.get_else() == 2 +assert True.then(choice) == 1 +``` + +## 集合演算子 + +### `[]`(...objs) + +引数から配列、またはオプション引数からディクトを生成する。 + +### `{}`(...objs) + +引数からセットを生成する。 + +### `{}`(...fields: ((Field, Value); N)) + +レコードを生成する。 + +### `{}`(layout, ...names, ...preds) + +篩型、ランク2型を生成する。 + +### `...` + +入れ子になったコレクションを展開する。パターンマッチでも使える。 + +```erg +[x, ...y] = [1, 2, 3] +assert x == 1 and y == [2, 3] +assert [x, ...y] == [1, 2, 3] +assert [...y, x] == [2, 3, 1] +{x; ...yz} = {x = 1; y = 2; z = 3} +assert x == 1 and yz == {y = 2; z = 3} +assert {x; ...yz} == {x = 1; y = 2; z = 3} +``` + +## 仮想演算子 + +ユーザーが直接使用できない演算子です。 + +### ref(x: T) -> Ref T | T + +オブジェクトの不変参照を返す。 + +### ref!(x: T!) -> Ref! T! | T! + +可変オブジェクトの可変参照を返す。 diff --git a/doc/JA/API/types.md b/doc/JA/API/types.md new file mode 100644 index 00000000..866198c1 --- /dev/null +++ b/doc/JA/API/types.md @@ -0,0 +1,270 @@ +# Erg組み込み型一覧 + +型自体の属性は`.__dict__`の中には格納されていないので、インスタンスからは参照できない + +## 汎用型 (Fundamental types) + +### Object + +* `__dir__`: オブジェクトの持つ属性を配列にして返す(dir関数) +* `__getattribute__`: 属性を取得して返す +* `__hash__`: オブジェクトのハッシュ値を返す +* `__repr__`: オブジェクトの文字列表現(リッチでない/デフォルト実装が存在) +* `__sizeof__`: オブジェクトのサイズ(ヒープに確保された分も含む)を返す + +### Show + +* `__str__`: オブジェクトの文字列表現(リッチな)を返す + +### Fmt + +* `__format__`: フォーマットされた文字列を返す + +### Doc + +* `__doc__`: オブジェクトの説明 + +### Named + +* `__name__`: オブジェクトの名前 + +### Pickle + +* `__reduce__`: Pickleによるオブジェクトのシリアライズを行う +* `__reduce_ex__`: プロトコルバージョンを指定できる__reduce__ + +## オブジェクトシステム + +Trait classはPythonでのABC(抽象基底クラス、インターフェース)に相当 +Instanceに属するのは1, True, "aaa"など +ClassはInt, Bool, Strなど + +### Type + +* `__supers__`: 上位型(`__mro__`は配列だが、こちらはSet) +* `__basicsize__`: +* `__dictoffset__`: Evmではサポートされない +* `__flags__`: +* `__itemsize__`: インスタンスのサイズ(Classでない場合は0) +* `__weakrefoffset__`: Evmではサポートされない +* `__membercheck__`: `ismember(x, T)`と同等 +* `__subtypecheck__`: `issubtype(U, T)`と同等、`__subclasshook__`というエイリアスが存在(CPythonとの互換) + +### Instance + +* `__class__`: インスタンスの生成元クラスを返す(`.new`で生成されたオブジェクトは自動で付く) + +### Class + +* `__mro__`: メソッド解決用の型配列(自身も入っている、最後は必ずObject) +* `__base__`: ベースとなった型(複数ある場合、`__mro__[1]`) +* `__new__`: インスタンスを生成する +* `__init__`: インスタンスを初期化する +* `__init_subclass__`: インスタンスを初期化する +* `__intstancecheck__`: `MyClass.__instancecheck__(x)`などのように使う、`isinstance(x, MyClass)`と同等 +* `__subclasscheck__`: `issubclass(C, MyClass)`と同等 + +## 演算子 + +ここで指定されている以外の演算子には特に専用の型はない + +### Eq + +* `__eq__(self, rhs: Self) -> Bool`: オブジェクトの比較関数(==) +* `__ne__`: オブジェクトの比較関数(!=)、デフォルト実装あり + +### Ord + +* `__lt__(self, rhs: Self) -> Bool`: オブジェクトの比較関数(<) +* `__le__`: オブジェクトの比較関数(<=)、デフォルト実装あり +* `__gt__`: オブジェクトの比較関数(>)、デフォルト実装あり +* `__ge__`: オブジェクトの比較関数(>=)、デフォルト実装あり + +### BinAdd + +* `__add__(self, rhs: Self) -> Self`: `+`を実装 + +### Add R, O + +* `__add__(self, rhs: R) -> O` + +### BinSub + +* `__sub__(self, rhs: Self) -> Self`: `-`を実装 + +### Sub R, O + +* `__sub__(self, rhs: R) -> O` + +### BinMul + +* `__mul__(self, rhs: Self) -> Self`: `*`を実装 +* `__pow__`: `**`を実装(デフォルト実装あり) + +### Mul R, O + +* `__mul__(self, rhs: R) -> O` +* `__pow__` + +### BinDiv + +* `__div__(self, rhs: Self) -> Self`: `/`を実装、0によりパニックしてもよい +* `__mod__`: `%`を実装(デフォルト実装あり) + +### Div R, O + +* `__div__(self, rhs: R) -> O` +* `__mod__` + +## 数値型 + +### Num (= Add and Sub and Mul and Eq) + +Complex以外の例として、Vector, Matrix, TensorはNum(Matrix, Tensorの*はそれぞれdot, productと同じ) + +### Complex (= Inherit(Object, Impl=Num)) + +* `imag: Ratio`: 虚部を返す +* `real: Ratio`: 実部を返す +* `conjugate self -> Complex`: 共役複素数を返す + +### Float (= Inherit(FloatComplex, Impl=Num)) + +### Ratio (= Inherit(Complex, Impl=Num)) + +* `numerator: Int`: 分子を返す +* `denominator: Int`: 分母を返す + +### Int (= Inherit Ratio) + +### Nat (= Inherit Int) + +* `times!`: self回procを実行する + +## その他基本型 + +### Bool + +* `__and__`: +* `__or__`: +* `not`: + +## Str (<: Seq) + +* `capitalize` +* `chomp`: 改行文字を除去 +* `isalnum`: +* `isascii`: +* `isalpha`: +* `isdecimal`: +* `isdight`: +* `isidentifier` +* `islower` +* `isnumeric` +* `isprintable` +* `isspace` +* `istitle` +* `isupper` +* `lower` +* `swapcase` +* `title` +* `upper` + +## その他 + +### Bit + +* `from_bytes`: Bytesから変換 +* `to_bytes`: Bytesへ変換(長さ(length)、エンディアン(byteorder)を指定) +* `bit_length`: Bit長を返す + +### Iterable T + +`Iterator`自体の型ではない点に注意。`Nat`は`Iterable`だが`Nat.next()`とはできず、`Nat.iter().next()`とする必要がある。 + +* `iter`: Iteratorを生成する。 + +### Iterator T + +NatやRangeはIteratorを持っているので、`Nat.iter().map n -> n**2`, `(3..10).iter().fold (sum, n) -> sum + n*2`などが可能。 +allやanyは使用後破壊されるので副作用なし。これらは副作用のない`next`を使って実装されていることになっているが、実行効率のため内部的には`Iterator!.next!`を使っている。 + +* `next`: 先頭の要素と残りのIteratorを返す。 +* `all` +* `any` +* `filter` +* `filter_map` +* `find` +* `find_map` +* `flat_map` +* `flatten` +* `fold` +* `for_each` +* `map` +* `map_while` +* `nth` +* `pos` +* `take` +* `unzip` +* `zip` + +### Iterator! T = Iterator T and ... + +* `next!`: 先頭の要素を取り出す。 + +## SizedIterator T = Iterator T and ... + +有限の要素を走査するIterator。 + +* `len`: +* `chain`: +* `count`: +* `is_empty`: +* `rev`: +* `next_back`: +* `nth_back`: +* `rfind`: +* `rfold`: +* `sum`: +* `max`: +* `min`: + +## Seq T = SizedIterable T and ... + +* `concat`: 2つのSeqを結合する +* `__getitem__`: `[]`によるアクセスと同等(なければパニック) +* `get`: __getitem__と違ってOptionで返す +* `maketrans`: 置換テーブルをつくる(スタティックメソッド) +* `replace`: 置換する +* `translate`: 置換テーブルにそって置換する +* `insert`: idx番目に追加 +* `remove`: idx番目を取り出し +* `prepend`: 先頭に追加 +* `dequeue`: 先頭を取り出し +* `push`: 最後尾に追加 +* `pop`: 最後尾を取り出し +* `dedup`: 連続する値を削除 +* `uniq`: 重複する要素を削除(sort |> dedupで実装されるため、順番が変わる可能性がある) +* `swap`: 要素を入れ替え +* `reverse`: 要素を反転 +* `sort`: 要素をソート +* `first`: +* `last`: + +### Seq! T (= Seq T and ...) + +* `__setitem__!`: +* `__delitem__!`: +* `insert!`: idx番目に追加 +* `remove!`: idx番目を取り出し +* `prepend!`: 先頭に追加 +* `dequeue!`: 先頭を取り出し +* `push!`: 最後尾に追加 +* `pop!`: 最後尾を取り出し +* `dedup!`: 連続する値を削除 +* `uniq!`: 重複する要素を削除(sort! |> dedup!で実装されるため、順番が変わる可能性がある) +* `swap!`: 要素を入れ替え +* `reverse!`: 要素を反転 +* `set!` +* `sort!`: 要素をソート +* `translate!` diff --git a/doc/JA/API/types/classes/Array!(T).md b/doc/JA/API/types/classes/Array!(T).md new file mode 100644 index 00000000..76044b45 --- /dev/null +++ b/doc/JA/API/types/classes/Array!(T).md @@ -0,0 +1,4 @@ +# Array! T + +可変長配列を表す型。コンパイル時に長さがわからない場合に使う。`[T]!`という糖衣構文がある。 +`Array! T = ArrayWithMutLength! T, !_`で定義される。 diff --git a/doc/JA/API/types/classes/Array(T).md b/doc/JA/API/types/classes/Array(T).md new file mode 100644 index 00000000..59cae131 --- /dev/null +++ b/doc/JA/API/types/classes/Array(T).md @@ -0,0 +1,3 @@ +# Array T: Type + +`Array T = ArrayWithLen T, _`で定義される。`[T]`という糖衣構文がある。 diff --git a/doc/JA/API/types/classes/ArrayWithLen(T,N).md b/doc/JA/API/types/classes/ArrayWithLen(T,N).md new file mode 100644 index 00000000..77fc65bb --- /dev/null +++ b/doc/JA/API/types/classes/ArrayWithLen(T,N).md @@ -0,0 +1,34 @@ +# ArrayWithLen T: Type, N: Nat + +`[T; N]`は糖衣構文。長さを省いた[`Array`型](./Array.md)もある。 + +## methods + +* values_at(self, selectors: [Nat; N]) -> [T; N] + +```erg +assert ["a", "b", "c", "d", "e"].values_at([0, 1, 3]) == ["a", "b", "d"] +``` + +* all(self, pred: T -> Bool) -> Bool + 全ての要素がpredを満たすかどうかを返す。 + 要素が0のときはpredに関わらず`True`となるが、Warningを出す。 + この仕様自体は多くの言語で採用されており、論理学的な整合性から要請される。 + + ```erg + assert [].all(_ -> False) + ``` + + ```python + assert all(False for _ in []) + ``` + +## methods of ArrayWithLen T, N | T <: Eq + +* freq self -> [{T: Nat}] + オブジェクトの出現頻度を返す。 + +```erg +assert ["a", "b", "c", "b", "c", "b"].freq() \ +== [{"a", 1}, {"b": 3}, {"c": 2}] +``` diff --git a/doc/JA/API/types/classes/ArrayWithMutLength!(T,N).md b/doc/JA/API/types/classes/ArrayWithMutLength!(T,N).md new file mode 100644 index 00000000..1012d84e --- /dev/null +++ b/doc/JA/API/types/classes/ArrayWithMutLength!(T,N).md @@ -0,0 +1,26 @@ +# ArrayWithMutLength! T: Type, N: Nat! + +コンパイル時に長さのわかる可変長配列。`ArrayWithMutLength(T, !N) == [T; !N]`という糖衣構文もある。 + +## methods + +* push! ref! self(N ~> N+1, ...), elem: T + +* pop! ref! (N ~> N-1, ...) -> T + +* sample!(ref! self) -> T +* sample! ref! self, M: Nat -> [T; M] + 中の要素をランダムに選んでコピーを返す。 + +* shuffle!(ref! self) + 中身をシャッフルする。 + +* assert_len ref! self(_ ~> N, ...), N: Nat -> () or Panic + 長さを検証する。 + 長さが不正な場合は`panic!`する。 + +## Impl + +* From Range Int +* From [T; N] +* Num diff --git a/doc/JA/API/types/classes/Class.md b/doc/JA/API/types/classes/Class.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/API/types/classes/Complex.md b/doc/JA/API/types/classes/Complex.md new file mode 100644 index 00000000..10dcb3cd --- /dev/null +++ b/doc/JA/API/types/classes/Complex.md @@ -0,0 +1,14 @@ +# Complex + +複素数を表す型です。Ergで数字を表す型、例えばFloatやInt、Natなどは、大抵この型を上位に持ちます。 + +## supers + +Num and Norm + +## methods + +* abs +* conjugate +* imag +* real diff --git a/doc/JA/API/types/classes/Dict!.md b/doc/JA/API/types/classes/Dict!.md new file mode 100644 index 00000000..302af630 --- /dev/null +++ b/doc/JA/API/types/classes/Dict!.md @@ -0,0 +1,7 @@ +# Dict! K, V + +辞書(ハッシュマップ)を表す型。`{K: V}`という構文糖が存在する。 + +## methods + +* invert!(self) -> Self! V, K diff --git a/doc/JA/API/types/classes/Either.md b/doc/JA/API/types/classes/Either.md new file mode 100644 index 00000000..bcde0468 --- /dev/null +++ b/doc/JA/API/types/classes/Either.md @@ -0,0 +1,12 @@ +# Either L, R = L or R + +「LかRかどちらか」を表す型。Or型の2つ限定形と考えて良い。 + +## methods + +* orl +* orr +* andl +* andr +* mapl +* mapr diff --git a/doc/JA/API/types/classes/Float.md b/doc/JA/API/types/classes/Float.md new file mode 100644 index 00000000..a874c3cf --- /dev/null +++ b/doc/JA/API/types/classes/Float.md @@ -0,0 +1,21 @@ +# Float size + +実数(小数を含む数)を表す型です。IEEE 754に準拠した浮動小数点数を表し、他の言語では一般的にfloatに相当する型です。 +Float sizeのsizeは、8(1byte)~128(16byte)となります。単にFloatとした場合`Float 64`を表します。 +Ergでの0.1は実はFloat型ではなく、Ratio型に属します。Float型のリテラルは存在せず、`(Ratioオブジェクト)f64`(e.g. (1/2)f64, 15f64)で生成します。f64は実数の1に対応します。 + +## supers + +Complex and Ord + +## methods + +* sgn(self) -> {-1, 0, 1} + 符号を返す。 + +* truncate(self) -> Int + 自身に最も近い整数を返す。 + +* separate(self) -> [Str] +* separate(self, dight: Nat) -> [Str] + dight桁ごとに区切る。引数なしだと3。 diff --git a/doc/JA/API/types/classes/Function(N).md b/doc/JA/API/types/classes/Function(N).md new file mode 100644 index 00000000..0955f2ec --- /dev/null +++ b/doc/JA/API/types/classes/Function(N).md @@ -0,0 +1,9 @@ +# Function N: Nat + +## methods of Function 1 + +* then(self, g: Self) -> Self + +```erg +assert f(g(x)) == f.then(g) x +``` diff --git a/doc/JA/API/types/classes/Inf.md b/doc/JA/API/types/classes/Inf.md new file mode 100644 index 00000000..45e579bc --- /dev/null +++ b/doc/JA/API/types/classes/Inf.md @@ -0,0 +1,7 @@ +# Inf + +Infはinfただひとつをインスタンスとするクラスである。 +infの主な使いみちは、区間型での使用である。 +例えば、2以上の整数型は`2.. Self(L1+L2, R1+R2) + +通常の加算。`Int`や`Nat`の加算はそれぞれのクラスで定義されていると見せかけて、ここで定義されている。 + +```erg +0..10 + 1..12 == 1..22 +Int + 0..10 == _..|Int|_ + 0..10 == _..|Int|_ == Int +Nat + Nat == 0.._ + 0.._ == 0.._ == Nat +``` diff --git a/doc/JA/API/types/classes/Interval.md b/doc/JA/API/types/classes/Interval.md new file mode 100644 index 00000000..c59e3b4f --- /dev/null +++ b/doc/JA/API/types/classes/Interval.md @@ -0,0 +1,18 @@ +# Interval begin, end: WellOrder + +整列集合型(WellOrder)の部分型を表す型です。Interval型にはPreOpen(x<..y)などの派生型が存在します。 + +```erg +Months = 1..12 +Alphabet = "a".."z" +Weekdays = Monday..Friday +Winter = November..December or January..February +``` + +```erg +0..1 # 整数の範囲 +0.0..1.0 # 実数(有理数)の範囲 +# or 0/1..1/1でも同じ +``` + +コンピュータは無限桁の数を上手く扱えないので、実数の範囲と言っても実際は有理数の範囲である。 diff --git a/doc/JA/API/types/classes/Iterator.md b/doc/JA/API/types/classes/Iterator.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/API/types/classes/Kind(N).md b/doc/JA/API/types/classes/Kind(N).md new file mode 100644 index 00000000..4a72aec9 --- /dev/null +++ b/doc/JA/API/types/classes/Kind(N).md @@ -0,0 +1,5 @@ +# Kind N: Nat + +```erg +Kind N: Nat = (Type; N) -> Type +``` diff --git a/doc/JA/API/types/classes/Matrix.md b/doc/JA/API/types/classes/Matrix.md new file mode 100644 index 00000000..a4803e45 --- /dev/null +++ b/doc/JA/API/types/classes/Matrix.md @@ -0,0 +1,7 @@ +# Matrix T: Num, Shape: [M, N] + +行列を表す型です。Tensor [M, N]を継承しています。 + +## def + +Inherit Tensor T, [M, N] diff --git a/doc/JA/API/types/classes/Module.md b/doc/JA/API/types/classes/Module.md new file mode 100644 index 00000000..03b89e9f --- /dev/null +++ b/doc/JA/API/types/classes/Module.md @@ -0,0 +1,3 @@ +# Module + +## methods diff --git a/doc/JA/API/types/classes/Nat.md b/doc/JA/API/types/classes/Nat.md new file mode 100644 index 00000000..5fe1d242 --- /dev/null +++ b/doc/JA/API/types/classes/Nat.md @@ -0,0 +1,18 @@ +# Nat + +自然数を表す型。配列のインデックスや範囲型などで使われる。 + +## def + +```erg +Nat = 0.._ +``` + +## methods + +* times!(self, p: () => NoneType) -> NoneType + +```erg +100.times! () => + print! "hello!" +``` diff --git a/doc/JA/API/types/classes/Neg.md b/doc/JA/API/types/classes/Neg.md new file mode 100644 index 00000000..f11db34f --- /dev/null +++ b/doc/JA/API/types/classes/Neg.md @@ -0,0 +1,8 @@ +# Neg + +負の整数を表す型です。Pos and Neg and {0} == Intとなります。 +ゼロ除算が起きない、Neg * Neg == Posとなるなど、いくつか特筆すべき性質も持っています。 + +## def + +Inf<..-1 diff --git a/doc/JA/API/types/classes/Never.md b/doc/JA/API/types/classes/Never.md new file mode 100644 index 00000000..4ab9ff94 --- /dev/null +++ b/doc/JA/API/types/classes/Never.md @@ -0,0 +1,13 @@ +# Never + +全ての型の下位型である。全てのメソッドを持っており、当然`.new`も持っているため`Class`である。しかしインスタンスは持たず、生成されそうになった瞬間にErgは停止する。 +`Panic`という同じくインスタンスを持たない型が存在するが、正常に終了する際や意図的な無限ループの際は`Never`、異常終了する際には`Panic`を使う。 + +```erg +# Never <: Panic +f(): Panic = exit 0 # OK +g(): Never = panic() # TypeError +``` + +`Never`/`Panic`のOr型、例えば`T or Never`は`T`に変換することができる。これは、`Never`は意味論上起こり得ない(起こった場合プログラムは即時停止する)選択肢であるためである。 +しかし、関数の戻り値型などで使用する場合、プログラムの終了が起こりうることを示すため`or Never`を省略することはできない。 diff --git a/doc/JA/API/types/classes/NonZero.md b/doc/JA/API/types/classes/NonZero.md new file mode 100644 index 00000000..d6e3c41e --- /dev/null +++ b/doc/JA/API/types/classes/NonZero.md @@ -0,0 +1,30 @@ +# NonZero N + +ゼロではない数を表すクラスです。ゼロ除算の安全性が保証されます。 + +```mermaid +classDiagram + class NonZero~Int~ { + ... + } + class Int { + ... + } + class Div { + <> + /(Self, R) -> O or Panic + } + class SafeDiv { + <> + /(Self, R) -> O + } + Int <|-- NonZero~Int~: Inherit + Div <|-- SafeDiv: Subsume + SafeDiv <|.. NonZero~Int~: Impl + Div <|.. Int: Impl +``` + +## methods + +@Impl SafeDiv R, O +.`/`: Self.(R) -> O diff --git a/doc/JA/API/types/classes/Object.md b/doc/JA/API/types/classes/Object.md new file mode 100644 index 00000000..26935cb4 --- /dev/null +++ b/doc/JA/API/types/classes/Object.md @@ -0,0 +1,7 @@ +# Object + +全ての型の上位型である。 + +## methods + +* __sizeof__: Nat diff --git a/doc/JA/API/types/classes/Operator.md b/doc/JA/API/types/classes/Operator.md new file mode 100644 index 00000000..9c45b70b --- /dev/null +++ b/doc/JA/API/types/classes/Operator.md @@ -0,0 +1,7 @@ +# Operator [...T], O + +演算子の型です。 + +## def + +Inherit Func [...T], O diff --git a/doc/JA/API/types/classes/Option.md b/doc/JA/API/types/classes/Option.md new file mode 100644 index 00000000..11dc2398 --- /dev/null +++ b/doc/JA/API/types/classes/Option.md @@ -0,0 +1,21 @@ +# Option T = T or NoneType + +「失敗するかもしれない」を表す型。 + +## methods + +* unwrap(self, msg = "unwrapped a None value") -> T or Panic + +中身が`T`型であると期待して取り出す。`None`であった場合`msg`を出力してパニックする。 + +```erg +x = "...".parse(Int).into(Option Int) +x.unwrap() # UnwrappingError: unwrapped a None value +x.unwrap("failed to convert from string to number") # UnwrappingError: failed to convert from string to number +``` + +* unwrap_or(self, else: T) -> T + +* unwrap_or_exec(self, f: () -> T) -> T + +* unwrap_or_exec!(self, p!: () => T) -> T diff --git a/doc/JA/API/types/classes/Pos.md b/doc/JA/API/types/classes/Pos.md new file mode 100644 index 00000000..dbade292 --- /dev/null +++ b/doc/JA/API/types/classes/Pos.md @@ -0,0 +1,8 @@ +# Pos + +Posは正数(1以上の整数)を表す型です。 +0が入らないので、ゼロ除算の可能性を排除できるなどのメリットがあります。 + +## Def + +`Pos = 1.._` diff --git a/doc/JA/API/types/classes/Ratio.md b/doc/JA/API/types/classes/Ratio.md new file mode 100644 index 00000000..70e5f8d7 --- /dev/null +++ b/doc/JA/API/types/classes/Ratio.md @@ -0,0 +1,5 @@ +# Ratio + +有理数を表す型です。主に、分数を使いたいときに使います。 +実はErgでの/演算子はRatioを返します。1/3などは0.33333...とは評価されず1/3のまま処理されます。また、0.1は1/10と等価です。なので、`0.1 + 0.2 == 0.3`となります。当たり前のように聞こえますが、PythonではなんとFalseになります。 +とはいえ、Ratio型はFloat型に比べて若干効率が下がる傾向があります。そこまで正確な数値は要らず、かつ実行速度が重要となるポイントではFloat型を使用するといいでしょう。とはいえ、Rob Pikeが言うように早すぎる最適化は諸悪の根源です。Ratio型を捨ててFloat型を使用するのは実際にパフォーマンステストを行ってからにしましょう。素人ほど無条件に軽量な型を好みます。 diff --git a/doc/JA/API/types/classes/Record.md b/doc/JA/API/types/classes/Record.md new file mode 100644 index 00000000..97be5017 --- /dev/null +++ b/doc/JA/API/types/classes/Record.md @@ -0,0 +1,14 @@ +# Record + +レコードの属するクラス。例えば`{i = 1}`は`Structural {i = Int}`型などの要素であり、`{i = Int}`クラスのインスタンスである。 +他のクラスのインスタンスはレコード型の要素であってもレコードクラスのインスタンスではないことに注意。 + +```erg +assert not Structural({i = Int}) in Class +assert {i = Int} in Class + +C = Class {i = Int} +c = C.new {i = 1} +assert c in Structural {i = Int} +assert not c in {i = Int} +``` diff --git a/doc/JA/API/types/classes/Result.md b/doc/JA/API/types/classes/Result.md new file mode 100644 index 00000000..8364f26f --- /dev/null +++ b/doc/JA/API/types/classes/Result.md @@ -0,0 +1,7 @@ +# Result T, E + +```erg +Result T, E <: Error = Either T, E +``` + +`Option`と同じく「失敗するかもしれない値」を表すが、失敗時のコンテクストを持てる。使い方は`Either`とほぼ同じである。 diff --git a/doc/JA/API/types/classes/Str!.md b/doc/JA/API/types/classes/Str!.md new file mode 100644 index 00000000..7e4bd67b --- /dev/null +++ b/doc/JA/API/types/classes/Str!.md @@ -0,0 +1,3 @@ +# StrWithLen! N: Nat! = Inherit StrWithLen N + +可変長文字列を表す型。 diff --git a/doc/JA/API/types/classes/Str.md b/doc/JA/API/types/classes/Str.md new file mode 100644 index 00000000..7adcc18c --- /dev/null +++ b/doc/JA/API/types/classes/Str.md @@ -0,0 +1,9 @@ +# Str + +(不変長)文字列を表す型。単なる`Str`型は`StrWithLen N`型から文字数の情報を落とした型である(`Str = StrWithLen _`)。 + +## methods + +* isnumeric + +文字列がアラビア数字であるかを返す。漢数字やその他の数字を表す文字の判定は`isunicodenumeric`を使う(この挙動はPythonと違うので注意)。 diff --git a/doc/JA/API/types/classes/StrWithLen.md b/doc/JA/API/types/classes/StrWithLen.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/API/types/classes/Subroutine.md b/doc/JA/API/types/classes/Subroutine.md new file mode 100644 index 00000000..09317713 --- /dev/null +++ b/doc/JA/API/types/classes/Subroutine.md @@ -0,0 +1,19 @@ +# Subroutine + +FuncやProcの基底型です。 + +## methods + +* return + +サブルーチンを中断して、指定した値を返す。ネストから一気に脱出する際に便利。 + +```erg +f x = + for 0..10, i -> + if i == 5: + do + f::return i + do + log i +``` diff --git a/doc/JA/API/types/classes/Tensor.md b/doc/JA/API/types/classes/Tensor.md new file mode 100644 index 00000000..0fdb374c --- /dev/null +++ b/doc/JA/API/types/classes/Tensor.md @@ -0,0 +1,24 @@ +# Tensor Shape: [Nat; N] + + 多次元配列を効率的に操作するためのクラス。多次元配列に対する積などの演算も定義する。 + Matrix, Vectorなどはこの型を継承している。 + +```erg +Tensor.arange(0..9) # Tensor [10] +``` + +* reshape(self, NewShape: [Nat; M]) -> Self NewShape + +```erg +(1..9).into(Tensor).reshape [3, 3] +``` + +* identity i: Nat -> Self shape: [Nat; N] +* zeros(Shape: [Nat; N]) -> Self +* ones(Shape: [Nat; N]) -> Self + +* diag + +* linspace +* logspace +* geomspace diff --git a/doc/JA/API/types/classes/TransCell(T).md b/doc/JA/API/types/classes/TransCell(T).md new file mode 100644 index 00000000..e30d479e --- /dev/null +++ b/doc/JA/API/types/classes/TransCell(T).md @@ -0,0 +1,12 @@ +# TransCell! T: Type! + +中身を型ごと変えられるセルです。T型のサブタイプとなるので、T型としても振る舞います。 +初期化時点ではT型で、ある時点以降はずっとU型、といった場合に便利です。 + +```erg +a = TransCell!.new None +a: TransCell! !NoneType +a.set! 1 +a: TransCell! !Int +assert a + 1 == 2 +``` diff --git a/doc/JA/API/types/classes/Tuple.md b/doc/JA/API/types/classes/Tuple.md new file mode 100644 index 00000000..8ea4182c --- /dev/null +++ b/doc/JA/API/types/classes/Tuple.md @@ -0,0 +1,27 @@ +# Tuple T: ...Type + +複数の型のオブジェクトを保持するコレクション。 + +## methods + +* zip self, other + + 2つの順番付けられたコレクション(配列かタプル)を合成する。 + + ```erg + assert ([1, 2, 3].zip [4, 5, 6])[0] == (1, 4) + ``` + +* zip_by self, op, other + + zipを一般化したメソッド。合成するための二項演算を指定できる。 + 演算子には`()`, `[]`, `{}`, `{:}`も指定可能で、それぞれタプル, 配列, セット, ディクトを生成する。 + + ```erg + assert ([1, 2, 3].zip([4, 5, 6]))[0] == (1, 4) + assert ([1, 2, 3].zip_by((), [4, 5, 6]))[0] == (1, 4) + assert ([1, 2, 3].zip_by([], [4, 5, 6]))[0] == [1, 4] + assert ([1, 2, 3].zip_by({}, [4, 5, 6]))[0] == {1, 4} + assert ([1, 2, 3].zip_by({:}, [4, 5, 6]))[0] == {1: 4} + assert ([1, 2, 3].zip_by(`_+_`, [4, 5, 6]))[0] == 5 + ``` diff --git a/doc/JA/API/types/classes/Type.md b/doc/JA/API/types/classes/Type.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/API/types/classes/Vector.md b/doc/JA/API/types/classes/Vector.md new file mode 100644 index 00000000..cfe0337e --- /dev/null +++ b/doc/JA/API/types/classes/Vector.md @@ -0,0 +1,3 @@ +# Vector T: Num, N: Nat + +ベクトルを表す型です。RustやC++などの同名の型と違い、この型が扱うのは数値専用です。 diff --git a/doc/JA/API/types/patches/BinOp.md b/doc/JA/API/types/patches/BinOp.md new file mode 100644 index 00000000..85ee08dd --- /dev/null +++ b/doc/JA/API/types/patches/BinOp.md @@ -0,0 +1,7 @@ +# BinOp L, R, O + +二項演算子の型です。 + +## Patches + +Operator [L, R], O diff --git a/doc/JA/API/types/patches/UnaryOp.md b/doc/JA/API/types/patches/UnaryOp.md new file mode 100644 index 00000000..8128dab2 --- /dev/null +++ b/doc/JA/API/types/patches/UnaryOp.md @@ -0,0 +1,7 @@ +# UnaryOp T, O + +単項演算子の型です。 + +## def + +Operator [T], O diff --git a/doc/JA/API/types/traits/Add(R,O).md b/doc/JA/API/types/traits/Add(R,O).md new file mode 100644 index 00000000..27db4769 --- /dev/null +++ b/doc/JA/API/types/traits/Add(R,O).md @@ -0,0 +1,33 @@ +# Add R, O + +```erg +Add R, O = Trait { + .`_+_` = (Self, R) -> O +} +``` + +`Add`は加算を定義する型である。加算としての`+`にはメソッドと関数の2種類がある。 +二項関数としての`+`、すなわち`_+_`は、以下のように定義されている。 + +```erg +`_+_`(l: Add(R, O), r: R): O = l.`_+_` r +``` + +わざわざこの定義があるのは、`+`をメソッドではなく関数として取り扱えるようにである。 + +```erg +assert [1, 2, 3].fold(0, `_+_`) == 6 + +call op, x, y = op(x, y) +assert call(`_+_`, 1, 2) == 3 +``` + +加算はこのように型付けされる。 + +```erg +f: |O: Type; A <: Add(Int, O)| A -> O +f x = x + 1 + +g: |A, O: Type; Int <: Add(A, O)| A -> O +g x = 1 + x +``` diff --git a/doc/JA/API/types/traits/Div(R,O).md b/doc/JA/API/types/traits/Div(R,O).md new file mode 100644 index 00000000..3ba59edf --- /dev/null +++ b/doc/JA/API/types/traits/Div(R,O).md @@ -0,0 +1,9 @@ +# Div R, O + +ゼロ除算によるエラーがない場合は`SafeDiv`を使ってください。 + +```erg +Div R, O = Trait { + .`/` = Self.(R) -> O or Panic +} +``` diff --git a/doc/JA/API/types/traits/Eq.md b/doc/JA/API/types/traits/Eq.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/API/types/traits/Into.md b/doc/JA/API/types/traits/Into.md new file mode 100644 index 00000000..9151f047 --- /dev/null +++ b/doc/JA/API/types/traits/Into.md @@ -0,0 +1,11 @@ +# Into T + +T型に型変換可能であることを示す型です。 +SelfとTの間に継承関係などがなくても、互いに変換可能な関係であるときに定義します。 +継承と違い暗黙には変換が行われません。必ず`.into`メソッドを呼び出す必要があります。 + +## methods + +* into(self, T) -> T + + 変換を行います。 diff --git a/doc/JA/API/types/traits/Iterable.md b/doc/JA/API/types/traits/Iterable.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/API/types/traits/Num.md b/doc/JA/API/types/traits/Num.md new file mode 100644 index 00000000..5745c5ce --- /dev/null +++ b/doc/JA/API/types/traits/Num.md @@ -0,0 +1,16 @@ +# Num + +## definition + +```erg +Num R = Add(R) and Sub(R) and Mul(R) and Eq +Num = Num Self +``` + +## supers + +Add and Sub and Mul and Eq + +## methods + +* `abs` diff --git a/doc/JA/API/types/traits/Ord.md b/doc/JA/API/types/traits/Ord.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/API/types/traits/SafeDiv(R,O).md b/doc/JA/API/types/traits/SafeDiv(R,O).md new file mode 100644 index 00000000..54c3a1b1 --- /dev/null +++ b/doc/JA/API/types/traits/SafeDiv(R,O).md @@ -0,0 +1,8 @@ +# SafeDiv R, O + +```erg +SafeDiv R, O = Subsume Div, { + @Override + .`/` = Self.(R) -> O +} +``` diff --git a/doc/JA/API/types/traits/Sample.md b/doc/JA/API/types/traits/Sample.md new file mode 100644 index 00000000..bd286d9d --- /dev/null +++ b/doc/JA/API/types/traits/Sample.md @@ -0,0 +1,31 @@ +# Sample + +インスタンスを「適当に」選出する`sample`メソッドと`sample!`メソッドを持つトレイト。`sample`メソッドは常に同じインスタンスを返し、`sample!`メソッドは呼び出しごとに変わる適当なインスタンスを返す。 + +これはテストなどで適当なインスタンスがほしい場合を想定したトレイトであり、必ずしも無作為ではないことに注意が必要である。無作為抽出が必要な場合は`random`モジュールを使用する。 + +主要な値クラスは全て`Sample`を実装する。また、`Sample`なクラスで構成されているタプル型やレコード型、Or型、篩型でも実装されている。 + +```erg +assert Int.sample() == 42 +assert Str.sample() == "example" +# Intの場合、標準では64bitの範囲でサンプルされる +print! Int.sample!() # 1313798 +print! {x = Int; y = Int}.sample!() # {x = -32432892, y = 78458576891} +``` + +以下は`Sample`の実装例である。 + +```erg +EmailAddress = Class {header = Str; domain = Str}, Impl=Sample and Show +@Impl Show +EmailAddress. + show self = "{self::header}@{self::domain}" +@Impl Sample +EmailAddress. + sample(): Self = Self.new "sample@gmail.com" + sample!(): Self = + domain = ["gmail.com", "icloud.com", "yahoo.com", "outlook.com", ...].sample!() + header = AsciiStr.sample!() + Self.new {header; domain} +``` diff --git a/doc/JA/API/types/traits/Seq.md b/doc/JA/API/types/traits/Seq.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/API/types/traits/Show.md b/doc/JA/API/types/traits/Show.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/API/types/traits/Unpack.md b/doc/JA/API/types/traits/Unpack.md new file mode 100644 index 00000000..b82282a8 --- /dev/null +++ b/doc/JA/API/types/traits/Unpack.md @@ -0,0 +1,13 @@ +# Unpack + +マーカートレイト。実装すると、レコードのようにパターンマッチで要素を分解できる。 + +```erg +C = Class {i = Int}, Impl=Unpack +C.new i = Self::new {i;} +{i} = C.new(1) +D = Class C or Int +log match D.new(1): + (i: Int) -> i + ({i}: C) -> i +``` diff --git a/doc/JA/compiler/TODO_hint.md b/doc/JA/compiler/TODO_hint.md new file mode 100644 index 00000000..e00e4860 --- /dev/null +++ b/doc/JA/compiler/TODO_hint.md @@ -0,0 +1,5 @@ +# Hint (not implemented) + +* `x is not defined` (x was deleted by `del`) => `hint: deleted in line X` +* patch method duplication: "hint: Specify patch (like `T.foo(1)`) or delete either `.foo` using `del`" +* `x was moved in line X` diff --git a/doc/JA/compiler/TODO_recov_suggest.md b/doc/JA/compiler/TODO_recov_suggest.md new file mode 100644 index 00000000..a11955fa --- /dev/null +++ b/doc/JA/compiler/TODO_recov_suggest.md @@ -0,0 +1,11 @@ +# Error recovery suggestions (not implemented yet) + +* `1 or 2`, `1 and 2` => `{1, 2}`? +* `U = Inherit T` => Non-class type cannot be inherited, or `U = Class T`? +* `Int and Str` => Multiple inheritance is not allowed, or `Int or Str`? +* `: [1, 2]` => `: {1, 2}`? +* `: [Int, 2]` => `: [Int; 2]`? +* `[Int; Str]` => `(Int, Str)`(Tuple) or `[Int: Str]`(Dict)? +* `{x: Int}` => `{x = Int}`? +* `{x = Int}!` => `{x = Int!}`? +* `ref! immut_expr` => `ref! !immut_expr`? diff --git a/doc/JA/compiler/TODO_warn.md b/doc/JA/compiler/TODO_warn.md new file mode 100644 index 00000000..0ba2ebf2 --- /dev/null +++ b/doc/JA/compiler/TODO_warn.md @@ -0,0 +1,5 @@ +# warnings (not implemented yet) + +* `t = {(record type)}` => `T = {(record type)}`? (only types defined as constants can be used for type specification) +* `{I: Int | ...}!` => `{I: Int! | ...}` +* `return x`(`x != ()`) in for/while block => `f::return` (outer block)? diff --git a/doc/JA/compiler/abandoned.md b/doc/JA/compiler/abandoned.md new file mode 100644 index 00000000..19bfe876 --- /dev/null +++ b/doc/JA/compiler/abandoned.md @@ -0,0 +1,10 @@ +# 放棄・却下された言語仕様 + +## オーバーロード(アドホック多相) + +パラメトリック+サブタイピング多相で代替できること、Pythonの意味論との相性の悪さなどを理由に放棄された。詳しくは[overload](../syntax/type/overloading.md)の記事を参照。 + +## 明示的ライフタイム付き所有権システム + +Rustのような所有権システムを導入する予定だったが、Pythonの意味論との相性の悪さ、ライフタイム注釈など煩雑な仕様の導入が必要であることなどから放棄され、不変オブジェクトは全てRCで管理され、可変オブジェクトの所有権は1つのみ、という文法になった。 +DyneはC#, NimのようにGILを持たず、値オブジェクトや安全な範囲での低レベル操作もできるようにする方針である。 diff --git a/doc/JA/compiler/architecture.md b/doc/JA/compiler/architecture.md new file mode 100644 index 00000000..78d5e5a5 --- /dev/null +++ b/doc/JA/compiler/architecture.md @@ -0,0 +1,42 @@ +# Architecture of `ergc` + +## 1. Scan an Erg script (.er) and generate a `TokenStream` (parser/lex.rs) + +* parser/lexer/Lexer generates `TokenStream` (this is an iterator of Token, TokenStream can be generated by lexer.collect()) + * `Lexer` is constructed from `Lexer::new` or `Lexer::from_str`, where `Lexer::new` reads the code from a file or command option. + * `Lexer` can generate tokens sequentially as an iterator; if you want to get a `TokenStream` all at once, use `Lexer::lex`. + * `Lexer` outputs `LexError`s as errors, but `LexError` does not have enough information to display itself. If you want to display the error, use the `LexerRunner` to convert the error. + * `LexerRunner` can also be used if you want to use `Lexer` as standalone; `Lexer` is just an iterator and does not implement the `Runnable` trait. + * `Runnable` is implemented by `LexerRunner`, `ParserRunner`, `Compiler`, and `VirtualMachine`. + +## 2. Convert `TokenStream` -> `AST` (parser/parse.rs) + +* `Parser`, like `Lexer`, has two constructors, `Parser::new` and `Parser::from_str`, and `Parser::parse` will give the `AST`. +* `AST` is the wrapper type of `Vec`. It is for "Abstract Syntax Tree". + +### 2.5 Desugaring `AST` + +* expand nested vars (`Desugarer::desugar_nest_vars_pattern`) +* desugar multiple pattern definition syntax (`Desugarer::desugar_multiple_pattern_def`) + +## 3. Type checking & inference, Convert `AST` -> `HIR` (compiler/lower.rs) + +* `HIR` has every variable's type information. It is for "High-level Intermediate Representation". +* `HIR` only holds the type of the variable, but that's enough. In extreme cases, this is because Erg has only conversion (or operator) applications. If we know the type of the conversion, we have already know the type of the object of the argument. +* `ASTLowerer` can be constructed in the same way as `Parser` and `Lexer`. +* `ASTLowerer::lower` will output a tuple of `HIR` and `CompileWarnings` if no errors occur. +* `ASTLowerer` is owned by `Compiler`. Unlike conventional structures, `ASTLowerer` handles code contexts and is not a one-time disposable. +* If the result of type inference is incomplete (if there is an unknown type variable), an error will occur during name resolution. + +## 4. Check side-effects (compiler/effectcheck.rs) + +## 4. Check ownerships (compiler/memcheck.rs) + +## 5. Generate Bytecode (`CodeObj`) from `HIR` (compiler/codegen.rs) + +* From the type information of the expression, name resolution of the quantified subroutines will be performed. + +## (6. (Future plans) Convert Bytecode -> LLVM IR) + +* Bytecode is stack-based, whereas LLVM IR is register-based. + There will be several more layers of intermediate processes for this conversion process. diff --git a/doc/JA/compiler/errors.md b/doc/JA/compiler/errors.md new file mode 100644 index 00000000..ac35b17b --- /dev/null +++ b/doc/JA/compiler/errors.md @@ -0,0 +1,131 @@ +# Erg Compiler Errors + +## AssignError + +イミュータブル変数を書き換えようとすると発生します。 + +## AttributeError + +存在しない属性にアクセスしようとすると発生します。 + +## SideEffectError + +副作用が許可されていないスコープ(関数、不変型など)で副作用を起こすコードを記述すると発生します。 + +## OwnershipError + +既にムーブ済みの変数にアクセスしようとすると発生します。 + +## BorrowError + +あるオブジェクトに対する借用が存在している間にもう一つ可変参照を取得しようとすると発生します。 + +## CyclicError + +明らかに停止しない循環を起こしている場合に発生します。 + +```erg +i: Int = i + +f(): Int = g() +g() = f() + +h(): Int = module::h() + +T = U +U = T +``` + +## BytecodeError + +読み込んだバイトコードが破損していた場合に発生します。 + +## CompileSystemError + +コンパイラ内部でエラーが起きた場合に発生します。 + +## EnvironmentError + +インストール時にアクセス権限がなかった場合などで発生します。 + +## FeatureError + +正式に提供されていない試験的機能を検出した際に発生します。 + +## ImportError + +## IndentationError + +不正なインデントを検出すると発生します。 +SyntaxErrorの派生です。 + +## NameError + +存在しない変数にアクセスすると発生します。 + +## NotImplementedError + +定義は存在し、実装のないAPIを呼び出すと発生します。 +TypeErrorの派生です。 + +## PatternError + +不正なパターンを検出すると発生します。 +SyntaxErrorの派生です。 + +## SyntaxError + +不正な文法を検出すると発生します。 + +## TabError + +インデント/スペースとしてタブ文字を使うと発生します。 +SyntaxErrorの派生です。 + +## TypeError + +オブジェクトの型が合わない際に発生します。 + +## UnboundLocalError + +変数を定義前に使用すると発生します。 +正確には、あるスコープ内で定義された変数がそれ以前に使われていると発生します。 + +```erg +i = 0 +f x = + y = i + x + i = 1 + y + i +``` + +このコードでは`y = i + x`の`i`が未定義変数になります。 +しかし、定数の場合は定義前に別の関数中で呼び出し可能です。 + +```erg +f() = g() +g() = f() +``` + +## Erg Compiler Warnings + +## SyntaxWarning + +文法上は問題ありませんが、冗長だったり一般的でないコードを検出した際に発生します(不要な`()`など)。 + +```erg +if (True): # SyntaxWarning: unnecessary parentheses + ... +``` + +## DeprecationWarning + +参照したオブジェクトが非推奨である場合に発生します。 +(開発者はこのWarningを発生させる際、必ず代替手段をHintとして提示してください) + +## FutureWarning + +将来的に問題が起こりそうなコードを検出すると発生します。 +このWarningはバージョンの互換性(ライブラリ含む)の問題や文法・APIの変更によって起こります。 + +## ImportWarning diff --git a/doc/JA/compiler/hir.md b/doc/JA/compiler/hir.md new file mode 100644 index 00000000..01f71b41 --- /dev/null +++ b/doc/JA/compiler/hir.md @@ -0,0 +1,148 @@ +# 高レベル中間表現(HIR, High-level Intermediate Representation) + +HIRはErgコンパイラがASTから生成する構造体です。 +この構造体にはソースコード中のあらゆる式の完全な型情報が含まれており、また構文糖が脱糖されています。 +ASTは(プレーンテキストとしての)ソースコードと一対一対応しますが、HIRは不要なコードの情報が除去されていたり、また省略された型情報が付記されたりしているため、HIRからソースコードを復元することは困難です。 +以下のコードでHIRの例を見てみましょう。 + +```erg +v = ![] +for! 0..10, i => + v.push! i +log v.sum() +``` + +このコードから生成されるASTは以下のようになります。 + +```erg +AST(Module[ + VarDef{ + sig: VarSignature{ + pat: VarPattern::Name(Name("v")), + spec_t: None, + }, + op: "=", + body: Block[ + UnaryOp{ + op: "!", + expr: Array([]), + }, + ], + }, + Call{ + obj: Accessor::Local("for!"), + args: [ + BinOp{ + op: "..", + lhs: Literal(0), + rhs: Literal(10), + }, + Lambda{ + sig: LambdaSignature{ + params: [ + ParamSignature{ + pat: ParamPattern::Name(Name("i")), + }, + ], + spec_ret_t: None, + }, + body: Block[ + Call{ + obj: Accessor::Attr{"v", "push!"}, + args: [ + Accessor::Local("i"), + ], + }, + ], + }, + ], + }, + Call{ + obj: Accessor::Local("log"), + args: [ + Call{ + obj: Accessor::Attr("v", "sum"), + args: [], + } + ], + } +]) +``` + +そしてASTから生成されるHIRは以下のようになります。 + +```erg +HIR(Module[ + VarDef{ + sig: VarSignature{ + pat: VarPattern::Name(Name("v")), + t: [0..10, _]!, + }, + op: "=", + body: Block[ + expr: UnaryOp{ + op: "!", + expr: Array([]), + t: [0..10, 0]!, + }, + ], + }, + Call{ + obj: Accessor::Local{ + name: "for!", + t: (Range Nat, Nat => NoneType) => NoneType, + }, + args: [ + BinOp{ + op: "..", + lhs: Literal(0), + rhs: Literal(10), + t: 0..10, + }, + Lambda{ + sig: LambdaSignature{ + params: [ + ParamSignature{ + pat: ParamPattern::Name(Name("i")), + t: 0..10, + }, + ], + t: 0..10 => NoneType, + }, + body: Block[ + Call{ + obj: Accessor::Attr{ + obj: Accessor::Local("v"), + field: "push!", + t: Ref!(Self![T ~> T, N ~> N+1]).(Nat) => NoneType, + }, + args: [ + Accessor::Local("i"), + ], + }, + ], + }, + ], + }, + Call{ + obj: Accessor::Local{ + name: "log", + t: ...Object => NoneType, + }, + args: [ + Call{ + obj: Accessor::Attr{ + obj: Accessor::Local("v"), + field: "sum", + t: [0..10, !_] -> Nat + }, + args: [], + t: Nat + } + ], + } +]) +``` + +オブジェクトの型は可能な限り小さく推論されます。それに対し、サブルーチンは実装が存在する型が推論されます。 +なので、実引数の型と仮引数の型が合わない場合もあります。 diff --git a/doc/JA/compiler/index.md b/doc/JA/compiler/index.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/compiler/inference.md b/doc/JA/compiler/inference.md new file mode 100644 index 00000000..ad79c113 --- /dev/null +++ b/doc/JA/compiler/inference.md @@ -0,0 +1,378 @@ +# 型推論アルゴリズム + +> __Info__: この項は編集中であり、一部に間違いを含む可能性があります。 + +以下で用いる表記方法を示します。 + +```erg +自由型変数(型、未束縛): ?T, ?U, ... +自由型変数(値、未束縛): ?a, ?b, ... +型環境(Γ): { x: T, ... } +型代入規則(S): { ?T -> T, ... } +型引数評価環境(E): { e -> e', ... } +``` + +以下のコードを例にして説明します。 + +```erg +v = ![] +v.push! 1 +print! v +``` + +Ergの型推論は、大枠としてHindley-Milner型推論アルゴリズムを用いています。具体的には以下の手順で型推論が行われます。用語の説明などは後述します。 + +1. 右辺値の型を推論する(search & get)。 +2. 得られた型を具体化する(instantiate)。 +3. 呼び出しならば型代入を行う(substitute)。 +4. 型変数値があれば評価・簡約する(eval)。 +5. 左辺値があり、かつCallableならば、引数型の一般化を行う(generalize)。 +6. 左辺値があれば、(戻り値)型の一般化を行う(generalize)。 +7. 代入ならば、シンボルテーブルに型情報を登録する(update)。 + +具体的な操作は以下になります。 + +line 1. Def{sig: v, block: ![]} + get block type: + get UnaryOp type: + get Array type: `['T; 0]` + instantiate: `[?T; 0]` + (substitute, eval are omitted) + update: `Γ: {v: [?T; 0]!}` + expr returns `NoneType`: OK + +line 2. CallMethod{obj: v, name: push!, args: [1]} + get obj type: `Array!(?T, 0)` + search: `Γ Array!(?T, 0).push!({1})` + get: `= Array!('T ~> 'T, 'N ~> 'N+1).push!('T) => NoneType` + instantiate: `Array!(?T, ?N).push!(?T) => NoneType` + substitute(`S: {?T -> Nat, ?N -> 0}`): `Array!(Nat ~> Nat, 0 ~> 0+1).push!(Nat) => NoneType` + eval: `Array!(Nat, 0 ~> 1).push!({1}) => NoneType` + update: `Γ: {v: [Nat; 1]!}` + expr returns `NoneType`: OK + +line 3. Call{obj: print!, args: [v]} + get args type: `[[Nat; 1]!]` + get obj type: + search: `Γ print!([Nat; 1]!)` + get: `= print!(...Object) => NoneType` + expr returns `NoneType`: OK + +## 型変数の実装 + +型変数は[ty.rs](../../src/common/ty.rs)の`Type`にて当初以下のように表現されていました。現在は違う形で実装されていますが、本質的には同じアイデアであるため、より素朴な表現であるこの実装で考えます。 +`RcCell`は`Rc>`のラッパー型です。 + +```rust +pub enum Type { + ... + Var(RcCell>), // a reference to the type of other expression, see docs/compiler/inference.md + ... +} +``` + +型変数は、実体型を外部の辞書に持っておき、型変数自体はそのキーのみを持つように実装できます。しかし、`RcCell`を使用した実装の方が一般に効率的だといわれています(要検証, [一応の出典](https://mobile.twitter.com/bd_gfngfn/status/1296719625086877696?s=21))。 + +型変数はまず`Type::Var(RcCell::new(None))`のようにして初期化されます。 +この型変数が、コードを解析していく中で書き換えられ、型が決定されます。 +最後まで中身がNoneのままだと、(その場では)具体的な型に決定できない型変数となります。例えば、`id x = x`の`x`の型がそうです。 +このような状態の型変数を __未束縛型変数(Unbound type variable)__ と呼ぶことにします(正確な用語が不明)。対して、何らかの具体的な型が代入されているものは __連携型変数(Linked type variable)__ と呼ぶことにします。 + +両者はどちらも自由型変数という種類のものです(この用語は明らかに「自由変数」に因んで命名されていると考えられます)。これらは、コンパイラが推論のために使う型変数です。`id: 'T -> 'T`の`'T`などのように、プログラマが指定するタイプの型変数とは異なるため、このように特別な名前がついています。 + +未束縛型変数は、`?T`, `?U`のように表すことにします。型理論の文脈ではαやβが使われる場合が多いですが、入力の簡便化のためこちらを採用します。 +これは一般的な議論のために採用した記法で、実際に文字列の識別子を使って実装されているわけではないので注意してください。 + +未束縛型変数`Type::Var`は、型環境に入れられる際`Type::MonoQuantVar`へと置き換えられます。これは __量化型変数(quantified type variable)__ と呼ばれるものです。こちらは、プログラマが指定する`'T`のような型変数と同種のものになります。中身は単なる文字列で、自由型変数のように具体的な型とリンクする機能はありません。 + +未束縛型変数を量化型変数に置き換える操作を __一般化(generalization)__ (または汎化)と言います。未束縛型変数のままだと一回の呼び出しで型が固定化されてしまう(例えば、`id True`の呼び出しの後`id 1`の戻り値型が`Bool`になってしまう)ため、一般化しなくてはならないのです。 +このようにして量化型変数を含む一般化された定義が型環境に登録されます。 + +## 一般化、型スキーム、具体化 + +未束縛型変数`?T`を一般化する操作を`gen`と表すことにします。このとき、得られる一般化型変数を`'T`とします。 +Ergの文法で一般化型変数は通常の型と同じ文法で表現されますが、ここではわかりやすさのために`'`を付けます。 +型理論では、ある量化された型αは`∀α.`を付けて区別します(∀のような記号を(全称)量化子といいます)。 +このような表現(e.g. `∀α.α`, `∀α. α->α`)を型スキームと呼びます。Ergでの型スキームは`'T`, `'T -> 'T`などと表されるわけです。 +型スキームは、通常は第一級の型とはみなされません。そのように型システムを構成すると、型推論がうまく動作しなくなる場合があるためです。ただしErgでは一定の条件下で第一級の型とみなせます。詳細は[ランク2型](../syntax/type/advanced/rank2type.md)を参照してください。 + +さて、得られた型スキーム(e.g. `'T -> 'T (idの型スキーム)`)を使用箇所(e.g. `id 1`, `id True`)の型推論で使う際は、一般化を解除する必要があります。この逆変換を __具体化(instantiation)__ と呼びます。操作は`inst`と呼ぶことにします。 + +```erg +gen ?T = 'T +inst 'T = ?T (?T ∉ Γ) +``` + +重要な点として、どちらの操作も、その型変数が出現する場所すべてを置換します。例えば、`'T -> 'T`を具体化すると、`?T -> ?T`が得られます。 +具体化の際は置換用のDictが必要ですが、一般化の際は`?T`に`'T`をリンクさせるだけで置換できます。 + +あとは引数なりの型を与えて目的の型を得ます。この操作を型代入(Type substitution)といい、`subst`と表すことにします。 +さらに、その式が呼び出しの場合に戻り値型を得る操作を`subst_call_ret`と表します。第1引数は引数型のリスト、第2引数は代入先の型です。 + +型代入規則`{?T -> X}`は、`?T`と`X`を同一の型とみなすよう書き換えるという意味です。この操作を __単一化(Unification)__ といいます。`X`は型変数もありえます。 +単一化の詳しいアルゴリズムは[別の項](./unification.md)で解説します。単一化操作は`unify`と表すことにします。 + +```erg +unify(?T, Int) == Ok(()) # ?T == (Int) + +# Sは型代入規則、Tは適用する型 +subst(S: {?T -> X}, T: ?T -> ?T) == X -> X +# 型代入規則は{?T -> X, ?U -> T} +subst_call_ret([X, Y], (?T, ?U) -> ?U) == Y +``` + +## 一般化 + +一般化は単純な作業ではありません。複数のスコープが絡むと、型変数の「レベル管理」が必要になります。 +レベル管理の必要性をみるために、まずはレベル管理を導入しない型推論では問題が起こることを確認します。 +以下の無名関数の型を推論してみます。 + +```erg +x -> + y = x + y +``` + +まず、Ergは以下のように型変数を割り当てます。 +yの型も未知ですが、現段階では割り当てないでおきます。 + +```erg +x(: ?T) -> + y = x + y +``` + +まず決定すべきは右辺値xの型です。右辺値は「使用」なので、具体化します。 +しかしxの型`?T`は自由変数なのですでに具体化されています。よってそのまま`?T`が右辺値の型になります。 + +```erg +x(: ?T) -> + y = x (: inst ?T) + y +``` + +左辺値yの型として登録する際に、一般化します。が、後で判明するようにこの一般化は不完全であり、結果に誤りが生じます。 + +```erg +x(: ?T) -> + y(: gen ?T) = x (: ?T) + y +``` + +```erg +x(: ?T) -> + y(: 'T) = x + y +``` + +yの型は量化型変数`'T`となりました。次の行で、`y`が早速使用されています。具体化します。 + +```erg +x: ?T -> + y(: 'T) = x + y(: inst 'T) +``` + +ここで注意してほしいのが、具体化の際にはすでに存在するどの(自由)型変数とも別の(自由)型変数を生成しなくてはならないという点です(一般化も同様です)。このような型変数をフレッシュ(新鮮)な型変数と呼びます。 + +```erg +x: ?T -> + y = x + y(: ?U) +``` + +そして得られた全体の式の型を見てください。`?T -> ?U`となっています。 +しかし明らかにこの式は`?T -> ?T`のはずで、推論に問題があるとわかります。 +こうなったのは、型変数の「レベル管理」を行っていなかったからです。 + +そこで、型変数のレベルを以下の表記で導入します。レベルは自然数で表します。 + +```erg +?T<1>, ?T<2>, ... +``` + +では、リトライしてみます。 + +```erg +x -> + y = x + y +``` + +まず、以下のようにレベル付き型変数を割り当てます。トップレベルのレベルは1です。スコープが深くなるたび、レベルが増えます。 +関数の引数は内側のスコープに属するため、関数自身より1大きいレベルにいます。 + +```erg +# level 1 +x (: ?T<2>) -> + # level 2 + y = x + y +``` + +先に右辺値`x`を具体化します。先ほどと同じで、何も変わりません。 + +```erg +x (: ?T<2>) -> + y = x (: inst ?T<2>) + y +``` + +ここからがキモです。左辺値`y`の型に代入する際の一般化です。 +さきほどはここで結果がおかしくなっていましたので、一般化のアルゴリズムを変更します。 +もし型変数のレベルが現在のスコープのレベル以下なら、一般化しても変化がないようにします。 + +```erg +gen ?T = if n <= current_level, then= ?T, else= 'T +``` + +```erg +x (: ?T<2>) -> + # current_level = 2 + y (: gen ?T<2>) = x (: ?T<2>) + y +``` + +つまり、左辺値`y`の型は`?T<2>`です。 + +```erg +x (: ?T<2>) -> + # ↓ not generalized + y (: ?T<2>) = x + y +``` + +yの型は未束縛型変数`?T<2>`となりました。次の行で具体化します。が、`y`の型は一般化されていないので、何も起こりません。 + +```erg +x (: ?T<2>) -> + y (: ?T<2>) = x + y (: inst ?T<2>) +``` + +```erg +x (: ?T<2>) -> + y = x + y (: ?T<2>) +``` + +無事に、正しい型`?T<2> -> ?T<2>`を得ました。 + +もう1つの例を見ます。こちらは更に一般的なケースで、関数・演算子適用、前方参照がある場合です。 + +```erg +f x, y = id x + y +id x = x + +f 10 +``` + +1行ずつ見ていきましょう。 + +`f`の推論中、後に定義される関数定数`id`が参照されています。 +このような場合、`f`の前に`id`の宣言を仮想的に挿入し、自由型変数を割り当てておきます。 +このときの型変数のレベル=`current_level`であることに注意してください。これは、他の関数内で一般化されないようにするためです。 + +```erg +id: ?T<1> -> ?U<1> +f x (: ?V<2>), y (: ?W<2>) = + id(x) (: subst_call_ret([inst ?V<2>], inst ?T<2> -> ?U<2>)) + y +``` + +型変数同士の単一化では、高いレベルの型変数が低いレベルの型変数に置き換えられます。 +レベルが同じ場合はどちらでも構いません。 + +```erg +f x (: ?T<1>), y (: ?W<2>) = + # ?V<2> -> ?T<1> + id(x) (: ?U<2>) + y (: ?W<2>) +``` + +```erg +f x (: ?T<1>), y (: ?W<2>) = + (id(x) + x): subst_call_ret([inst ?U<1>, inst ?W<2>], inst |'L <: Add('R, 'O)| ('L, 'R) -> 'O) +``` + +```erg +f x (: ?T<1>), y (: ?W<2>) = + (id(x) + x): subst_call_ret([inst ?U<1>, inst ?W<2>], (?L(<: Add(?R<2>, ?O<2>))<2>, ?R<2>) -> ?O<2>) +``` + +```erg +id: ?T<1> -> ?U<1> +f x (: ?T<2>), y (: ?R<2>) = + # ?L<2> -> ?U<1> + # ?W<2> -> ?R<2> + # ?U<1> <: Add(?R<2>, ?O<2>) (レベルが違うので単一化はせず、部分型関係を追加する) + (id(x) + x) (: ?O<2>) +``` + +```erg +# current_level = 1 +f(x, y) (: gen ?T<1>, gen ?R<2> -> gen ?O<2>) = + id(x) + x +``` + +```erg +id: ?T<1> -> ?U<1> +f(x, y) (: (?T<1>, 'R) -> gen ?O<2>) = + id(x) + x +``` + +```erg +# ?U<1> <: Add('R, 'O) +f(x, y) (: (?T<1>, 'R) -> 'O = + id(x) + x +``` + +定義の際には一般化できるようにレベルを上げます。 + +```erg +# ?T<1 -> 2> +# ?U<1 -> 2> +id x (: ?T<2>) -> ?U<2> = x (: inst ?T<2>) +``` + +戻り値型が既に割り当てられている場合は、得られた型と単一化します(`?U<2> -> ?T<2>`)。 + +```erg +# ?U<2> -> ?T<2> +f(x, y) (: ?T(<: Add('R, 'O))<2>, 'R -> 'O) = + id(x) + x +# current_level = 1 +id(x) (: gen ?T<2> -> gen ?T<2>) = x (: ?T<2>) +``` + +```erg +f(x, y) (: |'T <: Add('R, 'O)| 'T, 'R -> 'O) = + id(x) + x +id(x) (: 'T -> gen 'T) = x +``` + +```erg +f x, y (: |'T <: Add('R, 'O)| 'T, 'R -> 'O) = + id x + y +id(x) (: 'T -> 'T) = x + +f(10, 1) (: subst_call_ret([inst {10}, inst {1}], inst |'T <: Add('R, 'O)| ('T, 'R) -> 'O)) +``` + +```erg +f(10, 1) (: subst_call_ret([inst {10}, inst {1}], (?T(<: Add(?R<1>, ?O<1>))<1>, ?R<1>) -> ?O<1>)) +``` + +型変数は、デフォルトではクラスまで拡大されます。 + +```erg +# ?T<1> -> Nat +# ?R<1> -> Nat +# Nat <: Add(Nat, ?O<1>) +f(10, 1) (: ?O<1>) +``` + +型制約を解決します。詳しくは[型制約の解決](./ty_bound_resolving.md)を参照してください。 + +```erg +# ?O<1> -> Nat +f(10, 1) (: Nat) +``` diff --git a/doc/JA/compiler/memory_management.md b/doc/JA/compiler/memory_management.md new file mode 100644 index 00000000..dc9e682b --- /dev/null +++ b/doc/JA/compiler/memory_management.md @@ -0,0 +1,31 @@ +# オブジェクトの種別 + +オブジェクトにはSize, Mutabilityの2つの属性がある。 + +## Size(Small/Big/Unsized) + +比較的小さいサイズのオブジェクトは「Small」と呼ばれる。小さいとみなされるサイズはプラットフォーム依存である。 +これに該当するオブジェクトは基本的にスタックで管理される。複製(duplication)は値ごとする(Light clone)ので、参照カウントは行われない。 +ただしスタックの80%以上が消費された場合は、ヒープ管理に移行する。 + +比較的大きいサイズのオブジェクトは「Big」と呼ばれる。これに該当するオブジェクトはヒープで管理される。 +複製は大抵RCで管理される(copy)。 + +(実行時)可変サイズのオブジェクトは「Unsized」と呼ばれる。対してSmall/Bigは「Sized」と呼ばれる。 +可変オブジェクトだから全てUnsizedとは限らない。長さが10で中身を書き換えられる可変配列([T; !N])はMut Sizedオブジェクトである。 +不変オブジェクトは全てSizedとなる。当たり前のようにも聞こえるが、多くの言語においてスライス―配列の部分(不変)参照)―はUnsizedとなっている。 +逆にUnsizedオブジェクトは可変なので、全てヒープで管理される。コピー(`.frozen`で生成する)はオリジナルが破壊的に変更されると使用不可になる。完全な複製はクローンで行う。 + +## Mutability(Immut/Mut) + +不変オブジェクトは「Immut」と呼ばれる。これに該当するオブジェクトはLight duplicationまたはコピーができる。コピーは実体を1つだけ持つ。 +可変オブジェクトは「Mut」と呼ばれる。これに該当するオブジェクトはコピーができないので、複製したいときはHeavy duplication = cloneを使う。 +cloneは内容が同じオブジェクトを1から作り直すので、コピーより低速である。 + +## .dupの詳細 + +`.dup`はその時によって最適なアルゴリズムが選択される。 +不変オブジェクトに対しては大抵の場合RCを使う。可変オブジェクトにはcloneを使う。 + +可変オブジェクトを`.dup`すると可変オブジェクトが作られるが、不変オブジェクトが欲しい場合は`.frozen`を使う。 +これはRCで管理され、オブジェクトが改変されると使えなくなる。 diff --git a/doc/JA/compiler/overview.md b/doc/JA/compiler/overview.md new file mode 100644 index 00000000..2d9f7592 --- /dev/null +++ b/doc/JA/compiler/overview.md @@ -0,0 +1,23 @@ +# `erg`の概観 + +各レイヤーの働きと特に重要な関数、メソッドを紹介します。 + +## 1. 字句解析 + +* `Lexer`が字句解析を行います。`Lexer::next`が字句解析のメインロジックを担います。解析の結果として`Token`が出力されます。 + +## 2. 構文解析 + +* `Parser`が構文解析を行います。特に重要なのは`Parser::parse_expr`です。解析の結果として`ast::Expr`の集まりである`AST`が出力されます。 + +## 3. 型チェック + +* `ASTLowerer`がASTをHIRに変換して型付けを行います。型チェックは主に`SymbolTable`によって行われます。特に重要なのは`SymbolTable::supertype_of`(部分型関係を判定する), `SymbolTable::unify`(型変数の単一化を行う), `SymbolTable::init_builtin_*`(組み込みAPIを定義する)です。解析の結果として`HIR`が出力されます。 + +## 4. 副作用チェック + +## 5. 所有権チェック + +## 6. バイトコード生成 + +* `Compiler`が`HIR`を`CodeObj`に変換します。`CodeObj`はバイトコードと実行設定を保持します。特に重要なのは`Compiler::compile_expr`です。 diff --git a/doc/JA/compiler/parsing.md b/doc/JA/compiler/parsing.md new file mode 100644 index 00000000..60e073f7 --- /dev/null +++ b/doc/JA/compiler/parsing.md @@ -0,0 +1,33 @@ +# Erg言語の構文解析 + +## 空白の扱い + +Ergの文法において特異なのは、space-sensitive(空白による区別がある)である点である。 +これは、`()`の省略による表現力の低下を補うためである。同様の文法は同じく`()`を省略可能なNimでも見られる。 + +```erg +f +1 == f(+1) +f + 1 == (f+1) +f (1,) == f((1,)) +f(1,) == f(1) +(f () -> ...) == f(() -> ...) +(f() -> ...) == (f() -> ...) +``` + +## 左辺値、右辺値 + +Ergにおいて左辺値とは`=`の左側といった単純なものではない。 +実際、(非常に紛らわしいが)`=`の左側にも右辺値は存在するし、`=`の右側にも左辺値が存在する。 +右辺値の中に左辺値が存在することさえある。 + +```erg +# iは左辺値、Array(Int)と[1, 2, 3]は右辺値 +i: Array(Int) = [1, 2, 3] +# `[1, 2, 3].iter().map i -> i + 1`は右辺値だが、->の左側のiは左辺値 +a = [1, 2, 3].iter().map i -> i + 1 +# {x = 1; y = 2}は右辺値だが、x, yは左辺値 +r = {x = 1; y = 2} +``` + +左辺値、右辺値の正確な定義は、「評価可能ならば右辺値、そうでないならば左辺値」である。 +例として`i = 1; i`というコードを考える。2つ目の`i`は評価可能であるため右辺値だが、1つ目の`i`は左辺値となる。 diff --git a/doc/JA/compiler/query.md b/doc/JA/compiler/query.md new file mode 100644 index 00000000..5cedbe78 --- /dev/null +++ b/doc/JA/compiler/query.md @@ -0,0 +1,4 @@ +# About Query system + +query systemはErg compiler(以下ergc)の採用する(予定の)コンパイルシステムです。 +ergcはパスベースではなくデマンドベースのコンパイルを行います。つまり、最終的な目標であるバイトコード生成に必要なものを取得するためクエリ(照会要求)を生成し、クエリに従って連鎖的に処理を進めていきます。クエリの実行結果はキャッシュされるため、理想的にはスクリプトの差分だけコンパイルするincremental compileが可能となります。 diff --git a/doc/JA/compiler/refinement_subtyping.md b/doc/JA/compiler/refinement_subtyping.md new file mode 100644 index 00000000..6bd48d71 --- /dev/null +++ b/doc/JA/compiler/refinement_subtyping.md @@ -0,0 +1,156 @@ +# 篩型 + +篩型とは、以下のような型である。 + +```erg +{I: Int | I >= 0} +{S: StrWithLen N | N >= 1} +{T: (Ratio, Ratio) | T.0 >= 0; T.1 >= 0} +``` + +1,2番目のようにレイアウトが自明な場合は以下のように省略できる。 + +```erg +{I: Int | I >= 0} +StrWithLen N | N >= 1 +``` + +ErgではEnum, Interval, Refined dependent type型を篩型に変換してしまうことで、型判定を可能にしている。 + +## 篩型への変換 + +[篩型]の項では、区間型および列挙型は、篩型の糖衣構文であると述べた。それぞれ、以下のように変換される。 + +* {0} -> {I: Int | I == 0} +* {0, 1} -> {I: Int | I == 0 or I == 1} +* 1.._ -> {I: Int | I >= 1} +* 1<.._ -> {I: Int | I > 1} -> {I: Int | I >= 2} +* {0} or 1.._ -> {I: Int | I == 0 or I >= 1} +* {0} or {-3, -2} or 1.._ -> {I: Int | I == 0 or (I == -2 or I == -3) or I >= 1} +* {0} and {-3, 0} -> {I: Int | I == 0 and (I == -3 or I == 0)} +* {0} not {-3, 0} or 1.._ -> {I: Int | I == 0 and not (I == -3 or I == 0) or I >= 1} + +## 型の型判定 + +篩型Aが別の篩型Bのサブタイプであるか判定するアルゴリズムを説明する。形式的には、サブタイプ判定は以下のように定義される。 + +```console +A < B <=> ∀a∈A; a ∈ B +``` + +具体的には以下の推論規則を適用する。ブール式は簡約済みとする(よって、`(A or B or C)`などは現れない)。 + +* 区間化規則(型定義から自動で行われる) + * `Nat` => `{I: Int | I >= 0}` +* 切上規則 + * `{I: Int | I < n}` => `{I: Int | I <= n-1}` + * `{I: Int | I > n}` => `{I: Int | I >= n+1}` + * `{R: Ratio | R < n}` => `{R: Ratio | R <= n-ε}` + * `{R: Ratio | R > n}` => `{R: Ratio | R >= n+ε}` +* 反転規則 + * `{A not B}` => `{A and (not B)}` +* ド・モルガンの法則 + * `{not (A or B)}` => `{not A and not B}` + * `{not (A and B)}` => `{not A or not B}` +* 分配規則 + * `{A and (B or C)} <: D` => `{(A and B) or (A and C)} <: D` => `({A and B} <: D) and ({A and C} <: D)` + * `{(A or B) and C} <: D` => `{(C and A) or (C and B)} <: D` => `({C and A} <: D) and ({C and B} <: D)` + * `D <: {A or (B and C)}` => `D <: {(A or B) and (A or C)}` => `(D <: {A or B}) and (D <: {A or C})` + * `D <: {(A and B) or C}` => `D <: {(C or A) and (C or B)}` => `(D <: {C or A}) and (D <: {C or B})` + * `{A or B} <: C` => `({A} <: C) and ({B} <: C)` + * `A <: {B and C}` => `(A <: {B}) and (A <: {C})` +* 終端規則 + * {I: T | ...} <: T = True + * {} <: _ = True + * _ <: {...} = True + * {...} <: _ = False + * _ <: {} == False + * {I >= a and I <= b} (a < b) <: {I >= c} = (a >= c) + * {I >= a and I <= b} (a < b) <: {I <= d} = (b <= d) + * {I >= a} <: {I >= c or I <= d} (c >= d) = (a >= c) + * {I <= b} <: {I >= c or I <= d} (c >= d) = (b <= d) + * {I >= a and I <= b} (a <= b) <: {I >= c or I <= d} (c > d) = ((a >= c) or (b <= d)) + * 基本式 + * {I >= l} <: {I >= r} = (l >= r) + * {I <= l} <: {I <= r} = (l <= r) + * {I >= l} <: {I <= r} = False + * {I <= l} <: {I >= r} = False + +ブール式の簡約規則は以下の通り。min, maxは除去できない可能性がある。また、複数並んだor, andはネストしたmin, maxに変換される。 + +* 順序化規則 + * `I == a` => `I >= a and I <= a` + * `i != a` => `I >= a+1 or I <= a-1` +* 恒真規則 + * `I >= a or I <= b (a < b)` == `{...}` +* 恒偽規則 + * `I >= a and I <= b (a > b)` == `{}` +* 入替規則 + * 順序式は`I >= n`, `I <= n`の順に入れ替える。 +* 延長規則 + * `I == n or I >= n+1` => `I >= n` + * `I == n or I <= n-1` => `I <= n` +* 最大規則 + * `I <= m or I <= n` => `I <= max(m, n)` + * `I >= m and I >= n` => `I >= max(m, n)` +* 最小規則 + * `I >= m or I >= n` => `I >= min(m, n)` + * `I <= m and I <= n` => `I <= min(m, n)` +* 除去規則 + * 左辺にある`I == n`は、右辺に`I >= a (n >= a)`か`I <= b (n <= b)`か`I == n`があるとき除去できる。 + * 左辺の等式をすべて除去できなければFalse + +e.g. + +```python +1.._ <: Nat +=> {I: Int | I >= 1} <: {I: Int | I >= 0} +=> {I >= 1} <: {I >= 0} +=> (I >= 0 => I >= 1) +=> 1 >= 0 +=> True +# {I >= l} <: {I >= r} == (l >= r) +# {I <= l} <: {I <= r} == (l <= r) +``` + +```python +{I: Int | I >= 0} <: {I: Int | I >= 1 or I <= -3} +=> {I >= 0} <: {I >= 1 or I <= -3} +=> {I >= 0} <: {I >= 1} or {I >= 0} <: {I <= -3} +=> False or False +=> False +``` + +```python +{I: Int | I >= 0} <: {I: Int | I >= -3 and I <= 1} +=> {I >= 0} <: {I >= -3 and I <= 1} +=> {I >= 0} <: {I >= -3} and {I >= 0} <: {I <= 1} +=> True and False +=> False +``` + +```python +{I: Int | I >= 2 or I == -2 or I <= -4} <: {I: Int | I >= 1 or I <= -1} +=> {I >= 2 or I <= -4 or I == -2} <: {I >= 1 or I <= -1} +=> {I >= 2 or I <= -4} <: {I >= 1 or I <= -1} + and {I == -2} <: {I >= 1 or I <= -1} +=> {I >= 2} <: {I >= 1 or I <= -1} + and {I <= -4} <: {I >= 1 or I <= -1} + and + {I == -2} <: {I >= 1} + or {I == -2} <: {I <= -1} +=> {I >= 2} <: {I >= 1} + or {I >= 2} <: {I <= -1} + and + {I <= -4} <: {I >= 1} + or {I <= -4} <: {I <= -1} + and + False or True +=> True or False + and + False or True + and + True +=> True and True +=> True +``` diff --git a/doc/JA/compiler/trait_method_resolving.md b/doc/JA/compiler/trait_method_resolving.md new file mode 100644 index 00000000..1e01d778 --- /dev/null +++ b/doc/JA/compiler/trait_method_resolving.md @@ -0,0 +1,95 @@ +# パッチメソッドの解決 + +`Nat`は0以上の`Int`、つまり`Int`のサブタイプである。 +本来`Nat`はPythonのクラス階層には存在しない。Ergはこのパッチのメソッドをどうやって解決するのだろうか? + +```erg +1.times do + log "hello, world" +``` + +`.times`は`NatImpl`のパッチメソッドである。 +`1`は`Int`のインスタンスであるので、まず`Int`のMRO(Method Resolution Order)を辿って探索する。 +Ergは`Int`のMROに`Int`, `Object`を持っている。これはPython由来である(Pythonにおいて`int.__mro__ == [int, object]`)。 +`.times`メソッドはそのどちらにも存在しない。ここからは、そのサブタイプの探索に入る。 + +~ + +整数は明らかにその上位型に実数や複素数、ひいては数全体を持つはずだが、Pythonと互換性をもつレイヤーではその事実は現れない。 +だが実際にErgでは`1 in Complex`や`1 in Num`は`True`となる。 +`Complex`に至っては、`Int`と継承関係にないクラスであるのに、型として互換性があると判断されている。一体どうなっているのか。 + +~ + +あるオブジェクトに対して、その属する型は無数に存在する。 +だが実際に考えなくてはならないのはメソッドを持つ型、すなわち名前を持つ型のみである。 + +Ergコンパイラは、全ての提供メソッドとその実装を持つパッチ・型のハッシュマップを持っている。 +このテーブルは型が新たに定義されるたびに更新される。 + +```erg +provided_method_table = { + ... + "foo": [Foo], + ... + ".times": [Nat, Foo], + ... +} +``` + +`.times`メソッドを持つ型は`Nat`, `Foo`である。これらの中から、`{1}`型に適合するものを探す。 +適合判定は二種類ある。篩型判定とレコード型判定である。篩型判定から行われる。 + +## 篩型判定 + +候補の型が`1`の型`{1}`と互換性があるか確認する。篩型の中で`{1}`と互換性があるのは、`{0, 1}`, `0..9`などである。 +`0..1 or 3..4`, `-1..2 and 0..3`などの有限要素の代数演算型は、基底型として宣言すると篩型に正規化される(つまり、`{0, 1, 3, 4}`, `{0, 1, 2}`にする)。 +今回の場合、`Nat`は`0.._ == {I: Int | I >= 0}`であるので、`{1}`は`Nat`と互換性がある。 + +## レコード型判定 + +候補の型が1のクラスである`Int`と互換性を持つか確認する。 +その他、`Int`のパッチである、またその要求属性を`Int`がすべて持つ場合も互換性がある。 + +~ + +というわけで、`Nat`が適合した。ただ`Foo`も適合してしまった場合は、`Nat`と`Foo`の包含関係によって判定される。 +すなわち、サブタイプのメソッドが選択される。 +両者に包含関係がない場合は、コンパイルエラーとなる(これはプログラマーの意図に反したメソッドが実行されないための安全策である)。 +エラーを解消させるためには、パッチを明示的に指定する必要がある。 + +```erg +o.method(x) -> P.method(o, x) +``` + +## 全称パッチのメソッド解決 + +以下のようなパッチを定義する。 + +```erg +FnType T: Type = Patch T -> T +FnType.type = T +``` + +`FnType`パッチのもとで以下のようなコードが可能である。これはどのように解決されるのだろうか。 + +```erg +assert (Int -> Int).type == Int +``` + +まず、`provided_method_table`には`FnType(T)`が以下の形式で登録される。 + +```erg +provided_method_table = { + ... + "type": [FnType(T)], + ... +} +``` + +`FnType(T)`のパッチする型が適合するかチェックされる。この場合、`FnType(T)`のパッチ型は`Type -> Type`である。 +これは`Int -> Int`に適合する。適合したら、単相化を行って置換する(`T -> T`と`Int -> Int`のdiffを取る。`{T => Int}`)。 + +```erg +assert FnType(Int).type == Int +``` diff --git a/doc/JA/compiler/transpile.md b/doc/JA/compiler/transpile.md new file mode 100644 index 00000000..7edee8c6 --- /dev/null +++ b/doc/JA/compiler/transpile.md @@ -0,0 +1,92 @@ +# ErgコードはPythonコードにどのようにトランスパイルされるか? + +正確には、ErgコードはPythonバイトコードにトランスパイルされます。 +しかしPythonバイトコードはほぼPythonコードに復元できるので、ここでは等価なPythonコードを例として上げています。 +ちなみに、ここで紹介する例は最適化レベルの低いものです。 +さらに高度な最適化が施されると、実体を生成する必要のないものは消去されます。 + +## Record, Record type + +namedtupleにトランスパイルされます。 +namedtupleについては、[こちら](https://docs.python.jp/3/library/collections.html#collections.namedtuple)を参照してください。 +似たような機能にdataclassがありますが、dataclassは`__eq__`や`__hash__`が自動実装されるなどの影響で少しパフォーマンスが落ちます。 + +```erg +Employee = Class {.name = Str; .id = Int} + +employee = Employee.new({.name = "John Smith"; .id = 100}) + +assert employee.name == "John Smith" +``` + +```python +from typing import NamedTuple + +class Employee(NamedTuple): + __records__ = ['name', 'id'] + name: str + id: int + +employee = Employee('John Smith', 100) + +assert employee.name == 'John Smith' +``` + +また、更に最適化できる場合は単なるタプルに変換されます。 + +## Polymorphic Type + +## Instant Scope + +名前空間内での衝突が起きない場合は、単にマングリングして展開されます。 +`x::y`などの名前はバイトコードで使用されるものでPythonコードと対応させる事はできませんが、無理やり表現すると以下のようになります。 + +```erg +x = + y = 1 + y + 1 +``` + +```python +x::y = 1 +x = x::y + 1 +``` + +衝突する場合は、内部的にしか参照できない関数を定義して使用します。 + +```erg +x = + y = 1 + y + 1 +``` + +```python +def _(): + x = 1 + y = x + return y + 1 +x = _() +``` + +## Overloading + +マングリングを使用しています。 + +## Visibility + +公開変数に関してはPythonのデフォルトなので何もしません。 +非公開変数はマングリングで対処しています。 + +```erg +x = 1 +y = + x = 2 + assert module::x == 2 +``` + +```python +module::x = 1 +y::x = 2 +assert module::x == 2 +y = None +``` diff --git a/doc/JA/compiler/ty_bound_resolving.md b/doc/JA/compiler/ty_bound_resolving.md new file mode 100644 index 00000000..c4458e17 --- /dev/null +++ b/doc/JA/compiler/ty_bound_resolving.md @@ -0,0 +1 @@ +# 型制約の解決 diff --git a/doc/JA/compiler/type_var_normalization.md b/doc/JA/compiler/type_var_normalization.md new file mode 100644 index 00000000..66bb2962 --- /dev/null +++ b/doc/JA/compiler/type_var_normalization.md @@ -0,0 +1,39 @@ +# 正規化 + +* Ergの型引数正規化はSymPyのsimplify関数を参考にしています。 + +例えば`concat: |T, M, N|([T; M], [T; N] -> [T; M+N])`を定義するとき、型変数・引数を具体化せずに一致判定を行わなくてはならない。 +等式判定は自ずと限界があるが、現時点で可能な判定とその方式は以下の通り。 + +* 加算・乗算の対称性: + + `n+m == m+n` + + 型変数は文字列としてソートし正規化する。 + +* 加算と乗算、減算と除算の等価性: + + `n+n == 2*n` + + Σ[c] x == c*xに正規化する(cは定数)。 + 定数は二項演算の左辺に置いて正規化する。 + +* 複式の等価性: + + `n+m+l == m+n+l == l+m+n == ...` + `n+m*l == m*l+n` + + ソートで正規化して判定する。 + 乗算・除算のブロックは加算・減算より左側に出す。ブロック同士は最左辺の型変数を比較してソートする。 + +* 基本的な不等式: + + `n > m -> m + 1 > n` + +* 等式: + + `n >= m and m >= n -> m == n` + +* 不等式の推移性: + + `n > 0 -> n > -1` diff --git a/doc/JA/compiler/unification.md b/doc/JA/compiler/unification.md new file mode 100644 index 00000000..127ce247 --- /dev/null +++ b/doc/JA/compiler/unification.md @@ -0,0 +1,34 @@ +# 単一化(Unification) + +## 出現検査(Occur Check) + +Ergは再帰型を許可するが、意味の無い、ナンセンスな再帰型はエラーとする。ナンセンスな型とは、具体的な型を挙げることができない型である。そのチェックするのが出現検査である。 +下の例は、意味のある型である。 + +```erg +T = Int +T = Int or Option T # Int or Option Int or Option Option Int or ... +Maybe T = Option T +T = Int or T # will be warned (should just be `Int`) +``` + +対して以下は、意味をなさない型である。 + +```erg +T = T +T = Option T +T = T or T + +T = U +U = T + +T X = T X +U T = T U +``` + +判定のアルゴリズムは、大まかにはこうである。 + +* ある未判定の型`T`を判定する時、それが「定義済みの型を含むor型」でなく、`T`を含む場合はエラーとなる。 +* 多項カインドが単純型として扱われた場合はエラーとなる。 + +注意として、Ergは`F = F -> T`(`T`は任意の型)型の存在を許す。 diff --git a/doc/JA/dev_guide/branches.md b/doc/JA/dev_guide/branches.md new file mode 100644 index 00000000..b280bd50 --- /dev/null +++ b/doc/JA/dev_guide/branches.md @@ -0,0 +1,46 @@ +# ブランチの命名と運用方針 + +* main, beta, ver-*はread-onlyブランチである。直接のコミットは受け付けず、下流ブランチからのマージによってのみ更新される。 +* 基本的に開発は`dev`ブランチ一本で行う。どうしてもブランチを切らないと作業しにくい場合のみ`feature-*`ブランチか`issue-*`ブランチを作成する。 + +## major + +* 最新のメジャーリリース +* 以下の条件を満たしたbetaをマージする + +* コンパイルが成功する +* 全てのテストが成功する +* インストーラを用意する + +## beta + +* 最新のベータリリース +* 以下の条件を満たしたdevをマージする + +* コンパイルが成功する +* 全てのテストが成功する + +## main + +* メイン開発ブランチ + +* コンパイルが成功する + +## ver-* + +* 過去のメジャーリリース +* majorを切って作る +* 深刻なバグが見つかった場合はブランチごと削除する + +## feature-* + +* 特定の一機能を開発するブランチ +* mainを切って作る + +* 条件なし + +## issue-* + +* 特定のissueを解決するブランチ + +* 条件なし diff --git a/doc/JA/dev_guide/doc_guideline.md b/doc/JA/dev_guide/doc_guideline.md new file mode 100644 index 00000000..949c6929 --- /dev/null +++ b/doc/JA/dev_guide/doc_guideline.md @@ -0,0 +1,12 @@ +# 書式 + +以下のルールに従っていないドキュメントはすべて修正の対象となる。 + +* コードコメント、または内部用のドキュメントは、である調で書く。 +* 外部に見せるドキュメントは、ですます調で書く。 +* ドキュメント内で初出の用語は、必ず定義や意味、またはリンクを併記する。 +* ただし書きとしての()は、補助的ではあるものの本文の理解に必要な文の場合のみ使用し、本文の理解に必須でない文は脚注を使用する[1](#1)。 + +--- + +1 脚注の書き方はこれを参照すること。 [↩](#f1) diff --git a/doc/JA/dev_guide/env.md b/doc/JA/dev_guide/env.md new file mode 100644 index 00000000..a6b87e9f --- /dev/null +++ b/doc/JA/dev_guide/env.md @@ -0,0 +1,12 @@ +# Development Environment + +## Rust + +* ver >= 1.58.0 +* 2021 edition + +## Recommended Development Environment + +* Editor: Visual Studio Code +* VSCode Extensions: Rust-analyzer, GitLens, Git Graph, GitHub Pull Requests and Issues +* OS: Windows 10/11 | Ubuntu 20.04 | MacOS Monterey diff --git a/doc/JA/dev_guide/faq_syntax.md b/doc/JA/dev_guide/faq_syntax.md new file mode 100644 index 00000000..67c89ffa --- /dev/null +++ b/doc/JA/dev_guide/faq_syntax.md @@ -0,0 +1,116 @@ +# Erg design's "Why" and Answers + +## なぜ所有権システムがあるのにGCも共存させているのですか? + +Ergが所有権システムを導入した動機は、Rustのような「GCに頼らないメモリ管理」のためではないからです。 +そもそも、現在のところErgはPythonバイトコードに落とし込まれる言語のため、結局GCは使用されます。 +Ergが所有権システムを導入した狙いは「可変状態の局所化」です。Ergでは、可変オブジェクトに所有権の概念がついています。 +これは、共有可変状態がバグの温床になりやすく、さらに型安全性まで侵害すること(詳しくは[こちら](../syntax/type/advanced/shared.md#共有参照SharedReference)を参照)をみての判断です。 + +## なぜ型パラメータを囲むカッコが<>や[]ではなく||なのですか? + +`<>`や`[]`では文法の衝突が起きるからです。 + +```erg +# []版 +id[T: Type] [t]: [T] = t +y = id[Int] # これは関数? +# <>版 +id {t: T} = t +y = (id 1) # これはタプル? +# {}版 +id{T: Type} {t: T} = t +y = id{Int} # これは関数? +# ||版 +id|T: Type| t: T = t +y = id|Int| # OK +``` + +## {i = 1}の型は{i = Int}ですが、OCamlなどでは{i: Int}となっています。なぜErgは前者の構文を採用したのですか? + +Ergは型自体も値として扱える設計になっているためです。 + +```erg +A = [Int; 3] +assert A[2] == Int +T = (Int, Str) +assert T.1 == Str +D = {Int: Str} +assert D[Int] == Str +S = {.i = Int} +assert S.i == Int +``` + +## Ergにマクロが実装される予定はありますか? + +現在のところありません。マクロには大きく分けて4つの目的があります。1つ目は、コンパイル時計算です。これは、Ergではコンパイル時関数がその役割を担っています。 +2つ目は、コード実行の遅延です。これはdoブロックで代用できます。3つ目は処理の共通化ですが、これについては多相関数と全称型がマクロよりもよい解決策です。4つ目は自動コード生成ですが、これは可読性の低下をもたらすためErgではあえて実現出来ないようにしています。 +このようにマクロの持つ機能の大部分はErgの型システムが肩代わりしているため、導入のモチベーションがないのです。 + +## なぜErgには例外機構がないのですか? + +多くの場合において、`Result`型によるエラーハンドリングがより良い解決策であるからです。`Result`型は比較的新しいプログラミング言語では採用されている事例の多いエラーハンドリング手法です。 + +Ergでは`?`演算子によってエラーをあまり意識せずに書けます。 + +```erg +read_file!() = + f = open!("foo.txt")? # 失敗したらエラーをすぐさま返すので、fはFile型 + f.read_all!() + +# tryプロシージャで例外のような捕捉処理も可能 +try!: + do! + s = read_file!()? + print! s + e => + # エラー発生時に実行するブロック + print! e + exit 1 +``` + +Pythonの関数を導入する場合は、デフォルトではすべて例外を含む関数とみなされ、戻り値型は`Result`型となります。 +例外を送出しないとわかっている場合は、`assert`で明示します。 + +また、Ergが例外機構を導入していない理由として、並列プログラミングのための機能導入を予定しているからというのもあります。 +というのも、例外機構は並列実行と相性が悪い(並列実行により複数の例外が発生した場合などの対処が面倒など)のです。 + +## ErgはバッドプラクティスとされているPythonの機能を排除しているように見受けられますが、なぜ継承は廃止しなかったのですか? + +Pythonのライブラリには継承されることを前提に設計されているクラスがあり、継承を完全に廃止してしまうとこれらの運用に問題が生じるためです。 +とはいえ、Ergではデフォルトでクラスがfinalであり多重・多層継承も原則禁止されているので、継承は比較的安全に使用できます。 + +## なぜ多相関数のサブタイプ推論はデフォルトで記名的トレイトを指すのですか? + +デフォルトで構造的トレイトを指すと、型指定が複雑になり、プログラマの意図しない挙動を混入させる恐れがあるためです。 + +```erg +# If T is a subtype of a structural trait... +# f: |T <: Structural Trait {.`_+_`: Self.(Self) -> Self; .`_-_`: Self.(Self) -> Self| (T, T) -> T +f|T| x, y: T = x + y - x +# T is a subtype of a nominal trait +# g: |T <: Add and Sub| (T, T) -> T +g|T| x, y: T = x + y - x +``` + +## Ergには独自演算子を定義する機能は実装されませんか? + +A: その予定はありません。一番の理由は、独自演算子の定義を許可すると、その結合順位をどうするかという問題が立ち上がるからです。独自演算子の定義が可能なScalaやHaskellなどではそれぞれ違った対応をしていますが、これは解釈の違いを生みかねない文法である証拠とみることができます。また独自演算子には可読性の低いコードが作られかねないというデメリットもあります。 + +## なぜErgでは+=のような拡張代入演算子を廃止してしまったのですか? + +まず、Ergでは変数の可変性がありません。つまり、再代入ができません。一旦ある変数に紐付けられたオブジェクトは、スコープを外れて解放されるまでずっとその変数に束縛されます。Ergで可変性とはオブジェクトの可変性を意味します。これが分かれば、話は簡単です。例えば`i += 1`は`i = i + 1`を意味しますが、変数は再代入不可なので、このような構文は不正です。もう一つ、Ergの設計原則として演算子は副作用を持たないというものがあります。Pythonも概ねそうなっていますが、Dictなど一部のオブジェクトでは拡張代入演算子がオブジェクトの内部状態を変更します。これはあまり美しい設計とは言えません。 +そういうわけで拡張代入演算子はまるごと廃止されています。 + +## Ergはなぜ副作用のあるオブジェクトを文法的に特別扱いしているのですか? + +副作用の局所化は、コードのメンテナビリティにおいて重要な要素です。 + +しかし、確かに副作用を言語的に特別扱いしないで済む方法もなくはありません。例えば、プロシージャは代数的効果(型システム上の機能)で代用できます。 +しかしこのような合一化は常に正しいとは限りません。例えば、Haskellは文字列を特別扱いせず単なる文字の配列としましたが、この抽象化は間違っていました。 + +どのような場合に、合一化は間違っていたと言えるでしょうか。一つの指標は、「その合一化によってエラーメッセージが見にくくなるか」です。 +Erg設計者は副作用を特別扱いしたほうがエラーメッセージは読みやすくなると判断しました。 + +Ergは強力な型システムを持っていますが、全てを型で支配するわけではないのです。 +もしそうしてしまったら、Javaがすべてをクラスで支配しようとしたのと同じ末路を辿るでしょう。 diff --git a/doc/JA/dev_guide/features.md b/doc/JA/dev_guide/features.md new file mode 100644 index 00000000..afd0e19c --- /dev/null +++ b/doc/JA/dev_guide/features.md @@ -0,0 +1,11 @@ +# `erg` build feature + +## debug + +デバッグモードにする。これにより、Erg内部での挙動が逐次ログ表示される。 +Rustのdebug_assertionsフラグとは独立。 + +## japanese + +システム言語を日本語にする。 +Erg内部のオプション、ヘルプ(help, copyright, licenseなど)、エラー表示は日本語化が保証される。 diff --git a/doc/JA/dev_guide/index.md b/doc/JA/dev_guide/index.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/dev_guide/rust_code_guideline.md b/doc/JA/dev_guide/rust_code_guideline.md new file mode 100644 index 00000000..b65b1830 --- /dev/null +++ b/doc/JA/dev_guide/rust_code_guideline.md @@ -0,0 +1,24 @@ +# Rustコードに関するガイドライン + +## ローカルルール + +* デバッグ用の出力には`log!`を使用する(release時にも必要な出力処理は`println!`等を使用する)。 +* 未使用・または内部用の(privateかつ特定の機能のみに使用する)変数・メソッドは先頭に`_`を1つ付ける。予約語との衝突を回避したい場合は後ろに`_`を1つ付ける。 + +## 奨励されるコード + +* 狭い範囲の数値やboolの代わりにドメイン固有のEnumを定義する。 +* アクセス修飾子は必要最小限のものとする。公開する場合でも`pub(mod)`や`pub(crate)`を優先的に使用する。 +* for式でのiterableオブジェクトは明示的にイテレータに変換する(`for i in x`ではなく`for i in x.iter()`)。 +* 遅延評価。例えば、`default`がリテラル以外の場合は`unwrap_or`ではなく`unwrap_or_else`を使用する。 + +## 奨励されないコード + +* return type overloadingを多用する。具体的には自明でない`.into`を多用するコード。これは型推論結果が直感に反する場合があるためである。この場合は代わりに`from`を使うことを推奨する。 + +* `Deref`を多用する。これは実質的に継承と同じ問題を引き起こす。 + +## 文脈により判断が変わるコード + +* 未使用のヘルパーメソッドを定義する。 +* `unwrap`, `clone`を多用する。場合によってはそうするより他にないものもある。 diff --git a/doc/JA/dev_guide/terms.md b/doc/JA/dev_guide/terms.md new file mode 100644 index 00000000..fa079ffb --- /dev/null +++ b/doc/JA/dev_guide/terms.md @@ -0,0 +1,839 @@ +# 用語辞典 + +## 記号 + +### ! + +プロシージャ、または可変型であることを示すために識別子の末尾に付与するマーカー。 +または、可変化演算子。 + +### [#](../syntax/00_basic.md/#コメント) + +### $ + +### % + +### & + +### ′ (single quote) + +### () + +### * + +### + + +### , + +### − + +### -> + +### . + +### / + +### : + +### :: + +### ; + +### < + +### <: + +### << + +### <= + +### = + +### == + +### => + +### > + +### >> + +### >= + +### ? + +### @ + +### [] + +### \ + +### ^ + +### ^^ + +### _ + +### `` + +### {} + +### {:} + +### {=} + +### | + +### || + +### ~ + +## A + +### [algebraic type] + +### [And] + +### [and] + +### [assert] + +### [attribute] + +## B + +### [Base] + +### [Bool] + +## C + +### [Class] + +## D + +### Deprecated + +### [distinct] + +## E + +### [enum type] + +### [Eq] + +### [Erg] + +## F + +### [for] + +## G + +## H + +## I + +### [if] + +### [import] + +### [in] + +### [Int] + +## J + +## K + +## L + +### let-polymorphism -> [ランク1多相] + +### [log] + +## M + +### [match] + +## N + +### [Nat] + +### Never + +### None + +### [Not] + +### [not] + +## O + +### [Option] + +### [Or] + +### [or] + +### [Ord] + +## P + +### panic + +### [print!](../syntax/../API/procs.md#print) + +### [Python] + +## Q + +## R + +### ref + +### ref! + +### [Result] + +### [rootobj] + +## S + +### self + +### [Self](../syntax/type/special.md) + +### [side-effect](../syntax/07_side_effect.md) + +### [Str] + +## T + +### Trait + +### [True] + +### [Type] + +### [type] + +## U + +## V + +## W + +### [while!] + +## X + +## Y + +## Z + +## あ行 + +### [アサーション] + +コード中である条件が成立しているか(典型的には実行時に)調べること。`assert`関数などを使用して行う。 + +```erg +sum = !0 +for! 0..10, i => + sum.add! i + +assert sum == 55 +``` + +### 値オブジェクト + +Ergにおいては、基本オブジェクトと同等。コンパイル時に評価でき、自明な比較方法を持つ。 + +### [アタッチメントパッチ](../syntax/29_decorator.md#attach) + +トレイトに標準の実装を与えるパッチ。 + +### アドホック多相 -> [オーバーロードの禁止](../syntax/type/overloading.md) + +いわゆるオーバーロードによる多相。 + +### アトリビュート -> [属性] + +`x.y`という識別子における`y`の部分。 + +### アリティ + +演算子がいくつのオペランドを取るか。 + +### [依存型](../syntax/type/dependent_type.md) + +値(慣用的には、型ではない)を引数にとる型。 + +### イミュータブル -> [不変] + +対象が変更されないことを示す。 +他の言語では変数にもイミュータブル/ミュータブル性があるが、Ergでは変数はすべてイミュータブル。 + +### 引数(いんすう) -> [引数(ひきすう)] + +### インスタンス + +クラスによって作られたオブジェクト。クラス型の要素。 + +### [インスタントブロック](../syntax/00_basic.md#式セパレータ) + +```erg +x = + y = f(a) + z = g(b, c) + y + z +``` + +### インデックス + +`x[i]`という形式、またはそれにおける`i`の部分。`x`をIndexableオブジェクトという。 + +### [インデント](../syntax/00_basic.md#インデント) + +スペースに寄って文を右に寄せること。字下げ。 +Ergはインデントによってブロックを表現する。これをオフサイドルールという。 + +### エイリアス + +別名のこと。 + +### エラー + +仕様で定められた異常状態。 + +* [エラーハンドリング] + +### [演算子](../syntax/06_operator.md) + +オペランドに演算を適用するオブジェクト。またはそのオブジェクトを示す記号。 + +* [演算子の結合強度] + +### オーバーライド + +サブクラスでスーパークラスのメソッドを上書きすること。 +Ergではオーバーライドの際`Override`デコレータを付けなくてはならない。 + +### [オーバーロードの禁止](../syntax/type/overloading.md) + +### オフサイドルール -> [インデント](../syntax/00_basic.md#インデント) + +### [オブジェクト] + +* オブジェクト指向 + +### オペランド -> [被演算子](../syntax/06_operator.md) + +### オペレーター -> [演算子](../syntax/06_operator.md) + +## か行 + +### [カインド](../syntax/type/advanced/kind.md) + +いわゆる型の型。 + +### [可視性] + +識別子が外部(スコープ外、または別モジュール、別パッケージ)から参照可能であるかという性質。 + +### [型] + +項をグルーピングするオブジェクト。 + +* [型指定] +* [型消去](../syntax/type/advanced/erasure.md) +* [型推論] +* [型注釈](../syntax/type/conv_type.md) +* [型引数] +* [型付加](../syntax/type/advanced/erasure.md) +* [型変数](../syntax/type/type_variable.md) +* [型制約] + +### [ガード] + +### カプセル化 + +実装詳細を隠蔽すること。 + +### [可変] + +イミュータブルでないこと。 + +* [可変オブジェクト] +* [可変型] +* [可変参照] +* [可変配列] +* [可変長引数] + +### [関数](../syntax/04_function.md) + +副作用のないサブルーチン。 + +* [関数型プログラミング](../syntax/23_scope.md#可変状態の回避関数型プログラミング) + +### 基底型 + +### 記名的 + +対称の構造ではなく名前によって区別すること。 + +* [記名型] -> [クラス](../syntax/type/04_class.md) +* [記名化] +* [記名的部分型](../syntax/type/05_nst_vs_sst.md) + +### キャプチャ -> [クロージャ] + +### [共変] + +Ergにおいては、`T <: U`のとき`K(T) <: K(U)`ならば`K`は共変であるという。 + +### [キーワード引数] + +関数呼び出し`f(k: v)`の形式における`k`のこと。実引数を順番ではなく仮引数名で指定できる。 + +### 空集合 -> [{}] + +### 区間 + +* [区間型](../syntax/type/11_interval.md) +* 区間演算子 + +### 組み込み + +Erg標準APIのうち、.erファイル内で実装されていないAPI。 + +### [クラス](../syntax/type/04_class.md) + +継承機能を持つ構造体・抽象データ型。Ergにおいては記名的サブタイピング、およびオーバーライドを実現するための型である。 +他の言語ではモジュールや型の責務を担う場合もあるが、Ergにおいては、モジュールはモジュールオブジェクト、型は型オブジェクトがその責務を担う。 + +### [クロージャ] + +### [グローバル変数] + +### [クローン] + +### [継承](../syntax/type/07_inheritance.md) + +あるクラスを上位集合としたクラスを定義すること。 +継承元のクラスはスーパークラス、継承先のクラスはサブクラスと呼ばれる。 +サブクラスはスーパークラスの機能をすべて持つ。 + +### 高階 + +* [高階カインド](../syntax/type/advanced/kind.md) +* 高階型 +* 高階関数 + +### [公開変数] + +### [構造的部分型] + +### ~~後方参照~~ -> [前方参照] + +### [コピー] + +### コメント + +### [コレクション](../syntax/10_array.md) + +### コロン -> [:] + +### [コンストラクタ](../syntax/type/04_class.md) + +### コンテナ + +### コンパイラ + +### [コンパイル時計算](../syntax/04_function.md#コンパイル時関数) + +### コンマ -> [,] + +## さ行 + +### 再帰 + +自身を参照すること。 + +* 再帰型 +* [再帰関数](../syntax/04_function.md#再帰関数) + +### サブスクリプト -> [インデックス] + +### [サブタイピング多相](../syntax/type/overloading.md) + +サブタイピングによる多相。サブタイピングとは、型において集合の包含関係に対応するものである。 + +### サブルーチン + +処理をモジュール化したオブジェクト。Ergでは関数、プロシージャ、およびメソッドの総称。 + +### [参照](../syntax/18_memory_management.md#借用) + +* 参照オブジェクト +* [参照カウント(RC)](../syntax/18_memory_management.md#メモリ管理) +* 参照等価性 -> [副作用](../syntax/07_side_effect.md) + +### [識別子](../syntax/02_variable.md/#代入) + +### シグネチャ + +* 型シグネチャ + +### [辞書](../syntax/11_dict.md) + +### [自然数] -> [Nat] + +### ジェネリクス -> [全称型] + +### ジェネレータ + +### [射影型] + +### 借用 -> [参照](../syntax/18_memory_management.md#借用) + +### [シャドーイング](../syntax/02_name.md#変数) + +ある変数に対し、内側のスコープで同名の変数を定義してその参照を上書きすること。 + +### 種 -> [カインド](../syntax/type/advanced/kind.md) + +おおまかには型の型。 + +### [集合] -> [セット] + +ErgにおいてはSetオブジェクトのこと。 + +### 述語 + +* [述語関数] + +Bool型を返す関数。 + +### 条件分岐 + +### [所有権] + +オブジェクトのユニーク性に関する概念。 +オブジェクトの所有権を持つ場合、オブジェクトの可変参照を取ることができる。 + +### 真偽型 -> [Bool] + +### シングルトン + +インスタンスを一つしか生成できないクラスから生成されたインスタンス。また、クラスのインスタンスが1つしか生成されないことを保証するデザインパターンのこと。 + +### [シンボル] -> [識別子](../syntax/02_name.md) + +* [シンボル化] + +### [スクリプト](../syntax/00_basic.md#スクリプト) + +Ergプログラムが記述されたファイル。 + +### スコープ + +変数管理における単位。外側のスコープでは内側のスコープに存在する変数を参照できない。 +また、スコープを抜けたときに、参照カウントが0であるオブジェクトは解放される。 + +### スプレッド演算子 -> [展開代入] + +### [スライス](../syntax/10_array.md#スライス) + +`x[a..b]`の形式で生成される、配列の部分列を表すオブジェクト。 + +### 制御文字 + +### [整数] -> [Int] + +自然数に負数を合わせた集合。 + +### [セット](../syntax/12_set.md) + +### セミコロン -> [;] + +### [宣言](../syntax/03_declaration.md) + +変数を明示的に型付けること。 + +### 全称 + +* 全称型 -> [多相型](../syntax/type/quantified.md) + * 閉じた全称型 + * 開いた全称型 +* 全称関数 -> 多相関数 +* 全称量化 + +### 前置演算子 + +`∘x`の形式で適用される演算子`∘`。 + +### 相互再帰 + +### 添字 -> [インデックス] + +### [属性] + +* [属性的部分型] + +## た行 + +### [代数](../syntax/02_name.md) + +* [代数演算型](../syntax/type/13_algebraic.md) +* 代数的データ型 + +### [代入](../syntax/02_variable.md/#代入) + +### 多重 + +* [多重継承](../syntax/type/07_inheritance.md/#多重継承の禁止) +* 多重代入 +* 多重定義 -> [オーバーロードの禁止] + +### 多相 + +* [多相型](../syntax/type/quantified.md) +* 多相関数 + +### 多態 -> [ポリモーフィズム] + +### ダックタイピング + +### [タプル](../syntax/11_tuple.md) + +### 単相 + +* 単相化 +* 単相型 +* 単相関数 + +### [遅延初期化] + +### 抽出代入 + +### 抽象構文木 -> [AST] + +### 中置演算子 + +`x∘y`の形式で適用される演算子`∘`。 + +### [定数](../syntax/02_name.md/#定数) + +イミュータブルでコンパイル時評価可能な代数。 + +* [定数型](../syntax/type/advanced/const.md) +* [定数式](../syntax/type/advanced/const.md) + +### [定義] + +変数に対応するオブジェクトを割り当てること。 + +### 提供属性 + +APIとして利用可能な属性。特に、トレイトによって自動実装された属性。 + +### [適用] + +関数オブジェクトに引数を渡して評価結果を得ること。 + +### [デコレータ](../syntax/29_decorator.md) + +```erg +@deco +f x = ... +``` + +という糖衣構文、または`deco`のこと。`_f x = ...; f = deco _f`とおよそ等しい。`deco`自体は単なる高階サブルーチンにすぎない。 + +### デストラクタ + +オブジェクトが破棄されるときに呼ばれるメソッド。 + +### 手続き -> [プロシージャ](../syntax/08_procedure.md) + +サブルーチンのうち、可変状態を読み書きするもの。 +プロシージャは呼出順序によってプログラムの実行結果が変わりうるという説明がなされることがあるが、これは可換性のことを言っているならば誤りである。 +例えば関数のサブタイプである演算子は一般に可換でない。 + +### [デフォルト引数](../syntax/04_function.md/#デフォルト引数default-parameters) + +仮引数にデフォルトの値を指定することで、呼び出しの際に実引数の指定を省略できる機能。 + +### 展開 + +* [展開演算子] +* [展開代入] + +### [特殊形式](../syntax/../API/special.md) + +実引数に渡すことができないオブジェクト。 + +### 匿名関数 -> [無名関数](../syntax/20_lambda.md) + +無名関数演算子`->`によって生成される関数オブジェクト。名前を定義せずに使える。 + +### ドット演算子(`.`) -> [属性参照] + +### トップ + +* トップ型 -> [Structural Object] +* トップクラス -> [Object] + +### [トレイト](../syntax/type/03_trait.md) + +## な行 + +### [内包表記](../syntax/27_comprehension.md) + +### ~~中置(なかおき)演算子~~ -> [中置(ちゅうち)演算子] + +### [名前空間] + +## は行 + +### [配列](../syntax/10_array.md) + +### [派生型](../syntax/type/variances.md/#ユーザー定義型の変性) + +### [パターン(マッチ)](../syntax/26_pattern_matching.md) + +### [パッケージ](../syntax/33_package_system.md) + +### ハッシュマップ -> [辞書](../syntax/11_dict.md) + +### [パッチ](../syntax/type/07_patch.md) + +### パブリック変数 -> [公開変数](../syntax/19_visibility.md) + +### パラメーター -> [引数](../syntax/04_function.md) + +### [パラメトリック多相](../syntax/type/overloading.md) + +### [反変](../syntax/type/advanced/variance.md) + +### 比較 + +* [比較演算子] +* [比較可能型] + +### [非公開変数](../syntax/19_visibility.md) + +### 標準 + +* 標準出力 +* 標準入力 +* 標準ライブラリ + +### [副作用](../syntax/07_side_effect.md) + +コードが外部の可変状態を読み書きする/しないこと。 + +### 複素数 -> [Complex] + +### [浮動小数点数] -> [Float] + +### プライベート変数 -> [非公開変数] + +### ブール代数 -> [Bool] + +### [プロシージャ](../syntax/08_procedure.md) + +### [引数](../syntax/04_function.md) + +### 部分型付け -> [サブタイピング] + +### [不変] + +Ergにおいては、オブジェクトがその内容を変えないこと。 + +* [不変オブジェクト] +* [不変型] +* [不変参照] + +### [篩型](../syntax/type/12_refinement.md) + +### [ブロック] + +### 分解代入 + +### [変数](../syntax/02_variable.md) + +### ボトム + +* ボトム型 -> [{}] +* ボトムクラス -> [Never] + +### [ポリモーフィズム] + +## ま行 + +### ~~前置(まえおき)演算子~~ -> 前置(ぜんち)演算子 + +### [マーカー型](../syntax/type/advanced/marker_trait.md) + +### [無名関数](../syntax/21_lambda.md) + +### ミュータブル -> [可変性] + +### [ムーブ] + +### メソッド + +### メタキャラクタ + +### [モジュール](../syntax/24_module.md) + +### [文字列] -> [Str] + +* [文字列補間](../syntax/01_literal.md/#Strリテラル) + +### 戻り値 + +## や行 + +### [幽霊型](../syntax/type/advanced/phantom.md) + +### 要求属性 + +### [要素] + +### [呼び出し] + +## ら行 + +### [ライブラリ] + +### ラムダ式 -> [無名関数](../syntax/20_lambda.md) + +### ランク + +* [ランク2多相](../syntax/type/advanced/rank2type.md) + +### [リテラル](../syntax/01_literal.md) + +* [リテラル識別子](../syntax/18_naming_rule.md/#リテラル識別子) + +### [量化](../syntax/type/quantified.md) + +### [レイアウト](../syntax/type/mut.md) + +### [列挙型](../syntax/type/10_enum.md) + +### [レコード](../syntax/12_record.md) + +* [レコード型] +* レコード多相 -> [列多相] + +### [列多相] + +### [ローカル変数](../syntax/19_visibility.md) + +## わ行 + +### ワイルドカード diff --git a/doc/JA/dev_guide/unify_terms.md b/doc/JA/dev_guide/unify_terms.md new file mode 100644 index 00000000..fde909fa --- /dev/null +++ b/doc/JA/dev_guide/unify_terms.md @@ -0,0 +1,88 @@ +# 用語の統一 + +## Accessibility, Visibility (参照性、可視性) + +Visibility(可視性)を使用する。 + +## Complement(否定型、補型) + +否定型を使用する。Complementの結果Not型になるとは限らない。 + +## Diff(差型、除外型、直差型) + +除外型を使用する。Diffの結果Not型になるとは限らない。 + +## Intersection(共通部分型、交差型、直積型) + +交差型を使用する。直積型は使用しない。タプルを直積型とみなす用法もあるためである。 +ただし、属性的部分型の観点からはErgのAnd型と本質的に等価な概念である。 +また、Intersectionの結果And型になるとは限らない。例えば`{1, 2, 3} and {1, 2} == {1, 2}`である。 + +## Nominal subtypingの訳語 + +記名的/名目的/公称的部分型付けがあるが、記名的部分型付けを使用する。 + +## Ratio型の訳語 + +有理数型を使用する。Floatは別途提供されているので、浮動小数点数型とは呼ばない。 + +## Union(合併型、直和型) + +合併型を使用する。Unionの結果Or型になるとは限らない。 + +## 型境界(Type bound)、型制約(Type constraint) + +量化型、篩型に与えられている述語式のリスト。型境界を使用する。 + +## サブルーチン、ルーチン、サブプログラム + +サブルーチンを使用する。 + +## 参照透過である/でない、副作用あり/なし + +副作用あり/なしを使用する。 + +## 識別子、代数、変数、名前、シンボル + +元来の意味としては、 + +* シンボル(Symbol): 文字列オブジェクトでない(""で囲まれていない)ソースコードにベタ書きされた文字(記号や制御文字などを除く)。RubyやLispなどでのプリミティブ型としてのシンボルが存在するが、Ergではオブジェクト扱いされない。 +* 識別子(Identifier): シンボルのうち、予約語でなく、何らかのオブジェクトを指すもの(また指し得るもの)。例えばPythonではclassやdefは識別子として使えない。Ergには予約語がないため、一部の記号を除くすべてのシンボルが識別子として使える。 +* 名前(Name): 識別子とほぼ同じ意味。Ergにおいては代数と同じ意味で使われることもある。 +* 代数名(Algebra name): Ergにおいては識別子と同等の意味。C言語では関数名は識別子だが代数名ではない。「代数」は`=`(変数代入演算子)または`=`(定数代入演算子)でオブジェクトを代入できるという言語機能自体を指す。 + +```erg +代数名 <: (名前 == 識別子) <: シンボル +変数 + 定数 == 代数 +``` + +ただし、本来「代数」と呼ばれるべきものは「変数」と呼ばれる場合が多い。これは数学用語の影響である。 +値の内容が変わりうる変数はミュータブル変数、値の内容が変わらない変数はイミュータブル変数である。 +なお、定数は必ずイミュータブルである。 + +Ergでは代数名、名前は使用せず、識別子で統一する。 +ただし一般的には`v = 1`の`v`は「変数v」("Variable v")と呼び、`C = 1`の`C`は「定数C」("Constant C")と呼ぶ。 + +## 属性、フィールド、プロパティ(Attribute, Field, Property) + +Attribute、属性を使用する。 +因みにレコードはクラス無しで要素属性のあるオブジェクトを定義できる機能のことである。 + +## 適用(Application)、呼び出し(Call) + +サブルーチンオブジェクトに引数を与えて結果を得ること。 +呼び出し(Call)を使用する。Applicationは「応用ソフトウェア」の用法があるためである。 + +## 配列(Array)、リスト(List) + +Arrayを使用する。Ergの配列は(一般的には)メモリ上で連続的に配置されるからである。 +Listはいわゆる連結リスト、またはPythonのデータ型としてのリストを指すこととする。 + +## プロシージャ、手続き + +プロシージャに統一する。サブルーチンは関数(と演算子)、プロシージャ、メソッドの総称。Callableはさらに`__call__`を実装しているものすべて。 + +## ラムダ関数、ラムダ式、匿名関数、無名関数 + +無名関数で統一する。英語では字数短縮のためLambdaを使用してよいが、正式名はAnonymous functionである。 +また、Ergの無名関数は匿名なわけではないので、匿名関数は使わない。 diff --git a/doc/JA/faq_general.md b/doc/JA/faq_general.md new file mode 100644 index 00000000..5ef56c92 --- /dev/null +++ b/doc/JA/faq_general.md @@ -0,0 +1,36 @@ +# Erg FAQ + +このFAQは一般のErg入門者向けです。 +個別の(よくある)技術的な問題については[こちら](./faq_technical.md)を、文法の決定経緯(なぜこのような文法になったのか)などについては +[こちら](./dev_guide/why.md)を参照してください。 + +## ErgはPython互換言語というのはどういう意味なのですか? + +~~A: Ergの実行系であるEVM(Erg VirtualMachine)はPythonバイトコードを拡張したErgバイトコードを実行します。これは静的型付けシステムなどをPythonバイトコードに導入したものです(引数を取らない命令に引数を導入したり、空き番号に独自命令を実装しています)。これにより、ErgはPythonのコードをシームレスに呼び出し、かつ高速な実行を実現しています。~~ + +A: ErgスクリプトはPythonのバイトコードにトランスパイルされます。つまり、Pythonと同じインタープリタ上で動作します。もともとはPythonインタープリタ(CPython)を拡張した上位互換処理系を開発し、コンパイラと合わせて「Erg」とする予定でしたが、処理系の開発がコンパイラに対して大きく遅れたため、コンパイラのみ先行公開する運びとしました。現在処理系は鋭意開発中です。 + +## Ergはどの言語から影響を受けましたか? + +両手でも数え切れないほどの言語から影響を受けていますが、中でも特に強く影響を受けているのはPython/Rust/Nim/Haskellです。 +Pythonからはオフサイドルールと互換言語として多くの意味論、Rustからは式指向とトレイト、Nimからはプロシージャ、Haskellからは関数型プログラミング関連の機能を受け継いでいます。 + +## Pythonを呼び出せる言語はJuliaなどがあります。なぜErgを作ったのですか? + +A: Ergの設計動機の1つに、手軽に使えてなおかつ強力な型システムを持った言語がほしいというものがありました。すなわち、型推論、カインド、依存型などを持った言語です。 +Juliaは型付けができますが、実際のところは動的型付け言語であり、静的型付け言語のコンパイル時エラー検出というメリットを享受できません。 + +## Ergは関数型プログラミングやオブジェクト指向プログラミングなど複数のスタイルをサポートしています。これは、Pythonの”There should be one-- and preferably only one --obvious way to do it.”に反しているのではないですか? + +A: Ergでは、その言葉はもう少し狭い意味で捉えられます。例えば、一般にErgのAPIにエイリアスはありません。Ergはこの意味では"only one way"です。 +関数型やOOPなどのもう少し大きな意味・枠組みでは、1つのやり方しかないというのは必ずしも利便性をもたらすとは限りません。 +例えば、JavaScriptにはイミュータブルなプログラム作成を支援するライブラリが複数あり、C言語ではガベージコレクションのライブラリが複数あります。 +しかし、このような基本的な機能にまでライブラリが複数あると、選定に時間を取られるだけでなく、別々のライブラリを使うコード同士の統合に著しい困難が生じます。 +純粋関数型言語であるHaskellでさえ、オブジェクト指向をサポートするライブラリが存在します。 +プログラマは、なければ自前で作ってしまうものなのです。それならば、標準で提供してしまったほうがよいと考えます。 +これは、Pythonの"Battery included"にも適合します。 + +## なぜ`x = p!()`は有効なのに`f() = p!()`はEffectErrorとなるのですか? + +`!`は副作用の産物につけるマーカーではなく、副作用を起こしうるオブジェクトに付けるマーカーだからです。 +プロシージャ`p!`や可変型`T!`は副作用を起こす可能性がありますが、例えば`p!()`の戻り値が`Int`型だった場合、それ自体はもう副作用を起こしません。 diff --git a/doc/JA/faq_technical.md b/doc/JA/faq_technical.md new file mode 100644 index 00000000..118a2808 --- /dev/null +++ b/doc/JA/faq_technical.md @@ -0,0 +1,23 @@ +# 技術的なFAQ + +本項はErg言語を使用する上での技術的な質問に答えるものです。すなわち、WhatやWhichで始まる質問、Yes/Noで答えられる質問を載せています。 + +根本的な文法の決定経緯については[こちら](./dev_guide/faq_syntax.md)を、なぜこの言語を作ったのか、この機能はどのように実装されているのかなど、より大きな話題は[こちら](./dev_guide/../faq_general.md)を参照してください。 + +## Ergに例外機構はないのですか? + +A: ありません。Ergでは代わりに`Result`型を使います。なぜErgに例外機構がないのかは[こちら](./dev_guide/faq_syntax.md#なぜergには例外機構がないのですか)を参照してください。 + +## ErgにはTypeScriptのAnyに相当する型はないのですか? + +A: ありません。すべてのオブジェクトは少なくとも`Object`クラスに属しますが、この型は最小限の属性を提供するのみの型で、Anyのように好き放題はできません。 +`Object`クラスは`match`などによる動的検査を通し目的の型に変換して使用します。Javaなどの`Object`と同じ類です。 +Ergの世界では、TypeScriptのようにAPIの定義を辿ったらAnyだったという絶望・混沌は生まれないのです。 + +## Never, {}, None, (), NotImplemented, Ellipsisは何が違うのですか? + +A: `Never`は「起こりえない」型です。実行時エラーを出すサブルーチンが、`Never`(または`Never`の合併型)を戻り値型とします。これを検知するとプログラムはすぐさま停止します。`Never`型は定義上すべての型のサブクラスでもありますが、`Never`型オブジェクトは決してErgコード上に出現しませんし、生成もされません。`{}`は`Never`と等価です。 +`Ellipsis`は省略を表すオブジェクトで、Python由来です。 +`NotImplemented`もPython由来です。これは未実装を表すマーカーとして使われますが、Ergではエラーを出す`todo`関数の方を推奨します。 +`None`は`NoneType`のインスタンスです。`Option`型でよく使われます。 +`()`はユニット型であり、そのインスタンス自身でもあります。これはプロシージャの戻り値など「意味のない値」を返したいとき使われます。 diff --git a/doc/JA/improved_points.md b/doc/JA/improved_points.md new file mode 100644 index 00000000..eb3d7f51 --- /dev/null +++ b/doc/JA/improved_points.md @@ -0,0 +1,48 @@ +# Pythonから改良された点 + +## 静的解析を行う(静的型チェック、変数・プロパティチェック) + +静的型チェックの恩恵は今更強調するまでもないほどですが、変数・プロパティの存在チェックもかなり効いてくる部分です。 + +## 厳密にスコープを扱う + +Pythonでは文がスコープを持ちません。 +そのため、`for`や`if`の中で定義した変数は外に影響を与えてしまいます。気軽に変数を名付けることができません。 + +```python +for i in range(10): + x = 1 + print(i + x) +print(x) # 1 +``` + +Ergでは全てのブロックがスコープを持ち、完全に隔離されています。 + +## 可変オブジェクトと不変オブジェクトの区別が明確 + +Pythonは可変オブジェクトと不変オブジェクト、ヒープオブジェクトと値オブジェクトの区別が明確ではないため、タプルは不変だがリストは可変...などと言った知識を頭に入れておく必要があります。 +また、自作クラスを不変にしたいとき、面倒な手順をふまなくてはなりません。 + +```python +# このコードが過去のPythonでは有効だったと信じられますか? +i = 256 +assert i is 256 +i = 257 +assert i is not 257 +``` + +## トレイトを持つ + +Javaのインターフェースと同じように、契約に基づくプログラミングを行うことができます。 + +Pythonにも抽象基底クラスがありますが、この手の構造体は静的型付けと組み合わせることで最大の効果を発揮します。 + +## 依存関係を静的に解決する + +長時間実行の末にモジュールが足りずエラーなどといったゲンナリする体験を未然に防ぎます。 + +## 組み込みのパッケージマネージャー + +規格化されたディレクトリ構造とビルドファイルを用いて再現性のあるビルドが可能です。 +ロックファイルの生成やバージョン管理はもちろん行われます。 +anacondaやらpyenvやらpoetryやらをプロジェクトごとに取捨選択したり組み合わせたりする必要はありません。 diff --git a/doc/JA/index.md b/doc/JA/index.md new file mode 100644 index 00000000..db3516f8 --- /dev/null +++ b/doc/JA/index.md @@ -0,0 +1,25 @@ +# Index + +## [API/](./API/index.md) + + Ergの組み込みまたは標準ライブラリで提供されるサブルーチン、型、定数等の仕様が記述されている。 + +## [compiler/](./compiler/index.md) + + Ergコンパイラ(Centimetre)の設計について解説されている。 + +## [dev_guide/](./dev_guide/index.md) + + プロジェクトの開発方針、コントリビューションの仕方などが解説されている。 + +## [python/](./python/index.md) + + Ergを開発する上で必要となるPythonの知識が解説されている。 + +## [syntax/](./syntax/00_basic.md) + + Ergの文法が解説されている。 + +## [tools/](./tools/index.md) + + Ergの周辺ツール、コマンドオプションの使い方などが解説されている。 diff --git a/doc/JA/migration_from_py.md b/doc/JA/migration_from_py.md new file mode 100644 index 00000000..4ad1c57b --- /dev/null +++ b/doc/JA/migration_from_py.md @@ -0,0 +1,25 @@ +# PythonからErgへの移行に関してのTips + +## 文字列をint等に変換したい + +`Str`クラスの`parse`メソッドを使用してください。これは`Result`型を返します。 + +```python +s: str +i: int = int(s) +``` + +```erg +s: Str +res: Result(Int, IntParseError) = s.parse Int +i: Int = res.unwrap() +f: Result(Float, FloatParseError) = s.parse Float +``` + +`try_from`メソッドも使えます。 + +```erg +s: Str +i: Int = Int.try_from(s).unwrap() +f: Float = Float.try_from(s).unwrap() +``` diff --git a/doc/JA/python/bytecode_instructions.md b/doc/JA/python/bytecode_instructions.md new file mode 100644 index 00000000..7d102275 --- /dev/null +++ b/doc/JA/python/bytecode_instructions.md @@ -0,0 +1,96 @@ +# Python Bytecode Instructions + +Python bytecodeの変数操作系の命令はnamei (name index)を通してアクセスされる。これは、Pythonの動的変数アクセス(evalなどを使い、文字列でアクセスできる)を実現するためである。 +1命令は2byteで、命令、引数がlittle endianで格納されている。 +引数を取らない命令も2byte使っている(引数部は0)。 + +## STORE_NAME(namei) + +globals[namei] = stack.pop() + +## LOAD_NAME(namei) + +stack.push(globals[namei]) +トップレベルでしか呼び出されない。 + +## LOAD_GLOBAL(namei) + +stack.push(globals[namei]) +トップレベルでSTORE_NAMEしたものを内側のスコープでLoadするためのものだが +トップレベルでのnamei==あるスコープのコードオブジェクトでのnameiとは限らない(nameiではなくnameが同じ) + +## LOAD_CONST(namei) + +stack.push(consts[namei]) +定数テーブルにある定数をロードする +現在(Python 3.9)のところ、CPythonではいちいちラムダ関数を"\"という名前でMAKE_FUNCTIONしている + +```console +>>> dis.dis("[1,2,3].map(lambda x: x+1)") +1 0 LOAD_CONST 0 (1) + 2 LOAD_CONST 1 (2) + 4 LOAD_CONST 2 (3) + 6 BUILD_LIST 3 + 8 LOAD_ATTR 0 (map) + 10 LOAD_CONST 3 ( at 0x7f272897fc90, file "", line 1>) + 12 LOAD_CONST 4 ('') + 14 MAKE_FUNCTION 0 + 16 CALL_FUNCTION 1 + 18 RETURN_VALUE +``` + +## STORE_FAST(namei) + +fastlocals[namei] = stack.pop() +おそらくトップレベルにおけるSTORE_NAMEに対応する +参照のない(もしくは単一)変数がこれによって格納されると思われる +わざわざグローバル空間が独自の命令を持っているのは最適化のため? + +## LOAD_FAST(namei) + +stack.push(fastlocals[namei]) +fastlocalsはvarnames? + +## LOAD_CLOSURE(namei) + +cell = freevars[namei] +stack.push(cell) +そのあとBUILD_TUPLEが呼ばれている +クロージャの中でしか呼び出されないし、cellvarsはクロージャの中での参照を格納するものと思われる +LOAD_DEREFと違ってcell(参照を詰めたコンテナ)ごとスタックにpushする + +## STORE_DEREF(namei) + +cell = freevars[namei] +cell.set(stack.pop()) +内側のスコープで参照のない変数はSTORE_FASTされるが、参照される変数はSTORE_DEREFされる +Pythonではこの命令内で参照カウントの増減がされる + +## LOAD_DEREF(namei) + +cell = freevars[namei] +stack.push(cell.get()) + +## 名前リスト + +### varnames + +fast_localsに対対応する、関数の内部変数の名前リスト +namesで同名の変数があっても、基本的に同じものではない(新しく作られ、そのスコープからは外の変数にアクセスできない) +つまり、スコープ内で定義された外部参照のない変数はvarnamesに入る + +### names + +globalsに対応 +スコープ内で使われた外部定数(参照だけしている)の名前リスト(トップレベルでは普通の変数でもnamesに入る) +つまり、スコープ外で定義された定数はnamesに入る + +## free variable + +freevarsに対応 +クロージャがキャプチャした変数。同じ関数インスタンス内においてstaticな振る舞いをする。 + +## cell variables + +cellvarsに対応 +関数内で内側のクロージャ関数にキャプチャされる変数。コピーが作られるので、元の変数はそのまま。 diff --git a/doc/JA/python/bytecode_specification.md b/doc/JA/python/bytecode_specification.md new file mode 100644 index 00000000..db897428 --- /dev/null +++ b/doc/JA/python/bytecode_specification.md @@ -0,0 +1,71 @@ +# Python bytecode specification + +## Format + +* 0~3 byte(u32): magic number (see common/bytecode.rs for details) +* 4~7 byte(u32): 0 padding +* 8~12 byte(u32): timestamp +* 13~ byte(PyCodeObject): code object + +## PyCodeObject + +* 0 byte(u8): '0xe3' (this means code's 'c') +* 01~04 byte(u32): number of args (co_argcount) +* 05~08 byte(u32): number of position-only args (co_posonlyargcount) +* 09~12 byte(u32): number of keyword-only args (co_kwonlyargcount) +* 13~16 byte(u32): number of locals (co_nlocals) +* 17~20 byte(u32): stack size (co_stacksize) +* 21~24 byte(u32): flags (co_flags) () +* ? byte: bytecode instructions, ends with '0x53', '0x0' (83, 0): RETURN_VALUE (co_code) +* ? byte(PyTuple): constants used in the code (co_consts) +* ? byte(PyTuple): names used in the code (co_names) +* ? byte(PyTuple): variable names defined in the code, include params (PyTuple) (co_varnames) +* ? byte(PyTuple): variables captured from the outer scope (co_freevars) +* ? byte(PyTuple): variables used in the inner closure (co_cellvars) +* ? byte(PyUnicode or PyShortAscii): file name, where it was loaded from (co_filename) +* ? byte(PyUnicode or PyShortAscii): the name of code itself, default is \ (co_name) +* ?~?+3 byte(u32): number of first line (co_firstlineno) +* ? byte(bytes): line table, represented by PyStringObject? (co_lnotab) + +## PyTupleObject + +* 0 byte: 0x29 (means ')') +* 01~04 byte(u32): number of tuple items +* ? byte(PyObject): items + +## PyStringObject + +** ascii以外の文字を使うとPyUnicodeになる? +** "あ", "𠮷", "α"だとPyUnicodeになった(もう使われていない?) + +* 0 byte: 0x73 (means 's') +* 1~4 byte: length of string +* 5~ byte: payload + +## PyUnicodeObject + +* 0 byte: 0x75 (means 'u') +* 1~4 byte: length of string +* 5~ byte: payload + +## PyShortAsciiObject + +** shortと言っているが、100文字以上あってもこれになる +** というかshortじゃないasciiはない(shortはデータ型?) + +* 0 byte: 0xFA (means 'z') +* 1~4 byte: length of string +* 5~ byte: payload + +## PyInternedObject + +** intern化したオブジェクトは専用のmapに登録され、isで比較できるようになる +** 例えば文字列などが長さに関係なく定数時間で比較できる + +* 0 byte: 0x74 (means 't') + +## PyShortAsciiInternedObject + +* 0 byte: 0xDA (means 'Z') +* 1~4 byte: length of string +* 5~ byte: payload diff --git a/doc/JA/python/class_system.md b/doc/JA/python/class_system.md new file mode 100644 index 00000000..dfaf5620 --- /dev/null +++ b/doc/JA/python/class_system.md @@ -0,0 +1,94 @@ +# Pythonのクラスシステム(Ergとの比較) + +## メソッド + +メソッドは前方参照していてもかまわないが、これは特別なテクニックが使われているわけではなく、 +メソッドの実在が動的に検査されるためである。 +(Ergではメソッドの実在は静的に検査される。前方参照するためには関数を定数にしなくてはならない。) + +```python +>>> class C: +... def f(self, x): +... if x == 0: return 0 +... else: return self.g(x) +... def g(self, x): return self.f(x - 1) +``` + +## 継承、オーバーライド + +オーバーライドされたあるメソッドmは単に変数の再代入のように上書きされ、 +親クラスのmを参照するメソッドは子クラスではオーバーライドされたmを参照するようになる。 + +```python +>>> class C: +... def f(self): return 1 +... def g(self): return self.f() +... +>>> class D(C): +... def f(self): return 2 +... +>>> D().g() +2 +``` + +なので、明らかに間違ってオーバーライドされても実行時までエラーとならない。 + +```python +>>> class C: +... def f(self): return 1 +... def g(self): return self.f() + 1 +... +>>> class D(C): +... def f(self): return "a" +... +>>> D().g() +Traceback (most recent call last): + File "", line 1, in + File "", line 3, in g +TypeError: can only concatenate str (not "int") to str +``` + +Ergでは親クラスとの整合性が静的に検査される。 +オーバーライド時には`Override`デコレータを付与する必要があり、オーバーライドする関数の型はされる関数の型の部分型とならなくてはならない。 + +```erg +>>> C = Class() +... .f self = 1 +... .g self = self.f() + 1 +... +>>> D = Inherit C +... .f self = "a" +... +Error[#XX]: File "", line 5, in D +.f(self) is already defined in C. To override f, it must be added `Override` decorator and its type must be `Self.() -> Nat` or the subtype of that +.f(self)は既にCで定義されています。オーバーライドするためには`Override`デコレータを付与し、`Self.() -> Nat`型かそのサブタイプである必要があります。 +``` + +## 型チェック + +型チェックは概ね関数引数の型チェックに尽きる。 +Pythonでは、大半の操作がメソッド呼び出しである。呼び出し時にオブジェクトの属するクラスにメソッドがついていなればそれまでである。 + +```python +def f(x): + return x.m() + +class C: + def m(self): return None + +c = C() +f(c) +f(1) # TypeError +``` + +```erg +# f: |T, X <: {.m = Self.() -> T}| X -> T +f(x) = x.m() + +C = Class() +C.m(self) = None + +c = C.new() +f(c) +f(1) # TypeError: f takes a type has method `.m` as an argument, but passed Nat +``` diff --git a/doc/JA/python/index.md b/doc/JA/python/index.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/python/python_vers.txt b/doc/JA/python/python_vers.txt new file mode 100644 index 00000000..04f2a16a --- /dev/null +++ b/doc/JA/python/python_vers.txt @@ -0,0 +1,6 @@ +version magic number +3.7.5 3394 +3.8.0 3413 +3.8.5 3413 +3.9.0 3425 +3.9.1 3425 diff --git a/doc/JA/syntax/00_basic.md b/doc/JA/syntax/00_basic.md new file mode 100644 index 00000000..700f6390 --- /dev/null +++ b/doc/JA/syntax/00_basic.md @@ -0,0 +1,118 @@ +# 基本事項 + +> __Info__: 本ドキュメントは未完成です。校正(文体、正しいリンクが張られているか、など)がなされていません。また、Ergの文法はバージョン0.*の間に破壊的変更が加えられる可能性があり、それに伴うドキュメントの更新が追いついていない可能性があります。予めご了承ください。 +> また、本ドキュメントの誤りを見つけた場合は、[こちらのフォーム](https://forms.gle/HtLYRfYzWCAaeTGb6)または[GitHubリポジトリ](https://github.com/mtshiba/TheErgBook/issues/new)から修正の提案をしていただけると幸いです。 + +本ドキュメントは、Ergの基本文法について解説するものです。[標準API](../API/index.md)や[Ergコントリビューター向けの内部資料](../dev_guide/index.md)は別のディレクトリに置かれています。 + +## Hello, World! + +まずは恒例の、Hello Worldを行いましょう。 + +```erg +print!("Hello, World!") +``` + +Pythonや同系統の言語とほぼ同じです。目を引くのは`print`の後に付く`!`ですが、これの意味はおいおい説明します。 +また、Ergでは解釈に紛れのない限り括弧`()`を省略することが出来ます。 +括弧の省略ができるのはRubyと似ていますが、複数の解釈ができる括弧省略はできませんし、また引数が0個のときもPythonと同じく`()`の省略が出来ません。 + +```erg +print! "Hello, World!" # OK +print! "Hello,", "World!" # OK +print!() # OK +print! # OK, but this does not mean to call, simply to get `print!` as a callable object + +print! f x # OK, interpreted as `print!(f(x))` +print!(f(x, y)) # OK +print! f(x, y) # OK +print! f(x, g y) # OK +print! f x, y # NG, can be taken to mean either `print!(f(x), y)` or `print!(f(x, y))` +print!(f x, y) # NG, can be taken to mean either `print!(f(x), y)` or `print!(f(x, y))` +print! f(x, g y, z) # NG, can be taken to mean either `print!(x, g(y), z)` or `print!(x, g(y, z))` +``` + +## スクリプト + +Ergのコードはスクリプトと呼ばれます。スクリプトはファイル形式(.er)で保存・実行できます。 + +## コメント + +`#`以降はコメントとして無視されます。コードの意図を説明したいときや一時的にコードを無効化したいときなどに使います。 + +```erg +# コメント +## `#`以降は改行されるまで無視されるので、`#`は何個あってもOK +#[ +複数行コメント +対応する`]#`のところまでずっとコメントとして扱われます +]# +``` + +## 式、セパレータ + +スクリプトは、式(expression)の連なりです。式とは計算・評価ができるもので、Ergではほとんどすべてのものが式です。 +各式はセパレータ―改行かセミコロン`;`―で区切ります。 +Ergのスクリプトは基本的に左から右へ、上から下へ評価されます。 + +```erg +n = 1 # 代入式 +f(1, 2) # 関数適用式 +1 + 1 # 演算子適用式 +f(1, 2); 1 + 1 +``` + +以下のように、ブロック内で最後に評価した式を変数の値とするインスタントブロックという機能があります。 +これは引数なし関数とは違い、`()`をつけません。ブロックがその場で1度だけ評価されることに注意してください。 + +```erg +i = + x = 1 + x + 1 +assert i == 2 +``` + +これはセミコロン(`;`)では実現できません。 + +```erg +i = (x = 1; x + 1) # SyntaxError: cannot use `;` in parentheses +``` + +## インデント + +ErgはPythonと同じくインデントを使ってブロックを表します。ブロックの開始を示すトリガーとなる演算子(特殊形式)は、`=`, `->`, `=>`, `do`, `do!`の5種類です(その他に、演算子ではありませんが`:`と`|`もインデントを生成します)。それぞれの意味は後述します。 + +```erg +f x, y = + x + y + +for! 0..9, i => + print! i + +for! 0..9, i => + print! i; print! i + +ans = match x: + 0 -> "zero" + _: 0..9 -> "1 dight" + _: 10..99 -> "2 dights" + _ -> "unknown" +``` + +また1行が長くなりすぎる場合、`\`を使って途中で改行させることができます。 + +```erg +# this does not means `x + y + z` but means `x; +y; +z` +x ++ y ++ z + +# this means `x + y + z` +x \ ++ y \ ++ z +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/01_literal.md b/doc/JA/syntax/01_literal.md new file mode 100644 index 00000000..27534ff6 --- /dev/null +++ b/doc/JA/syntax/01_literal.md @@ -0,0 +1,150 @@ +# Literal + +## 基本的なリテラル + +### 整数リテラル(Int Literal) + +```erg +0, -0, 1, -1, 2, -2, 3, -3, ... +``` + +### 有理数リテラル(Ratio Literal) + +```erg +0.00, -0.0, 0.1, 400.104, ... +``` + +`Ratio`リテラルで整数部分または小数部分が`0`のときは、その`0`を省略できます。 + +```erg +assert 1.0 == 1. +assert 0.5 == 0.5 +``` + +> __Note__: この`assert`という関数は、`1.0`と`1.`が等しいことを示すために使用しました。 +以降のドキュメントでは、結果が等しいことを示すために`assert`を使用する場合があります。 + +### 文字列リテラル(Str Literal) + +Unicodeで表現可能な文字列は、すべて使用できます。 +Pythonとは違い、`'`ではクオーテーション(囲み)できません。文字列の中で`"`を使いたいときは`\"`としてください。 + +```erg +"", "a", "abc", "111", "1# 3f2-3*8$", "こんにちは", "السَّلَامُ عَلَيْكُمْ", ... +``` + +`{}`によって文字列の中に式を埋めこめます。これを文字列補間(string interpolation)といいます。 +`{`, `}`自体を出力したい場合は`\{`, `\}`とします。 + +```erg +assert "1 + 1 is 2" == "{1} + {1} is {1+1}" +s = "1+1" +assert "\{1+1}\" == "\{{s}\}" +``` + +### 指数リテラル(Exponential Literal) + +これは学術計算でよく使用される指数表記を表すリテラルです。`Ratio`型のインスタンスになります。 +非常に大きな/小さな数を表すときに使用します。Pythonと表記法は同じです。 + +```erg +1e-34, 0.4e-10, 2.455+e5, 245e5, 25E5, ... +``` + +```erg +assert 1e-10 == 0.0000000001 +``` + +## リテラルを組み合わせて生成するもの(複合リテラル) + +これらのリテラルは、それぞれ単独で解説されているドキュメントがあるので、詳しくはそちらを参照してください。 + +### [配列リテラル(Array Literal)](./10_array.md) + +```erg +[], [1], [1, 2, 3], ["1", "2",], [1, "1", True, [1]], ... +``` + +### [辞書リテラル(Dict Literal)](./11_dict.md) + +```erg +{:}, {"one": 1}, {"one": 1, "two": 2}, {"1": 1, "2": 2}, {1: "1", 2: True, "three": [1]}, ... +``` + +### [組リテラル(Tuple Literal)](./12_tuple.md) + +```erg +(), (1, 2, 3), (1, "hello", True), ... +``` + +### [レコードリテラル(Record Literal)](./13_record.md) + +```erg +{=}, {one = 1}, {one = 1; two = 2}, {.name = "John"; .age = 12}, {.name = Str; .age = Nat}, ... +``` + +### [集合リテラル(Set Literal)](./14_set.md) + +```erg +{}, {1}, {1, 2, 3}, {"1", "2", "1"}, {1, "1", True, [1]} ... +``` + +`Array`リテラルとの違いとして、`Set`では重複する要素が取り除かれます。 + +```erg +assert {1, 2, 1} == {1, 2} +``` + +### リテラルのように見えるがそうではないもの + +## 真偽値オブジェクト(Boolean Object) + +```erg +True, False +``` + +### Noneオブジェクト + +```erg +None +``` + +## 範囲オブジェクト(Range Object) + +```erg +assert 0..5 == {1, 2, 3, 4, 5} +assert 0..10 in 5 +assert 0..<10 notin 10 +assert 0..9 == 0..<10 +``` + +## 浮動小数点数オブジェクト(Float Object) + +```erg +assert 0.0f64 == 0 +assert 0.0f32 == 0.0f64 +``` + +`Ratio`オブジェクトに`Float 64`の単位オブジェクトである`f64`を乗算したものです。 + +## 複素数オブジェクト(Complex Object) + +```erg +1+2im, 0.4-1.2im, 0im, im +``` + +`Complex`オブジェクトは、単に虚数単位オブジェクトである`im`との演算の組み合わせで表します。 + +## *-less multiplication + +Ergでは、解釈に紛れがない限り乗算を表す`*`を省略できます。 +ただし、演算子の結合強度は`*`よりも強く設定されています。 + +```erg +# same as `assert (1*m) / (1*s) == 1*(m/s)` +assert 1m / 1s == 1 (m/s) +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/02_name.md b/doc/JA/syntax/02_name.md new file mode 100644 index 00000000..bd18402f --- /dev/null +++ b/doc/JA/syntax/02_name.md @@ -0,0 +1,165 @@ +# 変数 + +変数は代数の一種です。Ergにおける代数―紛れがなければ単に変数と呼ばれることもあります―とは、オブジェクトを名前付けしてコード中の別の場所から利用できるようにする機能を指します。 + +変数は以下のように定義します。 +`n`の部分を変数名(または、識別子)、`=`を代入演算子、`1`の部分を代入値と呼びます。 + +```erg +n = 1 +``` + +このようにして定義した`n`は、以降整数オブジェクトである`1`を示す変数として使用できます。このシステムを代入(または束縛)といいます。 +いま`1`はオブジェクトであると述べました。オブジェクトが何であるかは後述しますが、今は代入できるもの、すなわち代入演算子(`=`など)の右側におけるものとしておきます。 + +変数の「型」を指定したい場合は以下のようにします。型とは、これも後述しますが、大まかにはオブジェクトの属する集合です。 +ここでは`n`は自然数(`Nat`)型であると指定しています。 + +```erg +n: Nat = 1 +``` + +他の言語とは違い、多重代入はできないので注意してください。 + +```erg +# NG +l1 = l2 = [1, 2, 3] # SyntaxError: 多重代入はできません +# OK +l1 = [1, 2, 3] +l2 = l1.clone() +``` + +また、変数への再代入もできません。その代わりに使える機能、すなわち可変な状態を保持する機能については後述します。 + +```erg +i = 1 +i = i + 1 # AssignError: cannot assign twice +``` + +内側のスコープで同じ名前の変数を定義できますが、上に被せているだけで、値を破壊的に書き換えているわけではありません。外側のスコープに戻れば値も戻ります。 +これはPythonの「文」のスコープとは違う挙動なので注意してください。 +このような機能は一般にシャドーイングと言います。ただし他言語のシャドーイングとは違い同一スコープではシャドーイングできません。 + +```erg +x = 0 +# x = 1 # AssignError: cannot assign twice +if x.is_zero(), do: + x = 1 # 外側のxとは同名の別物 + assert x == 1 +assert x == 0 +``` + +以下は一見すると可能なように思えますが、やはりできません。これは技術的な制約ではなく、設計判断です。 + +```erg +x = 0 +if x.is_zero(), do: + x = x + 1 # NameError: cannot define variables refer to variables with the same name + assert x == 1 +assert x == 0 +``` + +## 定数 + +定数も代数の一種です。識別子を大文字で始めると定数として扱われます。一度定義したら変わらないので、定数と呼ばれます。 +`N`の部分を定数名(または、識別子)と呼びます。その他は変数と同じです。 + +```erg +N = 0 +if True, do: + N = 1 # AssignError: constants cannot be shadowed + pass() +``` + +定数は定義されたスコープ以降では不変になります。シャドーイングもできません。この性質から、定数はパターンマッチで使用できます。 +パターンマッチについては後に説明します。 + +定数は、数学的定数、外部リソースに関する情報など不変な値に対して使用すると良いでしょう。 +[型](./type/01_type_system.md)以外のオブジェクトは、オールキャップス(全ての文字を大文字にするスタイル)にするのが一般的です。 + +```erg +PI = 3.141592653589793 +URL = "https://example.com" +CHOICES = ["a", "b", "c"] +``` + +```erg +PI = 3.141592653589793 +match! x: + PI => print! "π" + other => print! "other" +``` + +上のコードは`x`が`3.141592653589793`のとき`π`を出力します。`x`を他の数字に変えると、`other`を出力します。 + +定数には代入できないものがあります。可変オブジェクトなどです。詳しくは後述しますが、可変オブジェクトは内容を変更することができるオブジェクトです。 +これは定数には定数式のみを代入できるという規則があるためです。定数式についても後述することとします。 + +```erg +X = 1 # OK +X = !1 # TypeError: cannot define Int! object as a constant +``` + +## 代数の削除 + +`Del`関数を使うことで、代数を削除することが出来ます。その代数に依存している(その代数の値を直接参照している)他の代数もまとめて削除されます。 + +```erg +x = 1 +y = 2 +Z = 3 +f a = x + a + +assert f(2) == 3 +Del x +Del y, Z + +f(2) # NameError: f is not defined (deleted in line 6) +``` + +ただし、`Del`によって削除できるのはモジュール内で定義された代数のみです。`True`などの組み込み定数は削除できません。 + +```erg +Del True # TypeError: cannot delete built-in constants +Del print! # TypeError: cannot delete built-in variables +``` + +## Appendix: 代入と同値性 + +注意として、`x = a`であるとき、`x == a`とは限らない。例としては`Float.NaN`がある。これはIEEE 754により定められた正式な浮動小数点数の仕様である。 + +```erg +x = Float.NaN +assert x != Float.NaN +assert x != x +``` + +その他、そもそも同値関係が定義されていないオブジェクトも存在する。 + +```erg +f = x -> x**2 + 2x + 1 +g = x -> (x + 1)**2 +f == g # TypeError: cannot compare function objects + +C = Class {i: Int} +D = Class {i: Int} +C == D # TypeError: cannot compare class objects +``` + +厳密に言うと`=`は右辺値をそのまま左辺の識別子に代入するわけではない。 +関数オブジェクトやクラスオブジェクトの場合、オブジェクトに変数名の情報を与えるなどの「修飾」を行う。 +ただし構造型の場合はその限りではない。 + +```erg +f x = x +print! f # +g x = x + 1 +print! g # + +C = Class {i: Int} +print! C # +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/03_declaration.md b/doc/JA/syntax/03_declaration.md new file mode 100644 index 00000000..117ebf94 --- /dev/null +++ b/doc/JA/syntax/03_declaration.md @@ -0,0 +1,50 @@ +# Declaration(宣言) + +宣言は、使用する変数の型を指定する構文です。 +宣言はコード中のどこでも可能ですが、宣言しただけでその変数を参照することはできません。必ず初期化する必要があります。 +代入後の宣言では、代入されたオブジェクトと型が適合するかをチェック可能です。 + +```erg +i: Int +# i: Int = 2のように代入と同時に宣言可能 +i = 2 +i: Num +i: Nat +i: -2..2 +i: {2} +``` + +代入後の宣言は`assert`による型チェックと似ていますが、コンパイル時にチェックされるという特徴があります。 +実行時の`assert`による型チェックは「〇〇型かもしれない」で検査が可能ですが、コンパイル時の`:`による型チェックは厳密です。 +「〇〇型である」ことが確定していなくては検査を通らず、エラーとなります。 + +```erg +i = (-1..10).sample!() +assert i in Nat # これは通るかもしれない +i: Int # これは通る +i: Nat # これは通らない(-1はNatの要素ではないから) +``` + +関数は以下の4種類の方法で宣言が可能です。どれも戻り値型の省略はできません。 + +```erg +f(x: Int, y: Int): Int +f(Int, Int): Int +f: (x: Int, y: Int) -> Int +f: (Int, Int) -> Int +``` + +引数名を明示して宣言した場合、定義時に名前が違うと型エラーとなります。引数名の任意性を与えたい場合は2, 4番目の方法で宣言すると良いでしょう。その場合、型検査で見られるのはメソッド名とその型のみです。 + +```erg +T = Trait { + .f(x: Int, y: Int): Int +} + +C = Class(U, Impl: T) +C.f(a: Int, b: Int): Int = ... # TypeError: `.f` must be type of `(x: Int, y: Int) -> Int`, not `(a: Int, b: Int) -> Int` +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/04_function.md b/doc/JA/syntax/04_function.md new file mode 100644 index 00000000..130cb07e --- /dev/null +++ b/doc/JA/syntax/04_function.md @@ -0,0 +1,283 @@ +# 関数 + +関数は「引数」を受け取ってそれを加工し、「戻り値」として返すブロックです。以下のように定義します。 + +```erg +add x, y = x + y +# or +add(x, y) = x + y +``` + +関数定義の際に指定される引数は、詳しくは仮引数(parameter)と呼ばれるものです。 +これに対し、関数呼び出しの際に渡される引数は、実引数(argument)と呼ばれるものです。 +`add`は`x`と`y`を仮引数として受け取り、それを足したもの、`x + y`を返す関数です。 +定義した関数は、以下のようにして呼び出し(適用)ができます。 + +```erg +add 1, 2 +# or +add(1, 2) +``` + +## コロン適用スタイル + +関数は`f x, y, ...`のように呼び出しますが、実引数が多く一行では長くなりすぎる場合は`:`(コロン)を使った適用も可能です。 + +```erg +f some_long_name_variable_1 + some_long_name_variable_2, some_long_name_variable_3 * some_long_name_variable_4 +``` + +```erg +f some_long_name_variable_1 + some_long_name_variable_2: + some_long_name_variable_3 * some_long_name_variable_4 +``` + +```erg +f: + some_long_name_variable_1 + some_long_name_variable_2 + some_long_name_variable_3 * some_long_name_variable_4 +``` + +上の3つのコードはすべて同じ意味です。このスタイルは`if`関数などを使用するときにも便利です。 + +```erg +result = if Bool.sample!(): + do: + log "True was chosen" + 1 + do: + log "False was chosen" + 0 +``` + +`:`の後はコメント以外のコードを書いてはならず、必ず改行しなくてはなりません。 + +## キーワード引数(Keyword Arguments) + +引数の数が多い関数を定義されていると、引数を渡す順番を間違える危険性があります。 +そのような場合はキーワード引数を使用して呼び出すと安全です。 + +```erg +f x, y, z, w, v, u: Int = ... +``` + +上に定義された関数は、引数が多く、分かりにくい並びをしています。 +このような関数は作るべきではありませんが、他人の書いたコードを使うときにこのようなコードにあたってしまうかもしれません。 +そこで、キーワード引数を使います。キーワード引数は並びよりも名前が優先されるため、順番を間違えていても名前から正しい引数に値が渡されます。 + +```erg +f u: 6, v: 5, w: 4, x: 1, y: 2, z: 3 +``` + +キーワード引数と`:`の後にすぐ改行してしまうとコロン適用スタイルとみなされるので注意してください。 + +```erg +# means `f(x: y)` +f x: y + +# means `f(x, y)` +f x: + y +``` + +## デフォルト引数(Default parameters) + +ある引数が大抵の場合決まりきっており省略できるようにしたい場合、デフォルト引数を使うと良いでしょう。 + +デフォルト引数は`|=`(or-assign operator)で指定します。`base`が指定されなかったら`math.E`を`base`に代入します。 + +```erg +math_log x: Ratio, base |= math.E = ... + +assert math_log(100, 10) == 2 +assert math_log(100) == math_log(100, math.E) +``` + +引数を指定しないことと`None`を代入することは区別されるので注意してください。 + +```erg +p! x |= 0 = print! x +p!(2) # 2 +p!() # 0 +p!(None) # None +``` + +型指定、パターンと併用することもできます。 + +```erg +math_log x, base: Ratio |= math.E = ... +f [x, y] |= [1, 2] = ... +``` + +しかしデフォルト引数内では、後述するプロシージャを呼び出したり、可変オブジェクトを代入したりすることができません。 + +```erg +f x |= p! 1 = ... # NG +``` + +また、定義したばかりの引数はデフォルト引数に渡す値として使えません。 + +```erg +f x |= 1, y |= x = ... # NG +``` + +## 可変長引数 + +引数をログ(記録)として出力する`log`関数は、任意の個数の引数を受け取ることができます。 + +```erg +log "Hello", "World", "!" # Hello World ! +``` + +このような関数を定義したいときは、引数に`...`を付けます。このようにすると、引数を可変長のタプルとして受け取ることができます。 + +```erg +f ...x = + for x, i -> + log i + +# x == [1, 2, 3, 4, 5] +f 1, 2, 3, 4, 5 +``` + +## 複数パターンによる関数定義 + +```erg +fib n: Nat = + match n: + 0 -> 0 + 1 -> 1 + n -> fib(n - 1) + fib(n - 2) +``` + +上のような定義直下に`match`が現れる関数は、下のように書き直すことができます。 + +```erg +fib 0 = 0 +fib 1 = 1 +fib(n: Nat): Nat = fib(n - 1) + fib(n - 2) +``` + +複数のパターンによる関数定義は、いわゆるオーバーロード(多重定義)ではないことに注意してください。1つの関数はあくまで単一の型のみを持ちます。上の例では、`n`は`0`や`1`と同じ型である必要があります。また、`match`と同じくパターンの照合は上から順に行われます。 + +違うクラスのインスタンスが混在する場合は、最後の定義で関数引数がOr型であることを明示しなくてはなりません。 + +```erg +f "aa" = ... +f 1 = ... +# `f x = ...` is invalid +f x: Int or Str = ... +``` + +また、`match`と同じく網羅性がなくてはなりません。応急的には`fib _ = not_implemented()`とすればよいでしょう。 + +```erg +fib 0 = 0 +fib 1 = 1 +# PatternError: pattern of fib's parameter is not exhaustive +``` + +## 再帰関数 + +再帰関数は自身を定義に含む関数です。 + +簡単な例として階乗の計算を行う関数`factorial`を定義してみます。階乗とは、「それ以下の正の数をすべてかける」計算です。 +5の階乗は`5*4*3*2*1 == 120`となります。 + +```erg +factorial 0 = 1 +factorial 1 = 1 +factorial(n: Nat): Nat = n * factorial(n - 1) +``` + +まず階乗の定義から、0と1の階乗はどちらも1です。 +順に考えて、2の階乗は`2*1 == 2`、3の階乗は`3*2*1 == 6`、4の階乗は`4*3*2*1 == 24`となります。 +ここでよく見ると、ある数nの階乗はその前の数n-1の階乗にnをかけた数となることがわかります。 +これをコードに落とし込むと、`n * factorial(n - 1)`となるわけです。 +`factorial`の定義に自身が含まれているので、`factorial`は再帰関数です。 + +注意として、型指定を付けなかった場合はこのように推論されます。 + +```erg +factorial: |T <: Sub(Int, T) and Mul(Int, Int) and Eq(Int)| T -> Int +factorial 0 = 1 +factorial 1 = 1 +factorial n = n * factorial(n - 1) +``` + +しかし例え推論が出来たとしても、再帰関数には型を明示的に指定しておくべきです。上の例では、`factorial(-1)`のようなコードは有効ですが、 + +```erg +factorial(-1) == -1 * factorial(-2) == -1 * -2 * factorial(-3) == ... +``` + +となって、この計算は停止しません。再帰関数は慎重に値の範囲を定義しないと無限ループに陥ってしまう可能性があります。 +型指定は想定しない値の受け入れを防ぐのにも役立つというわけです。 + +## コンパイル時関数 + +関数名を大文字で始めるとコンパイル時関数となります。ユーザー定義のコンパイル時関数は、引数がすべて定数で、かつ型を明示する必要があります。 +コンパイル関数ができることは限られています。コンパイル時関数内で使えるのは定数式のみ、すなわち、いくつかの演算子(四則演算や比較演算、型構築演算など)とコンパイル時関数のみです。代入する引数も定数式である必要があります。 +そのかわり、計算をコンパイル時に行うことができるというメリットがあります。 + +```erg +Add(X, Y: Nat): Nat = X + Y +assert Add(1, 2) == 3 + +Factorial 0 = 1 +Factorial(X: Nat): Nat = X * Factorial(X - 1) +assert Factorial(10) == 3628800 + +math = import "math" +Sin X = math.sin X # ConstantError: this function is not computable at compile time +``` + +コンパイル時関数は多相型の定義などでもよく使われます。 + +```erg +Option T: Type = T or NoneType +Option: Type -> Type +``` + +## Appendix: 関数の比較 + +Ergでは、関数に`==`が定義されていません。それは関数の構造的な同値性判定アルゴリズムが一般には存在しないためです。 + +```erg +f = x: Int -> (x + 1)**2 +g = x: Int -> x**2 + 2x + 1 + +assert f == g # TypeError: cannot compare functions +``` + +`f`と`g`は常に同じ結果を返しますが、その判定を行うのは至難の業です。コンパイラに代数学を教え込む必要があります。 +そのため、Ergは関数の比較をまるごと諦めており、`(x -> x) == (x -> x)`もコンパイルエラーになります。これはPythonとは違った仕様なので注意する必要があります。 + +```python +# Python, weird example +f = lambda x: x +assert f == f +assert (lambda x: x) != (lambda x: x) +``` + +## Appendix2: ()の補完 + +```erg +f x: Object = ... +# will be completed to +f(x: Object) = ... + +f a +# will be completed to +f(a) + +f a, b # TypeError: f() takes 1 positional argument but 2 were given +f(a, b) # TypeError: f() takes 1 positional argument but 2 were given +f((a, b)) # OK +``` + +関数型`T -> U`は実際のところ、`(T,) -> U`の糖衣構文です。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/05_builtin_funcs.md b/doc/JA/syntax/05_builtin_funcs.md new file mode 100644 index 00000000..279755ed --- /dev/null +++ b/doc/JA/syntax/05_builtin_funcs.md @@ -0,0 +1,49 @@ +# 組み込み関数 + +## if + +`if`は条件に応じて処理を変える関数です。 + +```erg +result: Option Int = if! Bool.sample!(), do: + log "True was chosen" + 1 +print! result # None (or 1) +``` + +`.sample!()`は集合の値をランダムに返します。もし戻り値が真ならば、`print! "True"`が実行されます。 +条件が偽であった際の処理も指定できます。2つ目のdoブロックはelseブロックと呼ばれます。 + +```erg +result: Nat = if Bool.sample!(): + do: + log "True was chosen" + 1 + do: + log "False was chosen" + 0 +print! result # 1 (or 0) +``` + +処理が1行ならば、インデントを省略できます。 + +```erg +result = if Bool.sample!(): + do 1 + do 0 +``` + +## for + +繰り返し行う処理を書くときは`for`が使えます。 + +```erg +match_s(ss: Iterator(Str), pat: Pattern): Option Str = + for ss, s -> + if pat.match(s).is_some(): + break s +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/06_operator.md b/doc/JA/syntax/06_operator.md new file mode 100644 index 00000000..f2e1e1de --- /dev/null +++ b/doc/JA/syntax/06_operator.md @@ -0,0 +1,29 @@ +# 演算子 + +演算子(オペレーター)は、演算を表す記号です。被演算子(オペランド)は演算子の(左)右にあるもので、Ergでは専らオブジェクトです。 + +演算子は関数の一種であり、したがってそれ自体も第一級オブジェクトで変数に束縛できます。束縛の際は``で囲む必要があります。 +`+`(と`-`)については、単項演算子と二項演算子の両方が存在するため、一意化するために`_+_`(二項演算)/`+_`(単項演算)のどちらかを指定する必要があります。 + +```erg +add = `+` # SyntaxError: specify `_+_` or `+_` +add = `_+_` +assert f(1, 2) == 3 +assert f("a", "b") == "ab" + +g = `*` # OK, this is binary only +assert g(1, 2) == 2 +``` + +ただし、特殊形式と呼ばれる一部の演算子は束縛できないことに注意してください。 + +```erg +def = `=` # SyntaxError: cannot bind `=` operator, this is a special form +# NG: def x, 1 +function = `->` # SyntaxError: cannot bind `->` operator, this is a special form +# NG: function x, x + 1 +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/07_side_effect.md b/doc/JA/syntax/07_side_effect.md new file mode 100644 index 00000000..e2c68597 --- /dev/null +++ b/doc/JA/syntax/07_side_effect.md @@ -0,0 +1,121 @@ +# 副作用とプロシージャ + +これまで`print!`の!の意味を説明せずにいましたが、いよいよその意味が明かされます。この!は、ズバリこのオブジェクトが「副作用」のある「プロシージャ」であることを示しています。プロシージャは関数に「副作用」という効果を与えたものです。 + +```erg +f x = print! x # EffectError: functions cannot be assigned objects with side effects +# hint: change the name to 'f!' +``` + +上のコードはコンパイルエラーになります。関数中でプロシージャを使用しているからです。このような場合は、プロシージャとして定義しなくてはなりません。 + +```erg +p! x = print! x +``` + +`p!`, `q!`, ...は、プロシージャを表すための典型的な変数名です。 +このようにして定義したプロシージャもまた関数内では使用できないため、副作用は完全に隔離されるわけです。 + +## メソッド + +関数とプロシージャにはそれぞれメソッドが存在します。関数メソッドは`self`の不変参照のみを取れ、プロシージャルメソッドは`self`の可変参照を取れます。 +`self`は特殊な引数で、メソッドの文脈では呼び出したオブジェクト自身を指します。参照の`self`は他のいかなる変数にも代入できません。 + +```erg +C. + method ref self = + x = self # OwnershipError: cannot move out 'self' + x +``` + +メソッドは`self`の所有権を奪うこともできます。そのメソッドの定義では`ref`または`ref!`を外します。 + +```erg +n = 1 +s = n.into(Str) # '1' +n # ValueError: n was moved by .into (line 2) +``` + +可変参照を持てるのは常に1つのプロシージャルメソッドのみです。さらに可変参照が取られている間は元のオブジェクトから参照を取れなくなります。その意味で`ref!`は`self`に副作用を引き起こします。 + +ただし、可変参照から(不変/可変)参照の生成はできることに注意してください。これによって、プロシージャルメソッド中で再帰したり`self`を`print!`できたりします。 + +```erg +T -> T # OK (move) +T -> Ref T # OK +T => Ref! T # OK (only once) +Ref T -> T # NG +Ref T -> Ref T # OK +Ref T => Ref! T # NG +Ref! T -> T # NG +Ref! T -> Ref T # OK +Ref! T => Ref! T # OK +``` + +## Appendix: 副作用の厳密な定義 + +コードに副作用があるかないかのルールはすぐに理解できるものではない。 +理解できるようになるまでは、とりあえず関数として定義してエラーが出ればプロシージャとするコンパイラ任せのスタイルを推奨する。 +しかし、言語の厳密な仕様を把握しておきたい人のために、以下ではもう少し詳しく副作用について説明しておく。 + +まず、Ergにおける副作用に関して、戻り値の同値性は関係がないということを明記しなくてはならない。 +任意の`x`に対して`p!(x) == p!(x)`となるプロシージャが存在する(常に`None`を返すなど)し、`f(x) != f(x)`となる関数も存在する。 + +前者の例は`print!`であり、後者の例は以下の関数である。 + +```erg +nan _ = Float.NaN +assert nan(1) != nan(1) +``` + +また、クラスのように同値判定自体ができないオブジェクトも存在する。 + +```erg +T = Structural {i = Int} +U = Structural {i = Int} +assert T == U + +C = Class {i = Int} +D = Class {i = Int} +assert C == D # TypeError: cannot compare classes +``` + +本題に戻ろう。Ergにおける「副作用」の正確な定義は、 + +* 外部の可変な情報にアクセスすること + +である。外部とは、一般には外側のスコープを指す。Ergがタッチできないコンピューターリソースや、実行前/後の情報については「外部」に含まれない。「アクセス」は書き込みだけでなく読み込みも含める。 + +例として`print!`プロシージャについて考える。`print!`は一見何の変数も書き換えていないように見える。が、もしこれが関数だったとすると、例えばこのようなコードで外側変数を書き換え可能である。 + +```erg +camera = import "some_camera_module" +ocr = import "some_ocr_module" + +n = 0 +_ = + f x = print x # 仮にprintを関数として使えたとする + f(3.141592) +cam = camera.new() # カメラはPCのディスプレイを向いている +image = cam.shot!() +n = ocr.read_num(image) # n = 3.141592 +``` + +`camera`モジュールはあるカメラ製品のAPIを提供する外部のライブラリ、`ocr`はOCR(光学文字認識)のためのライブラリと考えてほしい。 +直接の副作用は`cam.shot!()`によって引き起こされているが、明らかにその情報は`f`から漏洩している。よって、`print!`は性質上関数とはなれない。 + +とはいえ、関数中で値を一時的に確認したく、そのためだけに関連する関数まで`!`をつけたりしたくない場合もある。その際は`log`関数が使える。 +`log`はコード全体の実行後に値を表示する。これにより、副作用は伝搬されない。 + +```erg +log "this will be printed after execution" +print! "this will be printed immediately" +# this will be printed immediately +# this will be printed after execution +``` + +つまり、プログラムへのフィードバックがない、換言すればいかなる外部オブジェクトもその情報を使うことが出来ないならば、情報の「漏洩」自体は許される場合がある。「伝搬」されなければよいのである。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/08_procedure.md b/doc/JA/syntax/08_procedure.md new file mode 100644 index 00000000..961f4207 --- /dev/null +++ b/doc/JA/syntax/08_procedure.md @@ -0,0 +1,11 @@ +# プロシージャ + +プロシージャは可変オブジェクトを取り扱う際に必要となるが、可変オブジェクトを引数に持てばプロシージャであるとは限らない。 + +```erg +peek_str s: Str! = log s +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/09_builtin_procs.md b/doc/JA/syntax/09_builtin_procs.md new file mode 100644 index 00000000..a708c193 --- /dev/null +++ b/doc/JA/syntax/09_builtin_procs.md @@ -0,0 +1,13 @@ +# 組み込みプロシージャ + +## id! + +オブジェクトのユニークな識別番号を返す。 +純粋なErgの意味論の中では同一の構造を持つオブジェクトの間に差異を見出す事はできないが、実際のところ、オブジェクトはメモリ上の位置が異なる。`id!`はこの位置を表す数値を返す。 + +```erg +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/10_array.md b/doc/JA/syntax/10_array.md new file mode 100644 index 00000000..6ac4be67 --- /dev/null +++ b/doc/JA/syntax/10_array.md @@ -0,0 +1,40 @@ +# 配列 + +配列はもっとも基本的な __コレクション(集約)__ です。 +コレクションとは、内部にオブジェクトを複数保持できるオブジェクトのことです。 + +```erg +a = [1, 2, 3] +a: [Int; 3] # 型指定: セミコロンの後の数字は要素数 +# 要素数がわからない場合は省略可能 +a: [Int] + +mut_a = [!1, !2, !3] +mut_a[0].inc!() +assert mut_a == [2, 2, 3] +``` + +## スライス + +配列は、複数の値をまとめて取り出すこともできます。これをスライスと呼びます。 + +```erg +l = [1, 2, 3, 4] +# Pythonのl[1:3]に相当 +assert l[1..<3] == [2, 3] +assert l[1..2] == [2, 3] +# l[1]と同じ +assert l[1..1] == [2] +# Pythonのl[::2]に相当 +assert l[..].step(2) == [2, 4] +``` + +スライスで得られるオブジェクトは配列の(不変)参照です。 + +```erg +print! Typeof l[1..2] # Ref [Int; 4] +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/11_tuple.md b/doc/JA/syntax/11_tuple.md new file mode 100644 index 00000000..4b2a9a64 --- /dev/null +++ b/doc/JA/syntax/11_tuple.md @@ -0,0 +1,117 @@ +# タプル + +タプルは配列と似ていますが、違う型のオブジェクトを保持することができます。 +このようなコレクションを非等質なコレクションと呼びます。対して等質なコレクションには配列、セットなどがあります。 + +```erg +t = (1, True, "a") +(i, b, s) = t +assert(i == 1 and b == True and s == "a") +``` + +タプル`t`は`t.n`の形式でn番目の要素を取り出すことができます。Pythonと違い、`t[n]`ではないことに注意してください。 +これは、タプル要素のアクセスはメソッド(配列の`[]`はメソッドです)というより属性に近い(コンパイル時に要素の存在がチェックされる、nによって型が変わりうる)ためです。 + +```erg +assert t.0 == 1 +assert t.1 == True +assert t.2 == "a" +``` + +括弧`()`はネストしないとき省略可能です。 + +```erg +t = 1, True, "a" +i, b, s = t +``` + +タプルは違う型のオブジェクトを保持することができますが、そのかわり配列のようなイテレーションができなくなります。 + +```erg +t: ({1}, {2}, {3}) = (1, 2, 3) +(1, 2, 3).iter().map(x -> x + 1) # TypeError: type ({1}, {2}, {3}) has no method `.iter()` +# すべて同じ型の場合配列と同じように`(T; n)`で表せるが、これでもイテレーションは出来ない +t: (Int; 3) = (1, 2, 3) +assert (Int; 3) == (Int, Int, Int) +``` + +ただし、非等質なコレクション(タプルなど)はアップキャスト、Intersectionなどによって等質なコレクション(配列など)に変換できます。 +これを等質化といいます。 + +```erg +(Int, Bool, Str) can be [T; 3] | T :> Int, T :> Bool, T :> Str +``` + +```erg +t: (Int, Bool, Str) = (1, True, "a") # non-homogenous +a: [Int or Bool or Str; 3] = [1, True, "a"] # homogenous +_a: [Show; 3] = [1, True, "a"] # homogenous +_a.iter().map(x -> log x) # OK +t.try_into([Show; 3])?.iter().map(x -> log x) # OK +``` + +## ユニット(Unit) + +要素が0個のタプルはユニットと言います。ユニットは値ですが、自身の型そのものも指します。 + +```erg +unit = () +(): () +``` + +ユニットはすべての要素0のタプルのスーパークラスです。 + +```erg +() > (Int; 0) +() > (Str; 0) +``` + +このオブジェクトの使いみちは、引数、戻り値がないプロシージャなどです。Ergのサブルーチンは、必ず引数と戻り値を持つ必要があります。しかしプロシージャなどの場合、副作用を起こすだけで意味のある引数・戻り値がない場合もあります。その際に「意味のない、形式上の値」としてユニットを使うわけです。 + +```erg +# ↓ 実はこの括弧はユニット +p!() = + # `print!`は意味のある値を返さない + print! "Hello, world!" +p!: () => () +``` + +ただしPythonはこのようなときユニットではなく`None`を使う傾向があります。 +Ergでの使い分けとしては、プロシージャなどではじめから意味のある値を返さないことが確定しているときは`()`、要素の取得のように操作が失敗して何も得られない可能性があるときは`None`を返してください。 + +## 引数とタプル + +実は、Ergの`Callable`オブジェクトは全て1引数で1戻り値です。N個の引数を取るサブルーチンは、「N個の要素を持つタプル1つ」を引数として受け取っているだけだったのです。 + +```erg +# f x = ...は暗黙にf(x) = ...とみなされる +f x = x +assert f(1) == 1 +f(1, 2, 3) # ArgumentError: f takes 1 positional argument but 3 were given +# 可変個の引数を受け取る +g x: Int, ...y: Int = y +assert (2, 3) == g 1, 2, 3 +``` + +関数の型もこれで説明が付きます。 + +```erg +assert f in T: {(T,) -> T | T} +assert g in {(Int, ...(Int; N)) -> (Int; N) | N: Nat} +``` + +正確には、関数の入力はタプルではなく「デフォルト属性付きNamedタプル」です。これは関数の引数でだけ使える特殊なタプルで、レコードのように名前付けができ、デフォルト値を持つことができます。 + +```erg +f(x: Int, y=0) = x + y +f: (Int, y=Int) -> Int + +f(x=0, y=1) +f(y=1, x=0) +f(x=0) +f(0) +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/12_dict.md b/doc/JA/syntax/12_dict.md new file mode 100644 index 00000000..e45ccaaf --- /dev/null +++ b/doc/JA/syntax/12_dict.md @@ -0,0 +1,67 @@ +# Dict + +Dictはキーと値のペアを持つコレクションです。 + +```erg +ids = {"Alice": 145, "Bob": 214, "Charlie": 301} +assert ids["Alice"] == 145 +``` + +キーはHashであるならば文字列でなくても構いません。 + +```erg +# rangeオブジェクトをキーにするのは非推奨(スライスと混同される) +r = {1..3: "1~3", 4..6: "4~6", 7..9: "7~9"} +assert r[1..3] == "1~3" +l = {[]: "empty", [1]: "1"} +assert l[[]] == "empty" +``` + +Dictに順番は関係ありません。また、重複する要素を持つことも出来ません。この点でDictはSetと似ています。 +Dictはキー付きのSetと言うこともできるでしょう。 + +```erg +{"Alice": 145, "Bob": 214, "Charlie": 301} == {"Alice": 145, "Charlie": 301, "Bob": 214} +``` + +DictリテラルからDictを生成する場合、キーの重複がないかチェックされます。 +重複がある場合コンパイルエラーとなります。 + +```erg +{"Alice": 145, "Alice": 1} # KeyError: Duplicate key "Alice" +``` + +空のDictは`{:}`で生成します。`{}`は空の配列を表すことに注意してください。 + +```erg +mut_dict = !{:} +mut_dict.insert! "Alice", 145 +mut_dict.insert! "Bob", 214 +assert mut_dict["Alice"] == 145 +``` + +## Heterogeneous Dict + +キー・値の型は単一でなくてもよい。そのような辞書を __非等質な辞書(heterogenous dict)__ という。 + +```erg +d: {Str: Int, Int: Str} = {”a”: 1, 1: “a”} +assert d[”a”] == 1 +assert d[1] == “a” +``` + +しかし、違う型のキーに同じ型の値、または同じ型のキーに違う型の値をあてることはできない。 +このような場合は代わりにOr型を使う。 + +```erg +invalid1 = {1: “a”, “a”: “b”} +invalid2 = {1: “a”, 2: 2} + +# Ergの型推論はOr型を推論しないので、型指定が必要 +valid1: {Int or Str: Str} = {1: “a”, “a”: “b”} +valid2: {Int: Int or Str} = {1: “a”, 2: 2} +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/13_record.md b/doc/JA/syntax/13_record.md new file mode 100644 index 00000000..80d8e86a --- /dev/null +++ b/doc/JA/syntax/13_record.md @@ -0,0 +1,199 @@ +# レコード + +レコードは、キーでアクセスするDictとコンパイル時にアクセスが検査されるタプルの性質を併せ持つコレクションです。 +JavaScriptをやったことがある方ならば、オブジェクトリテラル記法の(より強化された)ようなものと考えてください。 + +```erg +john = {.name = "John"; .age = 21} + +assert john.name == "John" +assert john.age == 21 +assert john in {.name = Str; .age = Nat} +john["name"] # Error: john is not subscribable +``` + +`.name`, `.age`の部分を属性、`"John"`, `21`の部分を属性値と呼びます。 + +JavaScriptのオブジェクトリテラルとの相違点は、文字列でアクセスできない点です。すなわち、属性は単なる文字列ではありません。 +これは、値へのアクセスをコンパイル時に決定するためと、辞書とレコードが別物であるためといった理由があります。つまり、`{"name": "John"}`はDict,`{name = "John"}`はレコードである。 +では、辞書とレコードはどう使い分ければいいのか。 +一般的にはレコードの使用を推奨する。レコードには、コンパイル時に要素が存在するかチェックされる、 __可視性(visibility)__ を指定できるなどのメリットがある。 +可視性の指定は、Java言語などでみられるpublic/privateの指定に相当する。詳しくは[可視性](./15_visibility.md)を参照。 + +```erg +a = {x = 1; .y = x + 1} +a.x # AttributeError: x is private +# Hint: declare as `.x` +assert a.y == 2 +``` + +上の例はJavaScriptに習熟している人間からすると奇妙かもしれないが、単に`x`と宣言すると外部からアクセスできない。`.`をつけると`.`でアクセスできるというわけである。 + +属性に対する明示的な型指定もできる。 + +```erg +anonymous = { + .name: Option! Str = !None + .age = 20 +} +anonymous.name.set! "John" +``` + +レコードはメソッドも持てる。 + +```erg +o = { + .i = !0 + .inc! ref! self = self.i.inc!() +} + +assert o.i == 0 +o.inc!() +assert o.i == 1 +``` + +レコードに関して特筆すべき文法がある。レコードの属性値が全てクラス(構造型ではダメ)のとき、そのレコード自体が、自身の属性を要求属性とする型としてふるまうのである。 +このような型をレコード型と呼ぶ。詳しくは[レコード]の項を参照してほしい。 + +```erg +# レコード +john = {.name = "John"} +# レコード型 +john: {.name = Str} +Named = {.name = Str} +john: Named + +greet! n: Named = + print! "Hello, I am {n.name}" +greet! john # "Hello, I am John" + +print! Named.name # Str +``` + +## レコードの分解 + +レコードは以下のようにして分解することができる。 + +```erg +record = {x = 1; y = 2} +{x = a; y = b} = record +assert a == 1 +assert b == 2 + +point = {x = 2; y = 3; z = 4} +match point: + {x = 0; y = 0; z = 0} -> "origin" + {x = _; y = 0; z = 0} -> "on the x axis" + {x = 0; ...} -> "x = 0" + {x = x; y = y; z = z} -> "({x}, {y}, {z})" +``` + +また、レコードは属性と同名の変数があるとき、例えば`x = x`または`x = .x`を`x`に、`.x = .x`または`.x = x`を`.x`に省略することができる。 +ただし属性が一つのときはセットと区別するために`;`を付ける必要がある。 + +```erg +x = 1 +y = 2 +xy = {x; y} +a = 1 +b = 2 +ab = {.a; .b} +assert ab.a == 1 +assert ab.b == 2 + +record = {x;} +tuple = {x} +assert tuple.1 == 1 +``` + +この構文を利用して、レコードを分解して変数に代入することができる。 + +```erg +# same as `{x = x; y = y} = xy` +{x; y} = xy +assert x == 1 +assert y == 2 +# same as `{.a = a; .b = b} = ab` +{a; b} = ab +assert a == 1 +assert b == 2 +``` + +## 空レコード + +空のレコードは`{=}`で表される。空のレコードはUnitと同じく自身のクラスそのものでもある。 + +```erg +empty_record = {=} +empty_record: {=} +# Object: Type = {=} +empty_record: Object +empty_record: Structural {=} +{x = 3; y = 5}: Structural {=} +``` + +空のレコードは空のDict`{:}`や空のセット`{}`とは異なる。特に`{}`とは意味が正反対なので注意が必要である(Pythonでは`{}`は空の辞書となっているが、Ergでは`!{:}`である)。 +列挙型としての`{}`は何も要素に含まない空虚な型である。`Never`型は、これをクラス化したものである。 +逆に、レコードクラスの`{=}`は要求インスタンス属性がないので、全てのオブジェクトがこれの要素になる。`Object`は、これのエイリアスである。 +`Object`(のパッチ)は`.__sizeof__`などの極めて基本的な提供メソッドを持つ。 + +```erg +AnyPatch = Patch Structural {=} + .__sizeof__ self = ... + .clone self = ... + ... +Never = Class {} +``` + +注意として、`{}`, `Never`型と構造的に等価な型・クラスは他に存在できず、ユーザーが`{}`, `Class {}`を右辺に指定して型を定義するとエラーとなる。 +これにより、例えば`1..10 or -10..-1`とするところを`1..10 and -10..-1`としてしまうようなミスを防ぐことができる。 +また、合成の結果`Object`となるような型(`Int and Str`など)を定義すると単に`Object`とするように警告が出る。 + +## インスタントブロック + +Ergにはもう一つインスタントブロックという構文があるが、これは単に最後に評価した値を返すだけである。属性の保持はできない。 + +```erg +x = + x = 1 + y = x + 1 + y ** 3 +assert x == 8 + +y = + .x = 1 # SyntaxError: cannot define an attribute in an entity block +``` + +## データクラス + +素のレコード(レコードリテラルで生成されたレコード)は、これ単体でメソッドを実装しようとすると、直接インスタンスに定義する必要がある。 +これは効率が悪く、さらに属性の数が増えていくとエラー表示などが見にくくなり使いにくい。 + +```erg +john = { + name = "John Smith" + age = !20 + .greet! ref self = print! "Hello, my name is {self::name} and I am {self::age} years old." + .inc_age! ref! self = self::age.update! x -> x + 1 +} +john + 1 +# TypeError: + is not implemented for {name = Str; age = Int; .greet! = Ref(Self).() => None; inc_age! = Ref!(Self).() => None}, Int +``` + +そこで、このような場合はレコードクラスを継承するとよい。このようなクラスをデータクラスと呼ぶ。 +これについては[クラス](./type/04_class.md)の項で詳しく説明する。 + +```erg +Person = Inherit {name = Str; age = Nat} +Person. + greet! ref self = print! "Hello, my name is {self::name} and I am {self::age} years old." + inc_age! ref! self = self::age.update! x -> x + 1 + +john = Person.new {name = "John Smith"; age = 20} +john + 1 +# TypeError: + is not implemented for Person, Int +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/14_set.md b/doc/JA/syntax/14_set.md new file mode 100644 index 00000000..a43db30c --- /dev/null +++ b/doc/JA/syntax/14_set.md @@ -0,0 +1,46 @@ +# セット(Set) + +セットは集合を表し、データ構造的には重複、順序のない配列である。 + +```erg +assert Set.from([1, 2, 3, 2, 1]) == {1, 2, 3} +assert {1, 2} == {1, 1, 2} # 重複は自動で削除される +assert {1, 2} == {2, 1} +``` + +セットは集合演算を行える。 + +```erg +assert 1 in {1, 2, 3} +assert not 1 in {} +assert {1} or {2} == {1, 2} +assert {1, 2} and {2, 3} == {2} +assert {1, 2} not {2} == {1} +``` + +セットは等質なコレクションである。別のクラスのオブジェクトを共存させるためには等質化させなくてはならない。 + +```erg +s: {Int or Str} = {"a", 1, "b", -1} +``` + +## 型としてのセット + +セットは型としても扱える。このような型は __列挙型(Enum type)__ と呼ばれる。 + +```erg +i: {1, 2, 3} = 1 +assert i in {1, 2, 3} +``` + +セットの要素がそのまま型の要素になる。 +セット自身は違うことに注意してほしい。 + +```erg +mut_set = {1, 2, 3}.into {Int; !3} +mut_set.insert!(4) +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/15_type.md b/doc/JA/syntax/15_type.md new file mode 100644 index 00000000..840b24fc --- /dev/null +++ b/doc/JA/syntax/15_type.md @@ -0,0 +1,7 @@ +# 型 + +型はErgにおいて非常に重要な機能ですので、[専用のセクション](./type/01_type_system.md)を用意しています。そちらをご覧ください。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/16_iterator.md b/doc/JA/syntax/16_iterator.md new file mode 100644 index 00000000..404d2450 --- /dev/null +++ b/doc/JA/syntax/16_iterator.md @@ -0,0 +1,89 @@ +# イテレータ + +イテレータは、コンテナの要素を取り出すためのオブジェクトです。 + +```erg +for! 0..9, i => + print! i +``` + +このコードは0から9までの数字を出力します。 +それぞれの数字(=Intオブジェクト)は`i`に代入され、`=>`以下の動作(=`print! i`)が実行されます。このような繰り返し実行のことを __イテレーション__ といいます。 + +ではここで`for!`プロシージャの型シグネチャを見てみましょう。 + +```erg +for!: |T: Type, I <: Iterable T| (I, T => None) => None +``` + +第一引数は`Iterable`という型のオブジェクトを受け付けるようです。 + +`Iterable`は`.Iterator`属性, `.iter`メソッドを要求メソッドに持つ型です。 + +```erg +Iterable T = Trait { + .Iterator = {Iterator} + .iter = Self(T).() -> Self.Iterator T +} +``` + +`.Iterator`属性の型`{Iterator}`はいわゆるセットカインドです。 + +```erg +assert [1, 2, 3] in Iterable(Int) +assert 1..3 in Iterable(Int) +assert [1, 2, 3].Iterator == ArrayIterator +assert (1..3).Iterator == RangeIterator + +log [1, 2, 3].iter() # +log (1..3).iter() # +``` + +`ArrayIterator`と`RangeIterator`はどちらも`Iterator`を実装するクラスで、`Array`, `Range`にイテレーション機能を与えるためだけに存在します。 +このようなデザインパターンをコンパニオンクラス[1](#1)と呼びます。 +そして`IteratorImpl`パッチがイテレーション機能のコアです。`Iterator`は`.next`メソッド1つだけを要求し、`IteratorImpl`は実に数十個のメソッドを提供します。`ArrayIterator`や`RangeIterator`は`.next`メソッドを実装するだけで`IteratorImpl`の実装メソッドを使うことができるわけです。この利便性から、標準ライブラリでは多数のイテレータが実装されています。 + +```mermaid +classDiagram + class Array~T~ { + ... + iter() ArrayIterator~T~ + } + class Range~T~ { + ... + iter() RangeIterator~T~ + } + class Iterable~T~ { + <> + iter() Iterator~T~ + } + Iterable~T~ <|.. Array~T~: Impl + Iterable~T~ <|.. Range~T~: Impl + class ArrayIterator~T~ { + array: Array~T~ + next() T + } + class RangeIterator~T~ { + range: Range~T~ + next() T + } + class Iterator~T~ { + <> + next() T + } + Iterator~T~ <|.. ArrayIterator~T~: Impl + Iterator~T~ <|.. RangeIterator~T~: Impl + + Array <-- ArrayIterator + Range <-- RangeIterator +``` + +`Iterable`のような、トレイト(この場合は`Iterator`)を静的ディスパッチでありながら統一的に扱えるインターフェースを提供する型をコンパニオンクラスアダプターと呼びます。 + +--- + +1 このパターンには統一された名前がないようであるが、Rustでは[companion struct pattern](https://gist.github.com/qnighy/be99c2ece6f3f4b1248608a04e104b38#:~:text=%E3%82%8F%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%80%82-,companion%20struct,-%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%A8%E3%80%81%E3%81%9D%E3%81%AE)と呼ばれているおり、それになぞらえて命名した。[↩](#f1) + +

+ Previous | Next +

diff --git a/doc/JA/syntax/17_mutability.md b/doc/JA/syntax/17_mutability.md new file mode 100644 index 00000000..ae6e1cf4 --- /dev/null +++ b/doc/JA/syntax/17_mutability.md @@ -0,0 +1,98 @@ +# Mutability + +すでに見たように、Ergの変数は全て不変です。しかし、Ergのオブジェクトには可変性という概念があります。 +以下のコードを例にします。 + +```erg +a = [1, 2, 3] +a = a + [4, 5, 6] +print! a # [1, 2, 3, 4, 5, 6] + +b = [1, 2, 3].into [Int; !3] +b.concat! [4, 5, 6] +print! b # [1, 2, 3, 4, 5, 6] +``` + +上のコードの、更に上の部分は実際にはErgでは実現できません。再代入不可だからです。 +`a, b`は、最終的な結果は同じように見えますが、その意味は大きく異なります。 +`a`は`Nat`の配列を示す変数ですが、1行目と2行目では指しているオブジェクトが異なります。`a`という名前が同じだけで、中身はさし変わっているのです。 + +```erg +a = [1, 2, 3] +print! id! a # 0x000002A798DFE940 +_a = a + [4, 5, 6] +print! id! _a # 0x000002A798DFE980 +``` + +`id!`プロシージャはオブジェクトが存在するメモリ上のアドレスを返します。 + +`b`は`Nat`の「動的」配列です。オブジェクトの中身は変わりますが、変数の指すものは同じです。 + +```erg +b = [1,2,3].into [Int; !3] +print! id! b # 0x000002A798DFE220 +b.concat! [4, 5, 6] +print! id! b # 0x000002A798DFE220 +``` + +```erg +i = !0 +if! True: + do! i.inc!() # or i.add!(1) + do pass +print! i # 1 +``` + +`!`は可変化演算子(mutate operator)とよばれる特殊な演算子です。引数の不変オブジェクトを可変化して返します。 +`!`がついたオブジェクトの振る舞いはカスタム可能です。 + +```erg +Point = Class {.x = Int; .y = Int} + +# 可変型は同名の不変型が存在する場合、互換性がなくてはならない(継承関係にはない) +# この場合.xは可変化し、yは不変のまま +Point! = Class {.x = Int!; .y = Int} +Point!.inc_x! ref! self = self.x.update! x -> x+1 + +p = Point!.new {.x = !0; .y = 0} +p.inc_x!() +print! p.x # 1 +``` + +## Constant(定数) + +変数と違い、すべてのスコープで同じものを指すのが定数です。 +定数は`=`演算子で宣言します。 + +```erg +PI = 3.141592653589 +match! x: + PI => print! "this is pi" +``` + +定数はグローバル以下のすべてのスコープで同一であり、上書きができません。よって、`=`による再定義はできません。この制限により、パターンマッチで使うことができます。 +`True`や`False`がパターンマッチで使えるのは、この2つが定数だからなのです。 +また、定数は必ず不変オブジェクトを指しています。`Str!`型などは定数となれません。 +組み込み型がすべて定数なのは、コンパイル時に決定されているべきだからです。定数でない型も生成可能ですが、型指定には使えず、単なるレコードのようにしか使えません。逆に言えば、型はコンパイル時に内容が決定されているレコードとも言えるでしょう。 + +## Variable, Name, Identifier, Symbol + +ここで、Ergでの変数に関する用語を整理しておきましょう。 + +変数(Variable)はオブジェクトに名前(Name)をつけ、再利用できるようにする仕組み(またはその名前を指す)です。 +識別子(Identifier)は変数を指定する文法要素です。 +シンボルは名前を表すための文法要素、トークンです。 + +記号でない文字だけがシンボルであり、記号は演算子として識別子足り得ますが、シンボルとは呼びません。 +例えば、`x`は識別子でシンボルです。`x.y`も識別子ですが、これはシンボルとは言いません。`x`と`y`はシンボルです。 +また`x`が何のオブジェクトに紐づけられていなかったとしても、`x`は相変わらずSymbolかつIdentifierですが、Variableとは言いません。 +`x.y`という形の識別子はフィールドアクセサと言います。 +また、`x[y]`という形の識別子は添字アクセサと言います。 + +変数と識別子の違いですが、Ergの文法論的な意味での変数をいうのならば、実質この二つは同じです。 +変数と識別子が等価でない言語は、C言語などがあげられます。C言語では、型や関数は変数に代入できません。int, mainは識別子ですが変数ではないのです(厳密には代入出来る場合もありますが、制約があります)。 +しかし、Ergでは「全てがオブジェクト」です。関数や型は勿論、演算子でさえ変数に代入可能です。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/18_memory_management.md b/doc/JA/syntax/18_memory_management.md new file mode 100644 index 00000000..301841a3 --- /dev/null +++ b/doc/JA/syntax/18_memory_management.md @@ -0,0 +1,110 @@ +# メモリ管理 + +ErgはPythonをホスト言語にした言語であるため、メモリ管理の方法はPythonの処理系に依存している。 +しかし意味論的には、Ergのメモリ管理はPythonのそれとは別物である。顕著な違いは、所有権システムと循環参照の禁止に現れている。 + +## 所有権 + +ErgはRustから影響を受けた所有権システムを持っている。 +Rustの所有権システムは一般的に難解だと言われているが、Ergのそれは直感的になるよう簡略化されている。 +Ergでは __可変オブジェクト__ に所有権がついており、所有権を失った後はそのオブジェクトを参照できない。 + +```erg +v = [1, 2, 3].into [Int; !3] + +push! vec, x = + vec.push!(x) + vec + +# vの中身([1, 2, 3])の所有権はwに移る +w = push! v, 4 +print! v # error: v was moved +print! w # [1, 2, 3, 4] +``` + +所有権の移動はオブジェクトをサブルーチンに渡したときなどに発生する。 +渡した後も所有権をまだ持っていたい場合は、複製(cloning)、凍結(freeze)、または借用(borrowing)をする必要がある。 +ただし後述するように借用はできる場面が限られている。 + +## 複製 + +オブジェクトを複製してその所有権を移す。実引数に`.clone`メソッドを適用することで行う。 +複製したオブジェクトは複製元のオブジェクトと全く同一になるが、互いに独立しているので、変更の影響は受けない。 + +複製はPythonのディープコピーに相当し、同一のオブジェクトをまるごと作り直すので、凍結・借用と比べて一般に計算コスト、メモリコストが高い。 +オブジェクトを複製する必要があるようなサブルーチンは、「引数を消費する」サブルーチンという。 + +```erg +capitalize s: Str! = + s.capitalize!() + s + +s1 = !"hello" +s2 = capitalize s1.clone() +log s2, s1 # !"HELLO hello" +``` + +## 凍結 + +不変オブジェクトは複数の場所から参照できることを利用して、可変オブジェクトを不変オブジェクトに変換してしまう。 +これを凍結という。凍結は可変配列からイテレータを作るときなどで使われる。 +可変配列からは直接イテレータを作ることができないので、不変配列に変換するのである。 +配列を壊したくない場合は、[`.freeze_map`メソッド](./type/mut.md)等を使う。 + +```erg +# イテレータが出す値の合計を計算する +sum|T <: Add + HasUnit| i: Iterator T = ... + +x = [1, 2, 3].into [Int; !3] +x.push!(4) +i = x.iter() # TypeError: [Int; !4] has no method `iter` +y = x.freeze() +i = y.iter() +assert sum(i) == 10 +y # この後もyは触れられる +``` + +## 借用 + +借用は複製や凍結よりも低コストである。 +以下のような単純な場合では、借用を行える。 + +```erg +peek_str ref(s: Str!) = + log s + +s = !"hello" +peek_str s +``` + +借用した値は元のオブジェクトに対する __参照__ と呼ばれる。 +参照をまた別のサブルーチンに渡す「又貸し」はできるが、借りているだけなので消費することはできない。 + +```erg +steal_str ref(s: Str!) = + # log関数は引数を借用するだけなので、又貸しできる + log s + # discard関数は引数を消費するので、エラー + discard s # OwnershipError: cannot consume a borrowed value + # hint: use `clone` method +``` + +```erg +steal_str ref(s: Str!) = + # これもダメ(=は右辺を消費する) + x = s # OwnershipError: cannot consume a borrowed value + x +``` + +Ergの参照はRustより制約が強い。参照は言語上第一級のオブジェクトであるが、明示的に生成することはできず、`ref`/`ref!`によって実引数の渡し方として指定できるのみである。 +これは、参照を配列に詰めたり参照を属性とするクラスを作ったりはできないということを意味する。 + +とはいえ、このような制約はそもそも参照のない言語では当たり前の仕様であり、そこまで不便となることはない。 + +## 循環参照 + +Ergでは意図せずメモリリークを起こせないように設計されており、メモリーチェッカーが循環参照を検知するとエラーを出す。ほとんどの場合、このエラーは弱参照`Weak`で解消できる。しかしこれでは巡回グラフなどの循環構造を持つオブジェクトを生成できないため、unsafe操作として循環参照を生成できるAPIを実装予定である。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/19_visibility.md b/doc/JA/syntax/19_visibility.md new file mode 100644 index 00000000..c252bc2d --- /dev/null +++ b/doc/JA/syntax/19_visibility.md @@ -0,0 +1,191 @@ +# 可視性(Visibility) + +Ergの変数には __可視性__ という概念が存在します。 +今まで見てきた変数は全て __プライベート変数(非公開変数)__ と呼ばれます。これは、外部から不可視の変数です。 +例えば`foo`モジュールで定義したプライベート変数は、別のモジュールから参照できないのです。 + +```erg +# foo.er +x = "this is an invisible variable" +``` + +```erg +# bar.er +foo = import "foo" +foo.x # AttributeError: Module 'foo' has no attribute 'x' ('x' is private) +``` + +対して、 __パブリック(公開)変数__ というものもあり、こちらは外部から参照できます。 +公開変数は`.`を付けて定義します。 + +```erg +# foo.er +.x = "this is a visible variable" +``` + +```erg +# bar.er +foo = import "foo" +assert foo.x == "this is a visible variable" +``` + +非公開変数には何も付ける必要はないのですが、非公開であることを明示するために`::`または`self::`(型などなら`Self::`)を付けることもできます。またモジュールなら`module::`とすることもできます。 + +```erg +::x = "this is a invisible variable" +assert ::x == x +assert self::x == ::x +assert module::x == ::x +``` + +単なる逐次実行の文脈では、プライベート変数はローカル変数とほぼ同義です。内側のスコープからは参照することが出来ます。 + +```erg +::x = "this is a private variable" +y = + x + 1 # 正確にはmodule::x +``` + +`::`を使うことで、スコープ内の同名変数の区別ができます。 +参照したい変数のスコープを左側に指定します。トップレベルの場合は`module`を指定します。 +指定しなかった場合は通常の場合と同じく最も内側の変数が参照されます。 + +```erg +::x = 0 +assert x == 0 +y = + ::x = 1 + assert x == 1 + z = + ::x = 2 + assert ::x == 2 + assert z::x == 2 + assert y::x == 1 + assert module::x == 0 +``` + +無名サブルーチンのスコープでは`self`で自身のスコープを指定します。 + +```erg +x = 0 +f = x -> + log module::x, self::x +f 1 # 0 1 +``` + +`::`は、プライベートインスタンス属性にアクセスするという役割も持っています。 + +```erg +x = 0 +C = Class {x = Int} +C. + # トップレベルのxが参照される(module::xにするようwarningが出る) + f1 self = x + # インスタンス属性のxが参照される + f2 self = self::x +``` + +## 外部モジュールでの可視性 + +あるモジュールで定義されたクラスは、実は外部モジュールからでもメソッドを定義できます。 + +```erg +# foo.er +.Foo = Class() +``` + +```erg +# bar.er +{Foo; ...} = import "foo" + +Foo:: + private self = pass +Foo. + public self = self::private() + +.f() = + foo = Foo.new() + foo.public() + foo::private() # AttributeError +``` + +ただし、そのメソッドを使えるのはどちらもそのモジュール内でのみです。 +外部で定義された非公開メソッドは、定義モジュール内でのみ`Foo`クラスのメソッドから参照できます。 +公開メソッドはクラスの外には公開されますが、モジュール外までは公開されません。 + +```erg +# baz.er +{Foo; ...} = import "foo" + +foo = Foo.new() +foo.public() # AttributeError: 'Foo' has no attribute 'public' ('public' is defined in module 'bar') +``` + +また、Re-exportする型にメソッドを定義することはできません。 +インポート元のモジュールによってメソッドが見つかったり見つからなかったりといった混乱を防ぐためです。 + +```erg +# bar.er +{.Foo; ...} = import "foo" + +.Foo:: + private self = pass # Error +.Foo. + public self = self::private() # Error +``` + +このようなことを行いたい場合はパッチを定義します。 + +```erg +# bar.er +{Foo; ...} = import "foo" + +FooImpl = Patch Foo +FooImpl:: + private self = pass +FooImpl. + public self = self::private() +``` + +```erg +# baz.er +{Foo; ...} = import "foo" +{FooImpl; ...} = import "bar" + +foo = Foo.new() +foo.public() +``` + +## 制限公開変数 + +変数の可視性は完全な公開・非公開しかないわけではありません。 +制限付きで公開することもできます。 + +```erg +# foo.er +.record = { + .a = { + .(.record)x = 0 + .(module)y = 0 + .z = 0 + } + _ = .a.x # OK + _ = .a.y # OK + _ = .a.z # OK +} + +_ = .record.a.x # VisibilityError +_ = .record.a.y # OK +_ = .record.a.z # OK +``` + +```erg +foo = import "foo" +_ = foo.record.a.x # VisibilityError +_ = foo.record.a.y # VisibilityError +_ = foo.record.a.z # OK +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/20_naming_rule.md b/doc/JA/syntax/20_naming_rule.md new file mode 100644 index 00000000..7605f0f7 --- /dev/null +++ b/doc/JA/syntax/20_naming_rule.md @@ -0,0 +1,50 @@ +# 命名規則 + +変数を定数式として使いたい場合は大文字で始まらなくてはならない。二文字以降は小文字でもよい。 + +```erg +i: Option Type = Int +match i: + t: Type -> log "type" + None -> log "None" +``` + +副作用のあるオブジェクトは`!`で終わらなくてはならない。プロシージャとプロシージャルメソッド、そして可変型である。 +ただし、`Proc`型自体は可変型ではない。 + +```erg +# Callable == Func or Proc +c: Callable = print! +match c: + p! -> log "proc" # 自明なので`: Proc`を省略可 + f -> log "func" +``` + +属性を外部に公開したい場合は、`.`を始めにつけて定義する。`.`を初めにつけなかった場合非公開となる。混乱を避けるため同一のスコープ内で共存はできない。 + +```erg +o = {x = 1; .x = 2} # SyntaxError: private and public variables with the same name cannot coexist +``` + +## リテラル識別子(Literal Identifiers) + +以上の規則は、文字列をシングルクォート('')で囲むと回避できる。すなわち、プロシージャルオブジェクトも`!`をつけずに代入することができる。ただしこの場合、値が定数式でも定数とはみなされない。 +このようにシングルクォートで囲まれた文字列による識別子をリテラル識別子という。 +これは、Pythonなど他言語のAPI(FFI)を呼び出す際に使う。 + +```erg +bar! = pyimport("foo").'bar' +``` + +Ergでも有効な識別子の場合は、''で囲む必要はない。 + +さらに、リテラル識別子中では記号も空白も入れることができるため、通常は識別子として使えない文字列を識別子として使うことができる。 + +```erg +'∂/∂t' y +'test 1: pass x to y'() +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/21_lambda.md b/doc/JA/syntax/21_lambda.md new file mode 100644 index 00000000..b3089978 --- /dev/null +++ b/doc/JA/syntax/21_lambda.md @@ -0,0 +1,94 @@ +# 無名関数(anonymous function) + +無名関数は、関数オブジェクトを名付けずその場で生成するための文法である。 + +```erg +# `->`は無名関数演算子 +# same as `f x, y = x + y` +f = (x, y) -> x + y +# same as `g(x, y: Int): Int = x + y` +g = (x, y: Int): Int -> x + y +``` + +引数が1つの場合は`()`を省略できる。 + +```erg +assert [1, 2, 3].map_collect(i -> i + 1) == [2, 3, 4] +assert ((i, j) -> [i, j])(1, 2) == [1, 2] +``` + +下の場合`0..9, (i -> ...)`であって`(0..9, i) -> ...`ではない。 +`->`は左辺に一つだけ引数をとる。複数の引数は一つのタプルとして受け取る。 + +```erg +for 0..9, i: Int -> + ... +``` + +無名関数では、空白による構文解釈の差異が存在する。 + +```erg +# この場合は`T(() -> Int)`と解釈される +i: T () -> Int +# この場合は(U()) -> Intと解釈される +k: U() -> Int +``` + +無名関数は引数なしでも使える。`=>`は無名プロシージャ演算子。 + +```erg +p! = () => print! "`p!` was called" +# `() ->`, `() =>`には`do`, `do!`という糖衣構文がある +# p! = do! print! "`p!` was called" +p!() # `p!` was called +``` + +引数なし関数は遅延初期化に使える。 + +```erg +time = import "time" +date = import "datetime" +now = if! True: + do!: + time.sleep! 1000 + date.now!() + do date.new("1970", "1", "1", "00", "00") +``` + +型付け、パターンマッチもできる。このため、`match`関数はほとんど無名関数の力で実現されている。 +`match`関数の引数に与える無名関数は上から順番にトライされる。ので、上の方は特殊なケースを、下に行くほど一般的なケースを記述する必要がある。順番を間違えると(可能な限り)コンパイラがWarningを出す。 + +```erg +n = (Complex or Ratio or Int).sample!() +i = match n: + PI -> PI # 定数PIに等しい場合 + (i: 1..10) -> i # 1~10のIntの場合 + (i: Int) -> i # Intの場合 + (c: Complex) -> c.real() # Complexの場合。Int < Complexだが、フォールバックできる + _ -> panic "cannot convert to Int" # 以上のいずれにも該当しない場合。matchは全パターンを網羅していなくてはならない +``` + +エラーハンドリングも`?`か`match`を使用して行うのが一般的である。 + +```erg +res: ParseResult Int +match res: + i: Int -> i + err: Error -> panic err.msg + +res2: Result Int, Error +match res2: + ok: Not Error -> log Typeof ok + err: Error -> panic err.msg +``` + +## 無名多相関数 + +```erg +# same as id|T| x: T = x +id = |T| x: T -> x +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/22_subroutine.md b/doc/JA/syntax/22_subroutine.md new file mode 100644 index 00000000..bf867728 --- /dev/null +++ b/doc/JA/syntax/22_subroutine.md @@ -0,0 +1,61 @@ +# Subroutine signatures + +## Func + +```erg +some_func(x: T, y: U) -> V +some_func: (T, U) -> V +``` + +## Proc + +```erg +some_proc!(x: T, y: U) => V +some_proc!: (T, U) => V +``` + +## Func Method + +メソッド型は、外部からは`Self`で指定できない。 + +```erg +.some_method(self, x: T, y: U) => () +# Self.(T, U) => ()はselfの所有権を奪う +.some_method: Ref(Self).(T, U) => () +``` + +## Proc Method (dependent) + +以下で、型`T!`は`N: Nat`という型引数を取るとする。外部から指定する場合は型変数を使用する。 + +```erg +T!: Nat -> Type +# ~>は適用前後の型引数の状態を示す(このときselfは可変参照でなくてはならない) +T! N.some_method!: (ref! self(N ~> N+X), X: Nat) => () +``` + +注意として、`.some_method`の型は`Ref!(T(N ~> N+X)).({X}) => () | N, X: Nat`となる。 +`ref!`がついていない、すなわち適用後所有権が奪われるメソッドでは、型引数の遷移(`~>`)を使用できない。 + +所有権が奪われる場合は以下のようになる。 + +```erg +# Nを使わないなら_で省略可 +# .some_method!: T!(N).({X}) => T!(N+X) | N, X: Nat +.some_method!(self(N), X: Nat) => T!(N+X) | N, X: Nat +``` + +## Operator + +``で囲むことで通常の関数と同じように定義できる。 +`and`や`or`などの中置アルファベット演算子は囲むことで中置演算子として定義できる。 + +```erg +and(x, y, z) = x and y and z +`_+_`(x: Foo, y: Foo) = x.a + y.a +`-_`(x: Foo) = Foo.new(-x.a) +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/23_scope.md b/doc/JA/syntax/23_scope.md new file mode 100644 index 00000000..df76fac1 --- /dev/null +++ b/doc/JA/syntax/23_scope.md @@ -0,0 +1,96 @@ +# クロージャ + +Ergのサブルーチンには、外部変数を捕捉する「クロージャ」という機能がある。 + +```erg +outer = 1 +f x = outer + x +assert f(1) == 2 +``` + +不変オブジェクトと同じく、可変オブジェクトも捕捉できる。 + +```erg +sum = !0 +for! 1..10, i => + sum.add! i +assert sum == 45 + +p! x = + sum.add! x +p!(1) +assert sum == 46 +``` + +しかし、関数は可変オブジェクトを捕捉できないので注意が必要である。 +仮に可変オブジェクトが関数内で参照できると、以下のようなコードが書けてしまう。 + +```erg +# !!! このコードは実際にはエラーになる !!! +i = !0 +f x = i + x +assert f 1 == 1 +i.add! 1 +assert f 1 == 2 +``` + +関数は同じ引数に対して同じ値を返すべきだが、その前提が破れてしまっている。 +`i`は呼び出し時に初めて評価されることに注意してほしい。 + +関数定義時点での可変オブジェクトの内容がほしい場合は`.clone`する。 + +```erg +i = !0 +immut_i = i.clone().freeze() +f x = immut_i + x +assert f 1 == 1 +i.add! 1 +assert f 1 == 1 +``` + +## 可変状態の回避、関数型プログラミング + +```erg +# Erg +sum = !0 +for! 1..10, i => + sum.add! i +assert sum == 45 +``` + +上と同等のプログラムは、Pythonでは以下のように記述できる。 + +```python +# Python +sum = 0 +for i in range(1, 10): + sum += i +assert sum == 45 +``` + +しかしErgではもっとシンプルな書き方を推奨する。 +サブルーチンと可変オブジェクトを使って状態を持ち回す代わりに、関数を使用する状態を局所化するスタイルである。これは関数型プログラミングと呼ばれる。 + +```erg +# Functional style +sum = (1..10).sum() +assert sum == 45 +``` + +上のコードは先程と全く同じ結果となるが、こちらのほうが遥かにシンプルであることが見て取れる。 + +`fold`関数を使用すれば、合計以外にも多様な操作を行うことができる。 +`fold`はイテレータのメソッドで、各イテレーションごとに引数`f`を実行する。 +結果を蓄積するカウンタの初期値は`init`で指定し、`acc`に蓄積されていく。 + +```erg +# start with 0, result will +sum = (1..10).fold(init: 0, f: (acc, i) -> acc + i) +assert sum == 45 +``` + +Ergは不変オブジェクトによるプログラミングで自然と簡潔な記述となるように設計されている。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/24_module.md b/doc/JA/syntax/24_module.md new file mode 100644 index 00000000..954ec391 --- /dev/null +++ b/doc/JA/syntax/24_module.md @@ -0,0 +1,42 @@ +# モジュール + +Ergでは、ファイル自体を1つのレコードとみなすことができます。これをモジュールと呼びます。 + +```erg: foo.er +# foo.er +.i = 1 +``` + +```erg +# fooモジュールを定義するのはこのレコードを定義するのとほとんど同じ +foo = {.i = 1} +``` + +```erg: bar.er +# bar.er +foo = import "foo" +print! foo # +assert foo.i == 1 +``` + +モジュール型はレコード型でもあるので、分解代入が可能です。 + +```erg +{sin; cos; ...} = import "math" +``` + +## モジュールの可視性 + +```console +└─┬ ./src + ├─ lib.er + ├─ foo.er + ├─ bar.er + └─┬ bar + ├─ baz.er + └─ qux.er +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/25_object_system.md b/doc/JA/syntax/25_object_system.md new file mode 100644 index 00000000..9e8905d2 --- /dev/null +++ b/doc/JA/syntax/25_object_system.md @@ -0,0 +1,82 @@ +# Object(対象体) + +変数に代入できる全てのデータ。`Object`クラスの持つ属性は以下の通り。 + +* `.__repr__`: オブジェクトの(リッチでない)文字列表現を返す +* `.__sizeof__`: オブジェクトのサイズ(ヒープ確保分含む)を返す +* `.__dir__`: オブジェクトの属性を一覧にして返す +* `.__hash__`: オブジェクトのハッシュ値を返す +* `.__getattribute__`: オブジェクトの属性を取得して返す +* `.clone`: オブジェクトのクローン(メモリ上に独立な実体を持つ)を生成して返す +* `.copy`: オブジェクトのコピー(メモリ上で同じものをさす)を返す + +## Record(レコード) + +レコードリテラル(`{attr = value; ...}`)で生成されるオブジェクト。 +このオブジェクトは`.clone`や`.__sizeof__`などの基本的なメソッドを持つ。 + +```erg +obj = {.x = 1} +assert obj.x == 1 + +obj2 = {...x; .y = 2} +assert obj2.x == 1 and obj2.y == 2 +``` + +## Attribute(属性) + +オブジェクトと関連付けられたオブジェクト。特に自身(`self`)を暗黙の第一引数にとるサブルーチン属性はメソッド(method)と呼ばれる。 + +```erg +# private_attrには`.`がないことに注意 +record = {.public_attr = j; private_attr = 2; .method = self -> self.i + 1} +record.public_attr == 2 +record.private_attr # AttributeError: private_attr is private +assert record.method() == 3 +``` + +## Element(要素) + +特定の型に属するオブジェクト(e.g. `1`は`Int`型の要素)。全てのオブジェクトは、少なくとも`{...}`型の要素である。 +クラスの要素の場合特にインスタンス(Instance)と呼ぶこともある。 + +## Subroutine(サブルーチン) + +関数またはプロシージャのインスタンスであるオブジェクトを示す(メソッドも含む)。サブルーチンを表すクラスは`Subroutine`である。 +より一般に`.__call__`を実装するオブジェクトは`Callable`(呼び出し可能オブジェクト)と呼ばれる。 + +## Callable(呼び出し可能オブジェクト) + +`.__call__`を実装するオブジェクト。`Subroutine`のスーパークラス。 + +## Type(型) + +要求属性を定義し、オブジェクトを共通化するオブジェクト。 +大きく分けて多相型(Polymorphic Type)と単相型(Monomorphic Type)の2つがある。典型的な単相型は`Int`, `Str`などで、多相型には`Option Int`, `[Int; 3]`などがある。 +さらにオブジェクトの状態変更をするメソッドを定義した型は可変型(Mutable type)と呼ばれ、可変な属性に`!`をつける必要がある(e.g. 動的配列: `[T; !_]`)。 + +## Class(クラス) + +`.__new__`, `.__init__`メソッドなどを持つ型。クラスベースのオブジェクト指向を実現する。 + +## Function(関数、写像) + +外部変数(静的変数除く)のread権限はあるが、外部変数のread/write権限がないサブルーチン。つまり、外部に副作用を及ぼせない。 +Ergの関数(Function)は副作用を許さないのでPythonのそれとは定義が異なる。 + +## Procedure(手続) + +外部変数のread権限および`self`、静的変数のread/write権限があり、全てのサブルーチンの使用が許可されている。外部に副作用を及ぼせる。 + +## Method(メソッド) + +第一引数に`self`を暗黙的にとるサブルーチン。単なる関数/プロシージャとは別の型となっている。 + +## Entity(エンティティ) + +サブルーチンおよび型ではないオブジェクト。 +単相型エンティティ(`1`, `"a"`など)は値オブジェクト、多相型エンティティ(`[1, 2, 3], {"a": 1}`)はコンテナオブジェクトとも呼ばれる。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/26_pattern_matching.md b/doc/JA/syntax/26_pattern_matching.md new file mode 100644 index 00000000..7da4a6fc --- /dev/null +++ b/doc/JA/syntax/26_pattern_matching.md @@ -0,0 +1,189 @@ +# パターンマッチ、論駁可能性 + +## Ergで使用可能なパターン + +### 変数パターン + +```erg +# basic assignment +i = 1 +# with type +i: Int = 1 +# with anonymous type +i: {1, 2, 3} = 2 +# function +fn x = x + 1 +# equals +fn x: Add(Int) = x + 1 +# (anonymous) function +fn = x -> x + 1 +fn: Int -> Int = x -> x + 1 +# higher-order type +a: [Int; 4] = [0, 1, 2, 3] +# or +a: Array Int, 4 = [0, 1, 2, 3] +``` + +### リテラルパターン + +```erg +# if `i` cannot be determined to be 1 at compile time, TypeError occurs. +# short hand of `_: {1} = i` +1 = i +# simple pattern matching +match x: + 1 -> "1" + 2 -> "2" + _ -> "other" +# fibonacci function +fib 0 = 0 +fib 1 = 1 +fib n: Nat = fib n-1 + fib n-2 +``` + +### 定数パターン + +```erg +cond = False +match! cond: + True => print! "cond is True" + _ => print! "cond is False" + +PI = 3.141592653589793 +E = 2.718281828459045 +num = PI +name = match num: + PI -> "pi" + E -> "e" + _ -> "unnamed" +``` + +### 篩パターン + +```erg +Array(T, N: {N | N >= 3}) +# == +Array(T, N | N >= 3) + +f M, N | M >= 0, N >= 1 = ... +f(1, 0) # TypeError: N (2nd parameter) must be 1 or more +``` + +### 破棄(ワイルドカード)パターン + +```erg +_ = 1 +_: Int = 1 +zero _ = 0 +right(_, r) = r +``` + +### 可変長パターン + +後述するタプル/配列/レコードパターンと組み合わせて使う。 + +```erg +[i, ...j] = [1, 2, 3, 4] +assert j == [2, 3, 4] +first|T|(fst: T, ...rest: T) = fst +assert first(1, 2, 3) == 1 +``` + +### タプルパターン + +```erg +(i, j) = (1, 2) +((k, l), _) = ((1, 2), (3, 4)) +# ネストしていないなら()を省略可能(1, 2は(1, 2)として扱われる) +m, n = 1, 2 + +f(x, y) = ... +``` + +### 配列パターン + +```erg +[i, j] = [1, 2] +[[k, l], _] = [[1, 2], [3, 4]] + +length [] = 0 +length [_, ...rest] = 1 + length rest +``` + +#### レコードパターン + +```erg +record = {i = 1; j = 2; k = 3} +{j; ...} = record # i, k will be freed + +{sin; cos; tan; ...} = import "math" +{*} = import "math" # import all + +person = {name = "John Smith"; age = 20} +age = match person: + {name = "Alice"; _} -> 7 + {_; age} -> age + +f {x: Int; y: Int} = ... +``` + +### データクラスパターン + +```erg +Point = Inherit {x = Int; y = Int} +p = Point.{x = 1; y = 2} +Point.{x; y} = p + +Nil T = Class Impl: Phantom T +Cons T = Inherit {head = T; rest = List T} +List T = Enum Nil(T), Cons(T) +List T. + first self = + match self: + Cons.{head; ...} -> x + _ -> ... + second self = + match self: + Cons.{rest=Cons.{head; ...}; ...} -> head + _ -> ... +``` + +### 列挙パターン + +※実際には単なる列挙型 + +```erg +match x: + i: {1, 2} -> "one or two: {i}" + _ -> "other" +``` + +### 範囲パターン + +※実際には単なる区間型 + +```erg +# 0 < i < 1 +i: 0<..<1 = 0.5 +# 1 < j <= 2 +_: {[I, J] | I, J: 1<..2} = [1, 2] +# 1 <= i <= 5 +match i + i: 1..5 -> ... +``` + +### パターンではないもの、パターン化できないもの + +パターンは一意に指定できるものです。この点においてパターンマッチは通常の条件分岐とは異なります。 + +条件の指定は一意ではありません。例えば、数`n`が偶数か判定する場合、`n % 2 == 0`とするのがオーソドックスですが、`(n / 2).round() == n / 2`とも書けます。 +一意でない形式は、正しく作動するのか、別の条件と同等であるか自明ではありません。 + +#### セット + +セットのパターンはありません。なぜなら、セットは要素を一意に取り出す方法がないからです。 +イテレータで取り出すことはできますが、順番は保証されません。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/27_comprehension.md b/doc/JA/syntax/27_comprehension.md new file mode 100644 index 00000000..bb60509d --- /dev/null +++ b/doc/JA/syntax/27_comprehension.md @@ -0,0 +1,55 @@ +# Comprehension(内包表記) + +`[expr | (name <- iterable)+ (predicate)*]`で配列、 +`{expr | (name <- iterable)+ (predicate)*}`でセット、 +`{key: value | (name <- iterable)+ (predicate)*}`でDictが作れる。 + +`|`で区切られた節のうち最初の部分をレイアウト節(配置節)といい、2番目の部分をバインド節(束縛節)、3番目の部分をガード節(条件節)という。 +ガード節は省略可能であるがバインド節は省略できず、バインド節より先にガード節を置くことはできない。 + +e.g. + +```erg +assert [i | i <- [0, 1, 2]] == [0, 1, 2] +assert [i / 2 | i <- 0..2] == [0.0, 0.5, 1.0] +assert [(i, j) | i <- 0..2; j <- 0..2; (i + j) % 2 == 0] == [(0, 0), (0, 2), (1, 1), (2, 0), (2, 2)] +assert {i % 2 | i <- 0..9} == {0, 1} +assert {k: v | k <- ["a", "b"]; v <- [1, 2]} == {"a": 1, "b": 2} +``` + +Ergの内包表記はHaskellに影響を受けているが、若干の違いがある。 +Haskellのリスト内包表記の場合、変数の順番は結果に違いをもたらすが、Ergでは関係がない。 + +```haskell +-- Haskell +[(i, j) | i <- [1..3], j <- [3..5]] == [(1,3),(1,4),(1,5),(2,3),(2,4),(2,5),(3,3),(3,4),(3,5)] +[(i, j) | j <- [3..5], i <- [1..3]] == [(1,3),(2,3),(3,3),(1,4),(2,4),(3,4),(1,5),(2,5),(3,5)] +``` + +```erg +# Erg +assert [(i, j) | i <- 1..<3; j <- 3..<5] == [(i, j) | j <- 3..<5; i <- 1..<3] +``` + +これはPythonと同じである。 + +```python +# Python +assert [(i, j) for i in range(1, 3) for j in range(3, 5)] == [(i, j) for j in range(3, 5) for i in range(1, 3)] +``` + +## 篩型 + +内包表記と似たものに、篩型がある。篩型は`{Name: Type | Predicate}`という形式で作られる型(列挙型)である。 +篩型の場合、Nameは1つまででレイアウトは指定できず(ただしタプル型などにすれば複数の値は扱える)、Predicateはコンパイル時計算できるもの、つまり定数式でなくてはならない。 + +```erg +Nat = {I: Int | I >= 0} +# 述語式がandだけの場合、;で代替できる +# Nat2D = {(I, J): (Int, Int) | I >= 0; J >= 0} +Nat2D = {(I, J): (Int, Int) | I >= 0 and J >= 0} +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/28_spread_syntax.md b/doc/JA/syntax/28_spread_syntax.md new file mode 100644 index 00000000..ccec5762 --- /dev/null +++ b/doc/JA/syntax/28_spread_syntax.md @@ -0,0 +1,44 @@ +# Spread assignment (展開代入) + +分解代入において、変数の前に`...`を置くと残りの要素を全てその変数に展開できる。これを展開代入と呼ぶ。 + +```erg +[x, ...y] = [1, 2, 3] +assert x == 1 +assert y == [2, 3] +x, ...y = (1, 2, 3) +assert x == 1 +assert y == (2, 3) +``` + +## Extract assignment (抽出代入) + +`...`のあとに何も書かない場合、残りの要素は無視して代入される。このタイプの展開代入を特に抽出代入と呼ぶ。 +抽出代入は、モジュールやレコード内にある特定の属性をローカルに持ってくる際に便利な構文である。 + +```erg +{sin; cos; tan; ..} = import "math" +``` + +このようにすると、以降はローカルで`sin, cos, tan`が使用できる。 + +> __Note__ : `{} = import "math"`としてから`{}`の中身を書いていくと入力補完が効く。 + +レコードでも同じようにできる。 + +```erg +record = {x = 1; y = 2} +{x; y; ...} = record +``` + +全て展開したい場合は`{*} = ...`とする。OCamlなどでいう`open`である。 + +```erg +record = {x = 1; y = 2} +{*} = record +assert x == 1 and y == 2 +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/29_decorator.md b/doc/JA/syntax/29_decorator.md new file mode 100644 index 00000000..05faf49f --- /dev/null +++ b/doc/JA/syntax/29_decorator.md @@ -0,0 +1,107 @@ +# デコレータ(修飾子) + +デコレータは型や関数に特定の状態や振る舞いを追加したり明示するために使われます。 +デコレータの文法は以下の通りです。 + +```erg +@deco +X = ... +``` + +デコレータは、競合しない限り複数つけることができます。 + +デコレータは特別なオブジェクトではなく、その実体は単なる1引数関数です。デコレータは以下の疑似コードと等価です。 + +```erg +X = ... +X = deco(X) +``` + +Ergでは変数の再代入が出来ないので、上のようなコードは通らず、デコレータが必要なのです。 +以下に、頻出の組み込みデコレータを紹介します。 + +## Inheritable + +定義する型が継承可能クラスであることを示します。引数`scope`に`"public"`を指定すると、外部モジュールのクラスでも継承できるようになります。デフォルトでは`"private"`になっており、外部からは継承できません。 + +## Final + +メソッドをオーバーライド不能にします。クラスに付けると継承不能クラスになりますが、デフォルトなので意味はありません。 + +## Override + +属性をオーバーライドする際に使用します。Ergではデフォルトで基底クラスと同じ属性を定義しようとするとエラーになります。 + +## Impl + +引数のトレイトを実装することを示します。 + +```erg +Add = Trait { + .`_+_` = Self.(Self) -> Self +} +Sub = Trait { + .`_-_` = Self.(Self) -> Self +} + +C = Class({i = Int}, Impl: Add and Sub) +C. + @Impl Add + `_+_` self, other = C.new {i = self::i + other::i} + @Impl Sub + `_-_` self, other = C.new {i = self::i - other::} +``` + +## Attach + +トレイトにデフォルトで付属するアタッチメントパッチを指定します。 +これによって、Rustのトレイトと同じ挙動を再現できます。 + +```erg +# foo.er +Add R, O = Trait { + .`_+_` = Self.(R) -> O +} +@Attach IntIsBinAdd, OddIsBinAdd +BinAdd = Subsume Add(Self, Self.AddO), { + .AddO = Type +} + +IntIsBinAdd = Patch(Int, Impl: BinAdd) +IntIsBinAdd.AddO = Int +OddIsBinAdd = Patch(Odd, Impl: BinAdd) +OddIsBinAdd.AddO = Even +``` + +こうすると、他のモジュールからトレイトをインポートした際に、アタッチメントパッチが自動で適用されます。 + +```erg +# 本来IntIsBinAdd, OddIsBinAddも同時にインポートする必要があるが、アタッチメントパッチなら省略可 +{BinAdd; ...} = import "foo" + +assert Int.AddO == Int +assert Odd.AddO == Even +``` + +内部的にはトレイトの`.attach`メソッドを使って結びつけているだけです。コンフリクトする場合はトレイトの`.detach`メソッドで外すことができます。 + +```erg +@Attach X +T = Trait ... +assert X in T.attaches +U = T.detach(X).attach(Y) +assert X not in U.attaches +assert Y in U.attaches +``` + +## Deprecated + +変数の仕様が古く非推奨であることを示します。 + +## Test + +テストサブルーチンであることを示します。テストサブルーチンは`erg test`コマンドで実行されます。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/30_error_handling.md b/doc/JA/syntax/30_error_handling.md new file mode 100644 index 00000000..6888f70a --- /dev/null +++ b/doc/JA/syntax/30_error_handling.md @@ -0,0 +1,109 @@ +# エラーハンドリングシステム + +主にResult型を使用する。 +ErgではError型オブジェクトを捨てる(トップレベルで対応しない)とエラーが発生する。 + +## 例外、Pythonとの相互運用 + +Ergは例外機構(Exception)を持たない。Pythonの関数をインポートする際は + +* 戻り値を`T or Error`型とする +* `T or Panic`型(実行時エラーを出す可能性がある)とする + +の2つの選択肢があり、`pyimport`ではデフォルトで後者となる。前者としてインポートしたい場合は、 +`pyimport`の`exception_type`で`Error`を指定する(`exception_type: {Error, Panic}`)。 + +## 例外とResult型 + +`Result`型はエラーかもしれない値を表現する。`Result`によるエラーハンドリングはいくつかの点で例外機構よりも優れている。 +まず第一に、サブルーチンがエラーを出すかもしれないと型定義から分かり、実際に使用するときも一目瞭然なのである。 + +```python +# Python +try: + x = foo().bar() + y = baz() + qux() +except e: + print(e) +``` + +上の例では、例外がどの関数から送出されたものなのか、このコードだけでは分からない。関数定義まで遡っても、その関数が例外を出すかは判別しにくい。 + +```erg +# Erg +try!: + do!: + x = foo!()?.bar() + y = baz!() + qux!()? + e => + print! e +``` + +翻って、こちらの例では`foo!`と`qux!`がエラーを出しうるとわかる。 +正確には`y`も`Result`型である可能性があるが、中の値を使用するためにはいずれ対処しなくてはならない。 + +`Result`型を使用するメリットはそれだけではない。`Result`型はスレッドセーフでもある。これは、エラー情報を並列実行中に(容易に)受け渡しできるということを意味する。 + +## Context + +`Error`/`Result`型単体では副作用が発生しないので、例外と違い送出場所などの情報(Context、文脈)を持てないが、`.context`メソッドを使えば`Error`オブジェクトに情報を付加できる。`.context`メソッドは`Error`オブジェクト自身を消費して新しい`Error`オブジェクトを作るタイプのメソッドである。チェイン可能であり、複数のコンテクストを保持できる。 + +```erg +f() = + todo() \ + .context "to be implemented in ver 1.2" \ + .context "and more hints ..." + +f() +# Error: not implemented yet +# hint: to be implemented in ver 1.2 +# hint: and more hints ... +``` + +なお、`.msg`や`.kind`などの`Error`の属性は副次的なものではないのでcontextではなく、最初に生成されたときのまま上書きできない。 + +## スタックトレース + +`Result`型はその利便性から他言語でも多く取り入れられているが、例外機構と比較してエラーの発生元がわかりにくくなるというデメリットがある。 +そこで、Ergでは`Error`オブジェクトに`.stack`という属性を持たせており、擬似的に例外機構のようなスタックトレースを再現している。 +`.stack`は呼び出し元オブジェクトの配列である。Errorオブジェクトは`return`(`?`によるものも含む)されるたびにその呼出元サブルーチンを`.stack`に積んでいく。 +そして`return`ができないコンテクストで`?`されるなり`.unwrap`されるなりすると、トレースバックを表示しながらパニックする。 + +```erg +f x = + ... + y = foo.try_some(x)? + ... + +g x = + y = f(x)? + ... + +i = g(1)? +# Traceback (most recent call first): +# ... +# Foo.try_some, line 10, file "foo.er" +# 10 | y = foo.try_some(x)? +# module::f, line 23, file "foo.er" +# 23 | y = f(x)? +# module::g, line 40, file "foo.er" +# 40 | i = g(1)? +# Error: ... +``` + +## パニック + +Ergには回復不能なエラーへの対処として __パニッキング__ という機構も存在する。 +回復不能なエラーとは、例えばソフト/ハードウェアの不具合など外的要因によるエラーや、それ以上コードを実行し続けても意味がないほど致命的なエラー、あるいはプログラム作成者の想定だにしないエラーなどである。これが発生した場合、プログラマの努力によって正常系に復帰させることができないため、その場でプログラムを終了させる。これを「パニックさせる」という。 + +パニックは`panic`関数で行う。 + +```erg +panic "something went wrong!" +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/31_pipeline.md b/doc/JA/syntax/31_pipeline.md new file mode 100644 index 00000000..c1cddfeb --- /dev/null +++ b/doc/JA/syntax/31_pipeline.md @@ -0,0 +1,29 @@ +# パイプライン演算子 + +パイプライン演算子は、次のように使う。 + +```erg +assert f(g(x)) == (x |> g |> f) +assert f(g(x, y)) == ((x, y) |> g |> f) +``` + +つまり、`Callable(object)`という順序を`object |> Callable`と変えられるのである。 +パイプライン演算子はメソッドに対しても使える。メソッドの場合、`object.method(args)`が`object |>.method(args)`と変わる。 +単に`|>`が増えただけにも見えるが、結合強度が低めなので`()`の量を減らせる場合がある。 + +```erg +rand = -1.0..1.0 |>.sample!() +log rand # 0.2597... +1+1*2 |>.times do log("a", end="") # aaa +# without `|>`, the following will be `evens = (1..100).iter().filter(i -> i % 2 == 0).collect(Array)` +evens = 1..100 |>.iter |>.filter i -> i % 2 == 0 |>.collect Array +# or +_evens = 1..100 \ + .iter() \ + .filter i -> i % 2 == 0 \ + .collect Array +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/32_integration_with_Python.md b/doc/JA/syntax/32_integration_with_Python.md new file mode 100644 index 00000000..a54f3e46 --- /dev/null +++ b/doc/JA/syntax/32_integration_with_Python.md @@ -0,0 +1,57 @@ +# Pythonとの連携 + +Pythonから取り込んだオブジェクトはデフォルトですべて`Object`型になります。このままでは比較もできないので、型の絞り込みを行う必要があります。 + +## 標準ライブラリの型指定 + +Python標準ライブラリにあるAPIはすべてErg開発チームにより型が指定されています。 + +```erg +time = pyimport "time" +time.sleep! 1 +``` + +## ユーザースクリプトの型指定 + +Pythonの`foo`モジュールに型を付ける`foo.d.er`ファイルを作成します。 +Python側でのtype hintは100%の保証にならないので無視されます。 + +```python +# foo.py +X = ... +def bar(x): + ... +def baz(): + ... +``` + +```erg +# foo.d.er +foo = pyimport "foo" +.X = declare foo.'X', Int +.bar = declare foo.'bar', Int -> Int +.baz! = declare foo.'baz', () => Int +``` + +```erg +foo = pyimport "foo" +assert foo.bar(1) in Int +``` + +これは、実行時に型チェックを行うことで型安全性を担保しています。`declare`関数は概ね以下のように動作します。 + +```erg +declare sub!: S, T = + # 実は、=>はブロックの副作用がなければ関数にキャストできる + x => + assert x in T.Input + y = sub!(x) + assert y in T.Output + y +``` + +これは実行時オーバーヘッドとなるので、PythonスクリプトをErgの型システムで静的に型解析するプロジェクトが計画されています。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/33_package_system.md b/doc/JA/syntax/33_package_system.md new file mode 100644 index 00000000..8ae76114 --- /dev/null +++ b/doc/JA/syntax/33_package_system.md @@ -0,0 +1,83 @@ +# パッケージシステム + +Ergのパッケージはアプリケーションであるappパッケージとライブラリであるlibパッケージに大別できます。 +appパッケージのエントリポイントは`src/app.er`です。`app.er`内に定義された`main`関数が実行されます。 +libパッケージのエントリポイントは`src/lib.er`です。パッケージをインポートすることは`lib.er`をインポートすることと等価になります。 + +パッケージにはモジュールという下位構造があります。Ergにおいてモジュールとはすなわち、Ergファイルもしくはそれで構成されたディレクトリです。外部のErgファイル/ディレクトリはモジュールオブジェクトとして操作可能な対象になるのです。 + +ディレクトリをモジュールとして認識させるには、ディレクトリ内に`(ディレクトリ名).er`ファイルを置く必要があります。 +これはPythonの`__init__.py`と同じようなものですが、`__init__.py`と違ってディレクトリの外に置きます。 + +例として、以下のようなディレクトリ構成を考えてみましょう。 + +```console +└─┬ ./src + ├─ app.er + ├─ foo.er + ├─ bar.er + └─┬ bar + ├─ baz.er + └─ qux.er +``` + +`app.er`では`foo`モジュールと`bar`モジュールをインポートできます。`bar`ディレクトリがモジュールとして認識できるのは`bar.er`ファイルがあるためです。 +`foo`モジュールはファイルからなるモジュールで、`bar`モジュールはディレクトリからなるモジュールです。`bar`モジュールはさらに`baz`, `qux`モジュールを内部に持ちます。 +このモジュールは単に`bar`モジュールの属性であり、`app.er`からは以下のようにアクセスできます。 + +```erg +# app.er +foo = import "foo" +bar = import "bar" +baz = bar.baz +# or `baz = import "bar/baz"` + +main args = + ... +``` + +サブモジュールにアクセスするための区切り文字が`/`であることに注意してください。これは、`bar.baz.er`のようなファイル名があり得るためです。 +このようなファイル名は推奨されません。Ergでは`.er`のプレフィックスが意味を持つためです。 +例えば、テスト用のモジュールです。`.test.er`で終わるファイルは(ホワイトボックス)テスト用のモジュールであり、テスト実行時に`@Test`でデコレーションされたサブルーチンが実行されます。 + +```console +└─┬ ./src + ├─ app.er + ├─ foo.er + └─ foo.test.er +``` + +```erg +# app.er +foo = import "foo" + +main args = + ... +``` + +また、`.private.er`で終わるファイルはプライベートモジュールであり、同一ディレクトリのモジュールからしかアクセスできません。 + +```console +└─┬ + ├─ foo.er + ├─ bar.er + └─┬ bar + ├─ baz.private.er + └─ qux.er +``` + +```erg +# foo.er +bar = import "bar" +bar.qux +bar.baz # AttributeError: module 'baz' is private +``` + +```erg +# qux.er +baz = import "baz" +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/34_generator.md b/doc/JA/syntax/34_generator.md new file mode 100644 index 00000000..b7b3ca13 --- /dev/null +++ b/doc/JA/syntax/34_generator.md @@ -0,0 +1,32 @@ +# ジェネレータ + +ジェネレータは、ブロック中で`yield!`プロシージャを使う特殊なプロシージャです。 + +```erg +g!() = + yield! 1 + yield! 2 + yield! 3 +``` + +`yield!`はサブルーチンのブロックで定義されるプロシージャで、`self!.yield!`を呼び出します。 +これは`return`と同じく渡された値を戻り値として返すものですが、その時点でのブロックの実行状態を保存し、もう一度呼び出された際に続きから実行するという特徴があります。 +ジェネレータはプロシージャでありながらイテレータでもあります。Pythonのジェネレータはイテレータを生成する関数ですが、Ergは直接イテレートします。プロシージャ自体は一般に可変オブジェクトではありません(`!`が付かない)が、ジェネレータは実行ごとに自身の内容が変わる得るので可変オブジェクトです。 + +```erg +# Generator! < Proc +g!: Generator!((), Int) +assert g!() == 1 +assert g!() == 2 +assert g!() == 3 +``` + +Pythonスタイルのジェネレータは以下のようにして定義できます。 + +```erg +make_g() = () => + yield! 1 + yield! 2 + yield! 3 +make_g: () => Generator!((), Int) +``` diff --git a/doc/JA/syntax/SUMMARY.md b/doc/JA/syntax/SUMMARY.md new file mode 100644 index 00000000..a37efc4a --- /dev/null +++ b/doc/JA/syntax/SUMMARY.md @@ -0,0 +1,69 @@ +# Summary + +- [Basics](./00_basic.md) +- [Literal](./01_literal.md) +- [Name](02_name.md) +- [Declaration](./03_declaration.md) +- [Function](./04_function.md) +- [Builtin Functions](./05_builtin_funcs.md) +- [Operator](./06_operator.md) +- [Side Effect](./07_side_effect.md) +- [Procedure](./08_procedure.md) +- [Builtin Procedures](./09_builtin_procs.md) +- [Array](./10_array.md) +- [Dict](11_dict.md) +- [Tuple](./12_tuple.md) +- [Record](./13_record.md) +- [Set](./14_set.md) +- [Type](./15_type.md) + - [Type System](./type/01_type_system.md) + - [Basics](./type/02_basic.md) + - [Trait](./type/03_trait.md) + - [Class](./type/04_class.md) + - [Inheritance](./type/05_inheritance.md) + - [NST vs SST](./type/06_nst_vs_sst.md) + - [Patch](./type/07_patch.md) + - [Value Type](./type/08_value.md) + - [Attributive Type](./type/09_attributive.md) + - [Interval Type](./type/10_interval.md) + - [Enum Type](./type/11_enum.md) + - [Refinement Type](./type/12_refinement.md) + - [Algebraic Type](./type/13_algebraic.md) + - [Dependent Type](./type/14_dependent.md) + - [Quantified Type](./type/15_quantified.md) + - [Subtyping](./type/16_subtyping.md) + - [Type Casting](./type/17_type_casting.md) + - [Mutable Type](./type/18_mut.md) + - [Advanced](./type/advanced.md) + - [Default Parameter](./type/advanced/default_param.md) + - [Type Erasure](./type/advanced/erasure.md) + - [Existential](./type/advanced/existential.md) + - [GADTs](./type/advanced/GADTs.md) + - [Keyword Parameters](./type/advanced/keyword_param.md) + - [Kind](./type/advanced/kind.md) + - [Marker Trait](./type/advanced/marker_trait.md) + - [Mutable Struct](./type/advanced/mut_struct.md) + - [Phantom Type](./type/advanced/phantom.md) + - [Projection Type](./type/advanced/projection.md) + - [Quantified Dependent Type](./type/advanced/quantified_dependent_type.md) + - [Shared](./type/advanced/shared.md) +- [Iterator](./16_iterator.md) +- [Mutability](./17_mutability.md) +- [Memory Management](./18_memory_management.md) +- [Visibility](./19_visibility.md) +- [Naming Rule](20_naming_rule.md) +- [Lambda](./21_lambda.md) +- [Subroutine](./22_subroutine.md) +- [Scope](./23_scope.md) +- [Module](./24_module.md) +- [Object System](./25_object_system.md) +- [Pattern Matching](./26_pattern_matching.md) +- [Comprehension](./27_comprehension.md) +- [Spread Syntax](./28_spread_syntax.md) +- [Decorator](./29_decorator.md) +- [Error Handling](./30_error_handling.md) +- [Pipeline](./31_pipeline.md) +- [Integration With Python](./32_integration_with_python.md) +- [Package System](./33_package_system.md) +- [Generator](./34_generator.md) +- [Index](./indexes.md) diff --git a/doc/JA/syntax/container_ownership.md b/doc/JA/syntax/container_ownership.md new file mode 100644 index 00000000..f48b4aa3 --- /dev/null +++ b/doc/JA/syntax/container_ownership.md @@ -0,0 +1,42 @@ +# Subscript(添字アクセス) + +`[]`は通常のメソッドとは異なっています。 + +```erg +a = [!1, !2] +a[0].inc!() +assert a == [2, 2] +``` + +サブルーチンの戻り値には参照を指定できないということを思い出してください。 +`a[0]`の型は、ここでは明らかに`Ref!(Int!)`であるはずです(`a[0]`の型は文脈に依存します)。 +よって、`[]`は実際には`.`と同じく特別な構文の一部です。Pythonとは違い、オーバーロードできません。 +メソッドで`[]`の挙動を再現することもできません。 + +```erg +C = Class {i = Int!} +C.get(ref self) = + self::i # TypeError: `self::i` is `Int!` (require ownership) but `get` doesn't own `self` +C.steal(self) = + self::i +# NG +C.new({i = 1}).steal().inc!() # OwnershipWarning: `C.new({i = 1}).steal()` is not owned by anyone +# hint: assign to a variable or use `uwn_do!` +# OK (assigning) +c = C.new({i = 1}) +i = c.steal() +i.inc!() +assert i == 2 +# or (own_do!) +own_do! C.new({i = 1}).steal(), i => i.inc!() +``` + +また、`[]`は所有権を奪うこともできますが、その際に要素がシフトするわけではありません。 + +```erg +a = [!1, !2] +i = a[0] +i.inc!() +assert a[1] == 2 +a[0] # OwnershipError: `a[0]` is moved to `i` +``` diff --git a/doc/JA/syntax/grammar.txt b/doc/JA/syntax/grammar.txt new file mode 100644 index 00000000..36df1935 --- /dev/null +++ b/doc/JA/syntax/grammar.txt @@ -0,0 +1,77 @@ +# The Grammar of Erg (ver 0.1.0, provisional) +special_op ::= '=' | '->' | '=>' | '.' | ',' | ':' | '|>' | '&' +separator ::= ';' | '\n' +escape ::= '\' +comment_marker ::= '#' +comment ::= '#' .* '\n' +reserved_symbol ::= special_op | separator | comment_marker +number ::= [0-9] +first_last_dight ::= number +dight ::= number | '_' +bin_dight ::= [0-1] +oct_dight ::= [0-8] +hex_dight ::= [0-9] + | [a-f] + | [A-F] +int ::= first_last_dight + | first_last_dight dight* first_last_dight + | '0' ('b' | 'B') binary_dight+ + | '0' ('o' | 'O') octa_dight+ + | '0' ('x' | 'X') hex_dight+ +ratio ::= '.' dight* first_last_dight + | first_last_dight dight* '.' dight* first_last_dight +bool ::= 'True' | 'False' +none ::= 'None' +ellipsis ::= 'Ellipsis' +not_implemented ::= 'NotImplemented' +parenthesis ::= '(' | ')' +bracket ::= '{' | '}' +square_bracket ::= '[' | ']' +enclosure ::= parenthesis | bracket | square_bracket +infix_op ::= '+' | '-' | '*' | '/' | '//' | '**' | '%' | '&&' | '||' | '^^' | '<' | '<=' | '>' | '>=' + | 'and' | 'or' | 'is' | 'as' | 'isnot' | 'in' | 'notin' | 'dot' | 'cross' +prefix_op ::= '+' | '-' | '*' | '**' | '..' | '..<' | '~' | '&' | '!' +postfix_op ::= '?' | '..' | '<..' +operator ::= infix_op | prefix_op | postfix_op +char ::= /* ... */ +str ::= '\"' char* '\" +symbol_head ::= /* char except dight */ +symbol ::= symbol_head /* char except (reserved_symbol | operator | escape | ' ') */ +subscript ::= accessor '[' expr ']' +attr ::= accessor '.' symbol +accessor ::= symbol | attr | subscript +literal ::= int | ratio | str | bool | none | ellipsis | not_implemented +pos_arg ::= expr +kw_arg ::= symbol ':' expr +arg ::= pos_arg | kw_arg +enc_args ::= pos_arg (',' pos_arg)* ','? +args ::= '()' | '(' arg (',' arg)* ','? ')' | arg (',' arg)* +var_pattern ::= accessor | `...` accessor | '[' single_patterns ']' +var_decl_opt_t = var_pattern (':' type)? +var_decl = var_pattern ':' type +param_pattern ::= symbol | `...` symbol | literal | '[' param_patterns ']' +param_decl_opt_t = param_pattern (':' type)? +param_decl = param_pattern ':' type +params_opt_t ::= '()' (':' type)? + | '(' param_decl_opt_t (',' param_decl_opt_t)* ','? ')' (':' type)? + | param_decl_opt_t (',' param_decl_opt_t)* +params ::= '()' ':' type + | '(' param_decl (',' param_decl)* ','? ')' ':' type +subr_decl ::= accessor params +subr_decl_opt_t ::= accessor params_opt_t +decl ::= var_decl | subr_decl +decl_opt_t = var_decl_opt_t | subr_decl_opt_t +body ::= expr | indent line+ dedent +def ::= ('@' decorator '\n')* decl_opt_t '=' body +call ::= accessor args | accessor call +decorator ::= call +lambda_func ::= params_opt_t '->' body +lambda_proc ::= params_opt_t '=>' body +lambda ::= lambda_func | lambda_proc +array ::= '[' enc_args ']' +array_comprehension ::= '[' expr | (generator)+ ']' +anonymous_type ::= '{' enc_args '}' +indent ::= /* ... */ +expr ::= accessor | literal | prefix | infix | postfix | call | def | lambda +line ::= expr separator+ +program ::= expr? | (line | comment)* diff --git a/doc/JA/syntax/indexes.md b/doc/JA/syntax/indexes.md new file mode 100644 index 00000000..0db44b64 --- /dev/null +++ b/doc/JA/syntax/indexes.md @@ -0,0 +1,452 @@ +# 索引 + +この索引にないAPIについては[こちら](../API/index.md)を参照してください。 +用語の意味については[こちら](../dev_guide/terms.md)を参照。 + +## 記号 + +* ! + * !-type → [可変型](./type/mut.md) +* [#](./00_basic.md/#コメント) +* $ +* % +* & + * && +* ′ (single quote) +* () +* * + * [*-less multiplication](./01_literal.md/#less-multiplication) +* + (前置) + * +_ → + (前置) +* + (中置) +* , +* − (前置) + * −_ → − (前置) +* − (中置) + * −> +* . → [可視性] +* / +* : + * :: → [可視性] +* ; +* < + * <: + * << + * <= +* = + * == + * => +* > + * >> + * >= +* ? +* @ +* [] +* \ +* ^ + * ^^ +* _ + * _+_ → + (中置) + * _-_ → − (中置) +* `` +* {} + * {} type +* {:} +* {=} + * {=} type +* | + * || +* ~ + +## アルファベット + +### A + +* [algebraic type] +* [And] +* [and] +* [assert] +* [attribute] + +### B + +* [Base] +* [Bool] + +### C + +* [Class] + +### D + +* Deprecated +* [distinct] + +### E + +* [enum type] +* [Eq] +* [Erg] + +### F + +* [for] + +### G + +### H + +### I + +* [if] +* [import] +* [in] +* [Int] + +### J + +### K + +### L + +* let-polymorphism → [ランク1多相] +* [log] + +### M + +* [match] + +### N + +* [Nat] +* Never +* None +* None +* [Not] +* [not] + +### O + +* [Option] +* [Or] +* [or] +* [Ord] + +### P + +* panic +* [print!](./../API/procs.md#print) +* [Python] + +### Q + +### R + +* ref +* ref! +* [Result] +* [rootobj] + +### S + +* self +* [Self](./type/special.md) +* [side-effect](./07_side_effect.md) +* [Str] + +### T + +* Trait +* [True] +* [Type] +* [type] + +### U + +### V + +### W + +* [while!] + +### X + +### Y + +### Z + +## あ行 + +* [アサーション] +* 値オブジェクト +* [アタッチメントパッチ](./29_decorator.md#attach) +* アドホック多相 → [オーバーロードの禁止](./type/overloading.md) +* アトリビュート → [属性] +* アリティ +* [依存型](./type/dependent_type.md) +* イミュータブル → [不変] +* 引数(いんすう) → [引数(ひきすう)] +* インスタンス +* [インスタントブロック](./00_basic.md#式セパレータ) +* インデックス +* [インデント](./00_basic.md#インデント) +* エイリアス +* エラー + * [エラーハンドリング] +* [演算子](./06_operator.md) + * [演算子の結合強度] +* オーバーライド +* [オーバーロードの禁止](./type/overloading.md) +* オフサイドルール → [インデント](./00_basic.md#インデント) +* [オブジェクト] + * オブジェクト指向 +* オペランド → [被演算子](./06_operator.md) +* オペレーター → [演算子](./06_operator.md) + +## か行 + +* [カインド](./type/advanced/kind.md) +* [可視性] +* [型] + * [型指定] + * [型消去](./type/advanced/erasure.md) + * [型推論] + * [型注釈](./type/conv_type.md) + * [型引数] + * [型付加](./type/advanced/erasure.md) + * [型変数](./type/type_variable.md) + * [型制約] +* [ガード] +* カプセル化 +* [可変] + * [可変オブジェクト] + * [可変型] + * [可変参照] + * [可変配列] + * [可変長引数] +* [関数](./04_function.md) + * [関数型プログラミング](./23_scope.md#可変状態の回避関数型プログラミング) +* 基底型 +* 記名 + * [記名型] → [クラス](./type/04_class.md) + * [記名化] + * [記名的部分型](./type/05_nst_vs_sst.md) +* キャプチャ → [クロージャ] +* [共変] +* [キーワード引数] +* 空集合 → [{}] +* 区間 + * [区間型](./type/11_interval.md) + * 区間演算子 +* 組み込み + * [組み込み型] + * [組み込み関数](./05_builtin_funcs.md) + * [組み込みプロシージャ](./09_builtin_procs.md) +* [クラス](./type/04_class.md) +* [クロージャ] +* [グローバル変数] +* [クローン] +* [継承](./type/07_inheritance.md) +* 高階 + * [高階カインド](./type/advanced/kind.md) + * 高階型 + * 高階関数 +* [公開変数] +* [構造的部分型] +* ~~後方参照~~ → [前方参照] +* [コピー] +* コメント +* [コレクション](./10_array.md) +* コロン → [:] +* [コンストラクタ](./type/04_class.md) +* コンテナ +* コンパイラ +* [コンパイル時計算](./04_function.md#コンパイル時関数) +* コンマ → [,] + +## さ行 + +* 再帰 + * 再帰型 + * [再帰関数](./04_function.md#再帰関数) +* サブスクリプト → [インデックス] +* [サブタイピング多相](./type/overloading.md) +* サブルーチン +* [参照](./18_memory_management.md#借用) + * 参照オブジェクト + * [参照カウント(RC)](./18_memory_management.md#メモリ管理) + * 参照等価性 → [副作用](./07_side_effect.md) +* [識別子](./02_variable.md/#代入) +* シグネチャ + * 型シグネチャ +* [辞書](./11_dict.md) +* [自然数] → [Nat] +* ジェネリクス → [全称型] +* ジェネレータ +* [射影型] +* 借用 → [参照](./18_memory_management.md#借用) +* [シャドーイング](./02_name.md#変数) +* 種 → [カインド](./type/advanced/kind.md) +* [集合] → [セット] +* 述語 + * [述語関数] +* 条件分岐 +* [所有権] +* 真偽型 → [Bool] +* シングルトン +* [シンボル] → [識別子](./02_name.md) + * [シンボル化] +* [スクリプト](./00_basic.md#スクリプト) +* スコープ +* スプレッド演算子 → [展開代入] +* [スライス](./10_array.md#スライス) +* 制御文字 +* [整数] → [Int] +* [セット](./12_set.md) +* セミコロン → [;] +* [宣言](./03_declaration.md) +* 全称 + * 全称型 → [多相型](./type/quantified.md) + * 閉じた全称型 + * 開いた全称型 + * 全称関数 → 多相関数 + * 全称量化 +* 前置演算子 +* 相互再帰 +* 添字 → [インデックス] +* [属性] + * [属性的部分型] + +## た行 + +* [代数](./02_name.md) + * [代数演算型](./type/13_algebraic.md) + * 代数的データ型 +* [代入](./02_variable.md/#代入) +* 多重 + * [多重継承](./type/07_inheritance.md/#多重継承の禁止) + * 多重代入 + * 多重定義 → [オーバーロードの禁止] +* 多相 + * [多相型](./type/quantified.md) + * 多相関数 +* 多態 → [ポリモーフィズム] +* ダックタイピング +* [タプル](./11_tuple.md) +* 単相 + * 単相化 + * 単相型 + * 単相関数 +* [遅延初期化] +* 抽出代入 +* 抽象構文木 → [AST] +* 中置演算子 +* [定数](./02_name.md/#定数) + * [定数型](./type/advanced/const.md) + * [定数式](./type/advanced/const.md) +* [定義] +* 提供属性 +* [適用] +* [デコレータ](./29_decorator.md) +* デストラクタ +* 手続き → [プロシージャ](./08_procedure.md) +* [デフォルト引数](./04_function.md/#デフォルト引数default-parameters) +* 展開 + * [展開演算子] + * [展開代入] +* [特殊形式](./../API/special.md) +* 匿名関数 → [無名関数](./20_lambda.md) +* ドット演算子(`.`) → [属性参照] +* トップ + * トップ型 → [Structural Object] + * トップクラス → [Object] +* [トレイト](./type/03_trait.md) + +## な行 + +* [内包表記](./27_comprehension.md) +* ~~中置(なかおき)演算子~~ → [中置(ちゅうち)演算子] +* [名前空間] + +## は行 + +* [配列](./10_array.md) +* [派生型](./type/variances.md/#ユーザー定義型の変性) +* [パターン(マッチ)](./26_pattern_matching.md) +* [パッケージ](./33_package_system.md) +* ハッシュマップ → [辞書](./11_dict.md) +* [パッチ](./type/07_patch.md) +* パブリック変数 → [公開変数](./19_visibility.md) +* パラメーター → [引数](./04_function.md) +* [パラメトリック多相](./type/overloading.md) +* [反変](./type/advanced/variance.md) +* 比較 + * [比較演算子] + * [比較可能型] +* [非公開変数](./19_visibility.md) +* 標準 + * 標準出力 + * 標準入力 + * 標準ライブラリ +* [副作用](./07_side_effect.md) +* 複素数 → [Complex] +* [浮動小数点数] → [Float] +* プライベート変数 → [非公開変数] +* ブール代数 → [Bool] +* [プロシージャ](./08_procedure.md) +* [引数](./04_function.md) +* 部分型付け → [サブタイピング] +* [不変] + * [不変オブジェクト] + * [不変型] + * [不変参照] +* [篩型](./type/12_refinement.md) +* [ブロック] +* 分解代入 +* [変数](./02_variable.md) +* ボトム + * ボトム型 → [{}] + * ボトムクラス → [Never] +* [ポリモーフィズム] + +## ま行 + +* ~~前置(まえおき)演算子~~ → 前置(ぜんち)演算子 +* [マーカー型](./type/advanced/marker_trait.md) +* [無名関数](./21_lambda.md) +* ミュータブル → [可変性] +* [ムーブ] +* メソッド +* メタキャラクタ +* [モジュール](./24_module.md) +* [文字列] → [Str] + * [文字列補間](./01_literal.md/#strリテラル) +* 戻り値 + +## や行 + +* [幽霊型](./type/advanced/phantom.md) +* 要求属性 +* [要素] +* [呼び出し] + +## ら行 + +* [ライブラリ] +* ラムダ式 → [無名関数](./20_lambda.md) +* ランク + * [ランク2多相](./type/advanced/rank2type.md) +* [リテラル](./01_literal.md) + * [リテラル識別子](./18_naming_rule.md/#リテラル識別子) +* [量化](./type/quantified.md) +* [レイアウト](./type/mut.md) +* [列挙型](./type/10_enum.md) +* [レコード](./12_record.md) + * [レコード型] + * レコード多相 → [列多相] +* [列多相] +* [ローカル変数](./19_visibility.md) + +## わ行 + +* ワイルドカード diff --git a/doc/JA/syntax/quick_tour.md b/doc/JA/syntax/quick_tour.md new file mode 100644 index 00000000..bdffec57 --- /dev/null +++ b/doc/JA/syntax/quick_tour.md @@ -0,0 +1,267 @@ +# Quick Tour + +`syntax`以下のドキュメントは、概ねプログラミング初心者でも理解できることを目指して書かれています。 +すでにPythonやRust, Haskellなどの言語を習得されている方にとっては、少し冗長であるかもしれません。 + +そこで以下では概説的にErgの文法を紹介します。 +特に言及のない部分はPythonと同じと考えてください。 + +## 変数、定数 + +変数は`=`で定義します。Haskellと同じように、一度定義した変数は書き換えられません。ただし別のスコープではシャドーイングできます。 + +```erg +i = 0 +if True: + i = 1 +assert i == 0 +``` + +大文字で始まるものは定数です。コンパイル時計算できるものだけが定数にできます。 +また、定数は定義以降すべてのスコープで同一です。 + +```erg +PI = 3.141592653589793 +match random.random!(0..10): + PI: + log "You get PI, it's a miracle!" +``` + +## 宣言 + +Pythonと違い、変数の型のみを先に宣言することが可能です。 +当然、宣言の型と実際に代入されるオブジェクトの型は互換していなくてはなりません。 + +```erg +i: Int +i = 10 +``` + +## 関数 + +Haskellと同じように定義できます。 + +```erg +fib 0 = 0 +fib 1 = 1 +fib n = fib(n - 1) + fib(n - 2) +``` + +無名関数は以下のように定義できます。 + +```erg +i -> i + 1 +assert [1, 2, 3].map(i -> i + 1).to_arr() == [2, 3, 4] +``` + +## 演算子 + +Erg独自の演算子は以下の通りです。 + +### 可変化演算子(!) + +Ocamlの`ref`のようなものです。 + +```erg +i = !0 +i.update! x -> x + 1 +assert i == 1 +``` + +## プロシージャ + +副作用のあるサブルーチンはプロシージャと呼ばれ、`!`がついています。 + +```erg +print! 1 # 1 +``` + +## ジェネリック関数(多相関数) + +```erg +id|T|(x: T): T = x +id(1): Int +id("a"): Str +``` + +## レコード + +ML系言語にあるレコード(あるいはJSのオブジェクトリテラル)に相当するものを利用できます。 + +```erg +p = {x = 1; y = 2} +``` + +## 所有権 + +Ergは可変オブジェクト(`!`演算子で可変化したオブジェクト)に所有権がついており、複数の場所から書き換えられません。 + +```erg +i = !0 +j = i +assert j == 0 +i # MoveError +``` + +対して不変オブジェクトは複数の場所から参照できます。 + +## 可視性 + +変数の頭に`.`をつけると、その変数は公開変数となり、外部モジュールから参照できるようになります。 + +```erg +# foo.er +.x = 1 +y = 1 +``` + +```erg +foo = import "foo" +assert foo.x == 1 +foo.y # VisibilityError +``` + +## パターンマッチ + +### 変数パターン + +```erg +# basic assignment +i = 1 +# with type +i: Int = 1 +# function +fn x = x + 1 +fn: Int -> Int = x -> x + 1 +``` + +### リテラルパターン + +```erg +# if `i` cannot be determined to be 1 at compile time, TypeError occurs. +# short hand of `_: {1} = i` +1 = i +# simple pattern matching +match x: + 1 -> "1" + 2 -> "2" + _ -> "other" +# fibonacci function +fib 0 = 0 +fib 1 = 1 +fib n: Nat = fib n-1 + fib n-2 +``` + +### 定数パターン + +```erg +PI = 3.141592653589793 +E = 2.718281828459045 +num = PI +name = match num: + PI -> "pi" + E -> "e" + _ -> "unnamed" +``` + +### 破棄(ワイルドカード)パターン + +```erg +_ = 1 +_: Int = 1 +right(_, r) = r +``` + +### 可変長パターン + +後述するタプル/配列/レコードパターンと組み合わせて使う。 + +```erg +[i, ...j] = [1, 2, 3, 4] +assert j == [2, 3, 4] +first|T|(fst: T, ...rest: T) = fst +assert first(1, 2, 3) == 1 +``` + +### タプルパターン + +```erg +(i, j) = (1, 2) +((k, l), _) = ((1, 2), (3, 4)) +# ネストしていないなら()を省略可能(1, 2は(1, 2)として扱われる) +m, n = 1, 2 +``` + +### 配列パターン + +```erg +length [] = 0 +length [_, ...rest] = 1 + length rest +``` + +#### レコードパターン + +```erg +{sin; cos; tan; ...} = import "math" +{*} = import "math" # import all + +person = {name = "John Smith"; age = 20} +age = match person: + {name = "Alice"; _} -> 7 + {_; age} -> age +``` + +### データクラスパターン + +```erg +Point = Inherit {x = Int; y = Int} +p = Point.{x = 1; y = 2} +Point.{x; y} = p +``` + +## 内包表記 + +```erg +odds = [i | i <- 1..100; i % 2 == 0] +``` + +## クラス + +Ergでは多重・多段継承をサポートしていません。 + +## トレイト + +Rustのトレイトと似ていますが、より本来の意味に近いもので、合成や分離ができ、属性とメソッドは対等に扱われます。 +また、実装を伴いません。 + +```erg +XY = Trait {x = Int; y = Int} +Z = Trait {z = Int} +XYZ = XY and Z +Show = Trait {show: Self.() -> Str} + +@Impl XYZ, Show +Point = Class {x = Int; y = Int; z = Int} +Point. + ... +``` + +## パッチ + +クラスやトレイトに実装を与えたりできます。 + +## 篩型 + +述語式で型に制限をかけられます。 + +```erg +Nat = {I: Int | I >= 0} +``` + +## 値を含むパラメトリック型(依存型) + +```erg +a: [Int; 3] +b: [Int; 4] +a + b: [Int; 7] +``` diff --git a/doc/JA/syntax/type/01_type_system.md b/doc/JA/syntax/type/01_type_system.md new file mode 100644 index 00000000..3d91ce92 --- /dev/null +++ b/doc/JA/syntax/type/01_type_system.md @@ -0,0 +1,226 @@ +# Ergの型システム + +以下では、Ergの型システムを概略的に説明します。詳細については他の項で解説します。 + +## 定義方法 + +Ergの特徴的な点として、(通常の)変数、関数(サブルーチン)、型(カインド)の定義にあまり大きな構文上の違いがないというところがあります。すべて、通常の変数・関数定義の文法に従って定義されます。 + +```erg +f i: Int = i + 1 +f # +f(1) # 2 +f.method self = ... # SyntaxError: cannot define a method to a subroutine + +T I: Int = {...} +T # +T(1) # Type T(1) +T.method self = ... +D = Class {private = Int; .public = Int} +D # +o1 = {private = 1; .public = 2} # o1はどのクラスにも属さないオブジェクト +o2 = D.new {private = 1; .public = 2} # o2はDのインスタンス +o2 = D.new {.public = 2} # InitializationError: class 'D' requires attribute 'private'(: Int) but not defined +``` + +## 分類 + +Erg のオブジェクトは全て型付けされています。 +最上位の型は`{=}`であり、`__repr__`, `__hash__`, `clone`などを実装します(要求メソッドではなく、これらの属性はオーバーライドもできません)。 +Ergの型システムは構造的部分型(Structural subtyping, SST)を取り入れています。このシステムにより型付けされる型を構造型(Structural type)と呼びます。 +構造型には大きく分けて3種類、Attributive(属性型)/Refinement(篩型)/Algebraic(代数演算型)があります。 + +| | Record | Enum | Interval | Union | Intersection | Diff | +| --------- | ----------- | ---------- | -------------- | ----------- | ------------ | ------------ | +| kind | Attributive | Refinement | Refinement | Algebraic | Algebraic | Algebraic | +| generator | record | set | range operator | or operator | and operator | not operator | + +記名的部分型(Nominal subtyping, NST)を使用することもでき、SST型のNST型への変換を型の記名化(Nominalization)と呼びます。こうしてできた型を記名型(Nominal type)と呼びます。 +Ergでは、記名型はクラスとトレイトがそれに該当します。単にクラス/トレイトといった場合、それはレコードクラス/レコードトレイトを指す場合が多いです。 + +| | Type | Abstraction | Subtyping procedure | +| --- | -------------- | ---------------- | ------------------- | +| NST | NominalType | Trait | Inheritance | +| SST | StructuralType | Structural Trait | (Implicit) | + +記名型全体を表す型(`NominalType`)と構造型全体の型(`StructuralType`)は型全体の型(`Type`)のサブタイプです。 + +Ergは型定義に引数(型引数)を渡すことができます。型引数を持つ`Option`, `Array`などを多項カインドと呼びます。これら自体は型ではありませんが、引数を適用することで型となります。また、引数を持たない`Int`, `Str`型などを単純型(スカラー型)と呼びます。 + +型は集合とみなすことができ、包含関係も存在します。例えば`Num`は`Add`や`Sub`などを含んでおり、`Int`は`Nat`を含んでいます。 +全てのクラスの上位クラスは`Object == Class {:}`であり、全ての型の下位クラスは`Never == Class {}`です。これについては後述します。 + +## 型 + +`Array T`のような型は型`T`を引数にとり`Array T`型を返す、つまり`Type -> Type`型の関数とみなせます(型理論的にはカインドともいう)。`Array T`のような型は、特に多相型(Polymorphic Type)と呼び、`Array`そのものは1項カインドといいます。 + +引数、戻り値の型が判明している関数の型は`(T, U) -> V`のように表記します。型が同じ2引数関数全体を指定したい場合は`|T| (T, T) -> T`、N引数関数全体を指定したい場合、`Func N`で指定できる。ただし`Func N`型は引数の数や型に関する情報がないので、呼び出すと戻り値はすべて`Obj`型になります。 + +`Proc`型は`() => Int`などのように表記します。また、`Proc`型インスタンスの名前は最後に`!`をつけなくてはなりません。 + +`Method`型は第1引数に自身が属するオブジェクト`self`を(参照として)指定する 関数/プロシージャです。依存型においては、メソッド適用後の自身の型も指定できます。これは `T!(!N)`型で`T!(N ~> N-1).() => Int`などのようにメソッドを指定できるということです。 + +Ergの配列(Array)はPythonでいうところのリストとなります。`[Int; 3]`は`Int`型オブジェクトが3つ入る配列クラスです。 + +> __Note__: `(Type; N)`は型であり値でもあるので、このような使い方もできます。 +> +> ```erg +> Types = (Int, Str, Bool) +> +> for! Types, T => +> print! T +> # Int Str Bool +> a: Types = (1, "aaa", True) +> ``` + +```erg +pop|T, N|(l: [T; N]): ([T; N-1], T) = + [...l, last] = l + (l, last) + +lpop|T, N|(l: [T; N]): (T, [T; N-1]) = + [first, ...l] = l + (first, l) +``` + +`!`の付く型はオブジェクトの内部構造書き換えを許可する型です。例えば`[T; !N]`クラスは動的配列となります。 +`T`型オブジェクトから`T!`型オブジェクトを生成するには、単項演算子の`!`を使います。 + +```erg +i: Int! = !1 +i.update! i -> i + 1 +assert i == 2 +arr = [1, 2, 3] +arr.push! 4 # ImplError: +mut_arr = [1, 2, 3].into [Int; !3] +mut_arr.push! 4 +assert mut_arr == [1, 2, 3, 4] +``` + +## 型定義 + +型は以下のように定義します。 + +```erg +Point2D = {.x = Int; .y = Int} +``` + +なお、`i: Int`などのように`.`を省略すると、型内で使われる非公開変数になります。しかしこれも要求属性です。 +型もオブジェクトなので、型自体にも属性は存在します。このような属性を型属性といいます。クラスの場合はクラス属性ともいいます。 + +## 型クラス、データ型(に相当するもの) + +先に述べたように、Ergにおける「型」とは大まかにはオブジェクトの集合を意味します。 +以下は`+`(中置演算子)を要求する `Add`型の定義です。`R, O`はいわゆる型引数で、`Int`や`Str`など実装のある型(クラス)が入れられます。他の言語で型引数には特別な記法(ジェネリクス、テンプレートなど)が与えられていますが、Ergでは通常の引数と同じように定義できます。 +なお型引数は型オブジェクト以外も使用できます。例えば配列型`[Int; 3]`は`Array Int, 3`の糖衣文法です。型の実装がかぶる場合、ユーザは明示的に選択しなくてはなりません。 + +```erg +Add R, O = Trait { + .`_+_` = Self.(R) -> O +} +``` + +.`_+_`は Add.`_+_`の省略形です。前置演算子の.`+_`は`Num`型のメソッドです。 + +```erg +Num = Add and Sub and Mul and Eq +NumImpl = Patch Num +NumImpl. + `+_`(self): Self = self + ... +``` + +多相型は関数のように扱えます。`Mul Int, Str`などのように指定して単相化します(多くの場合は指定しなくても実引数で推論されます)。 + +```erg +1 + 1 +`_+_` 1, 1 +Nat.`_+_` 1, 1 +Int.`_+_` 1, 1 +``` + +上の4行は同じ結果を返しますが(正確には、一番下は`Int`を返します)、一番上を使うのが一般的です。 +```Ratio.`_+_`(1, 1)```とすると、エラーにはならず`2.0`が返ります。 +これは、`Int <: Ratio`であるために`1`が`Ratio`にダウンキャストされるからです。 +しかしこれはキャストされません。 + +```erg +i = 1 +if i: # TypeError: i: Int cannot cast to Bool, use Int.is_zero() instead. + log "a" + log "b" +``` + +これは、`Bool < Int`であるためです(`True == 1`, `False == 0`)。サブタイプへのキャストは一般に検証が必要です。 + +## 型推論システム + +Ergは静的ダックタイピングを採用しており、明示的に型を指定する必要は殆どありません。 + +```erg +f x, y = x + y +``` + +上のコードの場合、`+`を持つ型、すなわち`Add`が自動的に推論されます。Ergはまず最小の型を推論します。`f 0, 1`とすれば`f x: {0}, y: {1}`と推論され、`n: Nat; f n, 1`の場合`f x: Nat, y: {1}`と推論されます。最小化後は実装が見つかるまで型を大きくしていきます。`{0}, {1}`の場合`Nat`が`+`の実装がある最小型なので`Nat`に単相化されます。 +`{0}, {-1}`の場合は`Nat`にマッチしないので`Int`に単相化されます。部分型、上位型の関係にない場合は、濃度(インスタンス数)が低い(多相型の場合はさらに引数の少ない)方からトライされます。 +`{0}`と`{1}`は`Int`や`Nat`などの部分型となる列挙型です。 +列挙型などには名前を付けて要求/実装メソッドを付けられます。その型にアクセスできる名前空間では、要求を満たすオブジェクトは実装メソッドを使用できます。 + +```erg +Binary = Patch {0, 1} +Binary. + # selfにはインスタンスが格納される。この例では0か1のどちらか。 + # selfを書き換えたい場合、型名、メソッド名に!を付けなければならない。 + is_zero(self) = match self: + 0 -> True + 1 -> False # _ -> Falseとしてもよい + is_one(self) = not self.is_zero() + to_bool(self) = match self: + 0 -> False + 1 -> True +``` + +以降は`0.to_bool()`というコードが可能となります(もっとも`0 as Bool == False`がビルトインで定義されていますが)。 +コード中に示されたように、実際に`self`を書き換える事のできる型の例を示します。 + +```erg +Binary! = Patch {0, 1}! +Binary!. + switch! ref! self = match! self: + 0 => self = 1 + 1 => self = 0 + +b = !1 +b.switch!() +print! b # => 0 +``` + +## 構造型(無名型) + +```erg +Binary = {0, 1} +``` + +上のコードでの`Binary`は、`0`および`1`が要素の型です。`0`と`1`両方を持っている`Int`型の部分型とも言えます。 +`{}`のようなオブジェクトはそれ自体が型であり、上のように変数に代入して使ってもよいし、代入せずに使うこともできます。 +このような型を構造型といいます。クラス(記名型)と対比して後者としての使い方を強調したいときは無名型ともいいます。`{0, 1}`のような種類の構造型は列挙型と呼ばれ、他に区間型、レコード型などがあります。 + +### 型同一性 + +下のような指定はできません。`Add`はそれぞれ別のものを指すと解釈されるからです。 +例えば、`Int`と`Str`はともに`Add`だが、`Int`と`Str`の加算はできません。 + +```erg +add l: Add, r: Add = + l + r # TypeError: there is no implementation of `_+_`: |T, U <: Add| (T, U) -> +``` + +また、下の`A`, `B`は同じ型とはみなされません。しかし、型`O`は一致するとみなされます。 + +```erg +... |R1; R2; O; A <: Add(R1, O); B <: Add(R2, O)| +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/type/02_basic.md b/doc/JA/syntax/type/02_basic.md new file mode 100644 index 00000000..958469ed --- /dev/null +++ b/doc/JA/syntax/type/02_basic.md @@ -0,0 +1,154 @@ +# 型に関する基本的な文法 + +## 型指定 + +Ergでは以下のように`:`の後に変数の型を指定します。代入と同時に行うこともできます。 + +```erg +i: Int # これから使う変数iはInt型であると宣言する +i: Int = 1 +j = 1 # type specification can be omitted +``` + +単純な変数代入の場合、ほとんどの型指定は省略可能です。 +型指定は単純な変数よりもサブルーチンや型の定義時に役立ちます。 + +```erg +# 引数の型指定 +f x, y: Array Int = ... +T X, Y: Array Int = ... +``` + +上の場合、`x, y`は共に`Array Int`であることに注意して下さい。 + +```erg +# 大文字変数の値は定数式でなくてはならない +f X: Int = X +``` + +あるいは、型引数の情報が完全にいらない場合は`_`で省略することもできます。 + +```erg +g v: [T; _] = ... +``` + +ただし、型指定の箇所で`_`を指定するとそれは`Object`を意味することに注意して下さい。 + +```erg +f x: _, y: Int = x + y # TypeError: + is not implemented between Object and Int +``` + +## 部分型指定 + +Ergでは`:`(型宣言演算子)による型と式の関係指定の他に、`<:`(部分型宣言演算子)で型同士の関係を指定することもできます。 +`<:`の左辺はクラスしか指定できません。構造型同士の比較は`Subtypeof`などを使用して下さい。 + +これも単純な変数の指定より、サブルーチンや型の定義時に使うことが多いです。 + +```erg +# 引数の部分型指定 +f X <: T = ... + +# 要求属性の部分型指定(.Iterator属性はIterator型のサブタイプであることを要求する) +Iterable T = Trait { + .Iterator = {Iterator} # == {I | I <: Iterator} + .iter = Self.() -> Self.Iterator T + ... +} +``` + +また、クラス定義時に部分型指定を行うと、クラスが指定した型のサブタイプか静的に検査することができます。 + +```erg +# クラスCはShowのサブタイプ +C = Class Object, Impl=Show +C.show self = ... # Showの要求属性 +``` + +特定の場合だけ部分型指定することもできます。 + +```erg +K T: Eq +K Int <: Show and Eq +K T = Class Object +K(T). + `==` self, other = ... +K(Int). + show self = ... +``` + +構造型を実装する際は、部分型指定を行うことを推奨します。 +構造的部分型付けの特性から、要求属性の実装をする際にタイポや型指定の間違いがあってもエラーが出ないためです。 + +```erg +C = Class Object +C.shoe self = ... # TypoのせいでShowが実装できていない(単なる固有のメソッドとみなされる) +``` + +## 属性定義 + +トレイトやクラスには、モジュール内でのみ属性を定義することができます。 + +```erg +C = Class() +C.pub_attr = "this is public" +C::private_attr = "this is private" + +c = C.new() +assert c.pub_attr == "this is public" +``` + +`C.`か`C::`のあとに改行してインデント以下にまとめて定義する文法を一括定義(batch definition)といいます。 + +```erg +C = Class() +C.pub1 = ... +C.pub2 = ... +C::priv1 = ... +C::priv2 = ... +# is equivalent to +C = Class() +C. + pub1 = ... + pub2 = ... +C:: + priv1 = ... + priv2 = ... +``` + +## エイリアシング + +型には別名(エイリアス)を付けることができます。これにより、レコード型など長い型を短く表現できます。 + +```erg +Id = Int +Point3D = {x = Int; y = Int; z = Int} +IorS = Int or Str +Vector = Array Int +``` + +またエラー表示の際にも、コンパイラは複合型(上の例の場合、1番目以外の右辺型)にエイリアスが定義されている場合なるべくそれを使用するようになります。 + +ただし同じ型のエイリアスは1つのモジュールにつき1つまでで、複数のエイリアスがある場合warningが出ます。 +これは、違う目的の型は別々の型として新しく定義するべき、ということです。 +また、すでにエイリアスのある型に重ねてエイリアスを付けることを防ぐ目的もあります。 + +```erg +Id = Int +UserId = Int # TypeWarning: duplicate aliases: Id and UserId + +Ids = Array Id +Ints = Array Int # TypeWarning: duplicate aliases: Isd and Ints + +IorS = Int or Str +IorSorB = IorS or Bool +IorSorB_ = Int or Str or Bool # TypeWarning: duplicate aliases: IorSorB and IorSorB_ + +Point2D = {x = Int; y = Int} +Point3D = {...Point2D; z = Int} +Point = {x = Int; y = Int; z = Int} # TypeWarning: duplicate aliases: Point3D and Point +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/type/03_trait.md b/doc/JA/syntax/type/03_trait.md new file mode 100644 index 00000000..679a4d5d --- /dev/null +++ b/doc/JA/syntax/type/03_trait.md @@ -0,0 +1,150 @@ +# トレイト + +トレイトは、レコード型に型属性の要求を追加した記名型です。 +Pythonでいう抽象基底クラス(Abstract Base Class, ABC)に類似しますが、代数的演算を行えるという特徴があります。 + +```erg +Norm = Trait {.x = Int; .y = Int; .norm = Self.() -> Int} +``` + +トレイトは属性とメソッドを区別しません。 + +トレイトは宣言ができるのみで実装を持てないことに注意してください(実装は後に述べるパッチという機能で実現します)。 +トレイトは部分型指定でクラスに実装されているかチェックすることができます。 + +```erg +Point2D <: Norm +Point2D = Class {.x = Int; .y = Int} +Point2D.norm self = self.x**2 + self.y**2 +``` + +要求属性を実装していないとエラーになります。 + +```erg +Point2D <: Norm # TypeError: Point2D is not a subtype of Norm +Point2D = Class {.x = Int; .y = Int} +``` + +トレイトは構造型と同じく合成、置換、排除などの演算を適用できます(e.g. `T and U`)。このようにしてできたトレイトをインスタントトレイトと呼びます。 + +```erg +T = Trait {.x = Int} +U = Trait {.y = Int} +V = Trait {.x = Int; y: Int} +assert Structural(T and U) == Structural V +assert Structural(V not U) == Structural T +W = Trait {.x = Ratio} +assert Structural(W) != Structural(T) +assert Structural(W) == Structural(T.replace {.x = Ratio}) +``` + +トレイトは型でもあるので、通常の型指定にも使えます。 + +```erg +points: [Norm; 2] = [Point2D::new(1, 2), Point2D::new(3, 4)] +assert points.iter().map(x -> x.norm()).collect(Array) == [5, 25] +``` + +## トレイトの包摂 + +展開演算子`...`によって、あるトレイトを上位型として含むトレイトを定義できます。これをトレイトの __包摂(Subsumption)__ と呼びます。 +下の例でいうと、`BinAddSub`は`BinAdd`と`BinSub`を包摂しています。 +これはクラスにおける継承(Inheritance)に対応しますが、継承と違い複数の基底型を`and`で合成して指定できます。`not`によって一部を除外したトレイトでもOKです。 + +```erg +Add R, O = Trait { + .`_+_` = Self.(R) -> O +} +BinAdd = Subsume Add(Self, Self.AddO), { + .AddO = Type +} +Sub R, O = Trait { + .`_-_` = Self.(R) -> O +} +BinSub = Subsume Sub(Self, Self.SubO), { + .SubO = Type +} +BinAddSub = Subsume BinAdd(Self, Self.AddO) and BinSub(Self, Self.SubO) +``` + +## 構造的トレイト + +トレイトは構造化できます。 + +```erg +SAdd = Structural Trait { + .`_+_` = Self.(Self) -> Self +} +# |A <: SAdd|は省略できない +add|A <: SAdd| x, y: A = x.`_+_` y + +C = Class {i = Int} +C. + new i = Self.__new__ {i;} + `_+_` self, other: Self = Self.new {i = self::i + other::i} + +assert add(C.new(1), C.new(2)) == C.new(3) +``` + +記名的トレイトは単に要求メソッドを実装しただけでは使えず、実装したことを明示的に宣言する必要があります。 +以下の例では明示的な実装の宣言がないため、`add`が`C`型の引数で使えません。`C = Class {i = Int}, Impl: Add`としなくてはならないのです。 + +```erg +Add = Trait { + .`_+_` = Self.(Self) -> Self +} +# |A <: Add|は省略できる +add|A <: Add| x, y: A = x.`_+_` y + +C = Class {i = Int} +C. + new i = Self.__new__ {i;} + `_+_` self, other: Self = Self.new {i = self::i + other::i} + +add C.new(1), C.new(2) # TypeError: C is not subclass of Add +# hint: inherit or patch 'Add' +``` + +構造的トレイトはこの実装の宣言がなくてもよいのですが、そのかわり型推論が効きません。使う際は型指定が必要です。 + +## 依存トレイト + +トレイトは引数を取ることができます。これは依存型と同じです。 + +```erg +Mapper T: Type = Trait { + .MapIter = {Iterator} + .map = Self(T).(T -> U) -> Self.MapIter U +} + +# ArrayIterator <: Mapper +# ArrayIterator.MapIter == ArrayMapper +# [1, 2, 3].iter(): ArrayIterator Int +# [1, 2, 3].iter().map(x -> "{x}"): ArrayMapper Str +assert [1, 2, 3].iter().map(x -> "{x}").collect(Array) == ["1", "2", "3"] +``` + +## トレイトにおけるオーバーライド + +派生トレイトでは基底トレイトの型定義をオーバーライドできます。 +この場合、オーバーライドするメソッドの型は、基底メソッドの型の部分型でなければなりません。 + +```erg +# `Self.(R) -> O`は`Self.(R) -> O or Panic`の部分型 +Div R, O: Type = Trait { + .`/` = Self.(R) -> O or Panic +} +SafeDiv R, O = Subsume Div, { + @Override + .`/` = Self.(R) -> O +} +``` + +## Appendix: Rustのトレイトとの違い + +Ergのトレイトは[Schärli et al.](https://www.ptidej.net/courses/ift6251/fall06/presentations/061122/061122.doc.pdf)の提唱したトレイトに忠実です。 +代数演算を行えるようにするためトレイトは実装を持てないようにして、必要ならばパッチをあてる設計にしています。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/type/04_class.md b/doc/JA/syntax/type/04_class.md new file mode 100644 index 00000000..d1ce81f3 --- /dev/null +++ b/doc/JA/syntax/type/04_class.md @@ -0,0 +1,277 @@ +# Class + +Ergにおけるクラスは、大まかには自身の要素(インスタンス)を生成できる型と言えます。 +以下は単純なクラスの例です。 + +```erg +Person = Class {.name = Str; .age = Nat} +Person. + new name, str = Self::__new__ {.name = name; .age = age} + +john = Person.new "John Smith", 25 +print! john # +print! classof(john) # Person +``` + +`Class`に与えられる型を要件型(この場合は`{.name = Str; .age = Nat}`)といいます。 +インスタンスは`クラス名::__new__ {属性名 = 値; ...}`で生成できます。 +`{.name = "John Smith"; .age = 25}`は単なるレコードですが、`Person.new`を通すことで`Person`インスタンスに変換されるわけです。 +このようなインスタンスを生成するサブルーチンはコンストラクタと呼ばれます。 +上のクラスでは、フィールド名等を省略できるように`.new`メソッドを定義しています。 + +## インスタンス属性、クラス属性 + +Pythonやその他の言語では、以下のようにブロック側でインスタンス属性を定義することが多いが、このような書き方はErgでは別の意味になるので注意が必要である。 + +```python +# Python +class Person: + name: str + age: int +``` + +```erg +# Ergでこの書き方はクラス属性の宣言を意味する(インスタンス属性ではない) +Person = Class() +Person. + name: Str + age: Int +``` + +```erg +# 上のPythonコードに対応するErgコード +Person = Class { + .name = Str + .age = Nat +} +``` + +要素属性(レコード内で定義した属性)と型属性(クラスの場合は特にインスタンス属性/クラス属性とも呼ばれる)は全くの別物である。型属性は型自体の持つ属性である。型の要素は、自らの中に目当ての属性がないときに型属性を参照する。要素属性は要素が直接持つ固有の属性である。 +なぜこのような区分けがされているか。仮に全てが要素属性だと、オブジェクトを生成した際に全ての属性を複製・初期化する必要があり、非効率であるためである。 +また、このように分けたほうが「この属性は共用」「この属性は別々に持つ」などの役割が明確になる。 + +下の例で説明する。`species`という属性は全てのインスタンスで共通なので、クラス属性とした方が自然である。だが`name`という属性は各インスタンスが個々に持っておくべきなのでインスタンス属性とすべきなのである。 + +```erg +Person = Class {name = Str} +Person:: + species = "human" +Person. + describe() = + log "species: {species}" + greet self = + log "Hello, My name is {self::name}." + +Person.describe() # species: human +Person.greet() # TypeError: unbound method Person.greet needs an argument + +john = Person.new {name = "John"} +john.describe() # species: human +john.greet() # Hello, My name is John. + +alice = Person.new {name = "Alice"} +alice.describe() # species: human +alice.greet() # Hello, My name is Alice. +``` + +因みに、インスタンス属性と型属性で同名、同型のものが存在する場合、コンパイルエラーとなる。これは混乱を避けるためである。 + +```erg +C = Class {.i = Int} +C.i = 1 # AttributeError: `.i` is already defined in instance fields +``` + +## Class, Type + +`1`のクラスと型が違うものであることに注意してください。 +`1`の生成元であるクラスは`Int`ただひとつです。オブジェクトの属するクラスは`classof(obj)`または`obj.__class__`で取得できます。 +対して`1`の型は無数にあります。例としては、`{1}, {0, 1}, 0..12, Nat, Int, Num`などです。 +ただし最小の型はひとつに定めることができ、この場合は`{1}`です。オブジェクトの属する型は`Typeof(obj)`で取得できます。 +これはコンパイル時関数であり、その名の通りコンパイル時に計算されます。 +オブジェクトからは、クラスメソッドの他にパッチメソッドも使用可能です。 +Ergではクラスメソッドを追加したりはできませんが、[パッチ](./07_patch.md)で拡張可能です。 + +既存のクラスを継承することも出来ます([Inheritable](./../27_decorator.md/#inheritable)クラスの場合)。 +`Inherit`は継承を意味します。左辺の型を派生クラス、右辺の`Inherit`の引数型を基底クラスと言います。 + +```erg +MyStr = Inherit Str +# other: StrとしておけばMyStrでもOK +MyStr.`-` self, other: Str = self.replace other, "" + +abc = MyStr.new("abc") +# ここの比較はアップキャストが入る +assert abc - "b" == "ac" +``` + +Pythonと違い、定義されたErgのクラスはデフォルトで`final`(継承不可)です。 +継承可能にするためには`Inheritable`デコレータをクラスに付ける必要があります。 +`Str`は継承可能クラスのひとつです。 + +```erg +MyStr = Inherit Str # OK +MyStr2 = Inherit MyStr # NG + +@Inheritable +InheritableMyStr = Inherit Str +MyStr3 = Inherit InheritableMyStr # OK +``` + +`Inherit Object`と`Class()`は実用上ほぼ等価です。一般的には後者を使用します。 + +クラスは型とは同値判定の仕組みが異なります。 +型は構造に基づいて同値性が判定されます。 + +```erg +Person = {.name = Str; .age = Nat} +Human = {.name = Str; .age = Nat} + +assert Person == Human +``` + +クラスは同値関係が定義されていません。 + +```erg +Person = Class {.name = Str; .age = Nat} +Human = Class {.name = Str; .age = Nat} + +Person == Human # TypeError: cannot compare classes +``` + +## 構造型との違い + +クラスは自身の要素を生成することができる型といいましたが、それだけは厳密な説明ではありません。実際はレコード型+パッチでも同じことができるからです。 + +```erg +Person = {.name = Str; .age = Nat} +PersonImpl = Patch Person +PersonImpl. + new name, age = {.name; .age} + +john = Person.new("John Smith", 25) +``` + +クラスを使用するメリットは4つあります。 +1つはコンストラクタの正当性が検査されること、2つ目はパフォーマンスが高いこと、3つ目は記名的部分型(NST)が使用できること、4つ目は継承・オーバーライドができることです。 + +先程レコード型+パッチでもコンストラクタ(のようなもの)が定義できることを見ましたが、これはもちろん正当なコンストラクタとは言えません。`.new`と名乗っていても全く関係のないオブジェクトを返すことができるからです。クラスの場合は、`.new`が要件を満たすオブジェクトを生成するか静的に検査されます。 + +~ + +クラスの型検査は、単にオブジェクトの`.__class__`属性を見るだけで完了します。なので、オブジェクトが型に属しているかの検査が高速です。 + +~ + +ErgではクラスでNSTを実現します。NSTの利点として、堅牢性などが挙げられます。 +大規模なプログラムを書いていると、オブジェクトの構造が偶然一致することはままあります。 + +```erg +Dog = {.name = Str; .age = Nat} +DogImpl = Patch Dog +DogImpl.bark = log "Yelp!" +... +Person = {.name = Str; .age = Nat} +PersonImpl = Patch Person +PersonImpl.greet self = log "Hello, my name is {self.name}." + +john = {.name = "John Smith"; .age = 20} +john.bark() # "Yelp!" +``` + +`Dog`と`Person`の構造は全く同一ですが、動物が挨拶したり人間が吠えたりできるようにするのは明らかにナンセンスです。 +後者はともかく、前者は不可能なので適用できないようにする方が安全です。このような場合はクラスを使用すると良いでしょう。 + +```erg +Dog = Class {.name = Str; .age = Nat} +Dog.bark = log "Yelp!" +... +Person = Class {.name = Str; .age = Nat} +Person.greet self = log "Hello, my name is {self.name}." + +john = Person.new {.name = "John Smith"; .age = 20} +john.bark() # TypeError: `Person` object has no method `.bark` +``` + +もう一つ、パッチによって追加された型属性は仮想的なもので、実装するクラスが実体として保持している訳ではないという特徴があります。 +つまり、`T.x`, `T.bar`は`{i = Int}`と互換性のある型がアクセスできる(コンパイル時に結びつける)オブジェクトであり、`{i = Int}`や`C`に定義されているわけではありません。 +対してクラス属性はクラス自身が保持しています。なので、構造が同じであっても継承関係にないクラスからはアクセスできません。 + +```erg +C = Class {i = Int} +C. + foo self = ... +print! dir(C) # ["foo", ...] + +T = Patch {i = Int} +T. + x = 1 + bar self = ... +print! dir(T) # ["bar", "x", ...] +assert T.x == 1 +assert {i = 1}.x == 1 +print! T.bar # +{i = Int}.bar # TypeError: Record({i = Int}) has no method `.bar` +C.bar # TypeError: C has no method `.bar` +print! {i = 1}.bar # +print! C.new({i = 1}).bar # +``` + +## データクラスとの違い + +クラスには、レコードクラスに`Class`を通した通常のクラスと、レコードクラスを継承(`Inherit`)したデータクラスがあります。 +データクラスはレコードの機能を受け継いでおり、分解代入ができる、`==`や`hash`がデフォルトで実装されているなどの特徴があります。 +逆に独自の同値関係やフォーマット表示を定義したい場合は通常のクラスを使用するとよいでしょう。 + +```erg +C = Class {i = Int} +c = C.new {i = 1} +d = C.new {i = 2} +print! c # +c == d # TypeError: `==` is not implemented for `C` + +D = Inherit {i = Int} +e = D.new {i = 1} +f = D.new {i = 2} +print! e # D{i = 1} +assert e != f +``` + +## Enum Class + +Or型のクラスを定義しやすくするために、`Enum`が用意されています。 + +```erg +X = Class() +Y = Class() +XorY = Enum X, Y +``` + +それぞれの型には`XorY.X`, `XorY.Y`のようにしてアクセスでき、コンストラクタは`XorY.cons(X)`のようにして取得できます。 +`.cons`はクラスを受け取ってそのコンストラクタを返すメソッドです。 + +```erg +x1 = XorY.new X.new() +x2 = XorY.cons(X)() +assert x1 == x2 +``` + +## 包含関係 + +クラスは、要件型のサブタイプです。要件型のメソッド(パッチメソッド含む)を使用することができます。 + +```erg +T = Trait ... +T.foo: Foo +C = Class(..., impl: T) +C. + foo = foo + bar x = ... +assert C < T +assert C.foo == foo +assert not T < C +T.foo # AttributeError +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/type/05_inheritance.md b/doc/JA/syntax/type/05_inheritance.md new file mode 100644 index 00000000..6dc00bf7 --- /dev/null +++ b/doc/JA/syntax/type/05_inheritance.md @@ -0,0 +1,252 @@ +# 継承(Inheritance) + +継承を使うと、既存のクラスに機能を加えたり特化したりした新しいクラスを定義できます。 +継承はトレイトにおける包摂に似ています。継承してできたクラスは、もとのクラスのサブタイプになります。 + +```erg +NewInt = Inherit Int +NewInt.plus1 self = self + 1 + +assert NewInt.new(1).plus1() == 2 +assert NewInt.new(1) + NewInt.new(1) == 2 +``` + +新しく定義するクラスを継承可能なクラスにしたい場合は`Inheritable`デコレータを付与する必要があります。 + +オプション引数`additional`を指定すると追加のインスタンス属性を持つことができます。ただし値クラスの場合はインスタンス属性を追加できません。 + +```erg +@Inheritable +Person = Class {name = Str} +Student = Inherit Person, additional: {id = Int} + +john = Person.new {name = "John"} +alice = Student.new {name = "Alice", id = 123} + +MailAddress = Inherit Str, additional: {owner = Str} # TypeError: instance variables cannot be added to a value class +``` + +Ergでは例外的に`Never`型の継承はできない設計となっている。`Never`は決してインスタンスを生成できない特異なクラスであるためである。 + +## 列挙クラスの継承 + +[Or型](./13_algebraic.md)をクラス化した列挙クラスも継承ができます。この際、オプション引数`Excluding`を指定することで選択肢のどれか(`or`で複数選択可)を外せます。 +なお追加はできません。選択肢を追加したクラスは、元のクラスのサブタイプとはならないからです。 + +```erg +Number = Class Int or Float or Complex +Number.abs(self): Float = + match self: + i: Int -> i.abs().into Float + f: Float -> f.abs() + c: Complex -> c.abs().into Float + +# matchの選択肢でc: Complexは現れ得ない +RealNumber = Inherit Number, Excluding: Complex +``` + +同様に、[篩型](./12_refinement.md)も指定できます。 + +```erg +Months = Class 0..12 +MonthsNot31Days = Inherit Months, Excluding: {1, 3, 5, 7, 8, 10, 12} + +StrMoreThan3 = Class StrWithLen N | N >= 3 +StrMoreThan4 = Inherit StrMoreThan3, Excluding: StrWithLen N | N == 3 +``` + +## オーバーライド + +元の型に新しいメソッドを追加できるところはパッチと同じですが、クラスはさらに「上書き」が可能です。 +この上書きをオーバーライド(override: 上書き)といいます。オーバーライドを行うには3つの条件を満たす必要があります。 +まず、オーバーライドはデフォルトではエラーとなるため`Override`デコレータを付ける必要があります。 +さらに、オーバーライドによってメソッドの型を変えることはできません。元の型のサブタイプである必要があります。 +そして、他のメソッドから参照されているメソッドをオーバーライドする場合、参照しているメソッドも全てオーバーライドする必要があります。 + +なぜこのような条件が必要なのでしょうか。それは、オーバーライドが単に一つのメソッドの挙動を変えるだけでなく、別のメソッドの挙動に影響を及ぼす可能性があるからです。 + +まず、1つ目の条件から解説します。この条件は「不測のオーバーライド」を防ぐためです。 +つまり、たまたま派生クラス側で新しく定義したつもりだったメソッドの名前が基底クラスとかちあってしまうといったことを防ぐため、`Override`デコレータで明示する必要があるのです。 + +次に、2つ目の条件について考えます。これは型の整合性を保つためです。派生クラスは基底クラスのサブタイプであるため、その振る舞いも基底クラスのものと互換性がなくてはなりません。 + +最後に、3つ目の条件について考えます。この条件はErg特有で、他のオブジェクト指向言語ではあまり見られないものですが、これも安全のためです。これがなかったとき、どんなまずいことが起こりうるか見てみましょう。 + +```erg +# Bad example +@Inheritable +Base! = Class {x = Int!} +Base!. + f! ref! self = + print! self::x + self.g!() + g! ref! self = self::x.update! x -> x + 1 + +Inherited! = Inherit Base! +Inherited!. + @Override + g! ref! self = self.f!() # InfiniteRecursionWarning: This code falls into an infinite loop + # OverrideError: method `.g` is referenced by `.f` but not overridden +``` + +継承クラス`Inherited!`では、`.g!`メソッドをオーバーライドして処理を`.f!`に転送しています。しかし基底クラスの`.f!`メソッドはその処理を`.g!`に転送しているので、無限ループが発生してしまっています。`.f`は`Base!`クラスでは問題の無いメソッドでしたが、オーバーライドによって想定外の使われ方をされ、壊れてしまったのです。 + +なので、オーバーライドの影響を受ける可能性のあるメソッドは一般に全て書き直す必要があるわけです。Ergはこのルールを仕様に組み込んでいます。 + +```erg +# OK +@Inheritable +Base! = Class {x = Int!} +Base!. + f! ref! self = + print! self::x + self.g!() + g! ref! self = self::x.update! x -> x + 1 + +Inherited! = Inherit Base! +Inherited!. + @Override + f! ref! self = + print! self::x + self::x.update! x -> x + 1 + @Override + g! ref! self = self.f!() +``` + +しかし、この仕様はオーバーライドの問題を完全に解決するものではありません。コンパイラはオーバーライドで問題が修正されたか検知できないためです。 +オーバーライドによる影響の修正は派生クラスを作成するプログラマの責任です。可能な限り別名のメソッドを定義するようにしましょう。 + +### トレイトの差し替え(のように見えるもの) + +継承時にトレイトを差し替えることはできませんが、一見それを行っているようにみえる例があります。 + +例えば`Real`(`Add()`を実装する)のサブタイプである`Int`では`Add()`を再実装しているようにみえます。 + +```erg +Int = Class ..., Impl: Add() and ... +``` + +しかし実際は`Real`の`Add()`は`Add(Real, Real)`の略で、`Int`では`Add(Int, Int)`で上書きしているだけです。 +両者は別のトレイトです(`Add`は[共変](./advanced/variance.md)なので`Add(Real, Real) :> Add(Int, Int)`ではありますが)。 + +## 多重継承の禁止 + +Ergでは通常のクラス同士でIntersection(交差), Diff(除外), Complement(否定)が行えません。 + +```erg +Int and Str # TypeError: cannot unite classes +``` + +このルールにより、複数のクラスを継承すること、すなわち多重継承が行えません。 + +```erg +IntAndStr = Inherit Int and Str # SyntaxError: multiple inheritance of classes is not allowed +``` + +ただし、Pythonの多重継承されたクラスは使用可能です。 + +## 多層(多段)継承の禁止 + +Ergの継承は多層継承も禁止しています。すなわち、継承して作ったクラスを更に継承したクラスを定義することはできません。 +ただし、`Object`を継承している(Inheritable)クラスは例外的に継承可能です。 + +また、この場合もPythonの多層継承されたクラスは使用可能です。 + +## 継承元属性の書き換え禁止 + +Ergでは継承元の属性を書き換えることができません。これは2つの意味があります。 +1つ目は、継承元のクラス属性に対する更新操作です。再代入はもちろん、`.update!`メソッドなどによる更新もできません。 + +オーバーライドはより特化したメソッドで上書きする操作であるため書き換えとは異なります。オーバーライドの際も互換性のある型で置き換えなくてはなりません。 + +```erg +@Inheritable +Base! = Class {.pub = !Int; pri = !Int} +Base!. + var = !1 + inc_pub! ref! self = self.pub.update! p -> p + 1 + +Inherited! = Inherit Base!: +Inherited!. + var.update! v -> v + 1 + # TypeError: can't update base class variables + @Override + inc_pub! ref! self = self.pub + 1 + # OverrideError: `.inc_pub!` must be subtype of `Self!.() => ()` +``` + +2つ目は、継承元の(可変)インスタンス属性に対する更新操作です。これも禁止されています。基底クラスのインスタンス属性は、基底クラスの用意したメソッドからのみ更新できます。 +属性の可視性にかかわらず、直接更新はできません。ただし読み取りはできます。 + +```erg +@Inheritable +Base! = Class {.pub = !Int; pri = !Int} +Base!. + inc_pub! ref! self = self.pub.update! p -> p + 1 + inc_pri! ref! self = self::pri.update! p -> p + 1 + +Inherited! = Inherit Base!: +Inherited!. + # OK + add2_pub! ref! self = + self.inc_pub!() + self.inc_pub!() + # NG, `Child` cannot touch `self.pub` and `self::pri` + add2_pub! ref! self = + self.pub.update! p -> p + 2 +``` + +畢竟、Ergの継承ができることは新規属性の追加と基底クラスメソッドのオーバーライドのみといえるでしょう。 + +## 継承の使い所 + +継承は正しく使えば強力な機能である反面、クラス同士の依存関係が複雑になりやすいという欠点もあり、特に多重継承・多層継承を使用した場合はその傾向が顕著となります。依存関係の複雑化はコードのメンテナンス性を下げる恐れがあります。 +Ergが多重継承、多層継承を禁止したのはこの危険性を低減するためで、クラスパッチという機能を導入したのは、継承の「機能の追加」という側面を持ちながら依存関係の煩雑化を抑えるためです。 + +では逆に継承を使うべきところはどこでしょうか。一つの指標は、「基底クラスの意味論的なサブタイプがほしい」場合です。 +Ergはサブタイプ判定の一部を型システムが自動で判定してくれます(e.g. 0以上のIntであるところのNat)。 +しかし例えば、「有効なメールアドレスを表す文字列型」をErgの型システムのみに頼って作成することは困難です。通常の文字列にバリデーションを行うべきでしょう。そして、バリデーションが通った文字列オブジェクトには何らかの「保証書」を付加したいところです。それが継承クラスへのダウンキャストに相当するわけです。`Strオブジェクト`を`ValidMailAddressStr`にダウンキャストすることは、文字列が正しいメールアドレスの形式であるか検証することと一対一対応します。 + +```erg +ValidMailAddressStr = Inherit Str +ValidMailAddressStr. + init s: Str = + validate s # mail-address validation + Self.new s + +s1 = "invalid mail address" +s2 = "foo@gmail.com" +_ = ValidMailAddressStr.init s1 # panic: invalid mail address +valid = ValidMailAddressStr.init s2 +valid: ValidMailAddressStr # assurance that it is in the correct email address format +``` + +もう一つの指標は、「記名的な多相=多態を実現したい」場合です。 +例えば、以下に定義する`greet!`プロシージャは、`Named`型のオブジェクトならば何でも受け付けます。 +しかし、明らかに`Dog`型オブジェクトを適用するのは間違えています。そこで引数の型を`Person`クラスにします。 +こうすれば、引数として受け付けるのは`Person`オブジェクトとそれを継承したクラス、`Student`オブジェクトのみです。 +この方が保守的で、不必要に多くの責任を負う必要がなくなります。 + +```erg +Named = {name = Str; ...} +Dog = Class {name = Str; breed = Str} +Person = Class {name = Str} +Student = Inherit Person, additional: {id = Int} +structural_greet! person: Named = + print! "Hello, my name is {person::name}." +greet! person: Person = + print! "Hello, my name is {person::name}." + +max = Dog.new {name = "Max", breed = "Labrador"} +john = Person.new {name = "John"} +alice = Student.new {name = "Alice", id = 123} + +structural_greet! max # Hello, my name is Max. +structural_greet! john # Hello, my name is John. +greet! alice # Hello, my name is Alice. +greet! max # TypeError: +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/type/06_nst_vs_sst.md b/doc/JA/syntax/type/06_nst_vs_sst.md new file mode 100644 index 00000000..ad59ebe2 --- /dev/null +++ b/doc/JA/syntax/type/06_nst_vs_sst.md @@ -0,0 +1,41 @@ +# 記名的部分型 vs. 構造的部分型 + +```erg +# SST +Months = 0..12 +MonthsImpl = Patch Months +MonthsImpl. + name self = + match self: + 1 -> "January" + 2 -> "February" + 3 -> "March" + ... +# NST +MonthsClass = Class Months +MonthsClass. + name self = + match self: + 1 -> "january" + 2 -> "february" + 3 -> "march" + ... + +assert 12 in Months +assert 2.name() == "February" +assert not 12 in MonthsClass +assert MonthsClass.new(12) in MonthsClass +# クラスでラップしても構造型は使える +assert MonthsClass.new(12) in Months +# 両方ある場合クラスメソッドが優先 +assert MonthsClass.new(2).name() == "february" +``` + +## 結局、NSTとSSTどちらを使えばいいのか? + +どちらにすればよいか判断がつかないときはNSTを推奨します。 +SSTはどんなユースケースでも破綻しないコードを書く抽象化能力が要求されます。よい抽象化を実現できれば高い生産性を発揮できますが、間違った抽象化(__見かけによる共通化__)を行うと逆効果となってしまいます。NSTは抽象性をあえて抑え、このリスクを減らすことができます。あなたがライブラリの実装者でないならば、NSTのみでコーディングを行っても悪くはないでしょう。 + +

+ Previous | Next +

diff --git a/doc/JA/syntax/type/07_patch.md b/doc/JA/syntax/type/07_patch.md new file mode 100644 index 00000000..374d33e4 --- /dev/null +++ b/doc/JA/syntax/type/07_patch.md @@ -0,0 +1,221 @@ +# Patch + +Ergでは、既存の型・クラスに手を加えることはできません。 +クラスにメソッドを追加で定義することはできず、特殊化(specialization, 多相に宣言された型を単相化し専用のメソッドを定義する機能。C++などが持つ)も行えません。 +しかし、既存の型・クラスに機能を追加したいという状況は多々あり、これを実現するためにパッチという機能があります。 + +```erg +StrReverse = Patch Str +StrReverse. + reverse self = self.iter().rev().collect(Str) + +assert "abc".reverse() == "cba" +``` + +パッチの名前は、主に追加する機能を端的に表すものがよいでしょう。 +こうすると、パッチされる型(`Str`)のオブジェクトはパッチ(`StrReverse`)のメソッドを使えるようになります。 +実際、`.reverse`は`Str`のメソッドではなく、`StrRReverse`に追加されたメソッドです。 + +ただし、パッチのメソッドは記名型(クラス)のメソッドより優先度が低く、既存のクラスのメソッドをオーバーライド(上書き)できません。 + +```erg +StrangeInt = Patch Int +StrangeInt. + `_+_` = Int.`_-_` # AssignError: .`_+_` is already defined in Int +``` + +オーバーライドしたければ、クラスを継承する必要があります。 +ただし、基本的にはオーバーライドを行わず、別の名前のメソッドを定義することを推奨します。 +オーバーライドは安全のためいくつかの制約が課されており、それほど気軽に行えるものではないからです。 + +```erg +StrangeInt = Inherit Int +StrangeInt. + # オーバーライドするメソッドにはOverrideデコレータを付与する必要がある + # さらに、Int.`_+_`に依存するIntのメソッドすべてをオーバーライドする必要がある + @Override + `_+_` = Super.`_-_` # OverrideError: Int.`_+_` is referenced by ..., so these method must also be overridden +``` + +## パッチの選択 + +パッチは一つの型に対して複数定義し、まとめることもできます。 + +```erg +# foo.er + +StrReverse = Patch(Str) +StrReverse. + reverse self = ... +StrMultiReplace = Patch(Str) +StrMultiReverse. + multi_replace self, pattern_and_targets: [(Pattern, Str)] = ... +StrToCamelCase = Patch(Str) +StrToCamelCase. + to_camel_case self = ... +StrToKebabCase = Patch(Str) +StrToKebabCase. + to_kebab_case self = ... + +StrBoosterPack = StrReverse and StrMultiReplace and StrToCamelCase and StrToKebabCase +``` + +```erg +{StrBoosterPack; ...} = import "foo" + +assert "abc".reverse() == "cba" +assert "abc".multi_replace([("a", "A"), ("b", "B")]) == "ABc" +assert "to camel case".to_camel_case() == "toCamelCase" +assert "to kebab case".to_kebab_case() == "to-kebab-case" +``` + +複数のパッチが定義できると、中には実装の重複が発生する可能性があります。 + +```erg +# foo.er + +StrReverse = Patch(Str) +StrReverse. + reverse self = ... +# more efficient implementation +StrReverseMk2 = Patch(Str) +StrReverseMk2. + reverse self = ... + +"hello".reverse() # PatchSelectionError: multiple choices of `.reverse`: StrReverse, StrReverseMk2 +``` + +そのような場合は、メソッド形式ではなく関連関数形式とすることで一意化できます。 + +```erg +assert StrReverseMk2.reverse("hello") == "olleh" +``` + +また、選択的にインポートすることでも一意化できます。 + +```erg +{StrReverseMk2; ...} = import "foo" + +assert StrReverseMk2.reverse("hello") == "olleh" +``` + +## 接着パッチ(Glue Patch) + +パッチは型同士を関係付けることもできます。`StrReverse`は`Str`と`Reverse`を関係付けています。 +このようなパッチは接着パッチ(Glue Patch)と呼ばれます。 +`Str`は組み込みの型であるため、ユーザーがトレイトを後付けするためには接着パッチが必要なわけです。 + +```erg +Reverse = Trait { + .reverse = Self.() -> Self +} + +StrReverse = Patch Str, Impl: Reverse +StrReverse. + reverse self = + self.iter().rev().collect(Str) +``` + +接着パッチは一つの型とトレイトのペアに対して一つまでしか定義できません。 +これは、仮に複数の接着パッチが同時に「見える」場合、どの実装を選択するか一意に決められなくなるからです。 +ただし、別のスコープ(モジュール)に移る際にパッチを入れ替えることはできます。 + +```erg +NumericStr = Inherit Str +NumericStr. + ... + +NumStrRev = Patch NumericStr, Impl: Reverse +NumStrRev. + ... +# DuplicatePatchError: NumericStr is already associated with `Reverse` +# hint: `Str` (superclass of `NumericStr`) is associated with `Reverse` by `StrReverse` +``` + +## Appendix: Rustのトレイトとの関連 + +ErgのパッチはRustの(後付けの)implブロックに相当します。 + +```rust +// Rust +trait Reverse { + fn reverse(self) -> Self; +} + +impl Reverse for String { + fn reverse(self) -> Self { + self.chars().rev().collect() + } +} +``` + +RustのトレイトはErgのトレイトとパッチの機能を併せ持つ機能だと言えるでしょう。こう言うとRustのトレイトの方が便利に聞こえますが、実はそうとも限りません。 + +```erg +# Erg +Reverse = Trait { + .reverse = Self.() -> Self +} + +StrReverse = Patch(Str, Impl: Reverse) +StrReverse. + reverse self = + self.iter().rev().collect(Str) +``` + +Ergではimplブロックがパッチとしてオブジェクト化されているため、他のモジュールから取り込む際に選択的な取り込みが可能になります。さらに副次的な効果として、外部構造体への外部トレイトの実装も可能となっています。 +また、dyn traitやimpl traitといった文法も構造型によって必要なくなります。 + +```erg +# Erg +reversible: [Reverse; 2] = [[1, 2, 3], "hello"] + +iter|T|(i: Iterable T): Iterator T = i.iter() +``` + +```rust +// Rust +let reversible: [Box; 2] = [Box::new([1, 2, 3]), Box::new("hello")]; + +fn iter(i: I) -> impl Iterator where I: IntoIterator { + i.into_iter() +} +``` + +## 全称パッチ + +パッチはある特定の型ひとつだけではなく、「関数の型全般」などに対しても定義できます。 +この場合、自由度を与えたい項を引数にします(下の場合は`T: Type`)。このようにして定義したパッチを全称パッチといいます。 +見れば分かる通り、全称パッチは正確にはパッチを返す関数ですが、それ自体もパッチとみなすことが可能です。 + +```erg +FnType T: Type = Patch(T -> T) +FnType(T). + type = T + +assert (Int -> Int).type == Int +``` + +## 構造的パッチ + +さらにパッチは、ある構造を満たす型すべてに定義することもできます。 +ただしこれは記名的なパッチやクラスメソッドより優先度は低くなっています。 + +以下のように拡張によって成り立たなくなる性質もあるので、構造的パッチを定義する際は慎重に設計してください。 + +```erg +# これはStructuralにするべきではない +Norm = Structural Patch {x = Int; y = Int} +Norm. + norm self = self::x**2 + self::y**2 + +Point2D = Class {x = Int; y = Int} +assert Point2D.new({x = 1; y = 2}).norm() == 5 + +Point3D = Class {x = Int; y = Int; z = Int} +assert Point3D.new({x = 1; y = 2; z = 3}).norm() == 14 # AssertionError: +``` + +

+ Previous | Next +

diff --git a/doc/JA/syntax/type/08_value.md b/doc/JA/syntax/type/08_value.md new file mode 100644 index 00000000..2e2fb77c --- /dev/null +++ b/doc/JA/syntax/type/08_value.md @@ -0,0 +1,37 @@ +# 値型(Value types) + +値型はErg組み込み型のうちコンパイル時評価が可能な型で、具体的には以下のものです。 + +```erg +Value = ( + Int + or Nat + or Ratio + or Float + or Complex + or Bool + or Str + or NoneType + or Array Const + or Tuple Const + or Set Const + or ConstFunc(Const, _) + or ConstProc(Const, _) + or ConstMethod(Const, _) +) +``` + +値型のオブジェクト・定数、およびそれにコンパイル時サブルーチンを適用したものを定数式と呼びます。 + +```erg +1, 1.0, 1+2im, True, None, "aaa", [1, 2, 3], Fib(12) +``` + +サブルーチンについては注意が必要です。サブルーチンは値型であるものとそうでないものがあります。 +サブルーチンの実体は単なるポインタであるためすべて値として扱っても良い[1](#1)のですが、コンパイル時サブルーチンでないものを定数文脈で使えてもあまり意味がないため、値型とはなっていません。 + +値型に分類される型は、将来的には追加される可能性があります。 + +--- + +1 Ergにおける値型という用語は、他の言語での定義とは異なっています。純粋なErgの意味論内でメモリという概念は存在せず、スタックに置かれるから値型であるとか、実体としてポインタだから値型ではない、といった言明は正しくありません。あくまで、値型は`Value`型もしくはそのサブタイプであるという意味しか持ちません。 [↩](#f1) diff --git a/doc/JA/syntax/type/09_attributive.md b/doc/JA/syntax/type/09_attributive.md new file mode 100644 index 00000000..8bcfc101 --- /dev/null +++ b/doc/JA/syntax/type/09_attributive.md @@ -0,0 +1,8 @@ +# 属性型(Attributive Type) + +属性型は、レコードおよびデータクラス、パッチ、モジュールなどが含まれる型です。 +属性型に属する型は値型ではありません。 + +## レコード型の合成 + +合成されたレコード型は平坦化できます。例えば、`{...{.name = Str; .age = Nat}; ...{.name = Str; .id = Nat}}`は`{.name = Str; .age = Nat; .id = Nat}`となります。 diff --git a/doc/JA/syntax/type/10_interval.md b/doc/JA/syntax/type/10_interval.md new file mode 100644 index 00000000..77d7db1f --- /dev/null +++ b/doc/JA/syntax/type/10_interval.md @@ -0,0 +1,31 @@ +# Interval Type + +`Range`オブジェクトの最も基本的な使い方は、イテレータとしての使用です。 + +```erg +for! 0..9, i => + print! i +``` + +Pythonと違い、末尾の数字は含まれることに注意してください。 + +しかし、`Range`オブジェクトの使い道はこれだけではありません。型としても使うことが出来ます。このような型を区間型(Interval type)と呼びます。 + +```erg +i: 0..10 = 2 +``` + +`Nat`型は`0.. ExtraStatusであり、Statusの要素はExtraStatusのメソッドを使える +Status = Trait {"Ok", "Error"} + # ... +ExtraStatus = Trait {"Ok", "Error", "Unknown"} + # ... +``` + +patchingによってメソッドの追加もできます。 + +明示的に包含関係を示したい場合、または既存のEnum型に選択肢を追加したい場合は`or`演算子を使います。 + +```erg +ExtraStatus = Status or {"Unknown"} +``` + +要素の属するクラスがすべて同一である列挙型を等質(homogenous)な列挙型といいます。 +デフォルトでは、等質な列挙型を要件型とするクラスは、要素が属しているクラスのサブクラスとして扱えます。 +あえてそうしたくない場合は、ラッパークラスとするとよいでしょう。 + +```erg +Abc = Class {"A", "B", "C"} +Abc.new("A").is_uppercase() + +OpaqueAbc = Class {inner = {"A", "B", "C"}}. + new inner: {"A", "B", "C"} = Self.new {inner;} +OpaqueAbc.new("A").is_uppercase() # TypeError +``` diff --git a/doc/JA/syntax/type/12_refinement.md b/doc/JA/syntax/type/12_refinement.md new file mode 100644 index 00000000..f87decec --- /dev/null +++ b/doc/JA/syntax/type/12_refinement.md @@ -0,0 +1,75 @@ +# 篩型(Refinement Type) + +Refinement type(篩型、ふるいがた)は、述語式によって制約付けられた型です。列挙型や区間型は篩型の一種です。 + +篩型の標準形は`{Elem: Type | (Pred)*}`です。これは、`Pred`を満たす`Elem`を要素とする型である、という意味です。 +篩型に使えるのは[Const型](./advanced/const.md)のみです。 + +```erg +Nat = 0.._ +Odd = {N: Int | N % 2 == 1} +Char = StrWithLen 1 +# StrWithLen 1 == {_: StrWithLen N | N == 1} +[Int; 3] == {_: Array Int, N | N == 3} +Array3OrMore == {A: Array _, N | N >= 3} +``` + +複数のPredがあるとき、`;`か`and`, `or`で区切れます。`;`と`and`は同じ意味です。 + +`Odd`の要素は`1, 3, 5, 7, 9, ...`です。 +篩にかけるように既存の型の一部を要素とする型になることから篩型と呼ばれます。 + +`Pred`は(左辺)述語式と呼ばれます。これは代入式と同じく意味のある値を返すものではなく、左辺にはパターンしか置けません。 +すなわち、`X**2 - 5X + 6 == 0`のような式は篩型の述語式としては使えません。この点において、右辺式の述語式とは異なります。 + +```erg +{X: Int | X**2 - 5X + 6 == 0} # SyntaxError: the predicate form is invalid. Only names can be on the left-hand side +``` + +あなたが二次方程式の解法を知っているならば、上の篩型は`{2, 3}`と同等になるだろうと予想できるはずです。 +しかしErgコンパイラは代数学の知識をほとんど持ち合わせていないので、右の述語式を解決できないのです。 + +## Smart Cast + +`Odd`を定義したのはいいですが、このままではリテラル以外ではあまり使えないようにみえます。通常の`Int`オブジェクトの中の奇数を`Odd`に昇格させる、つまり`Int`を`Odd`にダウンキャストするためには、`Odd`のコンストラクタを通す必要があります。 +篩型の場合、通常のコンストラクタ`.new`はパニックする可能性があり、`.try_new`という`Result`型を返す補助的なコンストラクタもあります。 + +```erg +i = Odd.new (0..10).sample!() +i: Odd # or Panic +``` + +また、`match`中で型指定として使用することもできます。 + +```erg +# i: 0..10 +i = (0..10).sample!() +match i: + o: Odd -> + log "i: Odd" + n: Nat -> # 0..10 < Nat + log "i: Nat" +``` + +ただし、Ergは現在のところ`Odd`でなかったから`Even`、などといった副次的な判断はできません。 + +## 列挙型、区間型と篩型 + +今まで紹介した列挙型と区間型は、篩型の糖衣構文です。 +`{a, b, ...}`は`{I: Typeof(a) | I == a or I == b or ... }`に、`a..b`は`{I: Typeof(a) | I >= a and I <= b}`に脱糖されます。 + +```erg +{1, 2} == {I: Int | I == 1 or I == 2} +1..10 == {I: Int | I >= 1 and I <= 10} +1..<10 == {I: Int | I >= 1 and I < 10} == {I: Int | I >= 1 and I <= 9} +``` + +## 篩パターン + +`_: {X}`を`X`と書き換えられるように(定数パターン)、`_: {X: T | Pred}`は`X: T | Pred`と書き換えることができます。 + +```erg +# メソッド.mは長さ3以上の配列に定義される +Array(T, N | N >= 3) + .m(&self) = ... +``` diff --git a/doc/JA/syntax/type/13_algebraic.md b/doc/JA/syntax/type/13_algebraic.md new file mode 100644 index 00000000..8cfaa1c6 --- /dev/null +++ b/doc/JA/syntax/type/13_algebraic.md @@ -0,0 +1,84 @@ +# Algebraic type (代数演算型) + +代数演算型は、型を代数のようにみなして演算を施すことで生成される型のことです。 +代数演算型が扱う演算は、Union, Intersection, Diff, Complementなどがあります。 +通常のクラスはUnionのみが行えて、他の演算は型エラーになります。 + +## Union + +Union型では型について複数の可能性を与える事ができる。名前の通り、`or`演算子で生成されます。 +代表的なUnionは`Option`型です。`Option`型は`T or NoneType`のpatch typeで、主に失敗するかもしれない値を表現します。 + +```erg +IntOrStr = Int or Str +assert dict.get("some key") in (Int or NoneType) + +# 暗黙に`T != NoneType`となる +Option T = T or NoneType +``` + +## Intersection + +Intersection型は型同士を`and`演算で結合して得られます。 + +```erg +Num = Add and Sub and Mul and Eq +``` + +先述したように通常のクラス同士では`and`演算で結合できません。インスタンスは唯一つのクラスに属するからです。 + +## Diff + +Diff型は`not`演算で得られます。 +英文に近い表記としては`and not`とした方が良いですが、`and`, `or`と並べて収まりが良いので`not`だけで使うのが推奨されます。 + +```erg +CompleteNum = Add and Sub and Mul and Div and Eq and Ord +Num = CompleteNum not Div not Ord + +True = Bool not {False} +OneTwoThree = {1, 2, 3, 4, 5, 6} - {4, 5, 6, 7, 8, 9, 10} +``` + +## Complement + +Complementは`not`演算で得られますが、これは単項演算です。`not T`型は`{=} not T`の短縮記法です。 +`not T`型によるIntersectionはDiffと同等で、`not T`型によるDiffはIntersectionと同等です。 +しかしこのような書き方は推奨されません。 + +```erg +# the simplest definition of the non-zero number type +NonZero = Not {0} +# deprecated styles +{True} == Bool and not {False} # 1 == 2 + - 1 +Bool == {True} not not {False} # 2 == 1 - -1 +``` + +## 真の代数演算型 + +代数演算型には、簡約可能な見かけ上の代数演算型とそれ以上簡約できない「真の代数演算型」があります。 +そうではない「見かけの代数型」には、Enum型やInterval型、レコード型の`or`や`and`があります。 +これらは簡約が可能なので真の代数演算型ではなく、型指定に使うとWarningが出ます。Warningを消すためには簡約化するか型定義を行うかする必要があります。 + +```erg +assert {1, 2, 3} or {2, 3} == {1, 2, 3} +assert {1, 2, 3} and {2, 3} == {2, 3} +assert -2..-1 or 1..2 == {-2, -1, 1, 2} + +i: {1, 2} or {3, 4} = 1 # TypeWarning: {1, 2} or {3, 4} can be simplified to {1, 2, 3, 4} +p: {x = Int, ...} and {y = Int; ...} = {x = 1; y = 2; z = 3} +# TypeWaring: {x = Int, ...} and {y = Int; ...} can be simplified to {x = Int; y = Int; ...} + +Point1D = {x = Int; ...} +Point2D = Point1D and {y = Int; ...} # == {x = Int; y = Int; ...} +q: Point2D = {x = 1; y = 2; z = 3} +``` + +真の代数演算型には、`Or`型、`And`型があります。クラス同士の`or`などは`Or`型です。 + +```erg +assert Int or Str == Or(Int, Str) +assert Int and Marker == And(Int, Marker) +``` + +Diff, Complement型は必ず簡約できるので真の代数演算型ではありません。 diff --git a/doc/JA/syntax/type/14_dependent.md b/doc/JA/syntax/type/14_dependent.md new file mode 100644 index 00000000..f844cad8 --- /dev/null +++ b/doc/JA/syntax/type/14_dependent.md @@ -0,0 +1,74 @@ +# 依存型 + +依存型はErgの最大の特徴とも言っても良い機能です。 +依存型とは、値を引数に取る型です。通常の多相型は型のみを引数に取れますが、その制限を緩めたのが依存型といえます。 + +依存型は、`[T; N]`(`Array(T, N)`)などがそれに相当します。 +この型は、中身の型`T`だけでなく、中身の個数`N`にも依存して決まる型です。`N`には`Nat`型のオブジェクトが入ります。 + +```erg +a1 = [1, 2, 3] +assert a1 in [Nat; 3] +a2 = [4, 5, 6, 7] +assert a1 in [Nat; 4] +assert a1 + a2 in [Nat; 7] +``` + +関数引数で渡した型オブジェクトが戻り値型に関連する場合は、以下のように記述します。 + +```erg +narray: |N: Nat| {N} -> [{N}; N] +narray(N: Nat): [N; N] = [N; N] +assert narray(3) == [3, 3, 3] +``` + +依存型を定義する際は、型引数が全て定数でなくてはなりません。 + +依存型そのものは既存の言語にも存在するものですが、Ergでは依存型にプロシージャルメソッドを定義できるという特徴があります。 + +```erg +x = 1 +f x = + print! f::x, module::x + +# Phantom型は型引数と同じ値になるPhantomという属性を持っている +T X: Int = Class Impl: Phantom X +T(X). + x self = self::Phantom + +T(1).x() # 1 +``` + +可変依存型の型引数はメソッドの適用によって遷移させることができます。 +遷移指定は`~>`で行います。 + +```erg +# `Id`は不変型なので遷移させることはできないことに注意 +VM!(State: {"stopped", "running"}! |= _, Id: Nat |= _) = Class(..., Impl: Phantom! State) +VM!(). + # 変わらない変数は`_`を渡せば省略可能, デフォルト引数にしておけば書く必要すらない + start! ref! self("stopped" ~> "running") = + self.initialize_something!() + self::set_phantom!("running") + +# 型引数ごとに切り出すこともできる(定義されたモジュール内でのみ) +VM!.new() = VM!(!"stopped", 1).new() +VM!("running" ~> "running").stop! ref! self = + self.close_something!() + self::set_phantom!("stopped") + +vm = VM!.new() +vm.start!() +vm.stop!() +vm.stop!() # TypeError: VM!(!"stopped", 1) doesn't have .stop!() +# hint: VM!(!"running", 1) has .stop!() +``` + +既存の型を組み込んだり継承して依存型を作ることもできます。 + +```erg +MyArray(T, N) = Inherit [T; N] + +# .arrayと連動してself: Self(T, N)の型が変わる +MyStruct!(T, N: Nat!) = Class {.array: [T; !N]} +``` diff --git a/doc/JA/syntax/type/15_quantified.md b/doc/JA/syntax/type/15_quantified.md new file mode 100644 index 00000000..86bb9c6f --- /dev/null +++ b/doc/JA/syntax/type/15_quantified.md @@ -0,0 +1,281 @@ +# 型変数(Type Variable)、量化型 + +型変数はサブルーチン引数の型指定などに使用する変数で、その型が任意である(単相化しない)ことを示します。 +まず、型変数を導入するモチベーションとして、入力をそのまま返す`id`関数について考えましょう。 + +```erg +id x: Int = x +``` + +入力をそのまま返す`id`関数が`Int`型に対して定義されていますが、この関数は明らかに任意の型に対して定義できます。 +最大のクラスを表す`Object`を使用してみましょう。 + +```erg +id x: Object = x + +i = id 1 +s = id "foo" +b = id True +``` + +確かに任意の型を受け付けるようになりましたが、1つ問題があります。戻り値の型が`Object`に拡大されてしまうのです。 +入力が`Int`型なら`Int`型、`Str`型なら`Str`型が返るようになっていてほしいですね。 + +```erg +print! id 1 # +id(1) + 1 # TypeError: cannot add `Object` and `Int` +``` + +入力の型と戻り値の型が同じであるようにするには、 __型変数__ を使います。 +型変数は`||`(型変数リスト)中で宣言します。 + +```erg +id|T: Type| x: T = x +assert id(1) == 1 +assert id("foo") == "foo" +assert id(True) == True +``` + +これを関数の __全称量化(全称化)__ と呼びます。細かい違いはありますが、他言語でジェネリクスと呼ばれる機能に相当します。そして全称量化された関数を __多相関数__ と呼びます。 +多相関数の定義は、全ての型に対して同じ形の関数を定義するようなものです(Ergはオーバーロードを禁止しているので、下のコードは実際には書けません)。 + +```erg +id|T: Type| x: T = x +# pseudo code +# == +id x: Int = x +id x: Str = x +id x: Bool = x +id x: Ratio = x +id x: NoneType = x +... +``` + +また、型変数`T`は型指定で使用されているため、`Type`型と推論できます。なので、`|T: Type|`は単に`|T|`に省略できます。 +また、`|T, N| foo: [T; N]`など型オブジェクト以外の場合でも推論できる(`T: Type, N: Nat`)ならば省略できます。 + +また、任意の型では大きすぎる場合、制約を与えることも出来ます。 +制約を与えることにはメリットもあり、例えばサブタイプ指定をすると、特定のメソッドを使えるようになります。 + +```erg +# T <: Add +# => TはAddのサブクラス +# => 加算ができる +add|T <: Add| l: T, r: T = l + r +``` + +この例では、`T`は`Add`型のサブクラスであると要求され、実際に代入される`l`と`r`の型は同じでなくてはなりません。 +この場合、`T`を満たすのは`Int`や`Ratio`などです。`Int`と`Str`の加算などは定義されていないので弾かれるわけです。 + +このような型付けもできます。 + +```erg +f| + Y, Z: Type + X <: Add Y, O1 + O1 <: Add Z, O2 + O2 <: Add X, _ +| x: X, y: Y, z: Z = + x + y + z + x +``` + +注釈リストが長くなる場合は、事前宣言するとよいでしょう。 + +```erg +f: |Y, Z: Type, X <: Add(Y, O1), O1 <: Add(Z, O2), O2 <: Add(X, O3)| (X, Y, Z) -> O3 +f|X, Y, Z| x: X, y: Y, z: Z = + x + y + z + x +``` + +ジェネリクスを持つ多くの言語と違い、宣言した型変数はすべて、仮引数リスト内(`x: X, y: Y, z: Z`の部分)か他の型変数の引数内かで使用されていなければなりません。 +これは、型変数はすべて実引数から推論可能であるというErgの言語設計からの要求です。 +なので、戻り値の型など推論ができない情報は、実引数から渡します。Ergは型を実引数から渡すことができるのです。 + +```erg +Iterator T = Trait { + # 戻り値の型を引数から渡している + # .collect: |K: Type -> Type| Self(T).({K}) -> K(T) + .collect(self(T), K: Type -> Type): K(T) = ... + ... +} + +it = [1, 2, 3].iter().map i -> i + 1 +it.collect(Array) # [2, 3, 4] +``` + +型変数が宣言できるのは`||`の間のみである。ただし、宣言した後はスコープを抜けるまで任意の場所で使用できる。 + +```erg +f|X|(x: X): () = + y: X = x.clone() + log X.__name__ + log X + +f 1 +# Int +# +``` + +以下のようにして、使用時に明示的に単相化もできます。 + +```erg +f: Int -> Int = id|Int| +``` + +その場合、実引数の型よりも指定された型の方が優先されます(合致していないと実引数の型が間違っているという型エラーになる)。 +すなわち、実際に渡されたオブジェクトが指定された型に変換可能ならば変換され、そうでなければコンパイルエラーとなります。 + +```erg +assert id(1) == 1 +assert id|Int|(1) in Int +assert id|Ratio|(1) in Ratio +# キーワード引数も使える +assert id|T: Int|(1) == 1 +id|Int|("str") # TypeError: id|Int| is type `Int -> Int` but got Str +``` + +この文法が内包表記とバッティングする際は`()`で囲む必要があります。 + +```erg +# {id|Int| x | x <- 1..10}だと{id | ...}だと解釈される +{(id|Int| x) | x <- 1..10} +``` + +既に存在する型と同名の型変数は宣言出来ません。これは、型変数がすべて定数であるためです。 + +```erg +I: Type +# ↓ invalid type variable, already exists +f|I: Type| ... = ... +``` + +## メソッド定義における型引数 + +左辺における型引数はデフォルトで束縛型変数として扱われます。 + +```erg +K(T: Type, N: Nat) = ... +K(T, N). + foo(x) = ... +``` + +別の型変数名を使用すると警告が出ます。 + +```erg +K(T: Type, N: Nat) = ... +K(U, M). # Warning: K's type variable names are 'T' and 'N' + foo(x) = ... +``` + +定数は定義以降すべての名前空間で同一なので、当然型変数名にも使用できません。 + +```erg +N = 1 +K(N: Nat) = ... # NameError: N is already defined + +L(M: Nat) = ... +# M == N == 1のときのみ定義される +L(N). + foo(self, x) = ... +# 任意のM: Natに対して定義される +L(M). + .bar(self, x) = ... +``` + +型引数ごとに多重定義することはできませんが、型引数を代入していない依存型(非原始カインド)と代入した依存型(原始カインド)は関係がないので同名のメソッドを定義できます。 + +```erg +K(I: Int) = ... +K. + # Kは真の型(原子カインド)ではないので、メソッドを定義できない + # これはメソッドではない(スタティックメソッドに近い) + foo(x) = ... +K(0). + foo(self, x): Nat = ... +``` + +## 全称型 + +前章で定義した`id`関数は任意の型になれる関数です。では、「`id`関数自体の型」は何なのでしょうか? + +```erg +print! classof(id) # |T: Type| T -> T +``` + +`|T: Type| T -> T`という型が得られました。これは __閉じた全称量化型/全称型(closed universal quantified type/universal type)__ と呼ばれるもので、MLでは`['a. ...]`、Haskellでは`forall t. ...`という形式で提供される型に相当します。なぜ「閉じた」という形容詞がつくのかは後述します。 + +閉じた全称型には制約があり、全称化できる、すなわち左の節に置けるのはサブルーチン型のみです。しかしこれで十分です。Ergではサブルーチンがもっとも基本的な制御構造ですから、「任意のXを扱いたい」というとき、すなわち「任意のXを扱えるサブルーチンがほしい」という意味になります。なので、全称型は多相関数型と同じ意味になります。以降は基本的に、この種の型を多相関数型と呼ぶことにします。 + +無名関数と同じく、多相関数型には型変数名の任意性がありますが、これらはすべて同値となります。 + +```erg +assert (|T: Type| T -> T) == (|U: Type| U -> U) +``` + +ラムダ計算でいうところのα同値であるときに等号が成立します。型上の演算にはいくつかの制約があるので、同値性の判定は(停止性を考えなければ)常に可能です。 + +## 多相関数型の部分型付け + +多相関数型は、任意の関数型になれます。これは、任意の関数型と部分型関係があるということです。この関係について詳しくみていきましょう。 + +`OpenFn T: Type = T -> T`のような「型変数が左辺で定義され、右辺で使用されている型」を __開いた全称型(open universal type)__ と呼びます。 +対して`ClosedFn = |T: Type| T -> T`など「型変数が右辺で定義・使用されている型」を __閉じた全称型(closed universal type)__ と呼びます。 + +開いた全称型は、同形な全ての「真の型」のスーパータイプになります。対して、閉じた全称型は、同形な全ての「真の型」のサブタイプになります。 + +```erg +(|T: Type| T -> T) < (Int -> Int) < (T -> T) +``` + +閉じている方が小さい/開いている方が大きい、と覚えるとよいでしょう。 +しかし、どうしてそうなるのでしょうか。理解を深めるため、それぞれのインスタンスを考えてみます。 + +```erg +# id: |T: Type| T -> T +id|T|(x: T): T = x + +# iid: Int -> Int +iid(x: Int): Int = x + +# 任意の関数をそのまま返す +id_arbitrary_fn|T|(f1: T -> T): (T -> T) = f +# id_arbitrary_fn(id) == id +# id_arbitrary_fn(iid) == iid + +# 多相関数をそのまま返す +id_poly_fn(f2: (|T| T -> T)): (|T| T -> T) = f +# id_poly_fn(id) == id +id_poly_fn(iid) # TypeError + +# Int型関数をそのまま返す +id_int_fn(f3: Int -> Int): (Int -> Int) = f +# id_int_fn(id) == id|Int| +# id_int_fn(iid) == iid +``` + +`|T: Type| T -> T`型である`id`は`Int -> Int`型のパラメータ`f3`に代入できているため、`(|T| T -> T) < (Int -> Int)`と考えることができそうです。 +その逆、`Int -> Int`型である`iid`は`(|T| T -> T)`型のパラメータ`f2`に代入できていませんが、`T -> T`型のパラメータ`f1`に代入できているため、`(Int -> Int) < (T -> T)`です。 +よって、確かに`(|T| T -> T) < (Int -> Int) < (T -> T)`となっています。 + +## 全称型と依存型 + +依存型と全称型(多相関数型)はどんな関係があり、何が違うのでしょうか。 +依存型は引数を取る型であり、全称型は(全称化するサブルーチンの)引数に任意性を与える型だと言えます。 + +重要なのは、閉じた全称型自体には型引数が存在しないというところです。例えば、多相関数型`|T| T -> T`は多相関数 __だけ__ を取る型であり、その定義は閉じています。その型引数`T`を使ったメソッド等の定義はできません。 + +Ergでは型自体も値であるため、引数を取る型、例えば関数型なども須らく依存型になります。つまり、多相関数型は全称型でかつ依存型でもあるといえます。 + +```erg +PolyFn = Patch(|T| T -> T) +PolyFn. + type self = T # NameError: cannot find 'T' +DepFn T = Patch(T -> T) +DepFn. + type self = + log "by DepFn" + T + +assert (Int -> Int).type() == Int # by DepFn +assert DepFn(Int).type() == Int # by DepFn +``` diff --git a/doc/JA/syntax/type/16_subtyping.md b/doc/JA/syntax/type/16_subtyping.md new file mode 100644 index 00000000..61c8b8d1 --- /dev/null +++ b/doc/JA/syntax/type/16_subtyping.md @@ -0,0 +1,76 @@ +# 部分型付け + +Ergでは、クラス同士の包含関係は比較演算子`<`, `>`で判定可能です。 + +```erg +Nat < Int +Int < Object +1.._ < Nat +{1, 2} > {1} +{=} > {x = Int} +{I: Int | I >= 1} < {I: Int | I >= 0} +``` + +`<:`演算子とは別の意味を持つことに注意してください。左辺のクラスが右辺の型のサブタイプであると宣言するもので、コンパイル時にのみ意味を持ちます。 + +```erg +C <: T # T: StructuralType +f|D <: E| ... + +assert F < G +``` + +また、多相型の部分型指定について、例えば`Self(R, O) <: Add(R, O)`などの場合、`Self <: Add`と指定することもできます。 + +## 構造型、クラスの型関係 + +構造型は構造的型付けを実現するための型であり、構造が同じならば同じオブジェクトとみなされます。 + +```erg +T = Structural {i = Int} +U = Structural {i = Int} + +assert T == U +t: T = {i = 1} +assert t in T +assert t in U +``` + +対してクラスは記名的型付けを実現するための型であり、型およびインスタンスを構造的に比較することができません。 + +```erg +C = Class {i = Int} +D = Class {i = Int} + +assert C == D # TypeError: cannot compare classes +c = C.new {i = 1} +assert c in C +assert not c in D +``` + +## サブルーチンの部分型付け + +サブルーチンの引数、戻り値は、単一のクラスのみを取る。 +すなわち、構造型やトレイトを関数の型として直接指定することはできない。 +部分型指定を使って「その型のサブタイプである単一のクラス」として指定する必要がある。 + +```erg +# OK +f1 x, y: Int = x + y +# NG +f2 x, y: Add = x + y +# OK +# Aは何らかの具体的なクラス +f3 x, y: A = x + y +``` + +サブルーチンの型推論もこのルールに従っている。サブルーチン中の変数で型が明示されていないものがあったとき、コンパイラはまずその変数がいずれかのクラスのインスタンスでないかチェックし、そうでない場合はスコープ中のトレイトの中から適合するものを探す。それでも見つからない場合、コンパイルエラーとなる。このエラーは構造型を使用することで解消できるが、無名型を推論するのはプログラマの意図しない結果である可能性があるため、プログラマが明示的に`Structural`で指定する設計となっている。 + +クラスのアップキャスト + +```erg +i: Int +i as (Int or Str) +i as (1..10) +i as {I: Int | I >= 0} +``` diff --git a/doc/JA/syntax/type/17_type_casting.md b/doc/JA/syntax/type/17_type_casting.md new file mode 100644 index 00000000..44cd1363 --- /dev/null +++ b/doc/JA/syntax/type/17_type_casting.md @@ -0,0 +1,72 @@ +# キャスト + +## アップキャスト + +Pythonはダックタイピングを採用する言語のため、キャストという概念はありません。アップキャストはする必要がなく、ダウンキャストも基本的にはありません。 +しかしErgは静的に型付けされるため、キャストを行わなければいけない場合があります。 +簡単な例では、`1 + 2.0`が挙げられます。Ergの言語仕様上は`+`(Int, Ratio)、すなわちInt(<: Add(Ratio, Ratio))の演算は定義されていません。というのも、`Int <: Ratio`であるため、1はRatioのインスタンスである1.0にアップキャストされるからです。 + +~~Erg拡張バイトコードはBINARY_ADDに型情報を加えますが、この際の型情報はRatio-Ratioとなります。この場合はBINARY_ADD命令がIntのキャストを行うため、キャストを指定する特別な命令は挿入されません。なので、例えば子クラスでメソッドをオーバーライドしても、親を型に指定すれば型強制(type coercion)が行われ、親のメソッドで実行されます(コンパイル時に親のメソッドを参照するように名前修飾が行われます)。コンパイラが行うのは型強制の妥当性検証と名前修飾のみです。ランタイムがオブジェクトをキャストすることはありません(現在のところ。実行最適化のためにキャスト命令が実装される可能性はあります)。~~ + +```erg +@Inheritable +Parent = Class() +Parent. + greet!() = print! "Hello from Parent" + +Child = Inherit Parent +Child. + # オーバーライドする際にはOverrideデコレータが必要 + @Override + greet!() = print! "Hello from Child" + +greet! p: Parent = p.greet!() + +parent = Parent.new() +child = Child.new() + +greet! parent # "Hello from Parent" +greet! child # "Hello from Parent" +``` + +この挙動はPythonとの非互換性を生むことはありません。そもそもPythonでは変数に型が指定されないので、いわば全ての変数が型変数で型付けされている状態となります。型変数は適合する最小の型を選ぶので、Ergで型を指定しなければPythonと同じ挙動が達成されます。 + +```erg +@Inheritable +Parent = Class() +Parent. + greet!() = print! "Hello from Parent" + +Child = Inherit Parent +Child. + greet!() = print! "Hello from Child" + +greet! some = some.greet!() + +parent = Parent.new() +child = Child.new() + +greet! parent # "Hello from Parent" +greet! child # "Hello from Child" +``` + +継承関係にある型同士では`.from`, `.into`が自動実装されるので、それを使うこともできます。 + +```erg +assert 1 == 1.0 +assert Ratio.from(1) == 1.0 +assert 1.into() == 1.0 +``` + +## ダウンキャスト + +ダウンキャストは一般に安全ではなく、変換方法も自明ではないため、代わりに`TryFrom.try_from`の実装で実現します。 + +```erg +IntTryFromFloat = Patch Int +IntTryFromFloat. + try_from r: Float = + if r.ceil() == r: + then: r.ceil() + else: Error "conversion failed" +``` diff --git a/doc/JA/syntax/type/18_mut.md b/doc/JA/syntax/type/18_mut.md new file mode 100644 index 00000000..098f822b --- /dev/null +++ b/doc/JA/syntax/type/18_mut.md @@ -0,0 +1,164 @@ +# 可変型(Mutable Type) + +> __Warning__: この項の情報は古く、一部に間違いを含みます。 + +Ergではデフォルトですべての型が不変型、すなわち内部状態を更新できないようになっています。 +しかし可変な型ももちろん定義することができます。可変型は`!`を付けて宣言します。 + +```erg +Person! = Class({name = Str; age = Nat!}) +Person!. + greet! ref! self = print! "Hello, my name is {self::name}. I am {self::age}." + inc_age! ref! self = self::name.update! old -> old + 1 +``` + +正確には、可変型・または可変型を含む複合型を基底型とする型は型名の最後に`!`を付けなくてはなりません。`!`を付けない型も同一の名前空間に存在してよく、別の型として扱われます。 +上の例では、`.age`属性は可変で、`.name`属性は不変となっています。一つでも可変な属性がある場合、全体として可変型になります。 + +可変型はインスタンスを書き換えるプロシージャルメソッドを定義できますが、プロシージャルメソッドを持つからと言って可変型になるとは限りません。例えば配列型`[T; N]`には要素をランダムに選ぶ`sample!`メソッドが実装されていますが、これはもちろん配列に破壊的変更を加えたりはしません。 + +可変型オブジェクトの破壊的操作は、主に`.update!`メソッドを介して行います。`.update!`メソッドは高階プロシージャで、`self`に関数`f`を適用して更新します。 + +```erg +i = !1 +i.update! old -> old + 1 +assert i == 2 +``` + +`.set!`メソッドは単に古い内容を捨てて新しい値に差し替えます。`.set! x = .update! _ -> x`です。 + +```erg +i = !1 +i.set! 2 +assert i == 2 +``` + +`.freeze_map`メソッドは値を不変化して操作を行います。 + +```erg +a = [1, 2, 3].into [Nat; !3] +x = a.freeze_map a: [Nat; 3] -> a.iter().map(i -> i + 1).filter(i -> i % 2 == 0).collect(Array) +``` + +多相不変型において型の型引数`T`は暗黙に不変型であると仮定されます。 + +```erg +# ImmutType < Type +K T: ImmutType = Class ... +K! T: Type = Class ... +``` + +標準ライブラリでは、可変型`(...)!`型は不変型`(...)`型を基底としている場合が多いです。しかし`T!`型と`T`型に言語上特別な関連はなく、そのように構成しなくても構いません[1](#1)。 + +注意として、オブジェクトの可変性にはいくつかの種類があります。 +以下では組み込みのコレクション型について、その不変/可変型の意味を確認します。 + +```erg +# 配列型 +## 不変型(immutable types) +[T; N] # 可変操作は実行できない +## 可変型(mutable types) +[T!; N] # 中身を1つずつ変更できる +[T; !N] # 可変長、中身は変更不能だが要素の追加・削除で実質変更可能 +[!T; N] # 中身は不変オブジェクトだが、型を変えたものに差し替え可能(型を変えないという操作で実質差し替え可能) +[!T; !N] # 型、長さを変更可能 +[T!; !N] # 中身、長さを変更可能 +[!T!; N] # 中身、型を変更可能 +[!T!; !N] # ありとあらゆる可変操作を実行できる +``` + +もちろんこれら全てを暗記し、使いこなす必要はありません。 +可変配列型については、可変にしたい部分に`!`を付けるだけであり、実用上は`[T; N]`, `[T!; N]`, `[T; !N]`, `[T!; !N]`の4つでほとんどのケースをカバーできます。 + +これらの配列型は糖衣構文であり、実際の型は以下の通りです。 + +```erg +# 実際は5種類の型 +[T; N] = ArrayWithLength(T, N) +[T!; N] = ArrayWithLength!(T!, N) +[T; !N] = ArrayWithMutLength!(T, !N) +[!T; N] = ArrayWithMutType!(!T, N) +[!T; !N] = ArrayWithMutTypeAndLength!(!T, !N) +[T!; !N] = ArrayWithMutLength!(T!, !N) +[!T!; N] = ArrayWithMutType!(!T!, N) +[!T!; !N] = ArrayWithMutTypeAndLength!(!T!, !N) +``` + +なお、型を変更可能とはこのような意味です。 + +```erg +a = [1, 2, 3].into [!Nat; 3] +a.map!(_ -> "a") +a: [!Str; 3] +``` + +他のコレクション型についても同様です。 + +```erg +# タプル型 +## 不変型(immutable types) +(T, U) # 要素数不変、中身を変更できない +## 可変型(mutable types) +(T!, U) # 要素数不変、最初の要素は変更できる +(T, U)! # 要素数不変、中身を差し替えられる +... +``` + +```erg +# セット型 +## 不変型(immutable types) +{T; N} # 不変要素数、中身を変更できない +## 可変型(mutable types) +{T!; N} # 不変要素数、中身を(1つずつ)変更できる +{T; N}! # 可変要素数、中身は変更不能だが、要素の追加削除で実質可能、中の型を変更可能 +{T!; N}! # 可変要素数、中身も変更できる +... +``` + +```erg +# 辞書型 +## 不変型(immutable types) +{K: V} # 不変長、中身を変更できない +## 可変型(mutable types) +{K: V!} # 不変長、値を(1つずつ)変更できる +{K: V}! # 可変長、中身を変更できないが、要素の追加削除で実質可能、中の型も変更可能 +... +``` + +```erg +# レコード型 +## 不変型(immutable types) +{x = Int; y = Str} # 中身を変更できない +## 可変型(mutable types) +{x = Int!; y = Str} # xの値を変更できる +{x = Int; y = Str}! # {x = Int; y = Str}のインスタンスならば何でも差し替えられる +... +``` + +`T = (...)`のとき単に`T! = (...)!`となる型`(...)`を単純構造型と呼びます。単純構造型は(意味論上)内部構造を持たない型ともいえます。 +配列、タプル、セット、辞書、レコード型は全て単純構造型ではありませんが、Int型や篩型は単純構造型です。 + +```erg +# 篩型 +## 列挙型 +{1, 2, 3} # 1, 2, 3のうちどれか、変更できない +{1, 2, 3}! # 1, 2, 3のうちどれか、変更できる +## 区間型 +1..12 # 1~12のうちどれか、変更できない +1..12! # 1~12のうちどれか、変更できる +## 篩型(一般形) +{I: Int | I % 2 == 0} # 偶数型、変更できない +{I: Int! | I % 2 == 0} # 偶数型、変更できる +{I: Int | I % 2 == 0}! # 上と全く同じ型、だが上の記法が推奨される +``` + +以上の説明から、可変型とは自身が可変であるものだけでなく、内部に持つ型が可変であるものも含まれるということになります。 +`{x: Int!}`や`[Int!; 3]`などの型は、内部のオブジェクトが可変であり、インスタンス自身が可変なわけではない内部可変型です。 + +内部構造を持ち型構築子自体に`!`がついている型`K!(T, U)`の場合、`*self`はオブジェクト全体を変えうるものです。また、局所的な変更も可能です。 +ただし、変更権限はなるべく局所的に抑えることが望ましいので、変更されうるのが`T`のみの場合`K(T!, U)`とした方が良いでしょう。 +また内部構造を持たない型`T!`の場合、このインスタンスは単に入れ替え可能な`T`のボックスです。メソッドによって型を変更することはできません。 + +--- + +1 `T!`型と`T`型に言語上の特別な関係がないのは意図的な設計です。関連があったとすると、例えば名前空間に`T`/`T!`型が存在するときに別のモジュールから`T!`/`T`型を導入できなくなるなどの不都合が生じます。また、不変型に対し可変型は一意に定まりません。`T = (U, V)`という定義があった際、`(U!, V)`と`(U, V!)`という可変サブタイプが`T!`としてあり得えます。 [↩](#f1) diff --git a/doc/JA/syntax/type/advanced.md b/doc/JA/syntax/type/advanced.md new file mode 100644 index 00000000..57fa0935 --- /dev/null +++ b/doc/JA/syntax/type/advanced.md @@ -0,0 +1 @@ +以降は更に高度な型システムを解説します。入門者の方はすべての項を読まなくても問題ありません。 diff --git a/doc/JA/syntax/type/advanced/GADTs.md b/doc/JA/syntax/type/advanced/GADTs.md new file mode 100644 index 00000000..c3004767 --- /dev/null +++ b/doc/JA/syntax/type/advanced/GADTs.md @@ -0,0 +1,66 @@ +# 一般化代数的データ型(Generalized Algebraic Data Types, GADTs) + +ErgはOr型をクラス化することで一般化代数的データ型(GADTs)を作成出来ます。 + +```erg +Nil T = Class(Impl: Phantom T) +Cons T = Class {head = T; rest = List T}, Impl=Unpack +List T: Type = Class(Nil T or Cons T) +List. + nil() | T = Self(T).new Nil(T).new() + cons head, rest | T = Self(T).new Cons(T).new(head, rest) + head self = match self: + {head; ...}: Cons _ -> head + _: Nil -> panic "empty list" +{nil; cons; ...} = List + +print! cons(1, cons(2, nil())).head() # 1 +print! nil.head() # RuntimeError: "empty list" +``` + +`List(T).nil() = ...`ではなく`List.nil|T|() = ...`としているのは、使用時に型指定が不要になるからです。 + +```erg +i = List.nil() +_: List Int = cons 1, i +``` + +ここで定義した`List T`はGADTsですが、素朴な実装であり、GADTsの真価を発揮していません。 +例えば、上の`.head`メソッドはもし中身が空なら実行時エラーを出しますが、この検査はコンパイル時に行うことができます。 + +```erg +List: (Type, {"Empty", "Nonempty"}) -> Type +List T, "Empty" = Class(Impl: Phantom T) +List T, "Nonempty" = Class {head = T; rest = List(T, _)}, Impl=Unpack +List. + nil() | T = Self(T, "Empty").new Nil(T).new() + cons head, rest | T = Self(T, "Nonempty").new {head; rest} +List(T, "Nonempty"). + head {head; ...} = head +{nil; cons; ...} = List + +print! cons(1, cons(2, nil())).head() # 1 +print! nil().head() # TypeError +``` + +巷でよく説明されるGADTsの例は、以上のように中身が空か否か型で判定できるリストです。 +Ergではさらに精密化して、長さを持つリストを定義することができます。 + +```erg +List: (Type, Nat) -> Type +List T, 0 = Class(Impl: Phantom T) +List T, N = Class {head = T; rest = List(T, N-1)}, Impl=Unpack +List. + nil() | T = Self(T, 0).new Nil(T).new() + cons head, rest | T, N = Self(T, N).new {head; rest} +List(_, N | N >= 1). + head {head; ...} = head +List(_, N | N >= 2). + pair {head = first; rest = {head = second; ...}} = [first, second] +{nil; cons; ...} = List + +print! cons(1, cons(2, nil)).pair() # [1, 2] +print! cons(1, nil).pair() # TypeError +print! cons(1, nil).head() # 1 +print! nil.head() # TypeError +``` diff --git a/doc/JA/syntax/type/advanced/_rank2type.md b/doc/JA/syntax/type/advanced/_rank2type.md new file mode 100644 index 00000000..79fdd3ae --- /dev/null +++ b/doc/JA/syntax/type/advanced/_rank2type.md @@ -0,0 +1,142 @@ +# rank-2多相 + +> __Warning__: このドキュメントは情報が古く、全般に間違いを含みます。 + +Ergでは`id|T|(x: T): T = x`などのように色々な型を受け取れる関数、すなわち多相関数を定義できる。 +では、多相関数を受け取れる関数は定義できるだろうか? +例えば、このような関数である(この定義は誤りを含むことに注意してほしい)。 + +```erg +# tuple_map(i -> i * 2, (1, "a")) == (2, "aa")になってほしい +tuple_map|T|(f: T -> T, tup: (Int, Str)): (Int, Str) = (f(tup.0), f(tup.1)) +``` + +`1`と`"a"`の型が違うので、無名関数は一度単相化して終わりではないことに注意してほしい。2回単相化する必要がある。 +今まで説明してきた型の範疇では、このような関数の定義はできない。型変数にスコープの概念がないからである。 +ここで一旦型を離れて、値レベルでのスコープの概念を確認する。 + +```erg +arr = [1, 2, 3] +arr.map i -> i + 1 +``` + +上のコードの`arr`と`i`は違うスコープの変数である。故に、それぞれの生存期間は異なる(`i`の方が短い)。 + +今までの型は、全ての型変数で生存期間が同一なのである。すなわち、`T`, `X`, `Y`は同時に決定されていて、以降は不変でなければならない。 +逆に言えば、`T`を「内側のスコープ」にある型変数とみなすことができるならば`tuple_map`関数を構成できる。そのために用意されたのが、 __ランク2型__ である。 + +```erg +# tuple_map: ((|T: Type| T -> T), (Int, Str)) -> (Int, Str) +tuple_map f: (|T: Type| T -> T), tup: (Int, Str) = (f(tup.0), f(tup.1)) +assert tuple_map(i -> i * 2, (1, "a")) == (2, "aa") +``` + +`{(型) | (型変数のリスト)}`という形式の型を全称型といった(詳しくは[全称型](./../quantified.md)を参照)。 +いままで見てきた`id`関数は、典型的な全称関数=多相関数である。 + +```erg +id x = x +id: |T: Type| T -> T +``` + +全称型は関数型構築子`->`との間に特殊な結合の規則を持っており、結合の仕方によって全く型の意味が異なってしまう。 + +これについて、単純な1引数関数を使って考える。 + +```erg +f1: (T -> T) -> Int | T # 任意の関数を受け取り、Intを返す関数 +f2: (|T: Type| T -> T) -> Int # 多相関数を受け取り、Intを返す関数 +f3: Int -> (|T: Type| T -> T) # Intを受け取り、閉じた全称型関数を返す関数 +f4: |T: Type|(Int -> (T -> T)) # 上と同じ意味(こちらが推奨) +``` + +`f3`と`f4`が同じなのに対して、`f1`と`f2`は異なるというのは奇妙に思える。実際にそのような型の関数を構成してみる。 + +```erg +# id: |T: Type| T -> T +id x = x +# same type as `f1` +take_univq_f_and_return_i(_: (|T: Type| T -> T), i: Int): Int = i +# same type as `f2` +take_arbit_f_and_return_i|T: Type|(_: T -> T, i: Int): Int = i +# same type as `f3` +take_i_and_return_univq_f(_: Int): (|T: Type| T -> T) = id +# same type as `f4` +take_i_and_return_arbit_f|T: Type|(_: Int): (T -> T) = id +``` + +適用してみると、その違いがわかってくる。 + +```erg +_ = take_univq_f_and_return_i(x -> x, 1) # OK +_ = take_univq_f_and_return_i(x: Int -> x, 1) # NG +_ = take_univq_f_and_return_i(x: Str -> x, 1) # NG +_ = take_arbit_f_and_return_i(x -> x, 1) # OK +_ = take_arbit_f_and_return_i(x: Int -> x, 1) # OK +_ = take_arbit_f_anf_return_i(x: Str -> x, 1) # OK + +f: |T| T -> T = take_i_and_return_univq_f(1) +g: |T| T -> T = take_i_and_return_arbit_f(1) +assert f == g +f2: Int -> Int = take_i_and_return_univq_f|Int|(1) +g2: Int -> Int = take_i_and_return_arbit_f|Int|(1) +assert f2 == g2 +``` + +開いた多相関数型を特に __任意関数型(arbitrary function type)__ と呼ぶ。任意関数型には、`Int -> Int`, `Str -> Str`, `Bool -> Bool`, `|T: Type| T -> T`, ...など、無限個の可能性がある。 +対して閉じた(引数と同じ型のオブジェクトを返す)多相関数型は`|T: Type| T -> T`一種類のみである。このような型を特に __多相関数型(polymorphic function type)__ と呼ぶ。 +換言すると、`f1`には`x: Int -> x+1`や`x: Bool -> not x`, `x -> x`などを渡すことができる=`f1`は多相関数であるが、`f2`に渡せるのは`x -> x`などのみ=`f2`は多相関数 __ではない__ 。 +しかし、`f2`のような関数の型は明らかに通常の型と異なっており、これらをうまく扱える新しい概念が必要となる。それが型の「ランク」である。 + +ランクの定義だが、まず量化されていない型、すなわち`Int`, `Str`, `Bool`, `T`, `Int -> Int`, `Option Int`などは「ランク0」とされる。 + +```erg +# KはOptionなどの多項カインド +R0 = (Int or Str or Bool or ...) or (R0 -> R0) or K(R0) +``` + +次に`|T| T -> T`など一階の全称量化が行われている型、またはそれらを戻り値型に含む型を「ランク1」とする。 +さらに二階の全称量化が行われている型(`(|T| T -> T) -> Int`などランク1型を引数に持つ型)、またはそれらを戻り値型に含む型を「ランク2」とする。 +以上を繰り返して「ランクN」型が定義される。また、ランクN型はN以下のランクの型をすべて含む。ゆえに、複数のランクが混在する型のランクは、その中で最も高いランクと同じになる。 + +```erg +R1 = (|...| R0) or (R0 -> R1) or K(R1) or R0 +R2 = (|...| R1) or (R1 -> R2) or K(R2) or R1 +... +Rn = (|...| Rn-1) or (Rn-1 -> Rn) or K(Rn) or Rn-1 +``` + +いくつか例をみてみよう。 + +```erg + (|T: Type| T -> T) -> (|U: Type| U -> U) +=> R1 -> R1 +=> R1 -> R2 +=> R2 + +Option(|T: Type| T -> T) +=> Option(R1) +=> K(R1) +=> R1 +``` + +定義より、`tuple_map`はランク2型である。 + +```erg +tuple_map: + ((|T: Type| T -> T), (Int, Str)) -> (Int, Str) +=> (R1, R0) -> R0 +=> R1 -> R2 +=> R2 +``` + +Ergでは、ランク2までの型を扱うことができる(ランクN型はN以下のランクの型をすべて含むため、正確にいうとErgの型はすべてランク2型である)。それ以上の型の関数を構成しようとするとエラーになる。 +例えば、多相関数を多相関数のまま扱う関数はすべて他の引数の型指定が必要である。また、このような関数は構成できない。 + +```erg +# this is a rank-3 type function +# |X, Y: Type|((|T: Type| T -> T), (X, Y)) -> (X, Y) +generic_tuple_map|X, Y: Type| f: (|T: Type| T -> T), tup: (X, Y) = (f(tup.0), f(tup.1)) +``` + +ランク3以上の型は理論的に型推論が決定不能となる事実が知られており、型指定は省略可能というErgの性質を崩すものであるため排除されている。とはいえ、実用的なニーズはランク2型でほとんどカバーできる。 diff --git a/doc/JA/syntax/type/advanced/default_param.md b/doc/JA/syntax/type/advanced/default_param.md new file mode 100644 index 00000000..8b0c028d --- /dev/null +++ b/doc/JA/syntax/type/advanced/default_param.md @@ -0,0 +1,28 @@ +# デフォルト引数付きの関数型 + +まず、デフォルト引数の使用例を見る。 + +```erg +f: (Int, Int, |= Int) -> Int +f(x, y, z |= 0) = x + y + z + +g: (Int, Int, |= Int, Int) -> Int +g(x, y, z |= 0, w |= 1) = x + y + z + w + +fold: ((Int, Int) -> Int, [Int], |= Int) -> Int +fold(f, [], acc) = acc +fold(f, arr, acc |= 0) = fold(f, arr[1..], f(acc, arr[0])) +assert fold(f, [1, 2, 3]) == 6 +assert fold(g, [1, 2, 3]) == 8 +``` + +`|=`以降の引数はデフォルト引数である。 +部分型付け規則は以下の通り。 + +```erg +((X, |= Y) -> Z) < (X -> Z) +((X, |= Y, ...) -> Z) < ((X, |= ...) -> Z) +``` + +1番目は、デフォルト引数のある関数はない関数と同一視できる、という意味である。 +2番目は、任意のデフォルト引数は省略できる、という意味である。 diff --git a/doc/JA/syntax/type/advanced/erasure.md b/doc/JA/syntax/type/advanced/erasure.md new file mode 100644 index 00000000..08c0c66c --- /dev/null +++ b/doc/JA/syntax/type/advanced/erasure.md @@ -0,0 +1,43 @@ +# 型消去(Type erasure) + +型消去とは、型引数に`_`を指定し、その情報をあえて捨てることです。型消去は多相型を持つ言語の多くが併せて持つ機能ですが、Ergの文法に即して言えば型引数消去といった方が正確でしょう。 + +もっともよく見られる型消去された型の例は`[T, _]`でしょう。配列はコンパイル時にその長さが分からない場合もあります。例えば、コマンドライン引数を指す`sys.argv`は`[Str, _]`型です。コマンドライン引数の長さをErgのコンパイラは知りようがないため、長さに関する情報は諦めなくてはならないのです。 +しかし、型消去された型は、されていない型のスーパータイプになる(e.g. `[T; N] < [T; _]`)ため、より多くのオブジェクトを受け取れるようになります。 +`[T; N]`型のオブジェクトはもちろん`[T; _]`型のメソッドを使用できますが、使用後`n`の情報は消去されます。長さが変わってしまっているかもしれないからです。長さが変わらないならばシグネチャで示さなくてはなりません。 + +```erg +# 配列の長さが変わらないことが保証される関数(sortなど) +f: [T; N] -> [T; N] +# されない関数(filterなど) +g: [T; n] -> [T; _] +``` + +型指定自体で`_`を使うとその型は`Object`までアップキャストされます。 +型でない型引数(Int, Bool型など)の場合、`_`としたパラメータは未定義になります。 + +```erg +i: _ # i: Object +[_; _] == [Object; _] == Array +``` + +型消去は型指定の省略とは違います。一度型引数情報を消去してしまうと、再びアサーションしなければ情報は戻りません。 + +```erg +implicit = (1..5).iter().map(i -> i * 2).to_arr() +explicit = (1..5).iter().map(i -> i * 2).into(Array(Nat)) +``` + +Rustでは以下のコードに対応します。 + +```rust +let partial = (1..6).iter().map(|i| i * 2).collect::>(); +``` + +Ergでは型の部分省略はできず、代わりに高階カインド多相を使用します。 + +```erg +# collectはカインドを受け取る高階カインドのメソッド +hk = (1..5).iter().map(i -> i * 2).collect(Array) +hk: Array(Int) +``` diff --git a/doc/JA/syntax/type/advanced/existential.md b/doc/JA/syntax/type/advanced/existential.md new file mode 100644 index 00000000..4e433516 --- /dev/null +++ b/doc/JA/syntax/type/advanced/existential.md @@ -0,0 +1,41 @@ +# 存在型 + +∀に対応する全称型があるならば、∃に対応する存在型があると考えるのが自然です。 +存在型は難しいものではありません。そうと意識していないだけで、既にあなたは存在型を知っています。 + +```erg +T: Trait +f x: T = ... +``` + +上のトレイト`T`は存在型として使われています。 +対して下の場合の`T`はトレイトでしかなく、`X`は全称型です。 + +```erg +f|X <: T| x: X = ... +``` + +実際、存在型は全称型に置き換えられます。ではなぜ存在型などというものが存在するのでしょうか。 +まず、上で見たように存在型は型変数を伴わないので、型指定をシンプルにできます。 +また、型変数を除去できるので全称型ならランク2を超えてしまうような型も構成できます。 + +```erg +show_map f: (|T| T -> T), arr: [Show; _] = + arr.map x -> + y = f x + log y + y +``` + +しかし、見ればわかるように存在型は元の型を忘却・拡大してしまうので、戻り値の型を広げたくない場合などは全称型を使う必要があります。 +逆に、引数として受け取るだけで戻り値に関係のない型は存在型で記述して構いません。 + +```erg +# id(1): Intになって欲しい +id|T|(x: T): T = x +# |S <: Show|(s: S) -> ()は冗長 +show(s: Show): () = log s +``` + +ちなみに、クラスは存在型とは呼びません。予めその要素となるオブジェクトが定められているためです。 +存在型はあるトレイトを満たすすべての型という意味で、実際にどのような型が代入されるか知るところではないのです。 diff --git a/doc/JA/syntax/type/advanced/keyword_param.md b/doc/JA/syntax/type/advanced/keyword_param.md new file mode 100644 index 00000000..f601af75 --- /dev/null +++ b/doc/JA/syntax/type/advanced/keyword_param.md @@ -0,0 +1,26 @@ +# キーワード引数付き関数型 + +```erg +h(f) = f(y: 1, x: 2) +h: |T: Type|((y: Int, x: Int) -> T) -> T +``` + +キーワード引数付き関数の部分型付け規則は以下の通り。 + +```erg +((x: T, y: U) -> V) < ((T, U) -> V) # x, y are arbitrary keyword parameters +((y: U, x: T) -> V) < ((x: T, y: U) -> V) +((x: T, y: U) -> V) < ((y: U, x: T) -> V) +``` + +これは、キーワード引数は消去ないし入れ替えができるということを意味する。 +しかし、両者を同時に行うことはできない。 +すなわち、`(x: T, y: U) -> V`を`(U, T) -> V`にキャストすることはできない。 +なお、キーワード引数がつくのはトップレベルのタプル内のみで、配列やネストしたタプルでキーワード引数は付かない。 + +```erg +Valid: [T, U] -> V +Invalid: [x: T, y: U] -> V +Valid: (x: T, ys: (U,)) -> V +Invalid: (x: T, ys: (y: U,)) -> V +``` diff --git a/doc/JA/syntax/type/advanced/kind.md b/doc/JA/syntax/type/advanced/kind.md new file mode 100644 index 00000000..38399fca --- /dev/null +++ b/doc/JA/syntax/type/advanced/kind.md @@ -0,0 +1,149 @@ +# カインド(Kind) + +Ergでは全てが型付けられている。型自体も例外ではない。「型の型」を表すのが __カインド(種)__ である。例えば`1`が`Int`に属しているように、`Int`は`Type`に属している。`Type`は最もシンプルなカインドである __原子カインド(Atomic kind)__ である。型理論的の記法では、`Type`は`*`に対応する。 + +カインドという概念で実用上重要なのは1項以上のカインド(多項カインド)である。1項のカインドは、例えば`Option`などがそれに属する。1項カインドは`Type -> Type`と表される[1](#1)。`Array`や`Option`などの __コンテナ__ は特に型を引数に取る多項カインドのことなのである。 +`Type -> Type`という表記が示す通り、実は`Option`は`T`という型を受け取って`Option T`という型を返す関数である。ただし、この関数は通常の意味での関数ではないため、1項カインド(unary kind)と普通は呼称される。 + +なお、無名関数演算子である`->`自体も型を受け取って型を返す場合カインドとみることができる。 + +また、原子カインドでないカインドは型ではないことに注意してほしい。`-1`は数値だが`-`は数値ではないのと同じように、`Option Int`は型だが`Option`は型ではない。`Option`などは型構築子と呼ばれることもある。 + +```erg +assert not Option in Type +assert Option in Type -> Type +``` + +なので、以下のようなコードはエラーになる。 +Ergではメソッドを定義できるのは原子カインドのみで、メソッドの第一引数以外の場所で`self`という名前を使えない。 + +```erg +# K is an unary kind +K: Type -> Type +K T = Class ... +K. + foo x = ... # OK、これはいわゆるスタティックメソッドのようなもの + bar self, x = ... # TypeError: cannot define a method to a non-type object +K(T). + baz self, x = ... # OK +``` + +2項以上のカインドの例としては`{T: U}`(: `(Type, Type) -> Type`), `(T, U, V)`(: `(Type, Type, Type) -> Type`), ...などが挙げられる。 + +0項のカインド`() -> Type`も存在する。これは型理論的には原子カインドと同一視されることもあるが、Ergでは区別される。例としては`Class`などがある。 + +```erg +Nil = Class() +``` + +## カインドの包含関係 + +多項カインド間にも部分型関係、もとい部分カインド関係があります。 + +```erg +K T = ... +L = Inherit K +L <: K +``` + +すなわち、任意の`T`に対し`L T <: K T`ならば`L <: K`であり、その逆も成り立ちます。 + +```erg +∀T. L T <: K T <=> L <: K +``` + +## 高階カインド + +高階カインド(higher-order kind)というものもある。これは高階関数と同じコンセプトのカインドで、カインド自体を受け取るカインドである。`(Type -> Type) -> Type`などが高階カインドである。高階カインドに属するオブジェクトを定義してみよう。 + +```erg +IntContainerOf K: Type -> Type = K Int +assert IntContainerOf Option == Option Int +assert IntContainerOf Result == Result Int +assert IntContainerOf in (Type -> Type) -> Type +``` + +多項カインドの束縛変数はK, L, ...などと表されるのが通例である(KはKindのK)。 + +## セットカインド + +型理論において、レコードという概念がある。これはErgのレコードとほぼ同じものである[2](#2)。 + +```erg +# This is a record, and it corresponds to what is called a record in type theory +{x = 1; y = 2} +``` + +レコードの値が全て型であるとき、それはレコード型といって型の一種であった。 + +```erg +assert {x = 1; y = 2} in {x = Int; y = Int} +``` + +レコード型はレコードを型付けする。察しの良い方は、レコード型を型付けする「レコードカインド」があるはずだと考えたかもしれない。実際に、それは存在する。 + +```erg +log Typeof {x = Int; y = Int} # {{x = Int; y = Int}} +``` + +`{{x = Int; y = Int}}`のような型がレコードカインドである。これは特別な記法ではない。単に、`{x = Int; y = Int}`のみを要素に持つ列挙型である。 + +```erg +Point = {x = Int; y = Int} +Pointy = {Point} +``` + +レコードカインドの重要な特性は、`T: |T|`であり、`U <: T`であるとき、`U: |T|`であるという点にある。 +これは列挙型が実際には篩型の糖衣構文であることからもわかる。 + +```erg +# 通常のオブジェクトでは{c} == {X: T | X == c}だが、 +# 型の場合等号が定義されない場合があるので|T| == {X | X <: T}となる +{Point} == {P | P <: Point} +``` + +型制約中の`U <: T`は、実は`U: |T|`の糖衣構文である。 +このような型のセットであるカインドは一般にセットカインドと呼ばれる。セットカインドはIteratorパターンでも現れる。 + +```erg +Iterable T = Trait { + .Iterator = {Iterator} + .iter = Self(T).() -> Self.Iterator T +} +``` + +## 多項カインドの型推論 + +```erg +Container K: Type -> Type, T: Type = Patch K(T, T) +Container(K). + f self = ... +Option T: Type = Patch T or NoneType +Option(T). + f self = ... +Fn T: Type = Patch T -> T +Fn(T). + f self = ... +Fn2 T, U: Type = Patch T -> U +Fn2(T, U). + f self = ... + +(Int -> Int).f() # どれが選択される? +``` + +上の例で、メソッド`f`はどのパッチが選ばれるのだろうか。 +素朴に考えて`Fn T`が選ばれるように思われるが、`Fn2 T, U`もあり得るし、`Option T`は`T`そのままを含むので任意の型が該当し、`Container K, T`も``` `->`(Int, Int)```すなわち```Container(`->`, Int)```として`Int -> Int`にマッチする。なので、上の4つのパッチすべてが選択肢としてありえる。 + +この場合、以下の優先基準に従ってパッチが選択される。 + +* 任意の`K(T)`(e.g. `T or NoneType`)は`Type`よりも`Type -> Type`に優先的にマッチする。 +* 任意の`K(T, U)`(e.g. `T -> U`)は`Type`よりも`(Type, Type) -> Type`に優先的にマッチする。 +* 3項以上のカインドについても同様の基準が適用される。 +* 置換する型変数が少なく済むものが選択される。例えば`Int -> Int`は`K(T, T)`(置換する型変数: K, T)や`T -> U`(置換する型変数: T, U)よりも`T -> T`(置換する型変数: T)が優先的にマッチする。 +* 置換数も同じ場合は選択不能としてエラー。 + +--- + +1 型理論の記法では`*=>*` [↩](#f1) + +2 可視性などの微妙な違いはある。 [↩](#f2) diff --git a/doc/JA/syntax/type/advanced/marker_trait.md b/doc/JA/syntax/type/advanced/marker_trait.md new file mode 100644 index 00000000..6d51ce46 --- /dev/null +++ b/doc/JA/syntax/type/advanced/marker_trait.md @@ -0,0 +1,31 @@ +# Marker Trait + +マーカートレイトは、要求属性のないトレイトである。すなわち、メソッドを実装せずにImplすることができる。 +要求属性がないと意味がないように思えるが、そのトレイトに属しているという情報が登録されるので、パッチメソッドを使ったり、コンパイラが特別扱いしたりできる。 + +すべてのマーカートレイトは`Marker`トレイトに包摂される。 +標準で提供されている`Light`はマーカートレイトの一種である。 + +```erg +Light = Subsume Marker +``` + +```erg +Person = Class {.name = Str; .age = Nat} and Light +``` + +```erg +M = Subsume Marker + +MarkedInt = Inherit Int, Impl: M + +i = MarkedInt.new(2) +assert i + 1 == 2 +assert i in M +``` + +マーカークラスは`Excluding`引数で外すことも可能である。 + +```erg +NInt = Inherit MarkedInt, Impl: N, Excluding: M +``` diff --git a/doc/JA/syntax/type/advanced/mut_struct.md b/doc/JA/syntax/type/advanced/mut_struct.md new file mode 100644 index 00000000..da25f7a0 --- /dev/null +++ b/doc/JA/syntax/type/advanced/mut_struct.md @@ -0,0 +1,40 @@ +# 可変構造型 + +`T!`型は任意の`T`型オブジェクトを入れられて差し替え可能なボックス型であると説明した。 + +```erg +Particle! State: {"base", "excited"}! = Class(..., Impl: Phantom State) +Particle!. + # このメソッドはStateを"base"から"excited"に遷移させる + apply_electric_field!(ref! self("base" ~> "excited"), field: Vector) = ... +``` + +`T!`型は、データの差し替えは行えるが、その構造を変えることはできない。 +より現実のプログラムの振舞いに近い言い方をすれば、(ヒープ上の)サイズを変更できない。このような型を、不変構造(可変)型と呼ぶ。 + +実は、不変構造型では表すことのできないデータ構造が存在する。 +例えば、可変長配列である。`[T; N]!`型は任意の`[T; N]`であるオブジェクトを入れることができるが、`[T; N+1]`型オブジェクトなどに差し替えることはできない。 + +すなわち、長さを変えられないのである。長さを変えるためには、型自体の構造を変化させなくてはならない。 + +それを実現するのが可変構造(可変)型である。 + +```erg +v = [Str; !0].new() +v.push! "Hello" +v: [Str; !1] +``` + +可変構造型では可変化する型引数に`!`を付ける。上の場合は、`[Str; !0]`型を`[Str; !1]`型などに変更することができる。すなわち、長さを変更できる。 +因みに、`[T; !N]`型は`MutLenArray(T, !N)`型の糖衣構文である。 + +可変構造型はもちろんユーザー定義も可能である。ただし、不変構造型とは構成法に関していくつか違いがあるので注意が必要である。 + +```erg +Nil T = Class(Impl: Phantom T) +List T, !0 = Inherit Nil T +List T, N: Nat! = Class {head = T; rest = List(T, !N-1)]} +List(T, !N). + push! ref! self(N ~> N+1, ...) head: T = + self.update! old -> Self.new {head; old} +``` diff --git a/doc/JA/syntax/type/advanced/phantom.md b/doc/JA/syntax/type/advanced/phantom.md new file mode 100644 index 00000000..35f57e48 --- /dev/null +++ b/doc/JA/syntax/type/advanced/phantom.md @@ -0,0 +1,57 @@ +# 幽霊型(Phantom class) + +幽霊型は、コンパイラに注釈を与えるためだけに存在するマーカートレイトである。 +幽霊型の使い方として、リストの構成をみる。 + +```erg +Nil = Class() +List T, 0 = Inherit Nil +List T, N: Nat = Class {head = T; rest = List(T, N-1)} +``` + +このコードはエラーとなる。 + +```erg +3 | List T, 0 = Inherit Nil + ^^^ +TypeConstructionError: since Nil does not have a parameter T, it is not possible to construct List(T, 0) with Nil +hint: use 'Phantom' trait to consume T +``` + +このエラーはつまり、`List(_, 0).new Nil.new()`とされたときに`T`の型推論ができないという文句である。Ergでは型引数を未使用のままにすることができないのである。 +このような場合は何でもよいので`T`型を右辺で消費する必要がある。サイズが0の型、例えば長さ0のタプルならば実行時のオーバーヘッドもなく都合がよい。 + +```erg +Nil T = Class((T; 0)) +List T, 0 = Inherit Nil T +List T, N: Nat = Class {head = T; rest = List(T, N-1)} +``` + +このコードはコンパイルを通る。だが少しトリッキーで意図が分かりづらい上に、型引数が型のとき以外では使えない。 + +このようなときにちょうどよいのが幽霊型である。幽霊型はサイズ0の型を一般化した型である。 + +```erg +Nil T = Class(Impl: Phantom T) +List T, 0 = Inherit Nil T +List T, N: Nat = Class {head = T; rest = List(T, N-1)} + +nil = Nil(Int).new() +assert nil.__size__ == 0 +``` + +`Phantom`が`T`型を保持する。しかし実際には`Phantom T`型のサイズは0であり、`T`型のオブジェクトを保持してはいない。 + +また、`Phantom`は型以外にも任意の型引数を消費することができる。以下の例では`State`という`Str`のサブタイプオブジェクトである型引数を`Phantom`が保持している。 +この場合も、`state`はオブジェクトの実体に現れないハリボテの型変数である。 + +```erg +VM! State: {"stopped", "running"}! = Class(..., Impl: Phantom! State) +VM!("stopped"). + start ref! self("stopped" ~> "running") = + self.do_something!() + self::set_phantom!("running") +``` + +`state`は`update_phantom!`メソッドか`set_phantom!`メソッドを介して更新する。 +これは`Phantom!`(`Phantom`の可変版)の標準パッチが提供するメソッドで、使い方は可変型の`update!`, `set!`と同じである。 diff --git a/doc/JA/syntax/type/advanced/projection.md b/doc/JA/syntax/type/advanced/projection.md new file mode 100644 index 00000000..cb01218c --- /dev/null +++ b/doc/JA/syntax/type/advanced/projection.md @@ -0,0 +1,53 @@ +# Projection Type(射影型) + +射影型は、次のコードにおける`Self.AddO`のような型を表します。 + +```erg +Add R, O = Trait { + .`_+_` = Self, R -> O +} +BinAdd = Subsume Add(Self, Self.AddO), { + .AddO = Type +} + +IntIsBinAdd = Patch(Int, Impl: BinAdd) +IntIsBinAdd. + AddO = Int +``` + +`Add(R, O)`型は何らかのオブジェクトとの加算が定義されている型、`BinAdd`は自身のクラスとの加算(閉じた加算)が定義されている型といえます。メソッドは型属性であるべきなので、`+`の型宣言はインデント以下に記述します。 +引数のない`Add`型のミソとなるのが`.AddO: Type`という宣言で、これがないと右辺型がエラーになります。 + +```erg +BinAdd = Add(Self, Self.AddO) # TypeError: trait object 'BinAdd' has no attribute 'AddO' +``` + +射影型である`.AddO`型の実体は、`Add`のサブタイプである型が持ちます。例えば、`Int.AddO = Int`, `Odd.AddO = Even`です。 + +```erg +assert Int < Add +assert Int.AddO == Int +assert Odd < Add +assert Odd.AddO == Even +``` + +## Appendix: 射影型の型推論 + +```erg +f x: BinAdd = x + x + +f 10 +``` + +上のコードを例にして考えます。 +グローバル名前空間には以下の`+`の実装が存在します。 + +```erg +`_+_`: {(A, R) -> O | R; O; A <: Add(R, O)} +``` + +`BinAdd = Add(Self, Self.AddO)`なので、`Add`型であるオブジェクトに対しては````_+_`: {(A, A) -> A.AddO | A <: Add(A, A.AddO)}```と置換できます。 + +```erg +f|A <: BinAdd|(x: A): A.AddO = x + x +``` diff --git a/doc/JA/syntax/type/advanced/quantified_dependent_type.md b/doc/JA/syntax/type/advanced/quantified_dependent_type.md new file mode 100644 index 00000000..69b8ca56 --- /dev/null +++ b/doc/JA/syntax/type/advanced/quantified_dependent_type.md @@ -0,0 +1,26 @@ +# 量化依存型 + +Ergには量化型、依存型が存在します。すると当然、その二つを組み合わせた型を作ることができます。それが量化依存型です。 + +```erg +NonNullStr = |N: Nat| StrWithLen N | N != 0 # same as {S | N: Nat; S: StrWithLen N; N != 0} +NonEmptyArray = |N: Nat| [_; N | N > 0] # same as {A | N: Nat; A: Array(_, N); N > 0} +``` + +量化依存型の標準形は`T(A, ... | Pred)`です。`T`は型構築子、`A, B`は型引数、`Pred`は条件式です。 + +左辺値としての量化依存型は、元の型と同じモジュール内でのみメソッドを定義出来ます。 + +```erg +T A: Nat = Class ... +T(A). + ... +T(A | A >= 1). + method ref! self(A ~> A+1) = ... +``` + +右辺値としての量化依存型は、使用する型変数を型変数リスト(`||`)で宣言する必要がある。 + +```erg +a: |N: Nat| [T; N | N > 1] +``` diff --git a/doc/JA/syntax/type/advanced/rank2type.md b/doc/JA/syntax/type/advanced/rank2type.md new file mode 100644 index 00000000..14436423 --- /dev/null +++ b/doc/JA/syntax/type/advanced/rank2type.md @@ -0,0 +1,25 @@ +# ランク2型 + +Ergでは`id|T|(x: T) -> T = x`などのように色々な型を受け取れる関数、すなわち多相関数を定義できる。 +では、多相関数を受け取れる関数は定義できるだろうか? +例えば、このような関数である(この定義は誤りを含むことに注意してほしい)。 + +```erg +# pair_map(i -> i * 2, (1, "a")) == (2, "aa")になってほしい +pair_map|T, L, R|(f: T -> T, (l: L, r: R)) -> (L, R) = (f(l), f(r)) +``` + +`1`と`"a"`の型が違うので、無名関数は一度単相化して終わりではないことに注意してほしい。2回単相化する必要がある。 +しかしはErgは型変数を一度しか単相化しない。すなわち、`T -> T`は`f(tup.0)`の時点で`Int -> Int`に決定されてしまう。 + +```erg +# pair_map: |L, R: Type; F <: L -> L; F <: R -> R|(F, (L, R)) -> (L, R) +pair_map|L, R|(f, (l: L, r: R)): (L, R) = (f(l), f(r)) +``` + +何も指定しなかった場合はこうなる。 + +```erg +# pair_map: |F, T, U, V, W: Type; F <: T -> V; F <: U -> W| (F, (T, U)) -> (V, W) +pair_map(f, (l, r)) = (f(l), f(r)) +``` diff --git a/doc/JA/syntax/type/advanced/recursive_type_inferring.md b/doc/JA/syntax/type/advanced/recursive_type_inferring.md new file mode 100644 index 00000000..b199ef31 --- /dev/null +++ b/doc/JA/syntax/type/advanced/recursive_type_inferring.md @@ -0,0 +1,50 @@ +# 再帰型の型推論 + +再帰型の型推論では、任意回の呼び出しで生成されるオブジェクトの集合を計算することとなる。 +これは実質的には無限回の操作で生成される空間と同義である。 +`fn T`をサブルーチン`fn`に対する`T`型オブジェクトの適用による戻り値型とするとき、関数呼び出しをn回行って導出される型をfn^n Tと表す。つまり、fn(fn(...(fn(T)))) == fn^n Tである。 +複数引数の場合、前の呼び出しの結果が第1引数となる場合、つまりfn(fn(fn(fn(x, y_1)..., y_n-2) y_n-1) y_n)はfn^n X Y^nと表す。 +同様に、前の呼び出しの結果が第2引数となる場合、つまりfn(x_1 fn(x_2 ...fn(x_n y)))はfn^n X^n Yまたは(fn X)^n Yと表す。 +例としては階乗関数`fact n: Nat = n * fact n-1`がある。`fact: 1.. * fact (1.. - {1}) == (1.. *)^n {1}`である。 + +再帰型ではいくらでも呼び出しうるため、関数の戻り値に使われている関数fnの無眼回適用で生成されるfn^∞ Tが戻り値型である。 +現在実装されている推論規則は以下の通り。 + +指定のない限り、x, y, z: Nat + +* id^∞ t == {t} +* ({0} +)^∞ x..y == x..y +* ({0} -)^∞ x..y = -y..-x +* ({1} *)^∞ x..y == x..y +* ({z} +)^∞ x..y == Nat +* ({z} -)^∞ x..y = Int +* ({""} +)^∞ {s} == {s} +* ({s = Str len = 1..} +)^∞ {\_ = Str} == Str + +## 非依存化 + +Independizationは依存型を一般の型に戻す操作である。例として、{1} -> Natなどがある。 +普通のダウンキャストの場合は、{1} -> {0, 1} -> {0, 1, 2}などとなる場合がある。これはその名前空間で定義のある依存型による。 +依存型は無限にあるため、定義のあるものにしかダウンキャストしないのである。 +しかしIndependizationは一意的である。 +非依存化は依存型の推論が失敗した場合に行われる。 +{1, True}など、別々のクラスに属するオブジェクト同士の非依存化は失敗(コンパイルエラー)する。 +{-1, 0, 1}など同一のクラスであれば成功する。その際、クラスは可能な限り子クラスになる(この場合はInt)。 +型変数を含んだ型、例えば{True} or '0は、型変数を含まない方が非依存化されたあと消去される(この場合はBool)。 +もともとErgのランタイムにはクラスしか存在しないので、この操作は型消去の一種であり、情報は失われるが健全性が壊れることはない(TODO:証明を与える)。 + +## 依存推論のテスト + +```erg +func 0, 0, _ = True +func 0, _, _ = False +func(i, x, a) = + # a[i-1] を選ばない場合 (func(i-1, x, a) が OK なら OK) + if func(i-1, x, a): + return True + # a[i-1] を選ぶ場合 (func(i-1, x-a[i-1], a) が OK なら OK) + if func(i-1, x-a[i-1], a): + return True + + False +``` diff --git a/doc/JA/syntax/type/advanced/shared.md b/doc/JA/syntax/type/advanced/shared.md new file mode 100644 index 00000000..ea2a8700 --- /dev/null +++ b/doc/JA/syntax/type/advanced/shared.md @@ -0,0 +1,72 @@ +# 共有参照(Shared Reference) + +共有参照は気をつけて扱わねばならない言語機能の一つです。 +例えばTypeScriptでは以下のようなコードが型検査を通ってしまいます。 + +```typescript +class NormalMember {} +class VIPMember extends NormalMember {} + +let vip_area: VIPMember[] = [] +let normal_area: NormalMember[] = vip_area + +normal_area.push(new NormalMember()) +console.log(vip_area) # [NormalMember] +``` + +一般会員がVIPエリアに侵入してしまっています。これは明らかなバグですが、何がいけなかったのでしょうか。 +原因は共有参照の[変性](./variance.md)です。`normal_area`は`vip_area`をコピーして作成されていますが、その際に型が変わってしまっています。 +しかし`VIPMember`は`NormalMember`を継承しているので`VIPMember[] <: NormalMember[]`となり、これは問題ないとされてしまっているのです。 +`VIPMember[] <: NormalMember[]`という関係は、不変オブジェクトの場合は問題ありません。しかし上のように破壊的な操作を行ってしまうと、綻びが発生します。 + +Ergでは、所有権システムのおかげでこのようなコードは弾かれます。 + +```erg +NormalMember = Class() +VIPMember = Class() + +vip_area = [].into [VIPMember; !_] +normal_area: [NormalMember; !_] = vip_area + +normal_area.push!(NormalMember.new()) +log vip_area # OwnershipError: `vip_room` was moved to `normal_room` +``` + +しかし、オブジェクトの所有権が一箇所にしかない状態は不便である場合もあります。 +そのためにErgは`SharedCell! T!`という型があり、これが共有状態を表します。 + +```erg +$p1 = SharedCell!.new(!1) +$p2 = $p1.mirror!() +$p3 = SharedCell!.new(!1) +# $p1 == $p2とすると、中身の型Int!の比較が行われる +assert $p1 == $p2 +assert $p1 == $p3 +# $p1と$p2が同じものを指しているかは、`.addr!`で確認する +assert $p1.addr!() == $p2.addr!() +assert $p1.addr!() != $p3.addr!() +$p1.add! 1 +assert $p1 == 2 +assert $p2 == 2 +assert $p3 == 1 +``` + +`SharedCell!`型のオブジェクトは先頭に`$`を付ける必要があります。また、その性質上、定数にすることはできません。 + +`SharedCell! T!`型は`T!`型のサブタイプでもあり、`T!`型のメソッドを呼び出すことができます。`SharedCell! T!`型固有のメソッドは`.addr!`と`.mirror!`、`.try_take`のみです。 + +重要な事実として、`SharedCell! T!`は非変(non-variant)です。すなわち、型引数の違いによる包含関係が定義されません。 + +```erg +$vip_area = SharedCell!.new([].into [VIPMember; !_]) +$normal_area: SharedCell!([NormalMember; !_]) = $vip_area.mirror!() # TypeError: expected SharedCell!([NormalMember; !_]), but got SharedCell!([VIPMember; !_]) +# hint: SharedCell!(T) is non-variant, which means it cannot have a supertype or a subtype. +``` + +しかし、以下のコードは問題ありません。最後の行では、型変換されたのは引数の`VIPMember`の方です。 + +```erg +$normal_area = SharedCell!.new([].into [NormalMember; !_]) +$normal_area.push!(NormalMember.new()) # OK +$normal_area.push!(VIPMember.new()) # OK +``` diff --git a/doc/JA/syntax/type/advanced/type_inferring.md b/doc/JA/syntax/type/advanced/type_inferring.md new file mode 100644 index 00000000..198e32da --- /dev/null +++ b/doc/JA/syntax/type/advanced/type_inferring.md @@ -0,0 +1,164 @@ +# 型推論 + +> __Warning__: この文書の情報は古く、一部に間違いを含みます。 + +Ergでは多くの場合引数の型を省略することが可能である。 +内部的には、省略された引数は型変数で型付けされる。つまり、なんでも受け入れる型である。 +そこから、関数内部での使われ方に応じて最小の型制約が課される。 +多くの場合、型推論によってシグネチャから型変数がすべて除去されることはない。 +例えば、`x + 1`と書かれていたとき、`x`は`'0`型で、その制約は'0 <: `+`({1}, '1)である。 +ほかの言語、例えばOCamlではこの時点でxはIntと決定されてしまうが、ErgではNat, Int, Float, ...あるいはユーザー定義のクラス・型などが入る可能性もある。よって、この定義の時点では単に'0と置くだけで、戻り値型の推論は使用時に行われる。 +Ergでは再帰関数の定義も可能であるが、現在のところ、技術的な理由で再帰関数の戻り値は推論されない。推論が不可能な場合が多いからである。 + +```erg +fib 0 = 0 +fib 1 = 1 +fib n: Nat = fib(n-1) + fib(n-2) +``` + +上記の`fib`の定義は、 + +```erg +fib n: Nat = + match x: + 0 -> 0 + 1 -> 1 + n -> fib(n-1) + fib(n-2) +``` + +と同じである。 + +## 型推論の例 + +```erg +fib 0 = 0 +fib 1 = 1 +fib n: Nat = fib(n-1) + fib(n-2) + +# 相互再帰関数も定義可能 +even 0 = True +even n: 1.. = odd n-1 +odd 0 = False +odd n: 1.. = even n-1 + +sig _: Neg = -1 +sig 0 = 0 +sig _: Pos = 1 + +f 1 = 0 +f 2 = 1 +f(x): Nat = f x*2 +``` + +これらの関数の推論は以下のように行われる。 +ただし、以下の記法を使用する。 + +* `f x: T`なる関数があったとき、`f`の戻り値の型を`f T`と表す。すなわち、`f: T -> U`のとき`U = f T`である。 +* `∴`は「故に」「よって」を表す。 +* minはその型を実現する最小の単相型を表す。 + `'T`に制約がないとき、`min('T) = Never` + `T`が単相型のとき、`min(T) = T` + +## fib + +* fib 0とfib 1に関しては直接型付け可能。 + fib: {0} -> {0} + fib: {1} -> {1} + fib: {0, 1} -> {0, 1} (この関数は宣言だけ登録され、実体は使用されるまで生成されない。実体はmatchによって生成される) +* fib: 2.. -> '0 | '0 <: min(fib 2.. - {1} + fib 2.. - {2}) + fib: 2.. -> '0 | '0 <: min(fib 1.. + fib 0..) +* fib: 1.. -> '1とfib 0.. -> '2の型推論を行う(実際は新しい名前空間で推論するので'1, '2にインクリメントする必要はないが、見やすさのためにこうしている) + * fib: 1.. -> '1について、既に存在する宣言から順に決め打ちする + * fib {1} -> {1}が見つかったので、1..から{1}を抜く->2.. + * fib 2.. -> '0が見つかったので、2..から2..を抜く->Never + * Neverになったので完成、fib: 1.. -> {1} or '0 + * fib: 0.. -> '2についても同様にしてfib: 0.. -> {0, 1} or '0 +* fib: 2.. -> '0 | '0 <: min(({1} or '0) + ({0, 1} or '0)) + 再帰型の推論では「任意回の操作によって得られる型(see erg_type_inferring_recursive.md)」を計算する + つまり、'0は{1}と{0, 1}の任意回の加算によって生成される型である + {0}+{0}以外の正数集合の任意回可算はNatを生成する +* 合計で宣言されたのは + fib: {0} -> {0} + fib: {1} -> {1} + fib: {0, 1} -> {0, 1} + fib: 2.. -> Nat + fib: {0} or 2.. -> Nat + fib: 1.. -> Nat + fib: Nat -> Nat + の7種類。 + +## even, odd + +* even 0とodd 0に関しては直接型付け可能。 + even.{0} \_ = True + odd.{0} \_ = False +* even nとodd nの戻り値型を'0, '1としておく。 + even: 1.. -> '0 | '0 <: odd 1.. - 1 + ∴ even: 1.. -> '0 | '0 <: odd 0.. + odd 0..: + odd {0} -> {False} + odd 1.. -> '1 + ∴ even: 1.. -> '0 | '0 <: {False} or '1 + odd: 1.. -> '1 | '1 <: even 1.. -1 + ∴ odd: 1.. -> '1 | '1 <: even 0.. + even 0..: + even {0} -> {True} + even 1.. -> '0 | '0 <: {False} or '1 + odd: 1.. -> '1 | '1 <: {True, False} or '1 + odd: 1.. -> {True, False} + even: 1.. -> {True, False} +* 合計で宣言されたのは + even: {0} -> {True} + odd: {0} -> {False} + odd: 1.. -> Bool + even: 1.. -> Bool + even: Nat -> Bool + odd: Nat -> Bool + +## sig + +* sig _: ..=-1 = -1より + sig.(..0) \_ = -1 + が定義される。一意性のため、x: Natのとき..=xは..x+1に置換される(Floatでは置換できない)。 +* sig 0 = 0より内部で + sig.{0} \_ = 0 + sig.(..0) \_0 = + match _0: + _1: ..0 -> sig.(..0) _1 + _1: {0} -> sig.{0} _1 + が定義される。 +* sig: _: 1.. = 1より + sig.(1..) \_ = 1 + (..=-1 or 1..) <.> NZInt + sig.NZInt \_0 = + match \_0: + _1: ..0 -> sig.(..0) _1 + _1: 1.. -> sig.(1..) _1 + (..=-1 or 0 or 1..) <.> Int + sig.Int \_0 = + match \_0: + _1: ..0 -> sig.(..0) _1 + _1: {0} -> sig.{0} _1 + _1: 1.. -> sig.(1..) _1 + が定義される。 + +## f + +* まず宣言されるのは以下の通り。 + f: {1} -> {0} + f: {2} -> {1} + f: {1, 2} -> {0, 1} + f: {"a"} -> {2} + f: {1, "a"} -> {0, 2} + f: {2, "a"} -> {1, 2} + f: {1, 2, "a"} -> {0, 1, 2} +* f: '0 -> '1 | '1 <: f '0 * {2}, Mul('0, {2}, '2) +* '2を決め打つ + * '2 <: {1}とすると、Mul '0, {2}, {1}より'0 <: {0.5} + f: {0.5} -> {0} + * '2 <: {2}とすると、Mul '0, {2}, {2}より'0 <: {1} + f: {1} -> {1}だが、これはf: {1} -> {0}に矛盾、型エラー + * '2 <: {"a"}とすると、Mul '0, {2}, {"a"}を満たす解は存在しない + * '2を型変数のままにする('2 != {1, 2, "a"})と、'2 <: '0 + f: '0 -> '1 | Mul('0, {2}, '0) + '1の制約がないので、推論不能 diff --git a/doc/JA/syntax/type/advanced/type_inferring_v2.md b/doc/JA/syntax/type/advanced/type_inferring_v2.md new file mode 100644 index 00000000..07b05087 --- /dev/null +++ b/doc/JA/syntax/type/advanced/type_inferring_v2.md @@ -0,0 +1,149 @@ +# 型推論 + +> __Warning__: この項の情報は古く、一部に間違いを含みます。 + +Ergでは多くの場合サブルーチンの引数の型、および戻り値型を省略できる。 +この項では、その推論アルゴリズムと、推論ができない場合について解説する。 + +以下に示す`id`関数は最もシンプルな多相型関数である。 + +```erg +id x = x +``` + +xの型は特に指定されていないが、これは以下のコードと同じである。 + +```erg +id: |T| T -> T +id x = x +``` + +実際、型指定の省略されている部分は、内部的には`id: |'1, '2| '1 -> '2`と一旦型変数が割り当てられる +(仮に割り振られた型変数は`'数字`で表現することとする)。 +そして、ブロック内での`x`の使われ方によって`'2`の型が決定される。 +この場合はそのまま返しているので、`'2`型は`'1`型に単一化される。 + +次に、単相サブルーチンを使用しているサブルーチンの推論を見る。 + +```erg +p! x = x.times! do! print! x +``` + +```erg +p!: '1 => '2 +print!: ...Object => NoneType +do!: () => NoneType +.times!: Nat.(() => NoneType) => NoneType +'1 := Nat, '2 := NoneType +``` + +中で使用している`.times!`の戻り値型が単相(一つの種類の型しか受け付けない)なので、`p!`も単相である。 +もう少し複雑な例を見る。今度は多相サブルーチンを含むサブルーチンの型推論である。 + +```erg +f x, y, z = x + y + z +``` + +まず、このように型が割り当てられる。 + +```erg +f: ('1, '2, '3) -> '4 +f x, y, z = x + y + z +``` + +`x + y`について、この型は`+`('1, '2)である。フィットするものを`+`の実装及び宣言から探索する。 +グローバル空間には``+`: ('L, 'R) -> 'O`という宣言が存在する。この場合これがフィットする。 +では`+`('1, '2)の型は何だろうか。単純に'Oとしてはいけない。なぜなら、(x + y) + zの型、 +すなわち`+`(`+`('1, '2), '3)もフィットするため、x+yとx+y+zの型が同一になってしまう。 +なので、この場合は新しい型変数を割り当てる。 +具体的には、型変数を単一化せずに使う場合、戻り値型は新しく発行する。 +この場合、`+`('1, '2) == '5とする。 +`+`(`+`('1, '2), '3)についても同様に、`+`(`+`('1, '2), '3) == '6である。 +最終的な戻り値の'6は'4に単一化する。 +最後に型境界の処理を行う。'4型がどのような条件で与えられる型なのか明示する必要があるのである。 +結論から言うと以下のようになる。 + +```erg +f: |'1 <: {`+` = Self.'2 -> '5}, '5 <: {`+` = Self.'3 -> '4}| ('1, '2, '3) -> '4 +f x, y, z = x + y + z +``` + +この型境界により'4の型が決定される。 +このように見ると全てのサブルーチンが型推論可能なように思えるかもしれないが、残念ながらそうはいかない。 +推論ができない例としては、再帰関数がある。 + +```erg +fib 0 = 0 +fib 1 = 1 +fib n = fib(n-1) + fib(n-2) +``` + +先ほどと同じようにすると、型境界は以下のようになる。 + +```erg +fib: | + '1: -{0, 1} + '1 <: {`-` = ('Self, {1}) -> '3} + '1 <: {`-` = ('Self, {2}) -> '4} + '5 == '3, + '6 == '4, + '5 <: {`+` = (Self, '6) -> '2} +| '1 -> '2 +fib n = fib(n-1) + fib(n-2) +``` + +試しにNatを代入してみる。 + +```erg +fib: | + {`-` = (Nat-{0, 1}, {1}) -> 1.._} + {`-` = (Nat-{0, 1}, {2}) -> 0.._} + {fib = {1} -> {1}} + {fib = {0} -> {0}} + {fib = 1.._ -> ?} + {fib = 0.._ -> ?} + {`+` = ?, ? -> ?} +| Nat -> ? +``` + +```erg +fib: | + {`-` = (1.._).{1} -> 0.._} + {`-` = (1.._).{2} -> -1.._} + {fib = 0.._ -> ?} # fib Natに戻ってしまう! + {fib = -1.._ -> ?} + {`+` = ?.? -> ?} +| 1.._ -> ? +``` + +こういうわけで、型推論が無限ループしてしまう。 +よって、再帰関数は戻り値の指定が必須である(Haskell等の言語ではリテラルから型を推論するが、Ergは依存型を持つのでリテラルの型が一意に決まらない)。 +これは相互再帰関数の場合もだが、どれか一つを型指定するだけでよい。 + +```erg +odd 0 = False +odd(n): Bool = even n-1 +even 0 = True +even n = odd n-1 +``` + +```erg +odd:| + '1 <: Not {0} + {`-` = '1, {1} -> '2} + {even = '2 -> Bool} # 推論ではなく強制 +| '1 -> Bool +odd n = even n-1 + +even: | + '1 <: Not {0} + {`-` = '1, {1} -> '2} + {odd = '2 -> Bool} +| '1 -> Bool +even n = odd n-1 +``` + +しかし、この型付けには問題がある。減算が定義されている数なら何でも受け付けてしまうのである。 +つまり、`odd -1`が型検査を通ってしまう。これは明らかにナンセンスで、永遠に計算結果は現れない。 +この場合は`odd(n: Nat): Bool = even n-1`と、引数の型も明示しておくべきである。 +こうすれば`even`の方もナンセンスな引数は受け付けない。 diff --git a/doc/JA/syntax/type/advanced/typeof.md b/doc/JA/syntax/type/advanced/typeof.md new file mode 100644 index 00000000..f9fa02aa --- /dev/null +++ b/doc/JA/syntax/type/advanced/typeof.md @@ -0,0 +1,65 @@ +# Typeof, classof + +`Typeof`はErgの型推論システムを覗くことができる関数であり、その挙動は複雑である。 + +```erg +assert Typeof(1) == {I: Int | I == 1} +i: 1..3 or 5..10 = ... +assert Typeof(i) == {I: Int | (I >= 1 and I <= 3) or (I >= 5 and I <= 10)} + +C = Class {i = Int} +I = C.new {i = 1} +assert Typeof(I) == {X: C | X == I} +J: C = ... +assert Typeof(J) == {i = Int} + +assert {X: C | X == I} < C and C <= {i = Int} +``` + +`Typeof`関数ではオブジェクトのクラスではなく構造型が返される。 +なので、`C = Class T`なるクラスのインスタンス`I: C`に対しては`Typeof(I) == T`となる。 +値クラスに関しては本来対応するレコード型が存在しない。この問題を解消するため、値クラスは`__valueclass_tag__`属性を持っているレコード型ということになっている。 +なお、この属性にアクセスすることはできず、ユーザー定義型で`__valueclass_tag__`属性を定義することもできない。 + +```erg +i: Int = ... +assert Typeof(i) == {__valueclass_tag__ = Phantom Int} +s: Str = ... +assert Typeof(s) == {__valueclass_tag__ = Phantom Str} +``` + +`Typeof`で出力されるのは構造型のみである。構造型には属性型と篩型、(真の)代数演算型があると説明した。 +これらは独立な型(推論の優先順位が存在する)であり、推論の重解は発生しない。 +属性型、代数演算型は複数のクラスにまたがる可能性があるが、篩型は単一のクラスのサブタイプである。 +Ergは可能な限りオブジェクトの型を篩型として推論し、それができなくなった際は篩型のベースクラスを構造化(後述)した型に拡大する。 + +## 構造化 + +すべてのクラスは構造型に変換することができる。これを __構造化__ という。クラスの構造化された型は`Structure`関数で取得できる。 +クラスが`C = Class T`で定義されているとき(すべてのクラスはこの形式で定義されている)、`Structure(C) == T`になる。 + +```erg +C = Class {i = Int} +assert Structure(C) == {i = Int} +D = Inherit C +assert Structure(D) == {i = Int} +Nat = Class {I: Int | I >= 0} +assert Structure(Nat) == {I: Int | I >= 0} +Option T = Class (T or NoneType) +assert Structure(Option Int) == Or(Int, NoneType) +assert Structure(Option) # TypeError: only monomorphized types can be structurized +# 実際には__valueclass_tag__を持つレコードは定義できないが、概念上はこうなる +assert Structure(Int) == {__valueclass_tag__ = Phantom Int} +assert Structure(Str) == {__valueclass_tag__ = Phantom Str} +assert Structure((Nat, Nat)) == {__valueclass_tag__ = Phantom(Tuple(Nat, Nat))} +assert Structure(Nat -> Nat) == {__valueclass_tag__ = Phantom(Func(Nat, Nat))} +# マーカークラスも__valueclass_tag__を持つレコード型になる +M = Inherit Marker +assert Structure(M) == {__valueclass_tag__ = Phantom M} +D = Inherit(C and M) +assert Structure(D) == {i = Int; __valueclass_tag__ = Phantom M} +E = Inherit(Int and M) +assert Structure(E) == {__valueclass_tag__ = Phantom(And(Int, M))} +F = Inherit(E not M) +assert Structure(F) == {__valueclass_tag__ = Phantom Int} +``` diff --git a/doc/JA/syntax/type/advanced/variance.md b/doc/JA/syntax/type/advanced/variance.md new file mode 100644 index 00000000..1f711bc9 --- /dev/null +++ b/doc/JA/syntax/type/advanced/variance.md @@ -0,0 +1,143 @@ +# 変性(variance) + +Ergは多相型のサブタイピングを行えるが、一部注意しなくてはならない点がある。 + +まずは通常の多相型の包含関係を考える。一般に、コンテナ`K`と代入する型`A, B`があり、`A < B`のとき、`K A < K B`となる。 +例えば、`Option Int < Option Object`となる。よって、`Option Object`で定義されているメソッドは、`Option Int`でも使用可能である。 + +典型的な多相型である`Array!(T)`型について考える。 +今回は要素の数を問題にしないので`Array!(T, N)`ではないことに注意してほしい。 +さて、`Array!(T)`型には`.push!`と`.pop!`というメソッドが存在し、それぞれ、要素の追加・取り出しを意味する。型はこうである。 + +Array.push!: Self(T).(T) => NoneType +Array.pop!: Self(T).() => T + +直感的に理解できることとして、 + +* `s: Str`のとき`Array!(Object).push!(s)`はOK(`Str`を`Object`にアップキャストすれば良い) +* `o: Object`のとき`Array!(Str).push!(o)`はNG +* `Array!(Object).pop!().into(Str)`はNG +* `Array!(Str).pop!().into(Object)`はOK + +である。これは、型システム的には + +* (Self(Object).(Object) => NoneType) < (Self(Str).(Str) => NoneType) +* (Self(Str).() => Str) < (Self(Object).() => Object) + +を意味する。 + +前者は奇妙に思えるかもしれない。`Str < Object`なのに、それを引数に取る関数は包含関係が逆転している。 +型理論では、このような関係(`.push!`の型関係)を反変(contravariant)といい、その逆、`.pop!`の型関係は共変(covariant)という。 +つまり、関数型は引数の型に関して反変であり、戻り値の型に関して共変である、といえる。 +複雑に聞こえるが、先程見た通り実例に当てはめて考えれば合理的なルールである。 +それでもいまいちピンと来ない場合は次のように考えるとよい。 + +Ergの設計方針に、「入力の型は大きく、出力の型は小さく」というのがある。これはまさに関数の変性から言える。 +上のルールを見れば、入力型は大きい方が全体として小さい型になる。 +汎用の関数は明らかに専用の関数より希少だからである。 +そして出力型は小さい方が全体として小さくなる。 + +結果として上の方針は「関数の型を最小化せよ」と言っているのに等しい。 + +## 非変性 + +Ergにはもう一つ変性がある。それは非変性(non-variance)である。 +これは組み込み型では`SharedCell! T!`などが持っている変性である。これは、`T! != U!`なる2つの型`T!, U!`に関して、例え包含関係があったとしても`SharedCell! T!`と`SharedCell! U!`間でキャストができないことを意味する。 +これは、`SharedCell! T!`が共有参照であることに由来する。詳しくは[共有参照](shared.md)を参照。 + +## 変性指定された全称型 + +全称型の型変数は、その上限・下限を指定することができます。 + +```erg +|A <: T| K(A) +|B :> T| K(B) +``` + +型変数リスト内では型変数の __変性指定__ を行っています。上の変性指定において、型変数`A`は型`T`に対する任意のサブクラスであり、型変数`B`は型`T`に対する任意のスーパークラスであると宣言されています。 +このとき、`T`を`A`に対する上限型、`B`に対する下限型ともいいます。 + +変性指定は重ねがけすることもできます。 + +```erg +# U < A < T +{... | A <: T; A :> U} +``` + +以下に変性指定を使ったコードの例を示します。 + +```erg +show|S <: Show| s: S = log s + +Nil T = Class(Impl=Phantom T) +Cons T = Class(Nil T or List T) +List T = Class {head = T; rest = Cons T} +List(T). + push|U <: T|(self, x: U): List T = Self.new {head = x; rest = self} + upcast(self, U :> T): List U = self +``` + +## 変性指定 + +`List T`の例については注意が必要なので、もう少し詳しく説明します。 +上のコードを理解するためには多相型の変性について知っておく必要があります。変性については[この項](./variance.md)で詳しく解説していますが、さしあたって必要となる事実は以下の3つです: + +* 通常の多相型、`List T`などは`T`に対して共変(`U > T`のとき`List U > List T`) +* 関数`T -> U`は引数型`T`に対して反変(`S > T`のとき`(S -> U) < (T -> U)`) +* 関数`T -> U`は戻り値型`U`に対して共変(`U > S`のとき`(T -> U) > (T -> S)`) + +例えば、`List Int`は`List Object`にアップキャスト可能、`Obj -> Obj`は`Int -> Obj`にアップキャスト可能であるということです。 + +ここで、メソッドの変性指定を省略した場合どうなるか考えます。 + +```erg +... +List T = Class {head = T; rest = Cons T} +List(T). + # List T can be pushed U if T > U + push|U|(self, x: U): List T = Self.new {head = x; rest = self} + # List T can be List U if T < U + upcast(self, U): List U = self +``` + +この場合でも、Ergコンパイラは`U`の上限・下限型をよしなに推論してくれます。 +ただし、Ergコンパイラはメソッドの意味を理解しないことに注意してください。コンパイラはただ変数・型変数の使われ方に従って機械的に型関係を推論・導出します。 + +コメントに書いてある通り、`List T`の`head`に入れられる型`U`は`T`のサブクラス(`T: Int`ならば`Nat`など)です。すなわち、`U <: T`と推論されます。この制約は`.push{U}`の引数型を変更するアップキャスト`(List(T), U) -> List(T) to (List(T), T) -> List(T)`(e.g. `List(Int).push{Object}`)を禁止します。ただし、`U <: T`という制約は関数の型の包含関係を改変しているわけではないことに注意してください。`(List(Int), Object) -> List(Int) to (List(Int), Int) -> List(Int)`である事実は変わらず、ただ`.push`メソッドにおいてはそのようなアップキャストを実行できないという意味になります。 +同様に、`List T`から`List U`へのキャストは`U :> T`という制約のもとで可能なので、そのように変性指定が推論されます。この制約は、`.upcast(U)`の戻り値型を変更するアップキャスト`List(T) -> List(T) to List(T) -> List(T)`(e.g. `List(Object).upcast(Int)`)を禁止します。 + +では、このアップキャストを許可するようにした場合はどうなるか考えます。 +変性指定を反転させてみましょう。 + +```erg +... +List T = Class {head = T; rest = Cons T} +List(T). + push|U :> T|(self, x: U): List T = Self.new {head = x; rest = self} + upcast(self, U :> T): List U = self +# TypeWarning: `U` in the `.push` cannot take anything other than `U == T`. Replace `U` with `T`. Or you may have the wrong variance specification. +# TypeWarning: `U` in the `.upcast` cannot take anything other than `U == T`. Replace `U` with `T`. Or you may have the wrong variance specification. +``` + +`U <: T`という制約と`U :> T`という変性指定の両方を充足するのは`U == T`のときだけです。なので、この指定にはほとんど意味がありません。 +実際は「`U == T`であるようなアップキャスト」=「`U`の箇所については変えないアップキャスト」のみが許可されています。 + +## Appendix: ユーザー定義型の変性 + +ユーザー定義型の変性は、デフォルトでは非変である。しかし、`Inputs/Outputs`というマーカートレイトで変性を指定することもできる。 +`Inputs(T)`と指定すると、その型は`T`に関して反変となる。 +`Outputs(T)`と指定すると、その型は`T`に関して共変となる。 + +```erg +K T = Class(...) +assert not K(Str) <= K(Object) +assert not K(Str) >= K(Object) + +InputStream T = Class ..., Impl: Inputs(T) +# Objectを受け入れるストリームは、Strを受け入れるともみなせる +assert InputStream(Str) > InputStream(Object) + +OutputStream T = Class ..., Impl: Outputs(T) +# Strを出力するストリームは、Objectを出力するともみなせる +assert OutputStream(Str) < OutputStream(Object) +``` diff --git a/doc/JA/syntax/type/advanced/widening.md b/doc/JA/syntax/type/advanced/widening.md new file mode 100644 index 00000000..26d3adf2 --- /dev/null +++ b/doc/JA/syntax/type/advanced/widening.md @@ -0,0 +1,92 @@ +# 型拡大(Type Widening) + +例えば以下のような多相関数を定義する。 + +```erg +ids|T|(x: T, y: T) = x, y +``` + +同じクラスのインスタンスペアを代入する分には何の問題もない。 +包含関係にある別のクラスのインスタンスペアを代入すると、大きい方にアップキャストされて同じ型になる。 +また、包含関係にない別のクラスを代入するとエラーになるのも容易に理解できる。 + +```erg +assert ids(1, 2) == (1, 2) +assert ids(1, 2.0) == (1.0, 2.0) +ids(1, "a") # TypeError +``` + +さて、では別の構造型を持つ型の場合はどうなるのだろうか。 + +```erg +i: Int or Str +j: Int or NoneType +ids(i, j) # ? +``` + +これの説明を行う前に、Ergの型システムが実は(実行時の)クラスを見ていないという事実に注目しなくてはならない。 + +```erg +1: {__valueclass_tag__ = Phantom Int} +2: {__valueclass_tag__ = Phantom Int} +2.0: {__valueclass_tag__ = Phantom Ratio} +"a": {__valueclass_tag__ = Phantom Str} +ids(1, 2): {__valueclass_tag__ = Phantom Int} and {__valueclass_tag__ = Phantom Int} == {__valueclass_tag__ = Phantom Int} +ids(1, 2.0): {__valueclass_tag__ = Phantom Int} and {__valueclass_tag__ = Phantom Ratio} == {__valueclass_tag__ = Phantom Ratio} # Int < Ratio +ids(1, "a"): {__valueclass_tag__ = Phantom Int} and {__valueclass_tag__ = Phantom Str} == Never # TypeError +``` + +クラスを見ていないというのは、正確には見られない場合があるからで、これはErgにおいてオブジェクトのクラスは実行時情報に属するためである。 +例えば、`Int or Str`型オブジェクトのクラスは`Int`または`Str`であるが、これがどちらなのかは実行してはじめてわかることである。 +もちろん`Int`型のオブジェクトのクラスは`Int`で確定であるが、この場合も型システムから見えるのは`Int`の構造型`{__valueclass_tag__ = Int}`である。 + +さて、別の構造型の例に戻ろう。結論から言うと上のコードは型があっていないとしてTypeErrorになる。 +しかし型注釈で型拡大を行えばコンパイルが通る。 + +```erg +i: Int or Str +j: Int or NoneType +ids(i, j) # TypeError: types of i and j not matched +# hint: try type widening (e.g. ids) +ids(i, j) # OK +``` + +`A and B`は以下の可能性がある。 + +* `A and B == A`: `A < B`または`A == B`のとき。 +* `A and B == B`: `A > B`または`A == B`のとき。 +* `A and B == {}`: `A != B`のとき。 + +`A or B`は以下の可能性がある。 + +* `A or B == A`: `A > B`または`A == B`のとき。 +* `A or B == B`: `A < B`または`A == B`のとき。 +* `A or B`は簡約不能(独立した型): `A != B`のとき。 + +## サブルーチン定義での型拡大 + +Ergでは、戻り値型が一致しない場合デフォルトでエラーとなる。 + +```erg +parse_to_int s: Str = + if not s.is_numeric(): + do parse_to_int::return error("not numeric") + ... # return Int object +# TypeError: mismatch types of return values +# 3 | do parse_to_int::return error("not numeric") +# └─ Error +# 4 | ... +# └ Int +``` + +これを解決するためには、戻り値型を明示的にOr型と指定する必要がある。 + +```erg +parse_to_int(s: Str): Int or Error = + if not s.is_numeric(): + do parse_to_int::return error("not numeric") + ... # return Int object +``` + +これは、サブルーチンの戻り値型に意図せず別の型を混入させないようにという設計である。 +ただし、戻り値型の選択肢が`Int`か`Nat`など包含関係がある型であった場合、大きい方に揃えられる。 diff --git a/doc/JA/syntax/type/guard.md b/doc/JA/syntax/type/guard.md new file mode 100644 index 00000000..12ade5e8 --- /dev/null +++ b/doc/JA/syntax/type/guard.md @@ -0,0 +1,18 @@ +# 型境界 (Type Bound) + +型境界は型指定に条件を加えるものである。これを実現する機能がガード(ガード節)である。 +関数シグニチャ、無名関数シグニチャのほか、篩型でもこの機能を利用できる。 +ガードは戻り値型の後に記述する。 + +## 述語式 (Predicate) + +変数の満たす条件を、`Bool`を返す式(述語式)で指定できる。 +使用できるのは[基本オブジェクト](./basic.md)と演算子だけである。 + +```erg +f a: [T; N] | T, N, N > 5 = ... +g a: [T; N | N > 5] | T, N = ... +Odd = {I: Int | I % 2 == 1} +R2Plus = {(L, R) | L, R: Ratio; L > 0 and R > 0} +GeneralizedOdd = {I | U; I <: Div(Nat, U); I % 2 == 0} +``` diff --git a/doc/JA/syntax/type/newtype.md b/doc/JA/syntax/type/newtype.md new file mode 100644 index 00000000..fd2c5167 --- /dev/null +++ b/doc/JA/syntax/type/newtype.md @@ -0,0 +1,31 @@ +# Newtype pattern + +ここでは、Rustでよく使われるnewtypeパターンのErg版を紹介します。 + +Ergはでは以下のように型のエイリアスを定義することができますが、これはあくまで同じ型を指します。 + +```erg +UserId = Int +``` + +なので、例えば`UserId`型の数値は8桁の正数、という仕様があったとしても、`Int`型と同じなので10でも-1でも入れられてしまうわけです。`Nat`にすれば-1は弾くことができますが、8桁の数という性質はErgの型システムのみでは表現できません。 + +また、例えばあるデータベースのシステムを設計する時、いくつかの種類のIDがあったとします。ユーザーID, 商品ID, 注文IDなどとIDの種類が増えてくると、関数に違う種類のIDを渡すというバグが発生する可能性があります。ユーザーIDと商品IDなどは構造的に等価であっても、意味論的には異なるわけです。 + +newtypeパターンはこのような場合に適したデザインパターンです。 + +```erg +UserId = Class {id = Nat} +UserId. + new id: Nat = + assert id.dights().len() == 8, else="UserId must be a positive number with length 8" + UserId::__new__ {id;} + +i = UserId.new(10000000) +print! i # <__main__.UserId object> +i + UserId.new(10000001) # TypeError: + is not implemented between `UserId` and `UserId` +``` + +コンストラクタが8桁の数という事前条件を保証してくれます。 +この`UserId`は`Nat`の持つメソッドをすべて失ってしまうので、必要な演算を都度再定義する必要があります。 +再定義するコストが見合わない場合は、継承を使う方がよいでしょう。逆にメソッドがなくなるという性質が望ましい場合もあるので、状況に応じて適切な方法を選んでください。 diff --git a/doc/JA/syntax/type/option.md b/doc/JA/syntax/type/option.md new file mode 100644 index 00000000..f33af4ff --- /dev/null +++ b/doc/JA/syntax/type/option.md @@ -0,0 +1 @@ +# Option diff --git a/doc/JA/syntax/type/overloading.md b/doc/JA/syntax/type/overloading.md new file mode 100644 index 00000000..6d6f903a --- /dev/null +++ b/doc/JA/syntax/type/overloading.md @@ -0,0 +1,89 @@ +# Overloading + +Ergでは __アドホック多相__ をサポートしない。すなわち、関数・カインドの多重定義(オーバーロード)ができない。が、トレイトクラスとパッチを組み合わせることでオーバーロードの挙動を再現できる。 +トレイトクラスのかわりにトレイトを使用しても良いが、その場合`.add1`を実装している型全てが対象になってしまう。 + +```erg +Add1 = Trait { + .add1: Self.() -> Self +} +IntAdd1 = Patch Int, Impl=Add1 +IntAdd1. + add1 self = self + 1 +RatioAdd1 = Patch Ratio, Impl=Add1 +RatioAdd1. + add1 self = self + 1.0 + +add1|X <: Add1| x: X = x.add1() +assert add1(1) == 2 +assert add1(1.0) == 2.0 +``` + +このような、ある型のサブタイプすべてを受け入れることによる多相を __サブタイピング多相__ と呼ぶ。Ergにおけるサブタイピング多相は列多相も含む。 + +各型での処理が完全に同じなら下のように書くこともできる。上の書き方は、クラスによって挙動を変える(が、戻り値型は同じ)場合に使う。 +型引数を使う多相を __パラメトリック多相__ という。パラメトリック多相は下のように部分型指定と併用する場合が多く、その場合はパラメトリック多相とサブタイピング多相の合わせ技ということになる。 + +```erg +add1|T <: Int or Str| x: T = x + 1 +assert add1(1) == 2 +assert add1(1.0) == 2.0 +``` + +また、引数の数が違うタイプのオーバーロードはデフォルト引数で再現できる。 + +```erg +C = Class {.x = Int; .y = Int} +C. + new(x, y |= 0) = Self::__new__ {.x; .y} + +assert C.new(0, 0) == C.new(0) +``` + +引数の数によって型が違うなど全く挙動が変わる関数は定義できないが、そもそも振る舞いが異なるならば別の名前を付けるべきであるというスタンスをErgは取る。 + +結論として、Ergがオーバーロードを禁止してサブタイピング+パラメトリック多相を採用したのは以下の理由からである。 + +まず、オーバーロードされた関数は定義が分散する。このため、エラーが発生した際に原因となる箇所を報告するのが難しい。 +また、サブルーチンをインポートすることによって、すでに定義されたサブルーチンの挙動が変わる恐れもある。 + +```erg +{id; ...} = import "foo" +... +id x: Int = x +... +id x: Ratio = x +... +id "str" # TypeError: id is not implemented for Str +# But... where did this error come from? +``` + +次に、デフォルト引数との相性が悪い。デフォルト引数のある関数がオーバーロードされているとき、どれが優先されるかという問題がある。 + +```erg +f x: Int = ... +f(x: Int, y |= 0) = ... + +f(1) # which is chosen? +``` + +さらに、宣言との相性が悪い。 +宣言`f: Num -> Num`は、どちらの定義のことを指しているのか特定できない。`Int -> Ratio`と`Ratio -> Int`は包含関係がないためである。 + +```erg +f: Num -> Num +f(x: Int): Ratio = ... +f(x: Ratio): Int = ... +``` + +そして、文法の一貫性を損なう。Ergは変数の再代入を禁止するが、オーバーロードの文法は再代入のように見えてしまう。 +無名関数に置換することもできない。 + +```erg +# same as `f = x -> body` +f x = body + +# same as... what? +f x: Int = x +f x: Ratio = x +``` diff --git a/doc/JA/syntax/type/special.md b/doc/JA/syntax/type/special.md new file mode 100644 index 00000000..b4069260 --- /dev/null +++ b/doc/JA/syntax/type/special.md @@ -0,0 +1,52 @@ +# 特殊型(Self, Super) + +`Self`は自身の型を表します。単にエイリアスとして使うことも出来ますが、派生型中では意味が変わる(自身の型を指す)ので注意してください。 + +```erg +@Inheritable +C = Class() +C. + new_self() = Self.new() + new_c() = C.new() +D = Inherit C + +classof D.new_self() # D +classof D.new_c() # C +``` + +`Super`は基底クラスの型を表します。メソッド自体は基底クラスのものを参照しますが、インスタンスは自身の型を使います。 + +```erg +@Inheritable +C = Class() + +D = Inherit(C) +D. + new_super() = Super.new() + new_c() = C.new() + +classof D.new_super() # D +classof D.new_c() # C +``` + +## 特殊型変数 + +`Self`, `Super`は、構造型・トレイト中では型変数として使用できます。これは、その型のサブタイプであるところのクラスを指します。すなわち、型`T`中で`Self`は`Self <: T`を意味します。 + +```erg +Add R, O = Trait { + .`_+_`: Self, R -> O +} +BinAdd = Subsume Add(Self, Self.AddO), { + .AddO = Type +} + +IntIsBinAdd = Patch(Int, Impl: BinAdd) +IntIsBinAdd. + AddO = Int + +assert 1 in Add(Int, Int) +assert 1 in BinAdd +assert Int < Add(Int, Int) +assert Int < BinAdd +``` diff --git a/doc/JA/tips.md b/doc/JA/tips.md new file mode 100644 index 00000000..012f79d7 --- /dev/null +++ b/doc/JA/tips.md @@ -0,0 +1,135 @@ +# Tips + +## エラーの表示言語を変えたい + +各国語版のergをダウンロードしてください。 +ただし、標準ライブラリ以外では多言語対応がなされていない可能性があります。 + +## レコードの特定の属性だけ可変化したい + +```erg +record: {.name = Str; .age = Nat; .height = CentiMeter} +{height; rest; ...} = record +mut_record = {.height = !height; ...rest} +``` + +## 変数のシャドーイングがしたい + +Ergで同一スコープ内でのシャドーイングはできません。しかし、スコープが変われば定義しなおせるので、インスタントブロックを使うといいでしょう。 + +```erg +# T!型オブジェクトを取得し、最終的にT型として変数へ代入 +x: T = + x: T! = foo() + x.bar!() + x.freeze() +``` + +## final class(継承不能クラス)を何とかして再利用したい + +ラッパークラスを作りましょう。これはいわゆるコンポジション(合成)と言われるパターンです。 + +```erg +FinalWrapper = Class {inner = FinalClass} +FinalWrapper. + method self = + self::inner.method() + ... +``` + +## 文字列でない列挙型が使いたい + +以下のようにして、他の言語でよく見られる伝統的な列挙型(代数的データ型)を定義できます。 +`Singleton`を実装すると、クラスとインスタンスが同一視されます。 +また、`Enum`を使うと、その選択肢となる型がリダイレクト属性として自動的に定義されます。 + +```erg +Ok = Class Impl: Singleton +Err = Class Impl: Singleton +ErrWithInfo = Inherit {info = Str} +Status = Enum Ok, Err, ErrWithInfo +stat: Status = Status.cons(ErrWithInfo) {info = "error caused by ..."} +match! stat: + Status.Ok -> ... + Status.Err -> ... + Status.ErrWithInfo.{info;} -> ... +``` + +```erg +Status = Enum Ok, Err, ErrWithInfo +# is equivalent to +Status = Class Ok or Err or ErrWithInfo +Status. + Ok = Ok + Err = Err + ErrWithInfo = ErrWithInfo +``` + +## 1始まりでenumerateしたい + +method 1: + +```erg +arr = [...] +for! arr.iter().enumerate(start: 1), i => + ... +``` + +method 2: + +```erg +arr = [...] +for! arr.iter().zip(1..), i => + ... +``` + +## 非公開APIを(ホワイトボックス)テストしたい + +`foo.er`の非公開APIは`foo.test.er`というモジュールでは特別にアクセス可能となります。 +`foo.test.er`モジュールはインポートできないので、隠蔽性は保たれます。 + +```erg +# foo.er +private x = ... +``` + +```erg +# foo.test.er +foo = import "foo" + +@Test +'testing private' x = + ... + y = foo::private x + ... +``` + +## 外部からはread-onlyな(可変)属性を定義したい + +属性をプライベートにして、ゲッタを定義するとよいでしょう。 + +```erg +C = Class {v = Int!} +C:: + inc_v!(ref! self) = self::v.inc!() + ... +C. + get_v(ref self): Int = self::v.freeze() + ... +``` + +## 引数名を型システム上で識別させたい + +引数をレコードで受け取ると良いでしょう。 + +```erg +Point = {x = Int; y = Int} + +norm: Point -> Int +norm({x: Int; y: Int}): Int = x**2 + y**2 +assert norm({x = 1; y = 2}) == norm({y = 2; x = 1}) +``` + +## 警告を出さないようにしたい + +Ergに警告を止めるオプションはありません(これは意図的な設計です)。コードを書き直してください。 diff --git a/doc/JA/tools/build.md b/doc/JA/tools/build.md new file mode 100644 index 00000000..193be4cc --- /dev/null +++ b/doc/JA/tools/build.md @@ -0,0 +1,14 @@ +# buildサブコマンド + +buildサブコマンドでは、パッケージのビルドを行います。 +デフォルトのビルドで行われる工程は、以下の通りです。 + +1. コメント/ドキュメント(doc以下のmdファイル)内のコードを検査する。 +2. パッケージに必要なコードをコンパイルする。 +3. アプリケーションパッケージの場合は、コマンド相当のバッチファイルまたはシェルスクリプトを生成する。 +4. テストを実行する。 + +ビルド終了後の成果物は以下のディレクトリに出力されます。 + +* デバッグビルド時: build/debug +* リリースビルド時: build/release diff --git a/doc/JA/tools/env.md b/doc/JA/tools/env.md new file mode 100644 index 00000000..030c373c --- /dev/null +++ b/doc/JA/tools/env.md @@ -0,0 +1,7 @@ +# envサブコマンド + +envサブコマンドはerg実行環境の指定を行います。 +`erg env new [env name]`で新しい実行環境を作成します。対話ツールが開き、ergのバージョンを指定すると、そのバージョンのergがインストール(すでにあれば流用されます)され、新しい環境として使えるようになります。 +`erg env switch [env name]`で環境の切り替えができます。 +作成された環境は`erg env edit`で編集でき、パッケージをプリインストールしたり、他言語の依存関係を指定できる。 +このコマンドの最大の特徴は`erg env export`で環境を再現する情報を`[env name].env.er`ファイルとして出力できる点である。これにより、他人と同じ環境ですぐに開発を始められる。さらに`erg env publish`でパッケージのように環境を公開できる。 diff --git a/doc/JA/tools/fmt.md b/doc/JA/tools/fmt.md new file mode 100644 index 00000000..adc6165c --- /dev/null +++ b/doc/JA/tools/fmt.md @@ -0,0 +1,6 @@ +# fmt + +fmtサブコマンドででコードフォーマットが出来ます。 +よく使用されるフラッグは以下の通りです。 + +* explicit-type: 型指定が省略されている箇所を自動的に補完します。 diff --git a/doc/JA/tools/index.md b/doc/JA/tools/index.md new file mode 100644 index 00000000..e69de29b diff --git a/doc/JA/tools/install.md b/doc/JA/tools/install.md new file mode 100644 index 00000000..d8a1cc8f --- /dev/null +++ b/doc/JA/tools/install.md @@ -0,0 +1,10 @@ +# installサブコマンド + +installでレジストリサイトに登録されたパッケージをインストールできる。 +基本的な使い方はcargoなどのパッケージマネージャと同じ。 + +## 便利機能 + +* 似た名前のパッケージ名があり、そちらのほうが10倍以上ダウンロード数が多かった場合、間違えて入力したのではないかというサジェスチョンが出る。これにより、typo squattingを防止できる。 +* パッケージサイズが大きい場合(50MB以上)、サイズを表示して本当にインストールするかサジェスチョンする。 +* パッケージがduplicatedになっていた場合、代替のパッケージをサジェスチョンする。 diff --git a/doc/JA/tools/pack.md b/doc/JA/tools/pack.md new file mode 100644 index 00000000..0363fd0e --- /dev/null +++ b/doc/JA/tools/pack.md @@ -0,0 +1,100 @@ +# パッケージマネージャー + +Ergは標準でパッケージマネージャーが付属しており、`pack`サブコマンドで呼び出せる。 +以下は典型的なオプションである。 + +* `erg pack init`: 現在のディレクトリをパッケージとして初期化する。`package.er`ファイルや`src`ディレクトリが生成される。`app`と指定すると実行ファイルのパッケージ、`lib`と指定するとライブラリのパッケージ、`hybrid`を指定すると両方のパッケージとなる。`--license`を指定すると自動でライセンスファイルを置いてくれる。 +* `erg pack build`: パッケージをビルドする。`--release`をつけるとテストが実行され、最適化をする。成果物は`build/debug`か`build/release`に配置される。 +* `erg pack install`: パッケージをインストールする。ライブラリの場合は`.erg/lib`に`src`以下が置かれ、アプリケーションは`.erg/app`にシェルスクリプトとして置かれる。`--release`をつけると最適化をする。 +* `erg pack run`: パッケージをビルドしてアプリケーションを実行する(appパッケージのみ)。 +* `erg pack clean`: buildディレクトリの中身を削除します。 +* `erg pack test`: パッケージのテストを行う。詳しくは[test.md](./test.md)を参照。 +* `erg pack publish`: パッケージを公開/リリースします。GitHubのアカウントと公開鍵が必要です。 + +なお、このドキュメントでは自前のパッケージを管理する際の方法を説明する。 +外部パッケージをインストールしたり検索したりしたい場合は[install.md](./install.md)を参照。 +また、Ergのパッケージシステムについては[package_system.md](../syntax/33_package_system.md)を参照。 + +## パッケージ全体の標準ディレクトリ構成(アプリケーションパッケージの場合) + +```console +/package # パッケージのルートディレクトリ + /build # ビルド結果を格納するディレクトリ + /debug # デバッグビルド時の成果物 + /release # リリースビルド時の成果物 + /doc # ドキュメント(さらに`en`, `ja`などのサブディレクトリに分けることで各国語対応可能) + /src # ソースコード + /main.er # main関数を定義するファイル + /tests # (ブラックボックス)テストファイルを格納するディレクトリ + /package.er # パッケージの設定を定義するファイル +``` + +## package.er + +`erg pack init`すると以下のようなファイル、`package.er`が生成される。`package.er`にはパッケージの設定を記述する。 +以下は`package.er`の記述例である。 + +```erg +name = "example" # package name +author = "John Smith" # package author name +version = "0.1.0" +description = "An awesome package" +categories = ["cli"] # package categories +type = "app" # "app" or "lib" +license = "" # e.g. "MIT", "APACHE-2.0", "MIT OR Apache-2.0" +pre_build = "" # script filename to be executed before build +post_build = "" # script filename to be executed after build +dependencies = { + # The latest one is selected if the version is not specified + # If the version specification is omitted, the package manager automatically adds the version of the last successful build to the comments + foo = pack("foo") # [INFO] the last successfully built version: 1.2.1 + # Packages can be renamed + bar1 = pack("bar", "1.*.*") # [INFO] the last successfully built version: 1.2.0 + bar2 = pack("bar", "2.*.*") # [INFO] the last successfully built version: 2.0.0 + baz = pack("baz", "1.1.0") +} +deprecated = False +successors = [] # alternative packages (when a package is deprecated) +``` + +## セマンティックバージョニング + +Ergのパッケージは[セマンティックバージョニング](https://semver.org/lang/ja/)に基づいてバージョンの指定を行います。 +セマンティックバージョニングとは、大まかには`x.y.z`(x,y,zは0以上の整数)の書式で指定されるバージョニングです。 +それぞれの数字の意味は以下のようになります。 + +* x: メジャーバージョン(互換性を破壊する更新を行うとき1上げる) +* y: マイナーバージョン(互換性のある更新(API追加・非推奨化など)を行うとき1上げる、バグ修正などはパッチバージョンアップで対応する) +* z: パッチバージョン(バグ修正・互換性を保つ軽微な変更を行うとき1上げる、互換性を破壊する深刻な修正はメジャーバージョンアップで対応する) + +ただしバージョン`0.*.*`の変更はデフォルトで常に互換性がありません。互換性を保ったままバージョンアップしたい場合は後ろに`-compatible`と指定します(Erg独自ルール)。例えば、`0.2.1`を互換性を保ったまま機能追加したい、すなわち`0.3.0`にバージョンアップしたい場合は`0.3.0-compatible`と指定します。またバグフィックスを行った場合は`0.2.2-compatible`と指定します。 +こうすると、そのバージョンは直前のバージョンと互換性があると見なされるようになります。 +これは`0.*.*`を`1.0.0`にバージョンアップしたい場合でも使えます。すなわち、`1.0.0-compatible`は直前のバージョン`0.y.z`と互換性があります。 + +セマンティックバージョニングはロックファイルを生成する際非常に重要です。ロックファイルは依存パッケージの互換性を保つために生成されるファイルで、依存パッケージの新しいリリースがあっても明示的にアップデートしない限り古いパッケージに依存します。 +ロックファイルは依存パッケージのあるパッケージを複数人で開発する際に便利です。また、依存パッケージがさらに依存するパッケージについて、互換性があるならばパッケージを使いまわすことができるので、ローカルストレージの節約にもなります。 + +Ergのパッケージマネージャは以上のルールを厳密に適用しており、ルールに抵触するパッケージ更新は拒絶されます。 +Ergパッケージマネージャはバージョン管理システム(git等)と連携しており、パッケージのpublish時にコードの差分を検知し、バージョニングの正当性を検証します。 +具体的に言うと、パッケージマネージャはAPIの型を見ます。型が古いバージョンのサブタイプになっていれば、変更は互換性があるとみなされます(これは完全な検証ではないことに注意してください。型的には互換でも意味論的に非互換な変更はあり得ます。これを判断するのは開発者の仕事です)。 + +さらにパッケージはリポジトリ全体がレジストリに登録されるため、開発者であってもパッケージマネージャを通さずにパッケージの更新をすることは出来ません。 +また、パッケージは非推奨にはできても削除することはできません。 + +### Appendix: セマンティックバージョニングの問題と、その対策 + +セマンティックバージョニングには既知の問題が(少なくとも)2つあります。 +まず、セマンティックバージョニングは過大な制約を課す可能性があります。 +セマンティックバージョニングでは、たった1つの非互換なAPI変更でパッケージ全体のメジャーバージョンが上がってしまいます。 +こうなると、「新しいAPIを試したかったが、別の非互換なAPI変更に対処しなくてはならないのでバージョンアップを見送る」といったことが発生します。 +もう一つ、セマンティックバージョニングは過大な約束をする可能性があります。 +前項で述べたように、APIの「互換性ある変更」は理論的に証明できるものではありません。バージョン`1.0.1`のパッケージがほしいと指定した場合、セマンティックバージョニングの観点では`1.0.1`以上`2.0.0`未満のパッケージ全てを代わりに使うことができます(`1.0.0`は使えません。バグ修正が入ったからです)が、実際はパッケージ開発者の意図しないAPI利用によってビルドが成功しない可能性があります。 + +Ergではこの問題に対処するため、別のバージョンのパッケージを(リネームすることで)同時に利用することができるという方策を取っています。これによって、ver2のAPIを一部導入しながら、ver1のAPIも引き続き利用するといった事が可能になります。 +さらに、あまり望ましい状態ではありませんが、ある特定のマイナーバージョンのAPIのみがバグなしに使えるといった場合にそれだけを残して次のバージョンへ進むことが可能です。 + +## publish + +`publish`サブコマンドでパッケージの公開が可能です。公開にはGitHubアカウントが必要です。 +パッケージはデフォルトでは`(owner_name)/(package_name)`で登録されます。一定の条件(ダウンロード数、メンテナンスの頻度など)を満たすとオーナー名を省略したエイリアスを登録する申請が出来ます。 +なおパッケージ名の大文字/小文字や`_`, `-`などの区切り文字は区別されません。 diff --git a/doc/JA/tools/repl.md b/doc/JA/tools/repl.md new file mode 100644 index 00000000..de31cc24 --- /dev/null +++ b/doc/JA/tools/repl.md @@ -0,0 +1,13 @@ +# REPL + +`erg`コマンドを引数を与えず実行すると、REPLが起動されます。また、`repl`サブコマンドを指定して起動することもできます。 +さらに以下のフラグを指定できます。 + +* typed: オブジェクトとその型を表示します。 + +```console +>>> 1 +1: {1} +>>> id x = x +id = : |T: Type| T -> T +``` diff --git a/doc/JA/tools/test.md b/doc/JA/tools/test.md new file mode 100644 index 00000000..ac05c26f --- /dev/null +++ b/doc/JA/tools/test.md @@ -0,0 +1,45 @@ +# testサブコマンド + +ergコマンドにはtestというサブコマンドがあり、テスト実装、及び実行の支援を行う。 + +## Testデコレータ(@Test) + +Ergではパッケージ中の`tests`ディレクトリか`*.test.er`ファイル中の`@Test`を付けたサブルーチンを`erg test`コマンドでテストする。 +`tests`のサブルーチンはブラックボックステスト、`*.test.er`のサブルーチンはホワイトボックステストを担当する。 + +```erg +# tests/test1.er +{add; ...} = import "foo" + +@Test +test_1_plus_n(n: Nat) = + assert add(1, n) == n + 1 +``` + +実行結果がサマリとして表示され、各種ファイル形式(.md, .csv, etc.)で出力もできる。 + +## Doc Test + +Ergでは`#`, `#[`でコメント行となるが、`##`, `#[[`でdoc commentとなり、VSCodeなどエディタからコメントをmd表示できる。 +さらにdoc comment中のソースコードはergと指定されていれば、erg testコマンドで自動テストされる。 +以下はテストの例である。 + +```erg +VM = ... + ... + #[[ + execute commands. + ```erg + # VM in standard configuration + {vm1; ...} = import "tests/template" + + assert vm1.exec!("i = 0") == None + assert vm1.exec!("i").try_into(Int)? == 0 + ``` + ]]# + .exec! ref self, src = + ... + ... +``` + +テストを行う際に使う典型的なオブジェクトは`tests/template`モジュールに定義する。 diff --git a/examples/add.er b/examples/add.er new file mode 100644 index 00000000..8a1e002c --- /dev/null +++ b/examples/add.er @@ -0,0 +1,13 @@ +Point = Class {x = Int; y = Int}, Impl: Add() and Eq() +Point. + new x, y = Self::__new__ {x; y} + norm &self = self::x**2 + self::y**2 + @Impl Add() + `+` self, other = + Self.new(self::x + other::x, self::y + other::y) + +p = Point.new 1, 2 +q = Point.new 3, 4 +r = p + q +assert r == Point.new 4, 6 +assert r.norm() == 52 diff --git a/examples/array.er b/examples/array.er new file mode 100644 index 00000000..08cf0495 --- /dev/null +++ b/examples/array.er @@ -0,0 +1,16 @@ +immut_arr = [1, 2, 3, 4, 5] +mut_content_arr = [1, 2, 3, 4, 5].into [Int!; 5] +telescoping_arr = [1, 2, 3, 4, 5].into [Int; !5] +mut_arr = [1, 2, 3, 4, 5].into [Int!; !5] + +mut_content_arr.map!(x -> x + 1) +# A mutable array class inherits an immutable array class, so a mutable array can compare with an immutable array +assert mut_content_arr == [2, 3, 4, 5, 6] +# This is invalid: telescoping_arr.map!(x -> x + 1) +telescoping_arr.push!(6) +assert telescoping_arr == [1, 2, 3, 4, 5, 6] +# This is invalid: mut_content_arr.push!(6) + +# NOTE: `telescoping_arr` can actually do the same thing as `mut_content_arr` or `mut_arr`. +# `mut_arr[0].inc!()` is the same as `telescoping_arr.insert! 0, telescoping_arr.remove!(0) + 1`. +# The important thing is that the length of `mut_content_arr` will not change. This gives advantages such as guaranteed success of accessing. diff --git a/examples/class.er b/examples/class.er new file mode 100644 index 00000000..4a708b49 --- /dev/null +++ b/examples/class.er @@ -0,0 +1,17 @@ +@Inheritable +Point2D = Class {x = Int; y = Int} +Point2D. + new x, y = Self::__new__ {x; y} + norm ref self = self::x**2 + self::y**2 + +Point3D = Inherit Point2D, Additional: {z = Int} +Point3D. + @Override + new x, y, z = Self::__new__ {x; y; z} + @Override + norm ref self = self::x**2 + self::y**2 + self::z**2 + +UnpackPoint2D = Class {x = Int; y = Int}, Impl: Unpack + +p = UnpackPoint2D.{x = 1; y = 2} +UnpackPoint2D.{x; y} = p diff --git a/examples/control.er b/examples/control.er new file mode 100644 index 00000000..00dd8561 --- /dev/null +++ b/examples/control.er @@ -0,0 +1,33 @@ +cond = True +s = if cond: + do "then block" + do "else block" +assert s == "then block" + +# else: binary operator +x = cond.then 1 else 2 +assert x == 1 + +if! cond: + do!: + print! "then block" + do!: + print! "else block" + +a = [1, 2, 3] +sum = match a: + [x, y, z] -> x + y + z + (x, y, z) -> x + y + z + {x; y; z} -> x + y + z + i: Int -> i + _ -> panic "unknown object" + +for! 0.., i => + print! "i = {i}" + if i >= 100: + do return break() + +counter = !100 +while! not counter.is_zero(), do!: + print! "counter = {counter}" + counter.dec!() diff --git a/examples/dependent.er b/examples/dependent.er new file mode 100644 index 00000000..106b6c3d --- /dev/null +++ b/examples/dependent.er @@ -0,0 +1,16 @@ +Queue! T: Type, N: Nat! = Class {.payload = [T; !N]} +Queue!. + new = Self!(*, 0)::__new__ {.payload = []} +Queue!(T, N). + enqueue!(ref!(self(T, N ~> N+1)), x: T) = + self.payload.push! x + dequeue!(ref! self(T, N ~> N-1)): T = + self.payload.remove!(0) + +q = Queue!.new() +q.enqueue!(1) +q.enqueue!(2) +q: Queue!(Int, !2) +assert q.dequeue!() == 1 +assert q.dequeue!() == 2 +# q.dequeue!() will cause a TypeError diff --git a/examples/dict.er b/examples/dict.er new file mode 100644 index 00000000..2001bdc5 --- /dev/null +++ b/examples/dict.er @@ -0,0 +1,7 @@ +immut_dict: {Str: Int} = {"Alice": 1, "Bob": 2, "Charlie": 3} +# can insert / remove an element +telescoping_dict = {"Alice": 1, "Bob": 2, "Charlie": 3}.into {Str: Int; !*} +telescoping_dict.insert!("Dave", 4) +_ = telescoping_dict.remove!("Alice") +mut_content_dict: {Str: !Int} = {"Alice": !1, "Bob": !2, "Charlie": !3} +mut_content_dict["Bob"].update! 0 diff --git a/examples/enum.er b/examples/enum.er new file mode 100644 index 00000000..7d59878c --- /dev/null +++ b/examples/enum.er @@ -0,0 +1,40 @@ +LitExpr = Class {.i = Int}, Impl: Show +LitExpr. + new i = Self::__new__ {.i;} + show &self = "{self.i}" +AddExpr = Class {.lhs = Expr, .rhs = Expr}, Impl: Show +AddExpr. + new lhs, rhs = Self::__new__ {.lhs; .rhs} + show &self = "{self.lhs} + {self.rhs}" +SubExpr = Class {.lhs = Expr, .rhs = Expr}, Impl: Show +SubExpr. + new lhs, rhs = Self::__new__ {.lhs; .rhs} + show &self = "{self.lhs} - {self.rhs}" +PosExpr = Class {.expr = Expr}, Impl: Show +PosExpr. + new expr = Self::__new__ {.expr;} + show &self = "+{self.expr}" +NegExpr = Class {.expr = Expr}, Impl: Show +NegExpr. + new expr = Self::__new__ {.expr;} + show &self = "-{self.expr}" + +Expr = Enum: + LitExpr + AddExpr + SubExpr + NegExpr +Expr. + lit = Self.cons(LitExpr) + add = Self.cons(AddExpr) + eval self = + match self: + l: Expr.LitExpr -> l.i + a: Expr.AddExpr -> a.lhs + a.rhs + s: Expr.SubExpr -> s.lhs - s.rhs + p: Expr.PosExpr -> p.expr + n: Expr.NegExpr -> -n.expr + +expr = Expr.add Expr.lit(1), Expr.lit(2) +print! expr # 1 + 2 +assert expr.eval() == 3 diff --git a/examples/fib.er b/examples/fib.er new file mode 100644 index 00000000..7789d5ea --- /dev/null +++ b/examples/fib.er @@ -0,0 +1,6 @@ +fib 0 = 0 +fib 1 = 1 +# a type annotation is required for the recursive function +fib(n: Nat): Nat = fib(n-1) + fib(n-2) + +assert fib(10) == 55 diff --git a/examples/helloworld.er b/examples/helloworld.er new file mode 100644 index 00000000..6dc7837f --- /dev/null +++ b/examples/helloworld.er @@ -0,0 +1,7 @@ +print! "Hello, world!" +print! "こんにちは、世界!" +print! "Γειά σου Κόσμε!" +print! "!مرحبا بالعالم" + +greeting = "Hello" +print! "{greeting}, world!" diff --git a/examples/list.er b/examples/list.er new file mode 100644 index 00000000..083ca0cc --- /dev/null +++ b/examples/list.er @@ -0,0 +1,18 @@ +Nil T = Class Impl: Phantom(T) and Eq +Cons T, N = Inherit {head = T; rest = List(T, N-1)} +List: (Type, Nat) -> Type +List T, 0 = Class Nil T +List T, N = Class Cons(T, N), Impl: Eq +List. + nil T = List(T, 0).new Nil(T).new() + cons|T, N| rest: List(T, N-1), head: T = List(T, N).new Cons(T, N).{head; rest} +{nil, cons} = List + +a = cons(nil(Int), 1) |> cons 2 |> cons 3 +match a: + Cons(_, _).{head=h1; rest=Cons(_, _).{head=h2; rest}} -> + assert h1 == 3 + assert h2 == 2 + assert rest == Cons.{head = 1; rest = nil(Int)} + _ -> + pass diff --git a/examples/patch.er b/examples/patch.er new file mode 100644 index 00000000..5687bada --- /dev/null +++ b/examples/patch.er @@ -0,0 +1,12 @@ +Binary = Patch {0, 1} +Binary. + invert self = + if self == 0, 1 else 0 +assert 1.invert() == 0 +assert 0.invert() == 1 + +Nat = Patch {I | I >= 0} +Nat.times! self, block! = + for! 0.. block!() +10.times! do!: + print! "!" diff --git a/examples/quantified.er b/examples/quantified.er new file mode 100644 index 00000000..2cbf2268 --- /dev/null +++ b/examples/quantified.er @@ -0,0 +1,12 @@ +id|T|(x: T): T = x +assert id(1) == 1 +assert id(True) == True +assert id("hello") == "hello" + +const|T, C|(c: C): C = _: T -> c +assert const(1)(2) == 1 +assert const(True)(2) == True + +print_to_str!|S <: Show|(s: S): Str = + print! s + s.to_str() diff --git a/examples/rank2.er b/examples/rank2.er new file mode 100644 index 00000000..c73cfe5f --- /dev/null +++ b/examples/rank2.er @@ -0,0 +1,38 @@ +id x = x +add1 x = x + 1 + +Reversible = Trait { + .reverse = Self.() -> Self.RevReturnType + .RevReturnType = Type +} +Rev4Bool = Patch Bool, Impl=Reversible +Rev4Bool + .reverse self = not self + .RevReturnType = Bool +# Str already has the 'reverse' method +Rev4Str = Patch Str, Impl=Reversible +Rev4Str + .RevReturnType = Str +Rev4Nat = Patch Nat, Impl=Reversible +Rev4Nat + .reverse self = -self + .RevReturnType = Neg + +reverse x = x.reverse() # |R <: Reversible| R -> R.RevReturnType +assert False == reverse True +assert "olleh" == reverse "hello" +assert -5 == reverse 5 + +# rank-1 +# : {(T -> U, (T, T, T)) -> (U, U, U) | T, U: Type} +triple_map|T| f, (l: T, c: T, r: T) = f(l), f(c), f(r) + +assert triple_map(id, (1, 2, 3)) == (1, 2, 3) +assert triple_map(add1, (1, 2, 3)) == (2, 3, 4) + +# rank-2 +# : {(F, (T, U, V)) -> (W, X, Y) | F, T, U, V, W, X, Y: Type; F <: T -> W; F <: U -> X; F <: V -> Y} +triple_map2|T, U, V| f, (l: T, c: U, r: V) = f(l), f(c), f(r) + +assert triple_map2(id, (True, "a", 5)) == (True, "a", 5) +assert triple_map2(reverse, (True, "hello", 5)) == (False, "olleh", -5) diff --git a/examples/record.er b/examples/record.er new file mode 100644 index 00000000..da23ecaa --- /dev/null +++ b/examples/record.er @@ -0,0 +1,21 @@ +# Record is a feature similar to object (literal notation) in JS +# `.` means the field is public +john = { + .name = "John Smith" + .age = !27 +} + +assert john.name == "John Smith" +assert john.age == 27 +john.age.update! old -> old + 1 +assert john.age == 28 +# Record is not Dict, so `john["name"]` is invalid + +# A record whose values are all types will also behave as a type +Person! = { + .name = Str + .age = Nat! +} + +assert Person!.age == Str +assert john in Person! diff --git a/examples/slot.ts b/examples/slot.ts new file mode 100644 index 00000000..05ba736d --- /dev/null +++ b/examples/slot.ts @@ -0,0 +1,19 @@ +const assert = console.assert + +const john = { + name: "John Smith", + age: 27, +} + +assert(john.name == "John Smith") + +type Person = { + readonly name: String, + readonly age: number, +} + +// assert(Person['age'] == String) + +function isPerson(arg): arg is Person { return true } + +assert(isPerson(john)) diff --git a/examples/tuple.er b/examples/tuple.er new file mode 100644 index 00000000..66bd3724 --- /dev/null +++ b/examples/tuple.er @@ -0,0 +1,8 @@ +p = (1, 2) +assert p.0 == 1 + +q = (1, 1.0) +assert q.1 == 1.0 + +i, j = 0, 1 +assert i == 0 diff --git a/src/common/Cargo.toml b/src/common/Cargo.toml new file mode 100644 index 00000000..06f92203 --- /dev/null +++ b/src/common/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "common" +version = "0.1.0" +description = "A common components library of Erg" +authors = ["Shunsuke Shibayama "] +license = "MIT OR Apache-2.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +debug = [] +japanese = [] + +[dependencies] + +[lib] +path = "lib.rs" diff --git a/src/common/build.rs b/src/common/build.rs new file mode 100644 index 00000000..38e0df91 --- /dev/null +++ b/src/common/build.rs @@ -0,0 +1,17 @@ +use std::process::Command; +// use std::io::Write; + +mod datetime; + +fn main() -> std::io::Result<()> { + // recording the build date and the git hash + let output = Command::new("git") + .args(&["rev-parse", "--short", "HEAD"]) + .output() + .expect("failed to get the git hash"); + let git_hash_short = String::from_utf8(output.stdout).unwrap(); + let now = datetime::now(); + println!("cargo:rustc-env=GIT_HASH_SHORT={git_hash_short}"); + println!("cargo:rustc-env=BUILD_DATE={now}"); + Ok(()) +} diff --git a/src/common/cache.rs b/src/common/cache.rs new file mode 100644 index 00000000..c701da9b --- /dev/null +++ b/src/common/cache.rs @@ -0,0 +1,64 @@ +use std::rc::Rc; +use std::cell::RefCell; +use std::borrow::{Borrow, ToOwned}; +use std::hash::Hash; + +use crate::set::Set; +use crate::{Str, RcArray}; + +#[derive(Debug)] +pub struct Cache(RefCell>>); + +impl Default for Cache { + fn default() -> Self { Self::new() } +} + +impl Cache { + pub fn new() -> Self { Self(RefCell::new(Set::new())) } +} + +impl Clone for Cache { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Clone for Cache { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl Cache { + pub fn get(&self, s: &str) -> Str { + if let Some(cached) = self.0.borrow().get(s) { + return cached.clone().into() + } // &self.0 is dropped + let s = Str::rc(s); + self.0.borrow_mut().insert(s.clone().into_rc()); + s + } +} + +impl Cache<[T]> { + pub fn get(&self, q: &[T]) -> Rc<[T]> { + if let Some(cached) = self.0.borrow().get(q) { + return cached.clone() + } // &self.0 is dropped + let s = RcArray::from(q); + self.0.borrow_mut().insert(s.clone()); + s + } +} + +impl Cache { + pub fn get(&self, q: &Q) -> Rc + where Rc: Borrow, Q: ToOwned { + if let Some(cached) = self.0.borrow().get(q) { + return cached.clone() + } // &self.0 is dropped + let s = Rc::from(q.to_owned()); + self.0.borrow_mut().insert(s.clone()); + s + } +} diff --git a/src/common/codeobj.rs b/src/common/codeobj.rs new file mode 100644 index 00000000..b71b5a17 --- /dev/null +++ b/src/common/codeobj.rs @@ -0,0 +1,480 @@ +use std::fmt; +use std::fs::File; +use std::io::{BufReader, Read, Write}; +use std::path::Path; + +use crate::{Str}; +use crate::impl_display_from_debug; +use crate::value::ValueObj; +use crate::opcode::Opcode; +use crate::python_util::detect_magic_number; +use crate::serialize::*; +use crate::traits::HasType; +use crate::ty::{TypePair, Type}; +use crate::deserialize::{Deserializer, DeserializeResult}; + +pub fn consts_into_bytes(consts: Vec) -> Vec { + let mut tuple = vec![]; + if consts.len() > u8::MAX as usize { + tuple.push(DataTypePrefix::Tuple as u8); + tuple.append(&mut (consts.len() as u32).to_le_bytes().to_vec()); + } else { + tuple.push(DataTypePrefix::SmallTuple as u8); + tuple.push(consts.len() as u8); + } + for obj in consts { + tuple.append(&mut obj.into_bytes()); + } + tuple +} + +/// Bit masks for CodeObj.flags +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum CodeObjFlags { + Optimized = 0x0001, + NewLocals = 0x0002, + VarArgs = 0x0004, + VarKeywords = 0x0008, + Nested = 0x0010, + Generator = 0x0020, + NoFree = 0x0040, + Coroutine = 0x0080, + IterableCoroutine = 0x0100, + AsyncGenerator = 0x0200, + // CO_GENERATOR_ALLOWED = 0x0400, + FutureDivision = 0x2000, + FutureAbsoluteImport = 0x4000, + FutureWithStatement = 0x8000, + FuturePrintFunction = 0x1_0000, + FutureUnicodeLiterals = 0x2_0000, + FutureBarryAsBDFL = 0x4_0000, + FutureGeneratorStop = 0x8_0000, + FutureAnnotations = 0x10_0000, + // Erg-specific flags + EvmDynParam = 0x1000_0000, + EvmDynamic = 0x2000_0000, + EvmNoGC = 0x4000_0000, + Illegal = 0x0000, +} + +impl Into for u32 { + fn into(self) -> CodeObjFlags { + match self { + 0x0001 => CodeObjFlags::Optimized, + 0x0002 => CodeObjFlags::NewLocals, + 0x0004 => CodeObjFlags::VarArgs, + 0x0008 => CodeObjFlags::VarKeywords, + 0x0010 => CodeObjFlags::Nested, + 0x0020 => CodeObjFlags::Generator, + 0x0040 => CodeObjFlags::NoFree, + 0x0080 => CodeObjFlags::Coroutine, + 0x0100 => CodeObjFlags::IterableCoroutine, + 0x0200 => CodeObjFlags::AsyncGenerator, + // CO_GENERATOR_ALLOWED, + 0x2000 => CodeObjFlags::FutureDivision, + 0x4000 => CodeObjFlags::FutureAbsoluteImport, + 0x8000 => CodeObjFlags::FutureWithStatement, + 0x1_0000 => CodeObjFlags::FuturePrintFunction, + 0x2_0000 => CodeObjFlags::FutureUnicodeLiterals, + 0x4_0000 => CodeObjFlags::FutureBarryAsBDFL, + 0x8_0000 => CodeObjFlags::FutureGeneratorStop, + 0x10_0000 => CodeObjFlags::FutureAnnotations, + // EVM flags + 0x1000_0000 => CodeObjFlags::EvmDynParam, + 0x2000_0000 => CodeObjFlags::EvmDynamic, + 0x4000_0000 => CodeObjFlags::EvmNoGC, + _ => CodeObjFlags::Illegal, + } + } +} + +impl CodeObjFlags { + pub const fn is_in(&self, flags: u32) -> bool { + (flags & *self as u32) != 0 + } +} + +/// Implementation of `PyCodeObject`, see Include/cpython/code.h in CPython for details. +/// +/// 各属性をErg側のObjに変換すると遅くなりそうなので、アクサスされたときのみ変換して提供する +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct CodeObj { + pub argcount: u32, + pub posonlyargcount: u32, + pub kwonlyargcount: u32, + pub nlocals: u32, // == params + local vars + pub stacksize: u32, + pub flags: u32, + pub code: Vec, + pub consts: Vec, // objects used in the code (literal) + pub names: Vec, // names used in the code object + pub varnames: Vec, // names defined in the code object + pub freevars: Vec, // names captured from the outer scope + pub cellvars: Vec, // names used in the inner function (closure) + pub filename: Str, + pub name: Str, + pub firstlineno: u32, + // lnotab (line number table): see Object/lnotab_notes.txt in CPython for details + // e.g. +12bytes, +3line -> [.., 0x1C, 0x03, ..] + // ([sdelta, ldelta, sdelta, ldelta, ..]) + // if delta > 255 -> [255, 0, 255-delta, ...] + pub lnotab: Vec, +} + +impl HasType for CodeObj { + fn ref_t(&self) -> &Type { &Type::Code } + fn signature_t(&self) -> Option<&Type> { None } +} + +impl fmt::Debug for CodeObj { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "", + self.name, self, self.filename, self.firstlineno + ) + } +} + +impl_display_from_debug!(CodeObj); + +impl Default for CodeObj { + fn default() -> Self { + Self { + argcount: 0, + posonlyargcount: 0, + kwonlyargcount: 0, + nlocals: 0, + stacksize: 2, // Seems to be the default in CPython, but not sure why + flags: CodeObjFlags::NoFree as u32, + code: Vec::new(), + consts: Vec::new(), + names: Vec::new(), + varnames: Vec::new(), + freevars: Vec::new(), + cellvars: Vec::new(), + filename: "".into(), + name: "".into(), + firstlineno: 1, + lnotab: Vec::new(), + } + } +} + +impl CodeObj { + pub fn new>( + argcount: u32, + posonlyargcount: u32, + kwonlyargcount: u32, + nlocals: u32, + stacksize: u32, + flags: u32, + code: Vec, + consts: Vec, + names: Vec, + varnames: Vec, + freevars: Vec, + cellvars: Vec, + filename: Str, + name: S, + firstlineno: u32, + lnotab: Vec, + ) -> Self { + Self { + argcount, + posonlyargcount, + kwonlyargcount, + nlocals, + stacksize, + flags, + code, + consts, + names, + varnames, + freevars, + cellvars, + filename, + name: name.into(), + firstlineno, + lnotab, + } + } + + pub fn empty, T: Into>(params: Vec, filename: S, name: T, firstlineno: u32) -> Self { + Self { + argcount: params.len() as u32, + posonlyargcount: 0, + kwonlyargcount: 0, + nlocals: params.len() as u32, + stacksize: 2, // Seems to be the default in CPython, but not sure why + flags: CodeObjFlags::NoFree as u32, + code: Vec::with_capacity(8), + consts: Vec::with_capacity(4), + names: Vec::with_capacity(3), + varnames: params, + freevars: Vec::new(), + cellvars: Vec::new(), + filename: filename.into(), + name: name.into(), + firstlineno, + lnotab: Vec::with_capacity(4), + } + } + + pub fn from_pyc>(path: P) -> DeserializeResult { + let mut f = BufReader::new(File::open(path)?); + let v = &mut Vec::with_capacity(16); + f.read_to_end(v)?; + let python_ver = + get_magic_num_from_bytes(&Deserializer::consume::<4>(v)); + let _padding = Deserializer::deserialize_u32(v); + let _timestamp = Deserializer::deserialize_u32(v); + let _padding = Deserializer::deserialize_u32(v); + let code = Self::from_bytes(v, python_ver)?; + Ok(code) + } + + pub fn from_bytes(v: &mut Vec, python_ver: u32) -> DeserializeResult { + let mut des = Deserializer::new(); + let argcount = Deserializer::deserialize_u32(v); + let posonlyargcount = if python_ver >= 3413 { Deserializer::deserialize_u32(v)} else { 0 }; + let kwonlyargcount = Deserializer::deserialize_u32(v); + let nlocals = Deserializer::deserialize_u32(v); + let stacksize = Deserializer::deserialize_u32(v); + let flags = Deserializer::deserialize_u32(v); + let code = des.deserialize_bytes(v)?; + let consts = des.deserialize_const_vec(v, python_ver)?; + let names = des.deserialize_str_vec(v, python_ver)?; + let varnames = des.deserialize_str_vec(v, python_ver)?; + let freevars =des.deserialize_str_vec(v, python_ver)?; + let cellvars = des.deserialize_str_vec(v, python_ver)?; + let filename = des.deserialize_str(v, python_ver)?; + let name = des.deserialize_str(v, python_ver)?; + let firstlineno = Deserializer::deserialize_u32(v); + let lnotab = des.deserialize_bytes(v)?; + Ok(CodeObj::new( + argcount, + posonlyargcount, + kwonlyargcount, + nlocals, + stacksize, + flags, + code, + consts, + names, + varnames, + freevars, + cellvars, + filename, + name, + firstlineno, + lnotab + )) + } + + pub fn into_bytes(self, python_ver: u32) -> Vec { + let mut bytes = vec![DataTypePrefix::Code as u8]; + bytes.append(&mut self.argcount.to_le_bytes().to_vec()); + if python_ver >= 3413 { + bytes.append(&mut self.posonlyargcount.to_le_bytes().to_vec()); + } + bytes.append(&mut self.kwonlyargcount.to_le_bytes().to_vec()); + bytes.append(&mut self.nlocals.to_le_bytes().to_vec()); + bytes.append(&mut self.stacksize.to_le_bytes().to_vec()); + bytes.append(&mut self.flags.to_le_bytes().to_vec()); + // co_code is represented as PyStrObject (Not Ascii, Unicode) + bytes.append(&mut raw_string_into_bytes(self.code)); + bytes.append(&mut consts_into_bytes(self.consts)); // write as PyTupleObject + bytes.append(&mut strs_into_bytes(self.names)); + bytes.append(&mut strs_into_bytes(self.varnames)); + bytes.append(&mut strs_into_bytes(self.freevars)); + bytes.append(&mut strs_into_bytes(self.cellvars)); + bytes.append(&mut str_into_bytes(self.filename, false)); + bytes.append(&mut str_into_bytes(self.name, true)); + bytes.append(&mut self.firstlineno.to_le_bytes().to_vec()); + // lnotab is represented as PyStrObject + bytes.append(&mut raw_string_into_bytes(self.lnotab)); + bytes + } + + pub fn dump_as_pyc>(self, path: P, python_ver: Option) -> std::io::Result<()> { + let mut file = File::create(path)?; + let mut bytes = Vec::with_capacity(16); + let python_ver = python_ver.unwrap_or_else(|| detect_magic_number()); + bytes.append(&mut get_magic_num_bytes(python_ver).to_vec()); + bytes.append(&mut vec![0; 4]); // padding + bytes.append(&mut get_timestamp_bytes().to_vec()); + bytes.append(&mut vec![0; 4]); // padding + bytes.append(&mut self.into_bytes(python_ver)); + file.write_all(&bytes[..])?; + Ok(()) + } + + fn tables_info(&self) -> String { + let mut tables = "".to_string(); + if !self.consts.is_empty() { + tables += &format!("Constants:\n"); + } + for (i, obj) in self.consts.iter().enumerate() { + tables += &format!(" {}: {}\n", i, obj); + } + if !self.names.is_empty() { + tables += &format!("Names:\n"); + } + for (i, name) in self.names.iter().enumerate() { + tables += &format!(" {}: {}\n", i, name); + } + if !self.varnames.is_empty() { + tables += &format!("Varnames:\n"); + } + for (i, varname) in self.varnames.iter().enumerate() { + tables += &format!(" {}: {}\n", i, varname); + } + if !self.cellvars.is_empty() { + tables += &format!("Cellvars:\n"); + } + for (i, cellvar) in self.cellvars.iter().enumerate() { + tables += &format!(" {}: {}\n", i, cellvar); + } + if !self.freevars.is_empty() { + tables += &format!("Freevars:\n"); + } + for (i, freevar) in self.freevars.iter().enumerate() { + tables += &format!(" {}: {}\n", i, freevar); + } + tables + } + + fn attrs_info(&self) -> String { + let mut attrs = "".to_string(); + attrs += &format!("Name: {}\n", self.name); + attrs += &format!("FileName: {}\n", self.filename); + attrs += &format!("Argument count: {}\n", self.argcount); + attrs += &format!("Positional-only arguments: {}\n", self.posonlyargcount); + attrs += &format!("Kw-only arguments: {}\n", self.kwonlyargcount); + attrs += &format!("Number of locals: {}\n", self.nlocals); + attrs += &format!("Stack size: {}\n", self.stacksize); + let mut flagged = "".to_string(); + for i in 0..32 { + if (self.flags & (1 << i)) != 0 { + let flag: CodeObjFlags = 2u32.pow(i).into(); + flagged += &format!("{:?}, ", flag); + } + } + flagged.pop(); + flagged.pop(); + attrs += &format!("Flags: {}\n", flagged); + attrs + } + + fn instr_info(&self) -> String { + let mut lnotab_iter = self.lnotab.iter(); + let mut code_iter = self.code.iter(); + let mut idx = 0; + let mut line_offset = 0; + let mut lineno = self.firstlineno as u8; + let mut sdelta = lnotab_iter.next().unwrap_or(&0); + let mut ldelta = lnotab_iter.next().unwrap_or(&0); + let mut instrs = "".to_string(); + instrs += &format!("lnotab: {:?}\n", self.lnotab); + if *sdelta != 0 { + instrs += &format!("{}:\n", lineno); + } + loop { + if *sdelta == line_offset { + line_offset = 0; + lineno += ldelta; + instrs += &format!("{}:\n", lineno); + sdelta = lnotab_iter.next().unwrap_or(&0); + ldelta = lnotab_iter.next().unwrap_or(&0); + } + if let (Some(op), Some(arg)) = (code_iter.next(), code_iter.next()) { + let op = Opcode::from(*op); + let s_op = op.to_string(); + instrs += &format!("{:>15} {:<25}", idx, s_op); + match op { + Opcode::COMPARE_OP => { + let op = match arg { + 0 => "<", + 1 => "<=", + 2 => "==", + 3 => "!=", + 4 => ">", + 5 => ">=", + _ => "?", + }; + instrs += &format!("{} ({})", arg, op); + } + Opcode::STORE_NAME | Opcode::LOAD_NAME + | Opcode::STORE_GLOBAL | Opcode::LOAD_GLOBAL + | Opcode::STORE_ATTR | Opcode::LOAD_ATTR + | Opcode::LOAD_METHOD => { + instrs += &format!("{} ({})", arg, self.names.get(*arg as usize).unwrap()); + } + Opcode::STORE_DEREF | Opcode::LOAD_DEREF => { + instrs += + &format!("{} ({})", arg, self.freevars.get(*arg as usize).unwrap()); + } + Opcode::STORE_FAST | Opcode::LOAD_FAST => { + instrs += + &format!("{} ({})", arg, self.varnames.get(*arg as usize).unwrap()); + } + Opcode::LOAD_CONST => { + instrs += &format!("{} ({})", arg, self.consts.get(*arg as usize).unwrap()); + }, + Opcode::FOR_ITER => { + instrs += &format!("{} (to {})", arg, idx + arg*2 + 2); + } + Opcode::JUMP_FORWARD => { + instrs += &format!("{} (to {})", arg, idx + arg*2 + 2); + } + Opcode::JUMP_ABSOLUTE => { + instrs += &format!("{} (to {})", arg, arg*2); + } + Opcode::POP_JUMP_IF_FALSE | Opcode::POP_JUMP_IF_TRUE => { + instrs += &format!("{} (to {})", arg, arg*2); + } + Opcode::MAKE_FUNCTION => { + let flag = match arg { + 8 => "(closure)", + // TODO: + _ => "", + }; + instrs += &format!("{} {}", arg, flag); + } + // Ergでは引数で型キャストする + Opcode::BINARY_ADD | Opcode::BINARY_SUBTRACT + | Opcode::BINARY_MULTIPLY | Opcode::BINARY_TRUE_DIVIDE => { + instrs += &format!("{} ({:?})", arg, TypePair::from(*arg)); + } + other if other.take_arg() => { + instrs += &format!("{}", arg); + } + _ => {} + } + instrs.push('\n'); + idx += 2; + line_offset += 2; + } else { + break; + } + } + instrs + } + + pub fn code_info(&self) -> String { + let mut info = "".to_string(); + info += &format!("Disassembly of {:?}:\n", self); + info += &self.attrs_info(); + info += &self.tables_info(); + info += &self.instr_info(); + info.push('\n'); + for cons in self.consts.iter() { + if let ValueObj::Code(c) = cons { + info += &c.code_info(); + } + } + info + } +} diff --git a/src/common/color.rs b/src/common/color.rs new file mode 100644 index 00000000..40052884 --- /dev/null +++ b/src/common/color.rs @@ -0,0 +1,9 @@ +//! Escape sequences change the color of the terminal + +pub const RESET: &'static str = "\x1b[m"; +pub const DEEP_RED: &'static str = "\x1b[31m"; +pub const RED: &'static str = "\x1b[91m"; +pub const GREEN: &'static str = "\x1b[92m"; +pub const YELLOW: &'static str = "\x1b[93m"; +pub const BLUE: &'static str = "\x1b[94m"; +pub const CYAN: &'static str = "\x1b[96m"; diff --git a/src/common/combinations.rs b/src/common/combinations.rs new file mode 100644 index 00000000..6f14faa8 --- /dev/null +++ b/src/common/combinations.rs @@ -0,0 +1,199 @@ +// Copied and modified from https://github.com/rust-itertools/itertools/blob/master/src/combinations.rs +// and https://github.com/rust-itertools/itertools/blob/master/src/impl_macros.rs +// MIT license | Apache 2.0 license +// License files are placed at the root. + +use std::fmt; +use std::iter::FusedIterator; + +use super::lazy_buffer::LazyBuffer; + +pub struct TotalCombinations { + combinations: Combinations, + len: usize, +} + +/// ``` +/// let it = total_combinations(0..2); +/// itertools::assert_equal(it, vec![ +/// vec![0], +/// vec![1], +/// vec![2], +/// vec![0, 1], +/// vec![0, 2], +/// vec![1, 2], +/// vec![0, 1, 2], +/// ]); +/// +pub fn total_combinations(iter: I) -> TotalCombinations + where I: Iterator + ExactSizeIterator, + I::Item: Clone +{ + TotalCombinations { len: iter.len(), combinations: combinations(iter, 1) } +} + +impl Iterator for TotalCombinations + where I: Iterator, + I::Item: Clone +{ + type Item = Vec; + fn next(&mut self) -> Option { + if let Some(i) = self.combinations.next() { return Some(i) } + else { + self.combinations.reset(self.combinations.k() + 1); + self.len -= 1; + if self.len == 0 { return None } + self.combinations.next() + } + } +} + +impl FusedIterator for TotalCombinations + where I: Iterator, + I::Item: Clone +{} + +macro_rules! debug_fmt_fields { + ($tyname:ident, $($($field:tt/*TODO ideally we would accept ident or tuple element here*/).+),*) => { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(stringify!($tyname)) + $( + .field(stringify!($($field).+), &self.$($field).+) + )* + .finish() + } + } +} + +macro_rules! clone_fields { + ($($field:ident),*) => { + fn clone(&self) -> Self { + Self { + $($field: self.$field.clone(),)* + } + } + } +} + +/// An iterator to iterate through all the `k`-length combinations in an iterator. +/// +/// See [`.combinations()`](crate::Itertools::combinations) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct Combinations { + indices: Vec, + pool: LazyBuffer, + first: bool, +} + +impl Clone for Combinations + where I: Clone + Iterator, + I::Item: Clone, +{ + clone_fields!(indices, pool, first); +} + +impl fmt::Debug for Combinations + where I: Iterator + fmt::Debug, + I::Item: fmt::Debug, +{ + debug_fmt_fields!(Combinations, indices, pool, first); +} + +/// Create a new `Combinations` from a clonable iterator. +pub fn combinations(iter: I, k: usize) -> Combinations + where I: Iterator +{ + let mut pool = LazyBuffer::new(iter); + pool.prefill(k); + + Combinations { + indices: (0..k).collect(), + pool, + first: true, + } +} + +impl Combinations { + /// Returns the length of a combination produced by this iterator. + #[inline] + pub fn k(&self) -> usize { self.indices.len() } + + /// Returns the (current) length of the pool from which combination elements are + /// selected. This value can change between invocations of [`next`](Combinations::next). + #[inline] + pub fn n(&self) -> usize { self.pool.len() } + + /// Returns a reference to the source iterator. + #[inline] + pub fn src(&self) -> &I { &self.pool.it } + + /// Resets this `Combinations` back to an initial state for combinations of length + /// `k` over the same pool data source. If `k` is larger than the current length + /// of the data pool an attempt is made to prefill the pool so that it holds `k` + /// elements. + pub fn reset(&mut self, k: usize) { + self.first = true; + + if k < self.indices.len() { + self.indices.truncate(k); + for i in 0..k { + self.indices[i] = i; + } + + } else { + for i in 0..self.indices.len() { + self.indices[i] = i; + } + self.indices.extend(self.indices.len()..k); + self.pool.prefill(k); + } + } +} + +impl Iterator for Combinations + where I: Iterator, + I::Item: Clone +{ + type Item = Vec; + fn next(&mut self) -> Option { + if self.first { + if self.k() > self.n() { + return None; + } + self.first = false; + } else if self.indices.is_empty() { + return None; + } else { + // Scan from the end, looking for an index to increment + let mut i: usize = self.indices.len() - 1; + + // Check if we need to consume more from the iterator + if self.indices[i] == self.pool.len() - 1 { + self.pool.get_next(); // may change pool size + } + + while self.indices[i] == i + self.pool.len() - self.indices.len() { + if i > 0 { + i -= 1; + } else { + // Reached the last combination + return None; + } + } + + // Increment index, and reset the ones to its right + self.indices[i] += 1; + for j in i+1..self.indices.len() { + self.indices[j] = self.indices[j - 1] + 1; + } + } + + // Create result vector based on the indices + Some(self.indices.iter().map(|i| self.pool[*i].clone()).collect()) + } +} + +impl FusedIterator for Combinations + where I: Iterator, + I::Item: Clone +{} diff --git a/src/common/config.rs b/src/common/config.rs new file mode 100644 index 00000000..7d891f79 --- /dev/null +++ b/src/common/config.rs @@ -0,0 +1,219 @@ +//! defines a command-line parser for `ergc`. +//! +//! コマンドオプション(パーサー)を定義する +use std::env; +use std::env::consts::{ARCH, OS}; +use std::process; +use std::fs::File; +use std::io::{BufReader, BufRead}; + +use crate::stdin; +use crate::Str; +use crate::{power_assert, read_file}; +use crate::lazy::Lazy; + +pub const SEMVER: &str = env!("CARGO_PKG_VERSION"); +pub const GIT_HASH_SHORT: &str = env!("GIT_HASH_SHORT"); +pub const BUILD_DATE: &str = env!("BUILD_DATE"); +/// TODO: タグを含める +pub const BUILD_INFO: Lazy = Lazy::new(|| format!("(tags/?:{GIT_HASH_SHORT}, {BUILD_DATE}) on {ARCH}/{OS}")); + +/// 入力はファイルからだけとは限らないので +/// Inputで操作を一本化する +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Input { + /// filename + File(Str), + REPL, + /// same content as cfg.command + Pipe(Str), + /// from command option | eval + Str(Str), + Dummy, +} + +impl Input { + pub fn enclosed_name(&self) -> &str { + match self { + Self::File(filename) => &filename[..], + Self::REPL | Self::Pipe(_) => "", + Self::Str(_) => "", + Self::Dummy => "", + } + } + + /// ファイルに書き出すとき使う + pub fn filename(&self) -> &str { + match self { + Self::File(filename) => &filename[..], + Self::REPL | Self::Pipe(_) => "stdin", + Self::Str(_) => "string", + Self::Dummy => "dummy", + } + } + + pub fn read(&self) -> Str { + match self { + Self::File(filename) => { + let file = match File::open(&filename[..]) { + Ok(f) => f, + Err(e) => { + let code = e.raw_os_error().unwrap_or(1); + println!("cannot open '{filename}': [Errno {code}] {e}"); + process::exit(code); + } + }; + let src = match read_file(file) { + Ok(s) => s, + Err(e) => { + let code = e.raw_os_error().unwrap_or(1); + println!("cannot read '{filename}': [Errno {code}] {e}"); + process::exit(code); + } + }; + Str::from(src) + } + Self::Pipe(s) | Self::Str(s) => s.clone(), + Self::REPL => stdin::read(), + Self::Dummy => panic!("cannot read from a dummy file"), + } + } + + pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec { + power_assert!(ln_begin, >=, 1); + match self { + Self::File(filename) => { + match File::open(&filename[..]) { + Ok(file) => { + let mut codes = vec![]; + let mut lines = BufReader::new(file).lines().skip(ln_begin - 1); + for _ in ln_begin..=ln_end { + codes.push(Str::from(lines.next().unwrap().unwrap())); + } + codes + } + Err(_) => vec!["".into()], + } + } + Self::Pipe(s) | Self::Str(s) => { + s.split('\n') + .collect::>()[ln_begin-1..=ln_end-1] + .into_iter().map(|s| Str::rc(*s)).collect() + } + Self::REPL => stdin::reread_lines(ln_begin, ln_end), + Self::Dummy => panic!("cannot read lines from a dummy file"), + } + } + + pub fn reread(&self) -> Str { + match self { + Self::File(_filename) => todo!(), + Self::Pipe(s) | Self::Str(s) => s.clone(), + Self::REPL => stdin::reread(), + Self::Dummy => panic!("cannot read from a dummy file"), + } + } +} + +#[derive(Debug, Clone)] +pub struct ErgConfig { + /// options: lex | parse | compile | exec + pub mode: &'static str, + /// optimization level. + /// * 0: no optimization + /// * 1 (default): e.g. constant folding, dead code elimination + /// * 2: e.g. static dispatching, inlining, peephole + /// * 3: e.g. JIT compiling + pub opt_level: u8, + pub dump_as_pyc: bool, + pub python_ver: Option, + pub input: Input, + pub module: &'static str, + /// verbosity level for system messages. + /// * 0: display errors + /// * 1: display errors and warns + /// * 2 (default): display errors, warnings and hints + pub verbose: u8, +} + +impl Default for ErgConfig { + #[inline] + fn default() -> Self { + Self::new("exec", 1, false, None, Input::REPL, "", 2) + } +} + +impl ErgConfig { + pub const fn new( + mode: &'static str, + opt_level: u8, + dump_as_pyc: bool, + python_ver: Option, + input: Input, + module: &'static str, + verbose: u8, + ) -> Self { + Self { + mode, + opt_level, + dump_as_pyc, + python_ver, + input, + module, + verbose, + } + } + + /// cloneのエイリアス(実際のcloneコストは低いので) + #[inline] + pub fn copy(&self) -> Self { self.clone() } + + pub fn parse() -> Self { + let mut args = env::args(); + args.next(); // "ergc" + let mut cfg = Self::default(); + // ループ内でnextするのでforにしないこと + while let Some(arg) = args.next() { + match &arg[..] { + "-c" => { + cfg.input = Input::Str(Str::from(args.next().unwrap())); + } + "--dump-as-pyc" => { + cfg.dump_as_pyc = true; + } + "-?" | "-h" | "--help" => { + println!("erg [option] ... [-c cmd | -m mod | file | -] [arg] ..."); + // TODO: + process::exit(0); + } + "-m" => { + cfg.module = Box::leak(args.next().unwrap().into_boxed_str()); + } + "--mode" => { + cfg.mode = Box::leak(args.next().unwrap().into_boxed_str()); + } + "-o" | "--opt-level" | "--optimization-level" => { + cfg.opt_level = args.next().unwrap().parse::().unwrap(); + } + "-p" | "--py-ver" | "--python-version" => { + cfg.python_ver = Some(args.next().unwrap().parse::().unwrap()); + } + "--verbose\n" => { + cfg.verbose = args.next().unwrap().parse::().unwrap(); + } + "-V" | "--version" => { + println!("Erg {}", env!("CARGO_PKG_VERSION")); + process::exit(0); + } + other if other.starts_with('-') => { + panic!("invalid option: {other}"); + } + _ => { + cfg.input = Input::File(Str::from(arg)); + break; + } + } + } + cfg + } +} diff --git a/src/common/datetime.rs b/src/common/datetime.rs new file mode 100644 index 00000000..9011b7a4 --- /dev/null +++ b/src/common/datetime.rs @@ -0,0 +1,20 @@ +use std::process::Command; + +/// returns the current datetime as String +pub fn now() -> String { + let output = if cfg!(windows) { + Command::new("cmd") + .args(&["/C", "echo %date% %time%"]) + .output() + .expect("failed to execute a process to get current time") + } else { + Command::new("date") + .args(&["+%Y/%m/%d %T"]) + .output() + .expect("failed to execute process to get current time") + }; + String::from_utf8(output.stdout) + .unwrap() + .trim_end() + .to_string() +} diff --git a/src/common/deserialize.rs b/src/common/deserialize.rs new file mode 100644 index 00000000..dc993f08 --- /dev/null +++ b/src/common/deserialize.rs @@ -0,0 +1,255 @@ +//! バイトコードからオブジェクトを復元する +use std::string::FromUtf8Error; +use std::process; + +use crate::{Str, RcArray}; +use crate::cache::Cache; +use crate::{fn_name, switch_lang}; +use crate::serialize::DataTypePrefix; +use crate::codeobj::CodeObj; +use crate::config::{ErgConfig, Input}; +use crate::value::ValueObj; +use crate::error::{ErrorCore, Location, ErrorKind}; +use crate::traits::HasType; +use crate::ty::{Type, TyParam}; + +#[derive(Debug)] +pub struct DeserializeError { + pub errno: usize, + pub caused_by: Str, + pub desc: Str, +} + +impl From for DeserializeError { + fn from(err: std::io::Error) -> Self { + Self::new(0, "io::Error::into", err.to_string()) + } +} + +impl From for DeserializeError { + fn from(err: FromUtf8Error) -> Self { + Self::new(0, "Str::try_from", err.to_string()) + } +} + +impl From for ErrorCore { + fn from(err: DeserializeError) -> Self { + ErrorCore::new(err.errno, ErrorKind::ImportError, Location::Unknown, err.desc, Option::::None) + } +} + +impl DeserializeError { + pub fn new, T: Into>(errno: usize, caused_by: S, desc: T) -> Self { + Self { errno, caused_by: caused_by.into(), desc: desc.into() } + } + + pub fn file_broken_error() -> Self { + Self::new(0, fn_name!(), switch_lang!("the loaded .pyc file is broken", "読み込んだ.pycファイルは破損しています")) + } + + pub fn type_error(expect: &Type, found: &Type) -> Self { + Self::new(0, fn_name!(), switch_lang!( + format!("expect a {} object, but the deserialized object is {}", expect, found), + format!("{}型オブジェクトを予期しましたが、 読み込んだオブジェクトは{}型です", expect, found) + )) + } +} + +pub type DeserializeResult = Result; + +pub struct Deserializer { + str_cache: Cache, + arr_cache: Cache<[ValueObj]>, + dict_cache: Cache<[(ValueObj, ValueObj)]>, +} + +impl Deserializer { + pub fn new() -> Self { + Self { + str_cache: Cache::new(), + arr_cache: Cache::new(), + dict_cache: Cache::new(), + } + } + + pub fn run(cfg: ErgConfig) { + let filename = if let Input::File(f) = cfg.input { f } else { + eprintln!("{:?} is not a filename", cfg.input); + process::exit(1); + }; + let codeobj = CodeObj::from_pyc(&filename[..]) + .expect(&format!("failed to deserialize {filename}")); + println!("{}", codeobj.code_info()); + } + + fn get_cached_str(&mut self, s: &str) -> ValueObj { + ValueObj::Str(self.str_cache.get(s)) + } + + fn get_cached_arr(&mut self, arr: &[ValueObj]) -> ValueObj { + ValueObj::Array(self.arr_cache.get(arr)) + } + + /// TODO: 使わない? + pub fn get_cached_dict(&mut self, dict: &[(ValueObj, ValueObj)]) -> ValueObj { + ValueObj::Dict(self.dict_cache.get(dict)) + } + + pub fn vec_to_bytes(vector: Vec) -> [u8; LEN] { + let mut arr = [0u8; LEN]; + for (arr_elem, vec_elem) in arr.iter_mut().zip(vector.iter()) { + *arr_elem = *vec_elem; + } + arr + } + + pub fn consume(v: &mut Vec) -> [u8; LEN] { + Self::vec_to_bytes::(v.drain(..LEN).collect::>()) + } + + pub fn deserialize_u32(v: &mut Vec) -> u32 { + u32::from_le_bytes(Self::consume::<4>(v)) + } + + pub fn deserialize_const(&mut self, v: &mut Vec, python_ver: u32) -> DeserializeResult { + match DataTypePrefix::from(v.remove(0)) { + DataTypePrefix::Int32 => { + let bytes = Self::consume::<4>(v); + Ok(ValueObj::Int(i32::from_le_bytes(bytes))) + }, + DataTypePrefix::BinFloat => { + let bytes = Self::consume::<8>(v); + Ok(ValueObj::Float(f64::from_le_bytes(bytes))) + }, + DataTypePrefix::ShortAscii | DataTypePrefix::ShortAsciiInterned => { + let len = v.remove(0); + let bytes = v.drain(..len as usize).collect(); + Ok(self.get_cached_str(&String::from_utf8(bytes)?)) + }, + DataTypePrefix::Str | DataTypePrefix::Unicode => { + let len = Self::deserialize_u32(v); + let bytes = v.drain(..len as usize).collect(); + Ok(self.get_cached_str(&String::from_utf8(bytes)?)) + }, + DataTypePrefix::True => Ok(ValueObj::True), + DataTypePrefix::False => Ok(ValueObj::False), + DataTypePrefix::SmallTuple => { + let len = v.remove(0); + let mut arr = Vec::with_capacity(len as usize); + for _ in 0..len { + arr.push(self.deserialize_const(v, python_ver)?); + } + Ok(self.get_cached_arr(&arr)) + }, + DataTypePrefix::Tuple => { + let len = Self::deserialize_u32(v); + let mut arr = Vec::with_capacity(len as usize); + for _ in 0..len { + arr.push(self.deserialize_const(v, python_ver)?); + } + Ok(self.get_cached_arr(&arr)) + }, + DataTypePrefix::Code => { + let argcount = Self::deserialize_u32(v); + let posonlyargcount = + if python_ver >= 3413 { Self::deserialize_u32(v) } else { 0 }; + let kwonlyargcount = Self::deserialize_u32(v); + let nlocals = Self::deserialize_u32(v); + let stacksize = Self::deserialize_u32(v); + let flags = Self::deserialize_u32(v); + let code = self.deserialize_bytes(v)?; + let consts = self.deserialize_const_vec(v, python_ver)?; + let names = self.deserialize_str_vec(v, python_ver)?; + let varnames = self.deserialize_str_vec(v, python_ver)?; + let freevars = self.deserialize_str_vec(v, python_ver)?; + let cellvars = self.deserialize_str_vec(v, python_ver)?; + let filename = self.deserialize_str(v, python_ver)?; + let name = self.deserialize_str(v, python_ver)?; + let firstlineno = Self::deserialize_u32(v); + let lnotab = self.deserialize_bytes(v)?; + Ok(ValueObj::from(CodeObj::new( + argcount, + posonlyargcount, + kwonlyargcount, + nlocals, + stacksize, + flags, + code, + consts, + names, + varnames, + freevars, + cellvars, + filename, + name, + firstlineno, + lnotab + ))) + }, + DataTypePrefix::None => Ok(ValueObj::None), + other => { + Err(DeserializeError::new(0, fn_name!(), switch_lang!( + format!("cannot deserialize this object: {}", other), + format!("このオブジェクトは復元できません: {}", other) + ))) + }, + } + } + + pub fn deserialize_const_vec(&mut self, v: &mut Vec, python_ver: u32) -> DeserializeResult> { + match self.deserialize_const(v, python_ver)? { + ValueObj::Array(arr) => Ok(arr.to_vec()), + other => Err(DeserializeError::type_error(&Type::Str, &other.ref_t())) + } + } + + pub fn deserialize_const_array(&mut self, v: &mut Vec, python_ver: u32) -> DeserializeResult> { + match self.deserialize_const(v, python_ver)? { + ValueObj::Array(arr) => Ok(arr), + other => Err(DeserializeError::type_error(&Type::Str, &other.ref_t())) + } + } + + pub fn array_into_const(&mut self, arr: &[ValueObj]) -> ValueObj { + self.get_cached_arr(&arr) + } + + pub fn try_into_str(&mut self, c: ValueObj) -> DeserializeResult { + match c { + ValueObj::Str(s) => Ok(s), + other => Err(DeserializeError::type_error(&Type::Str, &other.ref_t())) + } + } + + pub fn deserialize_str_vec(&mut self, v: &mut Vec, python_ver: u32) -> DeserializeResult> { + match self.deserialize_const(v, python_ver)? { + ValueObj::Array(arr) => { + let mut strs = Vec::with_capacity(arr.len()); + for c in arr.to_vec().into_iter() { + strs.push(self.try_into_str(c)?); + } + Ok(strs) + } + other => Err(DeserializeError::type_error(&Type::array(Type::Str, TyParam::erased(Type::Nat)), &other.ref_t())) + } + } + + pub fn deserialize_str(&mut self, v: &mut Vec, python_ver: u32) -> DeserializeResult { + match self.deserialize_const(v, python_ver)? { + ValueObj::Str(s) => Ok(s), + other => Err(DeserializeError::type_error(&Type::Str, &other.ref_t())) + } + } + + pub fn deserialize_bytes(&self, v: &mut Vec) -> DeserializeResult> { + if DataTypePrefix::from(v.remove(0)) != DataTypePrefix::Str { + return Err(DeserializeError::new( + 0, + fn_name!(), + switch_lang!("failed to load bytes", "バイト列の読み込みに失敗しました"), + )) + } + let len = Self::deserialize_u32(v); + Ok(v.drain(0..len as usize).collect()) + } +} diff --git a/src/common/dict.rs b/src/common/dict.rs new file mode 100644 index 00000000..fb8522bb --- /dev/null +++ b/src/common/dict.rs @@ -0,0 +1,153 @@ +use std::collections::hash_map::{Keys, Values, ValuesMut, IntoValues, Iter, IntoIter, IterMut}; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::iter::FromIterator; +use std::borrow::Borrow; + +use crate::fxhash::FxHashMap; + +#[macro_export] +macro_rules! dict { + () => { $crate::dict::Dict::new() }; + ($($k: expr => $v: expr),+ $(,)?) => {{ + let mut dict = $crate::dict::Dict::new(); + $(dict.insert($k, $v);)+ + dict + }}; +} + +#[derive(Debug, Clone)] +pub struct Dict { + dict: FxHashMap +} + +impl PartialEq for Dict { + fn eq(&self, other: &Dict) -> bool { self.dict == other.dict } +} + +impl Eq for Dict {} + +impl Hash for Dict { + fn hash(&self, state: &mut H) { + let keys = self.dict.keys().collect::>(); + keys.hash(state); + let vals = self.dict.values().collect::>(); + vals.hash(state); + } +} + +impl fmt::Display for Dict { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = "".to_string(); + for (k, v) in self.dict.iter() { + s += &format!("{k}: {v}, "); + } + s.pop(); + s.pop(); + write!(f, "{{{s}}}") + } +} + +impl FromIterator<(K, V)> for Dict { + #[inline] + fn from_iter>(iter: I) -> Dict { + let mut dict = Dict::new(); + dict.extend(iter); + dict + } +} + +impl Default for Dict { + fn default() -> Dict { Dict::new() } +} + +impl Dict { + #[inline] + pub fn new() -> Self { + Self{ dict: FxHashMap::default() } + } + + pub fn with_capacity(capacity: usize) -> Self { + Self{ dict: FxHashMap::with_capacity_and_hasher(capacity, Default::default()) } + } + + #[inline] + pub fn len(&self) -> usize { self.dict.len() } + + #[inline] + pub fn capacity(&self) -> usize { self.dict.capacity() } + + #[inline] + pub fn keys(&self) -> Keys { self.dict.keys() } + + #[inline] + pub fn values(&self) -> Values { self.dict.values() } + + #[inline] + pub fn values_mut(&mut self) -> ValuesMut { self.dict.values_mut() } + + #[inline] + pub fn into_values(self) -> IntoValues { self.dict.into_values() } + + #[inline] + pub fn iter(&self) -> Iter { self.dict.iter() } + + #[inline] + pub fn into_iter(self) -> IntoIter { self.dict.into_iter() } + + #[inline] + pub fn iter_mut(&mut self) -> IterMut { self.dict.iter_mut() } + + pub fn clear(&mut self) { self.dict.clear(); } +} + +impl Dict { + #[inline] + pub fn get(&self, k: &Q) -> Option<&V> + where + K: Borrow, + Q: Hash + Eq { + self.dict.get(k) + } + + #[inline] + pub fn get_mut(&mut self, k: &Q) -> Option<&mut V> + where + K: Borrow, + Q: Hash + Eq { + self.dict.get_mut(k) + } + + #[inline] + pub fn contains_key(&self, k: &Q) -> bool + where + K: Borrow, + Q: Hash + Eq { + self.dict.contains_key(k) + } + + #[inline] + pub fn insert(&mut self, k: K, v: V) { self.dict.insert(k, v); } + + #[inline] + pub fn remove(&mut self, k: &Q) -> Option + where + K: Borrow, + Q: Hash + Eq { + self.dict.remove(k) + } + + #[inline] + pub fn extend>(&mut self, iter: I) { self.dict.extend(iter); } + + #[inline] + pub fn merge(&mut self, other: Self) { + self.dict.extend(other.dict); + } + + #[inline] + pub fn concat(mut self, other: Self) -> Self { + self.merge(other); + self + } +} diff --git a/src/common/error.rs b/src/common/error.rs new file mode 100644 index 00000000..54188c1a --- /dev/null +++ b/src/common/error.rs @@ -0,0 +1,444 @@ +//! provides common components for error handling. +//! +//! エラー処理に関する汎用的なコンポーネントを提供する +use std::cmp; +use std::fmt; +use std::io::{Write, BufWriter, stderr}; + +use crate::Str; +use crate::{fmt_option, switch_lang, impl_display_from_debug}; +use crate::config::Input; +use crate::traits::{Stream, Locational}; +use crate::color::*; + +/// ErrorKindと言っているが、ErrorだけでなくWarning, Exceptionも含まれる +/// Numbering of this is not specifically related to ErrFmt.errno(). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum ErrorKind { + /* compile errors */ + AssignError = 0, + AttributeError, + BytecodeError, + CompilerSystemError, + EnvironmentError, + FeatureError, + ImportError, + IndentationError, + NameError, + NotImplementedError, + PatternError, + SyntaxError, + TabError, + TypeError, + UnboundLocalError, + PurityError, + HasEffect, + MoveError, + /* compile warnings */ + AttributeWarning = 60, + CastWarning, + DeprecationWarning, + FutureWarning, + ImportWarning, + PendingDeprecationWarning, + SyntaxWarning, + TypeWarning, + NameWarning, + UnusedWarning, + Warning, + /* runtime errors */ + ArithmeticError = 100, + AssertionError, + BlockingIOError, + BrokenPipeError, + BufferError, + ChildProcessError, + ConnectionAbortedError, + ConnectionError, + ConnectionRefusedError, + ConnectionResetError, + EOFError, + FileExistsError, + FileNotFoundError, + IndexError, + InterruptedError, + IoError, + IsADirectoryError, + KeyError, + LookupError, + MemoryError, + ModuleNotFoundError, + NotADirectoryError, + OSError, + OverflowError, + PermissionError, + ProcessLookupError, + RecursionError, + ReferenceError, + RuntimeAttributeError, + RuntimeError, + RuntimeTypeError, + RuntimeUnicodeError, + TimeoutError, + UnicodeError, + UserError, + ValueError, + VMSystemError, + WindowsError, + ZeroDivisionError, + /* runtime warnings */ + BytesWarning = 180, + ResourceWarning, + RuntimeWarning, + UnicodeWarning, + UserWarning, + /* exceptions */ + BaseException = 200, + Exception, + GeneratorExit, + KeyboardInterrupt, + StopAsyncIteration, + StopIteration, + SystemExit, + UserException, +} + +use ErrorKind::*; + +impl_display_from_debug!(ErrorKind); + +impl From<&str> for ErrorKind { + fn from(s: &str) -> ErrorKind { + match s { + "AssignError" => Self::AssignError, + "AttributeError" => Self::AttributeError, + "BytecodeError" => Self::BytecodeError, + "CompilerSystemError" => Self::CompilerSystemError, + "EnvironmentError" => Self::EnvironmentError, + "FeatureError" => Self::FeatureError, + "ImportError" => Self::ImportError, + "IndentationError" => Self::IndentationError, + "NameError" => Self::NameError, + "NotImplementedError" => Self::NotImplementedError, + "PatternError" => Self::PatternError, + "SyntaxError" => Self::SyntaxError, + "TabError" => Self::TabError, + "TypeError" => Self::TypeError, + "UnboundLocalError" => Self::UnboundLocalError, + "HasEffect" => Self::HasEffect, + "PurityError" => Self::PurityError, + "MoveError" => Self::MoveError, + "AttributeWarning" => Self::AttributeWarning, + "CastWarning" => Self::CastWarning, + "DeprecationWarning" => Self::DeprecationWarning, + "FutureWarning" => Self::FutureWarning, + "ImportWarning" => Self::ImportWarning, + "PendingDeprecationWarning" => Self::PendingDeprecationWarning, + "SyntaxWarning" => Self::SyntaxWarning, + "TypeWarning" => Self::TypeWarning, + "NameWarning" => Self::NameWarning, + "UnusedWarning" => Self::UnusedWarning, + "Warning" => Self::Warning, + "ArithmeticError" => Self::ArithmeticError, + "AssertionError" => Self::AssertionError, + "BlockingIOError" => Self::BlockingIOError, + "BrokenPipeError" => Self::BrokenPipeError, + "BufferError" => Self::BufferError, + "ChildProcessError" => Self::ChildProcessError, + "ConnectionAbortedError" => Self::ConnectionAbortedError, + "ConnectionError" => Self::ConnectionError, + "ConnectionRefusedError" => Self::ConnectionRefusedError, + "ConnectionResetError" => Self::ConnectionResetError, + "EOFError" => Self::EOFError, + "FileExistsError" => Self::FileExistsError, + "FileNotFoundError" => Self::FileNotFoundError, + "IndexError" => Self::IndexError, + "InterruptedError" => Self::InterruptedError, + "IoError" => Self::IoError, + "IsADirectoryError" => Self::IsADirectoryError, + "KeyError" => Self::KeyError, + "LookupError" => Self::LookupError, + "MemoryError" => Self::MemoryError, + "ModuleNotFoundError" => Self::ModuleNotFoundError, + "NotADirectoryError" => Self::NotADirectoryError, + "OSError" => Self::OSError, + "OverflowError" => Self::OverflowError, + "PermissionError" => Self::PermissionError, + "ProcessLookupError" => Self::ProcessLookupError, + "RecursionError" => Self::RecursionError, + "ReferenceError" => Self::ReferenceError, + "RuntimeAttributeError" => Self::RuntimeAttributeError, + "RuntimeError" => Self::RuntimeError, + "RuntimeTypeError" => Self::RuntimeTypeError, + "RuntimeUnicodeError" => Self::RuntimeUnicodeError, + "TimeoutError" => Self::TimeoutError, + "UnicodeError" => Self::UnicodeError, + "UserError" => Self::UserError, + "ValueError" => Self::ValueError, + "VMSystemError" => Self::VMSystemError, + "WindowsError" => Self::WindowsError, + "ZeroDivisionError" => Self::ZeroDivisionError, + "BytesWarning" => Self::BytesWarning, + "ResourceWarning" => Self::ResourceWarning, + "RuntimeWarning" => Self::RuntimeWarning, + "UnicodeWarning" => Self::UnicodeWarning, + "UserWarning" => Self::UserWarning, + "BaseException" => Self::BaseException, + "Exception" => Self::Exception, + "GeneratorExit" => Self::GeneratorExit, + "KeyboardInterrupt" => Self::KeyboardInterrupt, + "StopAsyncIteration" => Self::StopAsyncIteration, + "StopIteration" => Self::StopIteration, + "SystemExit" => Self::SystemExit, + "UserException" => Self::UserException, + _ => Self::UserError, + } + } +} + +/// points the location (of an error) in a code +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Location { + RangePair{ ln_begin: usize, col_first: (usize, usize), ln_end: usize, col_second: (usize, usize), }, + Range{ ln_begin: usize, col_begin: usize, ln_end: usize, col_end: usize }, + LineRange(usize, usize), + Line(usize), + Unknown, +} + +impl Location { + pub fn concat(l: &L, r: &R) -> Self { + match (l.ln_begin(), l.col_begin(), r.ln_end(), r.col_end()) { + (Some(lb), Some(cb), Some(le), Some(ce)) => + Self::range(lb, cb, le, ce), + (Some(lb), _, Some(le), _) => Self::LineRange(lb, le), + (Some(l), _, _, _) | (_, _, Some(l), _) => Self::Line(l), + _ => Self::Unknown, + } + } + + pub const fn range(ln_begin: usize, col_begin: usize, ln_end: usize, col_end: usize) -> Self { + Self::Range{ ln_begin, col_begin, ln_end, col_end } + } + + pub fn pair(lhs: Self, rhs: Self) -> Self { + Self::RangePair{ + ln_begin: lhs.ln_begin().unwrap(), + col_first: (lhs.col_begin().unwrap(), lhs.col_end().unwrap()), + ln_end: rhs.ln_end().unwrap(), + col_second: (rhs.col_begin().unwrap(), rhs.col_end().unwrap()), + } + } + + pub const fn ln_begin(&self) -> Option { + match self { + Self::RangePair{ ln_begin, .. } + | Self::Range{ ln_begin, .. } + | Self::LineRange(ln_begin, _) + | Self::Line(ln_begin) => Some(*ln_begin), + Self::Unknown => None, + } + } + + pub const fn ln_end(&self) -> Option { + match self { + Self::RangePair{ ln_end, .. } + | Self::Range{ ln_end, .. } + | Self::LineRange(ln_end, _) + | Self::Line(ln_end) => Some(*ln_end), + Self::Unknown => None, + } + } + + pub const fn col_begin(&self) -> Option { + match self { + Self::RangePair{ col_first: (col_begin, _), .. } + | Self::Range{ col_begin, .. } => Some(*col_begin), + _ => None, + } + } + + pub const fn col_end(&self) -> Option { + match self { + Self::RangePair{ col_second: (_, col_end), .. } + | Self::Range{ col_end, .. } => Some(*col_end), + _ => None, + } + } +} + +/// Erg内で使われるエラーの共通部分 +/// 使用する場合は必ずwrapすること +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ErrorCore { + pub errno: usize, + pub kind: ErrorKind, + pub loc: Location, + pub desc: Str, + pub hint: Option, +} + +impl ErrorCore { + pub fn new>(errno: usize, kind: ErrorKind, loc: Location, desc: S, hint: Option) -> Self { + Self { errno, kind, loc, desc: desc.into(), hint } + } + + pub fn unreachable(fn_name: &str, line: u32) -> Self { Self::bug(0, Location::Unknown, fn_name, line) } + + pub fn bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self { + Self::new(errno, CompilerSystemError, loc, switch_lang!( + format!("this is a bug of Erg, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"), + format!("これはErgのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生") + ), None) + } +} + +pub const VBAR_UNICODE: &'static str = "│"; +pub const VBAR_BREAK_UNICODE: &'static str = "·"; + +/// format: +/// ```console +/// Error[#{.errno}]: File {file}, line {.loc (as line)}, in {.caused_by} +/// {.loc (as line)}| {src} +/// {pointer} +/// {.kind}: {.desc} +/// ``` +/// +/// example: +/// ```console +/// Error[#12]: File , line 1, in +/// 1| 100 = i +/// ^^^ +/// SyntaxError: cannot assign to 100 +/// ``` +pub trait ErrorDisplay { + fn core(&self) -> &ErrorCore; + fn input(&self) -> &Input; + /// The block name the error caused. + /// This will be None if the error occurred before semantic analysis. + /// As for the internal error, do not put the fn name here. + fn caused_by(&self) -> &str; + /// the previous error that caused this error. + fn ref_inner(&self) -> Option<&Box>; + + fn write_to_stderr(&self) { + let mut writer = BufWriter::new(stderr()); + writer.write(format!( + "{}{}{}: {}{}\n", + self.format_header(), + self.format_code_and_pointer(), + self.core().kind, + self.core().desc, + fmt_option!(pre format!("\n{GREEN}hint{RESET}: "), &self.core().hint), + ).as_bytes()).unwrap(); + writer.flush().unwrap(); + if let Some(inner) = self.ref_inner() { + inner.write_to_stderr() + } + } + + /// fmt::Display実装用 + fn format(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}{}{}: {}{}\n", + self.format_header(), + self.format_code_and_pointer(), + self.core().kind, + self.core().desc, + fmt_option!(pre format!("\n{GREEN}hint{RESET}: "), &self.core().hint), + )?; + if let Some(inner) = self.ref_inner() { + inner.format(f) + } else { Ok(()) } + } + + fn format_header(&self) -> String { + let kind = self.core().kind as u8; + let (color, err_or_warn) = + if kind < 100 { (RED, "Error") } + else if 100 <= kind && kind < 150 { (YELLOW, "Warning") } + else if 150 <= kind && kind < 200 { (DEEP_RED, "Error") } + else { ("", "Exception") }; + let loc = match self.core().loc { + Location::Range{ ln_begin, ln_end, .. } if ln_begin == ln_end => format!(", line {ln_begin}"), + Location::RangePair{ ln_begin, ln_end, .. } + | Location::Range{ ln_begin, ln_end, .. } + | Location::LineRange(ln_begin, ln_end) => format!(", line {ln_begin}..{ln_end}"), + Location::Line(lineno) => format!(", line {lineno}"), + Location::Unknown => "".to_string(), + }; + let caused_by = if self.caused_by() != "" { + format!(", in {}", self.caused_by()) + } else { "".to_string() }; + format!( + "{color}{err_or_warn}[#{errno:>04}]{RESET}: File {input}{loc}{caused_by}\n", + errno = self.core().errno, + input = self.input().enclosed_name(), + ) + } + + fn format_code_and_pointer(&self) -> String { + match self.core().loc { + Location::RangePair{ .. } => todo!(), + Location::Range { ln_begin, col_begin, ln_end, col_end } => { + let codes = if self.input() == &Input::REPL { + vec![self.input().reread()] + } else { + self.input().reread_lines(ln_begin, ln_end) + }; + let mut res = CYAN.to_string(); + let final_step = ln_end - ln_begin; + for (i, lineno) in (ln_begin..=ln_end).enumerate() { + let mut pointer = " ".repeat(lineno.to_string().len() + 2); // +2 means `| ` + if i == 0 && i == final_step { + pointer += &" ".repeat(col_begin); + pointer += &"^".repeat(cmp::max(1, col_end - col_begin)); + } else if i == 0 { + pointer += &" ".repeat(col_begin); + pointer += &"^".repeat(cmp::max(1, codes[i].len() - col_begin)); + } else if i == final_step { + pointer += &"^".repeat(col_end); + } else { + pointer += &"^".repeat(cmp::max(1, codes[i].len())); + } + res += &format!("{lineno}{VBAR_UNICODE} {code}\n{pointer}\n", code = codes[i]); + } + res + RESET + }, + Location::LineRange(_begin, _end) => { + todo!() + }, + Location::Line(lineno) => { + let code = if self.input() == &Input::REPL { + self.input().reread() + } else { + self.input().reread_lines(lineno, lineno).remove(0) + }; + format!("{CYAN}{lineno}{VBAR_UNICODE} {code}\n{RESET}") + }, + Location::Unknown => { + match self.input() { + Input::File(_) => "\n".to_string(), + other => format!("{CYAN}?{VBAR_UNICODE} {code}\n{RESET}", code = other.reread()), + } + }, + } + } +} + +pub trait MultiErrorDisplay: Stream { + fn fmt_all_stderr(&self) { + for err in self.iter() { + err.write_to_stderr(); + } + } + + fn fmt_all(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for err in self.iter() { + err.format(f)?; + } + write!(f, "") + } +} diff --git a/src/common/fxhash.rs b/src/common/fxhash.rs new file mode 100644 index 00000000..678d7bfd --- /dev/null +++ b/src/common/fxhash.rs @@ -0,0 +1,143 @@ +// Copied and modified from https://github.com/rust-lang/rustc-hash/blob/master/src/lib.rs +// MIT license | Apache 2.0 license +// License files are placed at the root. + +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Fast, non-cryptographic hash used by rustc and Firefox. +//! +//! # Example +//! +//! ```rust +//! # #[cfg(feature = "std")] +//! # fn main() { +//! use rustc_hash::FxHashMap; +//! let mut map: FxHashMap = FxHashMap::default(); +//! map.insert(22, 44); +//! # } +//! # #[cfg(not(feature = "std"))] +//! # fn main() { } +//! ``` + +// #![no_std] + +use core::convert::TryInto; +use core::default::Default; +use core::hash::BuildHasherDefault; +use core::hash::Hasher; +use core::mem::size_of; +use core::ops::BitXor; +use std::collections::{HashMap, HashSet}; + +/// Type alias for a hashmap using the `fx` hash algorithm. +pub type FxHashMap = HashMap>; +pub type FxHashSet = HashSet>; + +/// A speedy hash algorithm for use within rustc. The hashmap in liballoc +/// by default uses SipHash which isn't quite as speedy as we want. In the +/// compiler we're not really worried about DOS attempts, so we use a fast +/// non-cryptographic hash. +/// +/// This is the same as the algorithm used by Firefox -- which is a homespun +/// one not based on any widely-known algorithm -- though modified to produce +/// 64-bit hash values instead of 32-bit hash values. It consistently +/// out-performs an FNV-based hash within rustc itself -- the collision rate is +/// similar or slightly worse than FNV, but the speed of the hash function +/// itself is much higher because it works on up to 8 bytes at a time. +pub struct FxHasher { + hash: usize, +} + +#[cfg(target_pointer_width = "32")] +const K: usize = 0x9e3779b9; +#[cfg(target_pointer_width = "64")] +const K: usize = 0x517cc1b727220a95; + +impl Default for FxHasher { + #[inline] + fn default() -> FxHasher { + FxHasher { hash: 0 } + } +} + +impl FxHasher { + #[inline] + fn add_to_hash(&mut self, i: usize) { + self.hash = self.hash.rotate_left(5).bitxor(i).wrapping_mul(K); + } +} + +impl Hasher for FxHasher { + #[inline] + fn write(&mut self, mut bytes: &[u8]) { + #[cfg(target_pointer_width = "32")] + let read_usize = |bytes: &[u8]| u32::from_ne_bytes(bytes[..4].try_into().unwrap()); + #[cfg(target_pointer_width = "64")] + let read_usize = |bytes: &[u8]| u64::from_ne_bytes(bytes[..8].try_into().unwrap()); + + let mut hash = FxHasher { hash: self.hash }; + debug_assert!(size_of::() <= 8); + while bytes.len() >= size_of::() { + hash.add_to_hash(read_usize(bytes) as usize); + bytes = &bytes[size_of::()..]; + } + if (size_of::() > 4) && (bytes.len() >= 4) { + hash.add_to_hash(u32::from_ne_bytes(bytes[..4].try_into().unwrap()) as usize); + bytes = &bytes[4..]; + } + if (size_of::() > 2) && bytes.len() >= 2 { + hash.add_to_hash(u16::from_ne_bytes(bytes[..2].try_into().unwrap()) as usize); + bytes = &bytes[2..]; + } + if (size_of::() > 1) && bytes.len() >= 1 { + hash.add_to_hash(bytes[0] as usize); + } + self.hash = hash.hash; + } + + #[inline] + fn write_u8(&mut self, i: u8) { + self.add_to_hash(i as usize); + } + + #[inline] + fn write_u16(&mut self, i: u16) { + self.add_to_hash(i as usize); + } + + #[inline] + fn write_u32(&mut self, i: u32) { + self.add_to_hash(i as usize); + } + + #[cfg(target_pointer_width = "32")] + #[inline] + fn write_u64(&mut self, i: u64) { + self.add_to_hash(i as usize); + self.add_to_hash((i >> 32) as usize); + } + + #[cfg(target_pointer_width = "64")] + #[inline] + fn write_u64(&mut self, i: u64) { + self.add_to_hash(i as usize); + } + + #[inline] + fn write_usize(&mut self, i: usize) { + self.add_to_hash(i); + } + + #[inline] + fn finish(&self) -> u64 { + self.hash as u64 + } +} diff --git a/src/common/lazy.rs b/src/common/lazy.rs new file mode 100644 index 00000000..52da737e --- /dev/null +++ b/src/common/lazy.rs @@ -0,0 +1,364 @@ +// Copied and modified from https://github.com/rust-lang/rust/blob/master/library/core/src/lazy.rs +// and https://github.com/rust-lang/rust/blob/master/library/core/src/mem/maybe_uninit.rs +// MIT license | Apache 2.0 license +// License files are placed at the root. + +//! Lazy values and one-time initialization of static data. + +use std::cell::{Cell, UnsafeCell}; +use std::fmt; +use std::mem; +use std::ops::Deref; + +/// A cell which can be written to only once. +/// +/// Unlike `RefCell`, a `OnceCell` only provides shared `&T` references to its value. +/// Unlike `Cell`, a `OnceCell` doesn't require copying or replacing the value to access it. +/// +/// # Examples +/// +/// ``` +/// #![feature(once_cell)] +/// +/// use std::lazy::OnceCell; +/// +/// let cell = OnceCell::new(); +/// assert!(cell.get().is_none()); +/// +/// let value: &String = cell.get_or_init(|| { +/// "Hello, World!".to_string() +/// }); +/// assert_eq!(value, "Hello, World!"); +/// assert!(cell.get().is_some()); +/// ``` +pub struct OnceCell { + // Invariant: written to at most once. + inner: UnsafeCell>, +} + +impl Default for OnceCell { + fn default() -> Self { + Self::new() + } +} + +impl fmt::Debug for OnceCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.get() { + Some(v) => f.debug_tuple("OnceCell").field(v).finish(), + None => f.write_str("OnceCell(Uninit)"), + } + } +} + +impl Clone for OnceCell { + fn clone(&self) -> OnceCell { + let res = OnceCell::new(); + if let Some(value) = self.get() { + match res.set(value.clone()) { + Ok(()) => (), + Err(_) => unreachable!(), + } + } + res + } +} + +impl PartialEq for OnceCell { + fn eq(&self, other: &Self) -> bool { + self.get() == other.get() + } +} + +impl Eq for OnceCell {} + +impl From for OnceCell { + fn from(value: T) -> Self { + OnceCell { inner: UnsafeCell::new(Some(value)) } + } +} + +impl OnceCell { + /// Creates a new empty cell. + pub const fn new() -> OnceCell { + OnceCell { inner: UnsafeCell::new(None) } + } + + /// Gets the reference to the underlying value. + /// + /// Returns `None` if the cell is empty. + pub fn get(&self) -> Option<&T> { + // SAFETY: Safe due to `inner`'s invariant + unsafe { &*self.inner.get() }.as_ref() + } + + /// Gets the mutable reference to the underlying value. + /// + /// Returns `None` if the cell is empty. + pub fn get_mut(&mut self) -> Option<&mut T> { + // SAFETY: Safe because we have unique access + unsafe { &mut *self.inner.get() }.as_mut() + } + + /// Sets the contents of the cell to `value`. + /// + /// # Errors + /// + /// This method returns `Ok(())` if the cell was empty and `Err(value)` if + /// it was full. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// assert!(cell.get().is_none()); + /// + /// assert_eq!(cell.set(92), Ok(())); + /// assert_eq!(cell.set(62), Err(62)); + /// + /// assert!(cell.get().is_some()); + /// ``` + pub fn set(&self, value: T) -> Result<(), T> { + // SAFETY: Safe because we cannot have overlapping mutable borrows + let slot = unsafe { &*self.inner.get() }; + if slot.is_some() { + return Err(value); + } + + // SAFETY: This is the only place where we set the slot, no races + // due to reentrancy/concurrency are possible, and we've + // checked that slot is currently `None`, so this write + // maintains the `inner`'s invariant. + let slot = unsafe { &mut *self.inner.get() }; + *slot = Some(value); + Ok(()) + } + + /// Gets the contents of the cell, initializing it with `f` + /// if the cell was empty. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. Doing + /// so results in a panic. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// let value = cell.get_or_init(|| 92); + /// assert_eq!(value, &92); + /// let value = cell.get_or_init(|| unreachable!()); + /// assert_eq!(value, &92); + /// ``` + pub fn get_or_init(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + match self.get_or_try_init(|| Ok::(f())) { + Ok(val) => val, + Err(()) => unreachable!(), + } + } + + /// Gets the contents of the cell, initializing it with `f` if + /// the cell was empty. If the cell was empty and `f` failed, an + /// error is returned. + /// + /// # Panics + /// + /// If `f` panics, the panic is propagated to the caller, and the cell + /// remains uninitialized. + /// + /// It is an error to reentrantly initialize the cell from `f`. Doing + /// so results in a panic. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell = OnceCell::new(); + /// assert_eq!(cell.get_or_try_init(|| Err(())), Err(())); + /// assert!(cell.get().is_none()); + /// let value = cell.get_or_try_init(|| -> Result { + /// Ok(92) + /// }); + /// assert_eq!(value, Ok(&92)); + /// assert_eq!(cell.get(), Some(&92)) + /// ``` + pub fn get_or_try_init(&self, f: F) -> Result<&T, E> + where + F: FnOnce() -> Result, + { + if let Some(val) = self.get() { + return Ok(val); + } + let val = f()?; + // Note that *some* forms of reentrant initialization might lead to + // UB (see `reentrant_init` test). I believe that just removing this + // `assert`, while keeping `set/get` would be sound, but it seems + // better to panic, rather than to silently use an old value. + assert!(self.set(val).is_ok(), "reentrant init"); + Ok(self.get().unwrap()) + } + + /// Consumes the cell, returning the wrapped value. + /// + /// Returns `None` if the cell was empty. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let cell: OnceCell = OnceCell::new(); + /// assert_eq!(cell.into_inner(), None); + /// + /// let cell = OnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.into_inner(), Some("hello".to_string())); + /// ``` + pub fn into_inner(self) -> Option { + // Because `into_inner` takes `self` by value, the compiler statically verifies + // that it is not currently borrowed. So it is safe to move out `Option`. + self.inner.into_inner() + } + + /// Takes the value out of this `OnceCell`, moving it back to an uninitialized state. + /// + /// Has no effect and returns `None` if the `OnceCell` hasn't been initialized. + /// + /// Safety is guaranteed by requiring a mutable reference. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::OnceCell; + /// + /// let mut cell: OnceCell = OnceCell::new(); + /// assert_eq!(cell.take(), None); + /// + /// let mut cell = OnceCell::new(); + /// cell.set("hello".to_string()).unwrap(); + /// assert_eq!(cell.take(), Some("hello".to_string())); + /// assert_eq!(cell.get(), None); + /// ``` + pub fn take(&mut self) -> Option { + mem::take(self).into_inner() + } +} + +/// A value which is initialized on the first access. +/// +/// # Examples +/// +/// ``` +/// #![feature(once_cell)] +/// +/// use std::lazy::Lazy; +/// +/// let lazy: Lazy = Lazy::new(|| { +/// println!("initializing"); +/// 92 +/// }); +/// println!("ready"); +/// println!("{}", *lazy); +/// println!("{}", *lazy); +/// +/// // Prints: +/// // ready +/// // initializing +/// // 92 +/// // 92 +/// ``` +pub struct Lazy T> { + cell: OnceCell, + init: Cell>, +} + +impl fmt::Debug for Lazy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Lazy").field("cell", &self.cell).field("init", &"..").finish() + } +} + +impl Lazy { + /// Creates a new lazy value with the given initializing function. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// # fn main() { + /// use std::lazy::Lazy; + /// + /// let hello = "Hello, World!".to_string(); + /// + /// let lazy = Lazy::new(|| hello.to_uppercase()); + /// + /// assert_eq!(&*lazy, "HELLO, WORLD!"); + /// # } + /// ``` + pub const fn new(init: F) -> Lazy { + Lazy { cell: OnceCell::new(), init: Cell::new(Some(init)) } + } +} + +impl T> Lazy { + /// Forces the evaluation of this lazy value and returns a reference to + /// the result. + /// + /// This is equivalent to the `Deref` impl, but is explicit. + /// + /// # Examples + /// + /// ``` + /// #![feature(once_cell)] + /// + /// use std::lazy::Lazy; + /// + /// let lazy = Lazy::new(|| 92); + /// + /// assert_eq!(Lazy::force(&lazy), &92); + /// assert_eq!(&*lazy, &92); + /// ``` + pub fn force(this: &Lazy) -> &T { + this.cell.get_or_init(|| match this.init.take() { + Some(f) => f(), + None => panic!("`Lazy` instance has previously been poisoned"), + }) + } +} + +impl T> Deref for Lazy { + type Target = T; + fn deref(&self) -> &T { + Lazy::force(self) + } +} + +impl Default for Lazy { + /// Creates a new lazy value using `Default` as the initializing function. + fn default() -> Lazy { + Lazy::new(T::default) + } +} diff --git a/src/common/lazy_buffer.rs b/src/common/lazy_buffer.rs new file mode 100644 index 00000000..696cb53d --- /dev/null +++ b/src/common/lazy_buffer.rs @@ -0,0 +1,70 @@ +// Copied and modified from https://github.com/rust-itertools/itertools/blob/master/src/lazy_buffer.rs +// MIT license | Apache 2.0 license +// License files are placed at the root. + +use std::ops::Index; + +#[derive(Debug, Clone)] +pub struct LazyBuffer { + pub it: I, + done: bool, + buffer: Vec, +} + +impl LazyBuffer +where + I: Iterator, +{ + pub fn new(it: I) -> LazyBuffer { + LazyBuffer { + it, + done: false, + buffer: Vec::new(), + } + } + + pub fn len(&self) -> usize { + self.buffer.len() + } + + pub fn get_next(&mut self) -> bool { + if self.done { + return false; + } + let next_item = self.it.next(); + match next_item { + Some(x) => { + self.buffer.push(x); + true + } + None => { + self.done = true; + false + } + } + } + + pub fn prefill(&mut self, len: usize) { + let buffer_len = self.buffer.len(); + + if !self.done && len > buffer_len { + let delta = len - buffer_len; + + self.buffer.extend(self.it.by_ref().take(delta)); + self.done = self.buffer.len() < len; + } + } +} + +impl Index for LazyBuffer +where + I: Iterator, + I::Item: Sized, + Vec: Index +{ + type Output = as Index>::Output; + + fn index(&self, _index: J) -> &Self::Output { + self.buffer.index(_index) + } +} diff --git a/src/common/levenshtein.rs b/src/common/levenshtein.rs new file mode 100644 index 00000000..5c40707f --- /dev/null +++ b/src/common/levenshtein.rs @@ -0,0 +1,30 @@ +/// Calculates the Levenshtein distance (edit distance). +/// This shows how close the strings are to each other. +pub fn levenshtein(lhs: &str, rhs: &str) -> usize { + let lhs = lhs.chars().collect::>(); + let rhs = rhs.chars().collect::>(); + let l_len = lhs.len(); + let r_len = rhs.len(); + // l_len+1 × r_len+1 array + let mut table = vec![vec![0; r_len + 1]; l_len + 1]; + for i in 0..l_len + 1 { + table[i][0] = i; + } + for i in 0..r_len + 1 { + table[0][i] = i; + } + for i1 in 0..l_len { + for i2 in 0..r_len { + let cost = if lhs[i1] == rhs[i2] { 0 } else { 1 }; + table[i1 + 1][i2 + 1] = *[ + table[i1][i2 + 1] + 1, // delete cost + table[i1 + 1][i2] + 1, // insert cost + table[i1][i2] + cost, // replace cost + ] + .iter() + .min() + .unwrap(); + } + } + return table[l_len][r_len] +} diff --git a/src/common/lib.rs b/src/common/lib.rs new file mode 100644 index 00000000..1ee4b588 --- /dev/null +++ b/src/common/lib.rs @@ -0,0 +1,121 @@ +//! provides utilities for parser, compiler, and vm crate. +use std::fmt; + +pub mod cache; +pub mod config; +pub mod datetime; +pub mod error; +pub mod lazy_buffer; +pub mod levenshtein; +pub mod codeobj; +pub mod combinations; +pub mod value; +pub mod color; +pub mod macros; +pub mod opcode; +pub mod python_util; +pub mod serialize; +pub mod deserialize; +pub mod traits; +pub mod tsort; +pub mod ty; +pub mod lazy; +pub mod rccell; +pub mod stdin; +pub mod str; +pub mod fxhash; +pub mod set; +pub mod dict; + +pub use crate::str::Str; +use crate::set::Set; + +pub type RcArray = std::rc::Rc<[T]>; + +pub fn open_read(filename: &str) -> std::io::Result { + let f = std::fs::File::open(filename)?; + read_file(f) +} + +pub fn read_file(mut f: std::fs::File) -> std::io::Result { + let mut s = "".to_string(); + std::io::Read::read_to_string(&mut f, &mut s).unwrap(); + Ok(s) +} + +pub fn fmt_vec(v: &Vec) -> String { + fmt_iter(v.iter()) +} + +pub fn fmt_slice(v: &[T]) -> String { + fmt_iter(v.iter()) +} + +pub fn fmt_vec_split_with(v: &Vec, splitter: &str) -> String { + fmt_iter_split_with(v.iter(), splitter) +} + +pub fn fmt_set_split_with(s: &Set, splitter: &str) -> String { + fmt_iter_split_with(s.iter(), splitter) +} + +pub fn debug_fmt_iter>(iter: I) -> String { + let mut s = iter.fold("".to_string(), |sum, elem| format!("{sum}{elem:?}, ")); + s.pop(); + s.pop(); + s +} + +pub fn fmt_iter>(iter: I) -> String { + let mut s = iter.fold("".to_string(), |sum, elem| sum + &elem.to_string() + ", "); + s.pop(); + s.pop(); + s +} + +pub fn fmt_iter_split_with>(i: I, splitter: &str) -> String { + let mut s = i.fold("".to_string(), |sum, elem| { + sum + &elem.to_string() + splitter + }); + for _ in 0..splitter.len() { + s.pop(); + } + s +} + +pub fn fmt_indent(s: String, depth: usize) -> String { + let indent = " ".repeat(depth); + s.split("\n").map(|s| indent.clone() + s).collect() +} + +pub fn get_hash(t: &T) -> usize { + let mut s = fxhash::FxHasher::default(); + t.hash(&mut s); + let res = std::hash::Hasher::finish(&s); + if cfg!(target_pointer_width = "64") { res as usize } + else { (res % usize::MAX as u64) as usize } +} + +/// \r\n (Windows), \r (old MacOS) -> \n (Unix) +#[inline] +pub fn normalize_newline(src: &str) -> String { + src.replace("\r\n", "\n").replace("\r", "\n") +} + +/// cut \n +#[inline] +pub fn chomp(src: &str) -> String { + normalize_newline(src).replace("\n", "") +} + +pub fn try_map(i: I, f: F) -> Result, E> +where + F: Fn(T) -> Result, + I: Iterator { + let mut v = vec![]; + for x in i { + let y = f(x)?; + v.push(y); + } + Ok(v) +} diff --git a/src/common/macros.rs b/src/common/macros.rs new file mode 100644 index 00000000..d94cedb1 --- /dev/null +++ b/src/common/macros.rs @@ -0,0 +1,268 @@ +#[macro_export] +macro_rules! impl_display_for_single_struct { + ($Name: ident, $name: tt $(. $attr: tt)*) => { + impl std::fmt::Display for $Name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.$name $(. $attr)*) + } + } + }; + ($Name: ident) => { + impl std::fmt::Display for $Name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } + } + } +} + +#[macro_export] +macro_rules! impl_display_from_debug { + ($Name: ident) => { + impl std::fmt::Display for $Name { + #[inline] + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{self:#?}") } + } + } +} + +#[macro_export] +macro_rules! impl_display_for_enum { + ($Enum: ident; $($Variant: ident $(,)?)*) => { + impl std::fmt::Display for $Enum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + $($Enum::$Variant(v) => write!(f, "{}", v),)* + } + } + } + } +} + +#[macro_export] +macro_rules! impl_display_for_enum_with_variant { + ($Enum: ident; $($Variant: ident $(,)?)*) => { + impl std::fmt::Display for $Enum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + $($Enum::$Variant(v) => write!(f, "{} {}", stringify!($Variant), v),)* + } + } + } + } +} + +/// More languages will be added ... +/// マクロはパラメータを展開しないので、format!のロスがなくなる +#[macro_export] +macro_rules! switch_lang { + ($en: expr, $jp: expr $(,)*) => {{ + if cfg!(feature = "japanese") { $jp } else { $en } + }} +} + +/// 2重のunwrapまでサポート +/// :は制限を回避するためのdummy token +/// ``` +/// let i: IntObj = enum_unwrap!(obj, Obj::Int); +/// let i: i32 = enum_unwrap!(obj, Obj::Int:(IntObj:(_))); +/// ``` +#[macro_export] +macro_rules! enum_unwrap { + ($ex: expr, $Enum: path $(,)*) => {{ + if let $Enum(res) = $ex { res } else { common::switch_unreachable!() } + }}; + ($ex: expr, $Enum: path :( $Cons: path :(_) ) $(,)*) => {{ + if let $Enum($Cons(res)) = $ex { res } else { common::switch_unreachable!() } + }}; + // X::A{a, b} + ($ex: expr, $Enum: path {$($fields: ident $(,)*)*}) => {{ + if let $Enum{$($fields,)*} = $ex { ($($fields,)*) } else { common::switch_unreachable!() } + }}; +} + +/// ```rust +/// assert fmt_option!(Some(1)) == "1" +/// assert fmt_option!(None) == "" +/// assert fmt_option!(None, else 1) == "1" +/// assert fmt_option!(Some(1), post: ",") == "1," +/// assert fmt_option!("[", Some(1), "]") == "[1]" +/// ``` +#[macro_export] +macro_rules! fmt_option { + ($ex: expr $(,)*) => { + if let Some(x) = $ex { format!("{}", x) } else { "".to_string() } + }; + ($ex: expr $(,)*, else $els: expr $(,)*) => { + if let Some(x) = $ex { format!("{}", x) } else { $els.to_string() } + }; + (pre $prefix: expr, $ex: expr $(,)*) => { + if let Some(x) = $ex { format!("{}{}", $prefix, x) } else { "".to_string() } + }; + ($ex: expr, post $postfix: expr $(,)*) => { + if let Some(x) = $ex { format!("{}{}", x, $postfix) } else { "".to_string() } + }; + ($prefix: expr, $ex: expr, $postfix: expr $(,)*) => { + if let Some(x) = $ex { format!("{}{}{}", $prefix, x, $postfix) } else { "".to_string() } + }; +} + +#[macro_export] +macro_rules! switch_unreachable { + () => {{ + if cfg!(debug_assertions) { unreachable!() } + else { unsafe { std::hint::unreachable_unchecked() } } + }} +} + +#[macro_export] +macro_rules! assume_unreachable { + () => {{ unsafe { std::hint::unreachable_unchecked() } }} +} + +/// indicates the current invoked function. +#[macro_export] +macro_rules! fn_name_full { + () => {{ + const fn dummy() {} + fn type_name_of(_: T) -> &'static str { std::any::type_name::() } + let name = type_name_of(dummy); // "~::dummy" + &name[..name.len() - 7] // remove "::dummy" + }} +} + +#[macro_export] +macro_rules! fn_name { + () => {{ + const fn dummy() {} + fn type_name_of(_: T) -> &'static str { std::any::type_name::() } + let name = type_name_of(dummy).rsplit("::").nth(1).unwrap(); + &name[..] + }} +} + +// do not break lines (line!() is used) +#[macro_export] +macro_rules! caused_by { + () => {{ let fn_name = $crate::fn_name!(); &format!("{fn_name} at line {}", line!()) }} +} + +/// 一度文字列をパースするので +/// アドレスの比較をしたい場合はaddr_eq!またはAddrEq traitを使うこと +#[macro_export] +macro_rules! addr { + ($obj: expr) => {{ + let s = format!("{:p}", &$obj); + let s = s.trim_start_matches("0x"); + u64::from_str_radix(&s, 16).unwrap() + }} +} + +#[macro_export] +macro_rules! addr_eq { + ($l: expr, $r: expr $(,)*) => {{ &$l as *const _ == &$r as *const _ }} +} + +#[macro_export] +macro_rules! power_assert { + ($l: expr, $op: tt, $r: expr $(,)*) => { + if !($l $op $r) { + let s_l = stringify!($l); + let s_r = stringify!($r); + let s_op = stringify!($op); + panic!( + "assertion failed: `{s_l} {s_op} {s_r}` (`{s_l}` = {:#?}, `{s_r}` = {:#?})", + $l, $r, + ) + } + }; + ($cond: expr) => { + if !$cond { + let s_cond = stringify!($cond); + panic!("assertion failed: `{s_cond}` == {:#?}", $cond) + } + }; +} + +#[macro_export] +macro_rules! debug_power_assert { + ($l: expr, $op: tt, $r: expr) => { + if cfg!(debug_assertions) { common::power_assert!($l, $op, $r) } + }; + ($ex: expr) => { + if cfg!(debug_assertions) { common::power_assert!($ex) } + }; +} + +#[macro_export] +macro_rules! debug_enum_assert { + ($ex: expr, $Enum: ident :: $Variant: ident $(,)*) => { + debug_assert!(common::enum_is!($ex, $Enum::$Variant)); + }; + ($ex: expr, $Enum: ident :: $Variant: ident, $Enum2: ident :: $Variant2: ident $(,)*) => {{ + debug_assert!(common::enum_is!($ex, $Enum::$Variant, $Enum2::$Variant2)); + }}; + ($ex: expr, $TupleCons: ident, $Enum: ident :: $Variant: ident $(,)*) => {{ + debug_assert!(common::enum_is!($ex, $TupleCons, $Enum::$Variant)); + }} +} + +#[macro_export] +macro_rules! log { + (f $output: ident, $($arg: tt)*) => { + if cfg!(feature = "debug") { write!($output, "{}:{}: ", file!(), line!()).unwrap(); + write!($output, $($arg)*).unwrap(); + $output.flush().unwrap(); + } + }; + ($($arg: tt)*) => { + if cfg!(feature = "debug") { print!("{}:{}: ", file!(), line!()); println!($($arg)*); } + }; +} + +#[macro_export] +macro_rules! log_with_time { + (f $output: ident, $($arg: tt)*) => { + if cfg!(feature = "debug") { + write!($output, "{}: ", $crate::datetime::now()).unwrap(); + write!($output, $($arg)*).unwrap(); + $output.flush().unwrap(); + } + }; + ($($arg: tt)*) => { + if cfg!(feature = "debug") { + print!("{}: ", $crate::datetime::now()); + println!($($arg)*); + } + }; +} + +#[macro_export] +macro_rules! fmt_dbg { + ($arg: expr $(,)*) => { + if cfg!(feature = "debug") { print!("{}:{}:\n", file!(), line!()); + print!("{} = ", stringify!($arg)); + println!("{}", $arg); + } + }; + ($head: expr, $($arg: expr,)+) => { + if cfg!(feature = "debug") { print!("{}:{}:\n", file!(), line!()); + print!("{} = ", stringify!($head)); + println!("{}", $head); + $crate::fmt_dbg!(rec $($arg,)+); + } + }; + (rec $arg: expr,) => { + if cfg!(feature = "debug") { + print!("{} = ", stringify!($arg)); + println!("{}", $arg); + } + }; + (rec $head: expr, $($arg: expr,)+) => { + if cfg!(feature = "debug") { + print!("{} = ", stringify!($head)); + println!("{}", $head); + $crate::fmt_dbg!(rec $($arg,)+); + } + }; +} diff --git a/src/common/opcode.rs b/src/common/opcode.rs new file mode 100644 index 00000000..18980467 --- /dev/null +++ b/src/common/opcode.rs @@ -0,0 +1,298 @@ +//! defines `Opcode` (represents Python bytecode opcodes). +//! +//! Opcode(Pythonバイトコードオペコードを表す)を定義する + +#![allow(dead_code)] +#![allow(non_camel_case_types)] + +use crate::impl_display_from_debug; + +/// Based on Python opcodes. +/// This is represented by u8. +/// +/// TODO: implement all opcodes +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq)] +#[repr(u8)] +pub enum Opcode { + POP_TOP = 1, + ROT_TWO = 2, + ROT_THREE = 3, + DUP_TOP = 4, + DUP_TOP2 = 5, + ROT_FOUR = 6, + NOP = 9, + UNARY_POSITIVE = 10, + UNARY_NEGATIVE = 11, + UNARY_NOT = 12, + UNARY_INVERT = 15, + BINARY_MATRIX_MULTIPLY = 16, + INPLACE_MATRIX_MULTIPLY = 17, + BINARY_POWER = 19, + BINARY_MULTIPLY = 20, + BINARY_MODULO = 22, + BINARY_ADD = 23, + BINARY_SUBTRACT = 24, + BINARY_SUBSCR = 25, + BINARY_TRUE_DIVIDE = 27, + INPLACE_FLOOR_DIVIDE = 28, + INPLACE_TRUE_DIVIDE = 29, + GET_LEN = 30, + MATCH_MAPPING = 31, + MATCH_SEQUENCE = 32, + MATCH_KEYS = 33, + PUSH_EXC_INFO = 35, + CHECK_EXC_MATCH = 36, + CHECK_EG_MATCH = 37, + WITH_EXCEPT_START = 49, + GET_AITER = 50, + GET_ANEXT = 51, + BEFORE_ASYNC_WITH = 52, + BEFORE_WITH = 53, + END_ASYNC_FOR = 54, + // TODO: + INPLACE_ADD = 55, + INPLACE_SUBTRACT = 56, + INPLACE_MULTIPLY = 57, + INPLACE_MODULO = 59, + STORE_SUBSCR = 60, + BINARY_AND = 64, + BINARY_XOR = 65, + BINARY_OR = 66, + GET_ITER = 68, + GET_YIELD_FROM_ITER = 69, + PRINT_EXPR = 70, + LOAD_BUILD_CLASS = 71, + LOAD_ASSERTION_ERROR = 74, + RETURN_VALUE = 83, + /* ↓ These opcodes take an arg */ + STORE_NAME = 90, + DELETE_NAME = 91, + UNPACK_SEQUENCE = 92, + FOR_ITER = 93, + UNPACK_EX = 94, + STORE_ATTR = 95, + STORE_GLOBAL = 97, + LOAD_CONST = 100, + LOAD_NAME = 101, + BUILD_TUPLE = 102, + BUILD_LIST = 103, + BUILD_SET = 104, + BUILD_MAP = 105, // build a Dict object + LOAD_ATTR = 106, + COMPARE_OP = 107, + IMPORT_NAME = 108, + IMPORT_FROM = 109, + JUMP_FORWARD = 110, + JUMP_IF_FALSE_OR_POP = 111, + JUMP_IF_TRUE_OR_POP = 112, + JUMP_ABSOLUTE = 113, + POP_JUMP_IF_FALSE = 114, + POP_JUMP_IF_TRUE = 115, + LOAD_GLOBAL = 116, + IS_OP = 117, + CONTAINS_OP = 118, + LOAD_FAST = 124, + STORE_FAST = 125, + DELETE_FAST = 126, + RAISE_VARARGS = 130, + CALL_FUNCTION = 131, + MAKE_FUNCTION = 132, + LOAD_CLOSURE = 135, + LOAD_DEREF = 136, + STORE_DEREF = 137, + CALL_FUNCTION_KW = 141, + LOAD_METHOD = 160, + CALL_METHOD = 161, + // Erg-specific opcodes (must have a unary `ERG_`) + // Define in descending order from 219, 255 + ERG_POP_NTH = 196, + ERG_PEEK_NTH = 197, // get ref to the arg-th element from TOS + ERG_INC = 198, // name += 1; arg: typecode + ERG_DEC = 199, // name -= 1 + ERG_LOAD_FAST_IMMUT = 200, + ERG_STORE_FAST_IMMUT = 201, + ERG_MOVE_FAST = 202, + ERG_CLONE_FAST = 203, + ERG_COPY_FAST = 204, + ERG_REF_FAST = 205, + ERG_REF_MUT_FAST = 206, + ERG_MOVE_OUTER = 207, + ERG_CLONE_OUTER = 208, + ERG_COPY_OUTER = 209, + ERG_REF_OUTER = 210, + ERG_REF_MUT_OUTER = 211, + ERG_LESS_THAN = 212, + ERG_LESS_EQUAL = 213, + ERG_EQUAL = 214, + ERG_NOT_EQUAL = 215, + ERG_MAKE_SLOT = 216, + ERG_MAKE_TYPE = 217, + ERG_MAKE_PURE_FUNCTION = 218, + ERG_CALL_PURE_FUNCTION = 219, + /* ↑ These opcodes take an arg ↑ */ + /* ↓ These opcodes take no arg ↓ */ + // ... = 220, + ERG_LOAD_EMPTY_SLOT = 242, + ERG_LOAD_EMPTY_STR = 243, + ERG_LOAD_1_NAT = 244, + ERG_LOAD_1_INT = 245, + ERG_LOAD_1_REAL = 246, + ERG_LOAD_NONE = 247, + ERG_MUTATE = 248, // !x + /// `[] =` (it doesn't cause any exceptions) + ERG_STORE_SUBSCR = 249, + // ... = 250, + /// `= []` (it doesn't cause any exceptions) + ERG_BINARY_SUBSCR = 251, + ERG_BINARY_RANGE = 252, + /// `/?` (rhs may be 0, it may cause a runtime panic) + ERG_TRY_BINARY_DIVIDE = 253, + /// `/` (rhs could not be 0, it doesn't cause any exceptions) + ERG_BINARY_TRUE_DIVIDE = 254, + NOT_IMPLEMENTED = 255, +} + +use Opcode::*; + +impl_display_from_debug!(Opcode); + +impl From for Opcode { + fn from(byte: u8) -> Self { + match byte { + 1 => POP_TOP, + 2 => ROT_TWO, + 3 => ROT_THREE, + 4 => DUP_TOP, + 5 => DUP_TOP2, + 6 => ROT_FOUR, + 9 => NOP, + 10 => UNARY_POSITIVE, + 11 => UNARY_NEGATIVE, + 12 => UNARY_NOT, + 15 => UNARY_INVERT, + 19 => BINARY_POWER, + 20 => BINARY_MULTIPLY, + 22 => BINARY_MODULO, + 23 => BINARY_ADD, + 24 => BINARY_SUBTRACT, + 25 => BINARY_SUBSCR, + 27 => BINARY_TRUE_DIVIDE, + 28 => INPLACE_FLOOR_DIVIDE, + 29 => INPLACE_TRUE_DIVIDE, + 30 => GET_LEN, + 31 => MATCH_MAPPING, + 32 => MATCH_SEQUENCE, + 33 => MATCH_KEYS, + 35 => PUSH_EXC_INFO, + 36 => CHECK_EXC_MATCH, + 37 => CHECK_EG_MATCH, + 49 => WITH_EXCEPT_START, + 50 => GET_AITER, + 51 => GET_ANEXT, + 52 => BEFORE_ASYNC_WITH, + 53 => BEFORE_WITH, + 54 => END_ASYNC_FOR, + 55 => INPLACE_ADD, + 56 => INPLACE_SUBTRACT, + 57 => INPLACE_MULTIPLY, + 59 => INPLACE_MODULO, + 60 => STORE_SUBSCR, + 64 => BINARY_AND, + 65 => BINARY_XOR, + 66 => BINARY_OR, + 68 => GET_ITER, + 69 => GET_YIELD_FROM_ITER, + 70 => PRINT_EXPR, + 71 => LOAD_BUILD_CLASS, + 74 => LOAD_ASSERTION_ERROR, + 83 => RETURN_VALUE, + /* ↓ These opcodes take an arg */ + 90 => STORE_NAME, + 91 => DELETE_NAME, + 92 => UNPACK_SEQUENCE, + 93 => FOR_ITER, + 94 => UNPACK_EX, + 95 => STORE_ATTR, + 97 => STORE_GLOBAL, + 100 => LOAD_CONST, + 101 => LOAD_NAME, + 102 => BUILD_TUPLE, + 103 => BUILD_LIST, + 104 => BUILD_SET, + 105 => BUILD_MAP, + 106 => LOAD_ATTR, + 107 => COMPARE_OP, + 108 => IMPORT_NAME, + 109 => IMPORT_FROM, + 110 => JUMP_FORWARD, + 111 => JUMP_IF_FALSE_OR_POP, + 112 => JUMP_IF_TRUE_OR_POP, + 113 => JUMP_ABSOLUTE, + 114 => POP_JUMP_IF_FALSE, + 115 => POP_JUMP_IF_TRUE, + 116 => LOAD_GLOBAL, + 117 => IS_OP, + 118 => CONTAINS_OP, + 124 => LOAD_FAST, + 125 => STORE_FAST, + 126 => DELETE_FAST, + 130 => RAISE_VARARGS, + 131 => CALL_FUNCTION, + 132 => MAKE_FUNCTION, + 135 => LOAD_CLOSURE, + 136 => LOAD_DEREF, + 137 => STORE_DEREF, + 141 => CALL_FUNCTION_KW, + 160 => LOAD_METHOD, + 161 => CALL_METHOD, + // Erg-specific opcodes + 196 => ERG_POP_NTH, + 197 => ERG_PEEK_NTH, + 198 => ERG_INC, + 199 => ERG_DEC, + 200 => ERG_LOAD_FAST_IMMUT, + 201 => ERG_STORE_FAST_IMMUT, + 202 => ERG_MOVE_FAST, + 203 => ERG_CLONE_FAST, + 204 => ERG_COPY_FAST, + 205 => ERG_REF_FAST, + 206 => ERG_REF_MUT_FAST, + 207 => ERG_MOVE_OUTER, + 208 => ERG_CLONE_OUTER, + 209 => ERG_COPY_OUTER, + 210 => ERG_REF_OUTER, + 211 => ERG_REF_MUT_OUTER, + 212 => ERG_LESS_THAN, + 213 => ERG_LESS_EQUAL, + 214 => ERG_EQUAL, + 215 => ERG_NOT_EQUAL, + // ERG_GREATER_THAN is not necessary (can be done by inverting the argument of LESS_THAN) + 216 => ERG_MAKE_SLOT, + 217 => ERG_MAKE_TYPE, + 218 => ERG_MAKE_PURE_FUNCTION, + 219 => ERG_CALL_PURE_FUNCTION, + /* ↑ These opcodes take an arg ↑ */ + /* ↓ These opcodes take no arg ↓ */ + // ... = 220, + 242 => ERG_LOAD_EMPTY_SLOT, + 243 => ERG_LOAD_EMPTY_STR, + 244 => ERG_LOAD_1_NAT, + 245 => ERG_LOAD_1_INT, + 246 => ERG_LOAD_1_REAL, + 247 => ERG_LOAD_NONE, + 248 => ERG_MUTATE, + 249 => ERG_STORE_SUBSCR, + // 250 => + 251 => ERG_BINARY_SUBSCR, + 252 => ERG_BINARY_RANGE, + 253 => ERG_TRY_BINARY_DIVIDE, + 254 => ERG_BINARY_TRUE_DIVIDE, + 255 => NOT_IMPLEMENTED, + other => panic!("not implemented opcode: {other}"), + } + } +} + +impl Opcode { + pub const fn take_arg(&self) -> bool { 90 <= (*self as u8) && (*self as u8) < 220 } +} diff --git a/src/common/python_util.rs b/src/common/python_util.rs new file mode 100644 index 00000000..2d1752b9 --- /dev/null +++ b/src/common/python_util.rs @@ -0,0 +1,61 @@ +//! utilities for calling CPython. +//! +//! CPythonを呼び出すためのユーティリティー +use std::process::Command; + +use crate::serialize::get_magic_num_from_bytes; + +pub fn which_python() -> String { + let python = if cfg!(windows) { "python" } else { "python3" }; + let out = Command::new("which") + .arg(python) + .output() + .expect("python not found"); + String::from_utf8(out.stdout) + .unwrap() + .replace("\n", "") + .replace("\r", "") +} + +pub fn detect_magic_number() -> u32 { + let command = if cfg!(windows) { "cmd" } else { "sh" }; + let arg = if cfg!(windows) { "/C" } else { "-c" }; + let out = Command::new(command) + .arg(arg) + .arg(which_python()) + .arg("-c") + .arg("import importlib.util as util;print(util.MAGIC_NUMBER.hex())") + .output() + .expect("cannot get the magic number from python"); + let s_hex_magic_num = String::from_utf8(out.stdout).unwrap(); + let first_byte = u8::from_str_radix(&s_hex_magic_num[0..=1], 16).unwrap(); + let second_byte = u8::from_str_radix(&s_hex_magic_num[2..=3], 16).unwrap(); + get_magic_num_from_bytes(&[first_byte, second_byte, 0, 0]) +} + +pub fn exec_pyc>(file: S) { + // executes over a shell, cause `python` may not exist as an executable file (like pyenv) + let command = if cfg!(windows) { "cmd" } else { "sh" }; + let arg = if cfg!(windows) { "/C" } else { "-c" }; + let mut out = Command::new(command) + .arg(arg) + .arg(which_python()) + .arg(&file.into()) + .spawn() + .expect("python not found"); + out.wait().expect("python doesn't work"); +} + +pub fn eval_pyc>(file: S) -> String { + // executes over a shell, cause `python` may not exist as an executable file (like pyenv) + let command = if cfg!(windows) { "cmd" } else { "sh" }; + let arg = if cfg!(windows) { "/C" } else { "-c" }; + let out = Command::new(command) + .arg(arg) + .arg(which_python()) + .arg(&file.into()) + .spawn() + .expect("python not found"); + let out = out.wait_with_output().expect("python doesn't work"); + String::from_utf8(out.stdout).expect("failed to decode python output") +} diff --git a/src/common/rccell.rs b/src/common/rccell.rs new file mode 100644 index 00000000..c66a9ca4 --- /dev/null +++ b/src/common/rccell.rs @@ -0,0 +1,71 @@ +use std::fmt; +use std::rc::Rc; +use std::cell::{RefCell, Ref, RefMut}; +use std::hash::{Hash, Hasher}; + +#[derive(Debug)] +pub struct RcCell(Rc>); + +impl PartialEq for RcCell { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Clone for RcCell { + fn clone(&self) -> RcCell { + Self(Rc::clone(&self.0)) + } +} + +impl Eq for RcCell {} + +impl Hash for RcCell { + fn hash(&self, state: &mut H) { + self.borrow().hash(state); + } +} + +impl Default for RcCell { + fn default() -> Self { Self::new(Default::default()) } +} + +impl fmt::Display for RcCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.borrow()) + } +} + +impl RcCell { + pub fn new(t: T) -> Self { Self(Rc::new(RefCell::new(t))) } + + #[inline] + pub fn into_inner(self) -> T { + let refcell = match Rc::try_unwrap(self.0) { + Ok(refcell) => refcell, + Err(_rc) => panic!("unwrapping failed"), + }; + RefCell::into_inner(refcell) + } +} + +impl RcCell { + #[inline] + pub fn copy(&self) -> Self { Self(self.0.clone()) } + + #[inline] + pub fn borrow(&self) -> Ref<'_, T> { + RefCell::borrow(&self.0) + } + + #[inline] + pub fn borrow_mut(&self) -> RefMut<'_, T> { + RefCell::borrow_mut(&self.0) + } +} + +impl RcCell { + #[inline] + pub fn clone_inner(&self) -> T { self.borrow().clone() } +} diff --git a/src/common/serialize.rs b/src/common/serialize.rs new file mode 100644 index 00000000..70409753 --- /dev/null +++ b/src/common/serialize.rs @@ -0,0 +1,159 @@ +//! オブジェクトのシリアライズ(バイナリ列化)のためのユーティリティーを定義・実装する +use std::time::{SystemTime, UNIX_EPOCH}; + +use crate::impl_display_from_debug; +use crate::Str; + +/* Python bytecode specification */ +// 0~3 byte: magic number +// 4~7 byte: padding (0;4) +// 8~B byte: UNIX timestamp +// C~F byte: padding (0;4) +// 10~ byte: marshalled code objects +// the unary magic number of Python bytecode +// magic number = version number (2byte) + 168624128 (0x0A0D0000) +// e.g. Python 3.7.4 b5's version number: 3394 +// -> magic number: 0x0AD0D042 +// -> bytes (little endian): 42 0D 0D 0A +pub const fn get_magic_num_bytes(python_ver: u32) -> [u8; 4] { + pub const PREFIX: u32 = 0xA0D0000; + (PREFIX | python_ver).to_le_bytes() +} + +pub const fn get_magic_num_from_bytes(bytes: &[u8; 4]) -> u32 { + u32::from_le_bytes([bytes[0], bytes[1], 0, 0]) +} + +pub fn get_timestamp_bytes() -> [u8; 4] { + let secs = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() as u32; + secs.to_le_bytes() +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum DataTypePrefix { + /* sized objects */ + Illegal = 0, + Int32 = 'i' as u8, // 0x69 + Int64 = 'I' as u8, // 0x49 + Float = 'f' as u8, // 0x66 (float32, not generated anymore?) + BinFloat = 'g' as u8, // 0x67 (float64) + Complex = 'x' as u8, // 0x78 + BinComplex = 'y' as u8, // 0x79 + True = 'T' as u8, // 0x54 + False = 'F' as u8, // 0x46 + None = 'N' as u8, // 0x4E + StopIter = 'S' as u8, // 0x53 + Ref = 'r' as u8, + /* unsized objects (ref counted) */ + Long = 'l' as u8, // 0x6C + len: u32 + payload: 2*len+3byte (~ -2^31-1 && 2^31 ~) + Str = 's' as u8, // 0x73 + len: u32 + payload + ShortAscii = 'z' as u8, // 0x7A + len: u8 + payload + ShortAsciiInterned = 'Z' as u8, // 0x5A + len: u8 + payload + Unicode = 'u' as u8, // 0x75 + len: u32 + payload + Interned = 't' as u8, // 0x74 + len + payload + SmallTuple = ')' as u8, // 0x29 + len: u8 + payload + Tuple = '(' as u8, // 0x28 + len: u32 + payload + Code = 'c' as u8, // 0x63 + /* Erg specific prefix */ + Builtin = 'b' as u8, // 0x62 + str + Nat = 'n' as u8, +} + +impl_display_from_debug!(DataTypePrefix); + +impl From for DataTypePrefix { + fn from(item: u8) -> Self { + match item as char { + 'i' | '\u{00E9}' => Self::Int32, + 'I' => Self::Int64, + 'l' => Self::Long, + 'f' => Self::Float, + 'g' => Self::BinFloat, + 'x' => Self::Complex, + 'y' => Self::BinComplex, + 'T' => Self::True, + 'F' => Self::False, + 'N' => Self::None, + 'S' => Self::StopIter, + 's' | '\u{00F3}' => Self::Str, + 'Z' | '\u{00DA}' => Self::ShortAsciiInterned, + 'z' | '\u{00FA}' => Self::ShortAscii, + 'u' => Self::Unicode, + 't' => Self::Interned, + '(' | '\u{00A8}' => Self::Tuple, + ')' | '\u{00A9}' => Self::SmallTuple, + 'c' | '\u{00E3}' => Self::Code, + 'b' => Self::Builtin, + 'n' => Self::Nat, + /*'\u{00F9}' => DataTypeUnaryOp::ErgInt8, + '\u{00FA}' => DataTypeUnaryOp::ErgInt32, + '\u{00FB}' => DataTypeUnaryOp::ErgFloat32, + '\u{00FC}' => DataTypeUnaryOp::ErgStr, + '\u{00FD}' => DataTypeUnaryOp::ErgFloat, + '\u{00FE}' => DataTypeUnaryOp::HeapArray, + '\u{00FF}' => DataTypeUnaryOp::NumArray,*/ + _ => Self::Illegal, + } + } +} + +impl DataTypePrefix { + pub const fn is_sized(&self) -> bool { + match self { + Self::Long + | Self::Str + | Self::ShortAscii + | Self::ShortAsciiInterned + | Self::Unicode + | Self::Interned + | Self::SmallTuple + | Self::Tuple + | Self::Code + | Self::Builtin => false, + _ => true, + } + } +} + +pub fn strs_into_bytes(names: Vec) -> Vec { + let mut tuple = vec![]; + if names.len() > u8::MAX as usize { + tuple.push(DataTypePrefix::Tuple as u8); + tuple.append(&mut (names.len() as u32).to_le_bytes().to_vec()); + } else { + tuple.push(DataTypePrefix::SmallTuple as u8); + tuple.push(names.len() as u8); + } + for name in names.into_iter() { + tuple.append(&mut str_into_bytes(name, true)); + } + tuple +} + +pub fn str_into_bytes(cont: Str, is_interned: bool) -> Vec { + let mut bytes = vec![]; + if cont.is_ascii() { + if is_interned { + bytes.push(DataTypePrefix::ShortAsciiInterned as u8); + } else { + bytes.push(DataTypePrefix::ShortAscii as u8); + } + bytes.push(cont.len() as u8); + } else { + bytes.push(DataTypePrefix::Unicode as u8); + bytes.append(&mut (cont.len() as u32).to_le_bytes().to_vec()); + }; + bytes.append(&mut cont.as_bytes().to_vec()); + bytes +} + +pub fn raw_string_into_bytes(mut cont: Vec) -> Vec { + let mut tuple = vec![DataTypePrefix::Str as u8]; + tuple.append(&mut (cont.len() as u32).to_le_bytes().to_vec()); + tuple.append(&mut cont); + tuple +} diff --git a/src/common/set.rs b/src/common/set.rs new file mode 100644 index 00000000..3e3af035 --- /dev/null +++ b/src/common/set.rs @@ -0,0 +1,178 @@ +use std::borrow::Borrow; +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::iter::FromIterator; +use std::collections::hash_set::{Iter, IntoIter}; + +use crate::{fmt_iter, debug_fmt_iter}; +use crate::fxhash::FxHashSet; +use crate::value::ValueObj; +use crate::ty::Type; + +#[macro_export] +macro_rules! set { + () => { $crate::set::Set::new() }; + ($($x: expr),+ $(,)?) => {{ + let mut set = $crate::set::Set::new(); + $(set.insert($x);)+ + set + }}; +} + +#[derive(Clone)] +pub struct Set { + elems: FxHashSet +} + +impl PartialEq for Set { + fn eq(&self, other: &Set) -> bool { + self.len() == other.len() + && self.iter().all(|key| other.contains(key)) + } +} + +impl Default for Set { + fn default() -> Self { Self::new() } +} + +impl Eq for Set {} + +impl Hash for Set { + fn hash(&self, state: &mut H) { + self.elems.iter().collect::>().hash(state); + } +} + +impl From> for Set { + fn from(vec: Vec) -> Self { vec.into_iter().collect() } +} + +impl fmt::Debug for Set { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{{{}}}", debug_fmt_iter(self.elems.iter())) + } +} + +impl fmt::Display for Set { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{{{}}}", fmt_iter(self.elems.iter())) + } +} + +impl FromIterator for Set { + #[inline] + fn from_iter>(iter: I) -> Set { + let mut set = Set::new(); + set.extend(iter); + set + } +} + +impl Set { + #[inline] + pub fn new() -> Self { Self{ elems: FxHashSet::default() } } +} + +impl Set { + pub fn with_capacity(capacity: usize) -> Self { + Self{ elems: FxHashSet::with_capacity_and_hasher(capacity, Default::default()) } + } + + #[inline] + pub fn len(&self) -> usize { self.elems.len() } + + #[inline] + pub fn is_empty(&self) -> bool { self.elems.is_empty() } + + #[inline] + pub fn iter(&self) -> Iter<'_, T> { self.elems.iter() } + + #[inline] + pub fn into_iter(self) -> IntoIter { self.elems.into_iter() } +} + +impl Set { + #[inline] + pub fn get(&self, value: &Q) -> Option<&T> + where T: Borrow, Q: ?Sized + Hash + Eq { self.elems.get(value) } + + #[inline] + pub fn contains(&self, value: &Q) -> bool + where T: Borrow, Q: ?Sized + Hash + Eq { self.elems.contains(value) } + + #[inline] + pub fn insert(&mut self, value: T) { self.elems.insert(value); } + + #[inline] + pub fn remove(&mut self, value: &Q) -> bool + where T: Borrow, Q: ?Sized + Hash + Eq { self.elems.remove(value) } + + #[inline] + pub fn extend>(&mut self, iter: I) { self.elems.extend(iter); } + + #[inline] + pub fn is_superset(&self, other: &Set) -> bool { + self.elems.is_superset(&other.elems) + } + + #[inline] + pub fn merge(&mut self, other: Self) { + self.elems.extend(other.elems); + } + + #[inline] + pub fn concat(mut self, other: Self) -> Self { + self.elems.extend(other.elems); + self + } +} + +impl Set { + #[inline] + pub fn union(&self, other: &Set) -> Set { + let u = self.elems.union(&other.elems); + Self{ elems: u.into_iter().map(|x| x.clone()).collect() } + } + + #[inline] + pub fn intersection(&self, other: &Set) -> Set { + let u = self.elems.intersection(&other.elems); + Self{ elems: u.into_iter().map(|x| x.clone()).collect() } + } +} + +impl Set { + pub fn max(&self) -> Option<&T> { + self.iter().max_by(|x, y| x.cmp(y)) + } + + pub fn min(&self) -> Option<&T> { + self.iter().min_by(|x, y| x.cmp(y)) + } +} + +impl Set { + // false -> SyntaxError + pub fn is_homogeneous(&self) -> bool { + let l_first = self.iter().next().unwrap().class(); + self.iter().all(|c| c.class() == l_first) + } + + pub fn inner_class(&self) -> Type { + self.iter().next().unwrap().class() + } + + pub fn max(&self) -> Option { + if !self.is_homogeneous() { return None } + self.iter() + .max_by(|x, y| x.try_cmp(y).unwrap()) + .map(Clone::clone) + } + + pub fn min(&self) -> Option { + if !self.is_homogeneous() { return None } + self.iter() + .min_by(|x, y| x.try_cmp(y).unwrap()) + .map(Clone::clone) + } +} diff --git a/src/common/stdin.rs b/src/common/stdin.rs new file mode 100644 index 00000000..c394dbfe --- /dev/null +++ b/src/common/stdin.rs @@ -0,0 +1,45 @@ +use std::io::{stdin, BufReader, BufRead}; +use std::cell::RefCell; + +use crate::Str; + +pub struct StdinReader { + pub lineno: usize, + buf: Vec, +} + +impl StdinReader { + pub fn read(&mut self) -> Str { + let mut buf = "".to_string(); + let stdin = stdin(); + let mut reader = BufReader::new(stdin.lock()); + reader.read_line(&mut buf).unwrap(); + self.lineno += 1; + self.buf.push(buf.into()); + self.buf.last().unwrap().clone() + } + + pub fn reread(&self) -> Str { + self.buf.last().unwrap().clone() + } + + pub fn reread_lines(&self, ln_begin: usize, ln_end: usize) -> Vec { + self.buf[ln_begin-1..=ln_end-1].to_vec() + } +} + +thread_local! { + pub static READER: RefCell = RefCell::new(StdinReader{ lineno: 0, buf: vec![] }); +} + +pub fn read() -> Str { + READER.with(|s| s.borrow_mut().read()) +} + +pub fn reread() -> Str { + READER.with(|s| s.borrow().reread()) +} + +pub fn reread_lines(ln_begin: usize, ln_end: usize) -> Vec { + READER.with(|s| s.borrow_mut().reread_lines(ln_begin, ln_end)) +} diff --git a/src/common/str.rs b/src/common/str.rs new file mode 100644 index 00000000..cfc85dc4 --- /dev/null +++ b/src/common/str.rs @@ -0,0 +1,117 @@ +use std::hash::{Hash, Hasher}; +use std::fmt; +use std::borrow::Borrow; +use std::ops::{Add, Deref}; + +pub type RcStr = std::rc::Rc; + +/// Used to hold an immutable string. +/// +/// It can construct as a const (by Str::ever). +#[derive(Debug, Clone, Eq)] +pub enum Str { + Rc(RcStr), + Static(&'static str), +} + +impl PartialEq for Str { + #[inline] + fn eq(&self, other: &Str) -> bool { &self[..] == &other[..] } +} + +impl Add<&str> for Str { + type Output = Str; + #[inline] + fn add(self, other: &str) -> Str { + Str::from(&format!("{self}{other}")) + } +} + +impl Hash for Str { + fn hash(&self, state: &mut H) { + match self { + Str::Rc(s) => (&s[..]).hash(state), + Str::Static(s) => s.hash(state), + } + } +} + +impl fmt::Display for Str { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Str::Rc(s) => write!(f, "{s}"), + Str::Static(s) => write!(f, "{s}"), + } + } +} + +// &'static str -> &strになってしまわないように +// あえて`impl> From for Str { ... }`はしない +impl From<&'static str> for Str { + #[inline] + fn from(s: &'static str) -> Self { Str::ever(s) } +} + +impl From<&String> for Str { + #[inline] + fn from(s: &String) -> Self { Str::Rc((&s[..]).into()) } +} + +impl From for Str { + #[inline] + fn from(s: String) -> Self { Str::Rc((&s[..]).into()) } +} + +impl From<&RcStr> for Str { + #[inline] + fn from(s: &RcStr) -> Self { Str::Rc(s.clone()) } +} + +impl From for Str { + #[inline] + fn from(s: RcStr) -> Self { Str::Rc(s) } +} + +impl From<&Str> for Str { + #[inline] + fn from(s: &Str) -> Self { + match s { + Str::Rc(s) => Str::Rc(s.clone()), + Str::Static(s) => Str::Static(s), + } + } +} + +impl Deref for Str { + type Target = str; + fn deref(&self) -> &Self::Target { self.borrow() } +} + +impl Borrow for Str { + #[inline] + fn borrow(&self) -> &str { + match self { + Str::Rc(s) => s.borrow(), + Str::Static(s) => s, + } + } +} + +impl Str { + pub const fn ever(s: &'static str) -> Self { Str::Static(s) } + + pub fn rc(s: &str) -> Self { Str::Rc(s.into()) } + + pub fn as_ref(&self) -> &str { self.borrow() } + + pub fn into_rc(self) -> RcStr { + match self { + Str::Rc(s) => s, + Str::Static(s) => RcStr::from(s), + } + } + + pub fn is_uppercase(&self) -> bool { + self.chars().next().map(|c| c.is_uppercase()).unwrap_or(false) + } +} diff --git a/src/common/traits.rs b/src/common/traits.rs new file mode 100644 index 00000000..d798536f --- /dev/null +++ b/src/common/traits.rs @@ -0,0 +1,414 @@ +//! defines common traits used in the compiler. +//! +//! コンパイラ等で汎用的に使われるトレイトを定義する +use std::slice::{Iter, IterMut}; +use std::vec::IntoIter; +use std::io::{BufWriter, Write, stdout}; +use std::process; +use std::mem; + +use crate::Str; +use crate::{addr_eq, switch_unreachable, chomp, log}; +use crate::color::{GREEN, RESET}; +use crate::config::{ErgConfig, Input}; +use crate::error::{Location, ErrorDisplay, MultiErrorDisplay}; +use crate::ty::Type; + +pub trait Stream: Sized { + fn payload(self) -> Vec; + fn ref_payload(&self) -> &Vec; + fn ref_mut_payload(&mut self) -> &mut Vec; + + #[inline] + fn clear(&mut self) { self.ref_mut_payload().clear(); } + + #[inline] + fn len(&self) -> usize { self.ref_payload().len() } + + fn size(&self) -> usize { + std::mem::size_of::>() + std::mem::size_of::() * self.ref_payload().capacity() + } + + #[inline] + fn is_empty(&self) -> bool { self.ref_payload().is_empty() } + + #[inline] + fn insert(&mut self, idx: usize, elem: T) { + self.ref_mut_payload().insert(idx, elem); + } + + #[inline] + fn remove(&mut self, idx: usize) -> T { self.ref_mut_payload().remove(idx) } + + #[inline] + fn push(&mut self, elem: T) { self.ref_mut_payload().push(elem); } + + fn append>(&mut self, s: &mut S) { self.ref_mut_payload().append(s.ref_mut_payload()); } + + #[inline] + fn pop(&mut self) -> Option { self.ref_mut_payload().pop() } + + fn lpop(&mut self) -> Option { + let len = self.len(); + if len == 0 { None } else { Some(self.ref_mut_payload().remove(0)) } + } + + #[inline] + fn get(&self, idx: usize) -> Option<&T> { self.ref_payload().get(idx) } + + #[inline] + fn get_mut(&mut self, idx: usize) -> Option<&mut T> { self.ref_mut_payload().get_mut(idx) } + + #[inline] + fn first(&self) -> Option<&T> { self.ref_payload().first() } + + #[inline] + fn first_mut(&mut self) -> Option<&mut T> { self.ref_mut_payload().first_mut() } + + #[inline] + fn last(&self) -> Option<&T> { self.ref_payload().last() } + + #[inline] + fn last_mut(&mut self) -> Option<&mut T> { self.ref_mut_payload().last_mut() } + + #[inline] + fn iter(&self) -> Iter<'_, T> { self.ref_payload().iter() } + + #[inline] + fn iter_mut(&mut self) -> IterMut<'_, T> { self.ref_mut_payload().iter_mut() } + + #[inline] + fn into_iter(self) -> IntoIter { self.payload().into_iter() } + + #[inline] + fn take_all(&mut self) -> Vec { self.ref_mut_payload().drain(..).collect() } +} + +#[macro_export] +macro_rules! impl_displayable_stream_for_wrapper { + ($Strc: ident, $Inner: ident) => { + impl $Strc { + pub const fn new(v: Vec<$Inner>) -> $Strc { $Strc(v) } + #[inline] + pub fn empty() -> $Strc { $Strc(Vec::with_capacity(20)) } + } + + impl From> for $Strc { + #[inline] + fn from(errs: Vec<$Inner>) -> Self { Self(errs) } + } + + impl std::fmt::Display for $Strc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{}]", common::fmt_iter(self.iter()).replace("\n", "\\n")) + } + } + + impl Default for $Strc { + #[inline] + fn default() -> Self { Self::empty() } + } + + impl std::ops::Index for $Strc { + type Output = $Inner; + fn index(&self, idx: usize) -> &Self::Output { common::traits::Stream::get(self, idx).unwrap() } + } + + impl common::traits::Stream<$Inner> for $Strc { + #[inline] + fn payload(self) -> Vec<$Inner> { self.0 } + #[inline] + fn ref_payload(&self) -> &Vec<$Inner> { &self.0 } + #[inline] + fn ref_mut_payload(&mut self) -> &mut Vec<$Inner> { &mut self.0 } + } + }; +} + +#[macro_export] +macro_rules! impl_stream_for_wrapper { + ($Strc: ident, $Inner: ident) => { + impl $Strc { + pub const fn new(v: Vec<$Inner>) -> $Strc { $Strc(v) } + pub const fn empty() -> $Strc { $Strc(Vec::new()) } + #[inline] + pub fn with_capacity(capacity: usize) -> $Strc { $Strc(Vec::with_capacity(capacity)) } + } + + impl Default for $Strc { + #[inline] + fn default() -> $Strc { $Strc::with_capacity(0) } + } + + impl std::ops::Index for $Strc { + type Output = $Inner; + fn index(&self, idx: usize) -> &Self::Output { common::traits::Stream::get(self, idx).unwrap() } + } + + impl common::traits::Stream<$Inner> for $Strc { + #[inline] + fn payload(self) -> Vec<$Inner> { self.0 } + #[inline] + fn ref_payload(&self) -> &Vec<$Inner> { &self.0 } + #[inline] + fn ref_mut_payload(&mut self) -> &mut Vec<$Inner> { &mut self.0 } + } + } +} + +#[macro_export] +macro_rules! impl_stream { + ($Strc: ident, $Inner: ident, $field: ident) => { + impl common::traits::Stream<$Inner> for $Strc { + #[inline] + fn payload(self) -> Vec<$Inner> { self.$field } + #[inline] + fn ref_payload(&self) -> &Vec<$Inner> { &self.$field } + #[inline] + fn ref_mut_payload(&mut self) -> &mut Vec<$Inner> { &mut self.$field } + } + + impl std::ops::Index for $Strc { + type Output = $Inner; + fn index(&self, idx: usize) -> &Self::Output { common::traits::Stream::get(self, idx).unwrap() } + } + } +} + +pub trait ImmutableStream: Sized { + fn ref_payload(&self) -> &[T]; + fn capacity(&self) -> usize; + + #[inline] + fn len(&self) -> usize { self.ref_payload().len() } + + fn size(&self) -> usize { + std::mem::size_of::>() + std::mem::size_of::() * self.capacity() + } + + #[inline] + fn is_empty(&self) -> bool { self.ref_payload().is_empty() } + + #[inline] + fn get(&self, idx: usize) -> Option<&T> { self.ref_payload().get(idx) } + + #[inline] + fn first(&self) -> Option<&T> { self.ref_payload().first() } + + #[inline] + fn last(&self) -> Option<&T> { self.ref_payload().last() } + + #[inline] + fn iter(&self) -> Iter<'_, T> { self.ref_payload().iter() } +} + +// for Runnable::run +fn expect_block(src: &str) -> bool { + src.ends_with(&['=', ':']) || src.ends_with(":=") + || src.ends_with("->") || src.ends_with("=>") + || src.ends_with("do") || src.ends_with("do!") +} + +/// this trait implements REPL (Read-Eval-Print-Loop) automatically +pub trait Runnable: Sized { + type Err: ErrorDisplay; + type Errs: MultiErrorDisplay; + fn new(cfg: ErgConfig) -> Self; + fn input(&self) -> &Input; + fn start_message(&self) -> String; + fn clear(&mut self); + fn eval(&mut self, src: Str) -> Result; + + fn ps1(&self) -> String { ">>> ".to_string() } // TODO: &str (VMのせいで参照をとれない) + fn ps2(&self) -> String { "... ".to_string() } + + #[inline] + fn quit(&self, code: i32) { process::exit(code); } + + fn run(cfg: ErgConfig) { + let mut instance = Self::new(cfg); + let res = match instance.input() { + Input::File(_) | Input::Pipe(_) | Input::Str(_) => { + match instance.eval(instance.input().read()) { + Ok(s) => { + println!("{s}"); + Ok(()) + } + Err(e) => Err(e), + } + } + Input::REPL => { + let output = stdout(); + let mut output = BufWriter::new(output.lock()); + log!(f output, "{GREEN}[DEBUG] The REPL has started.{RESET}\n"); + output.write(instance.start_message().as_bytes()).unwrap(); + output.write(instance.ps1().as_bytes()).unwrap(); + output.flush().unwrap(); + let mut lines = String::new(); + loop { + let line = chomp(&instance.input().read()); + if &line[..] == ":quit" || &line[..] == ":exit" { + log!(f output, "{GREEN}[DEBUG] The REPL has finished successfully.{RESET}\n"); + process::exit(0); + } + lines.push_str(&line); + if expect_block(&line) || line.starts_with(' ') { + lines += "\n"; + output.write(instance.ps2().as_bytes()).unwrap(); + output.flush().unwrap(); + continue; + } + match instance.eval(mem::take(&mut lines).into()) { + Ok(out) => { + output.write((out + "\n").as_bytes()).unwrap(); + output.flush().unwrap(); + }, + Err(e) => { e.fmt_all_stderr(); } + } + output.write(instance.ps1().as_bytes()).unwrap(); + output.flush().unwrap(); + instance.clear(); + } + }, + Input::Dummy => switch_unreachable!(), + }; + if let Err(e) = res { + e.fmt_all_stderr(); + std::process::exit(1); + } + } +} + +pub trait Locational { + fn loc(&self) -> Location; + + fn ln_begin(&self) -> Option { + match self.loc() { + Location::RangePair{ ln_begin, .. } + | Location::Range{ ln_begin, .. } + | Location::LineRange(ln_begin, _) => Some(ln_begin), + Location::Line(lineno) => Some(lineno), + Location::Unknown => None, + } + } + + fn ln_end(&self) -> Option { + match self.loc() { + Location::RangePair{ ln_end, .. } + | Location::Range{ ln_end, .. } + | Location::LineRange(_, ln_end) => Some(ln_end), + Location::Line(lineno) => Some(lineno), + Location::Unknown => None, + } + } + + fn col_begin(&self) -> Option { + match self.loc() { + Location::Range{ col_begin, .. } => Some(col_begin), + _ => None, + } + } + + fn col_end(&self) -> Option { + match self.loc() { + Location::Range{ col_end, .. } => Some(col_end), + _ => None, + } + } +} + +#[macro_export] +macro_rules! impl_locational_for_enum { + ($Enum: ident; $($Variant: ident $(,)?)*) => { + impl common::traits::Locational for $Enum { + fn loc(&self) -> common::error::Location { + match self { + $($Enum::$Variant(v) => v.loc(),)* + } + } + } + } +} + +#[macro_export] +macro_rules! impl_locational { + ($T: ty, $begin: ident, $end: ident) => { + impl Locational for $T { + fn loc(&self) -> Location { + match (self.$begin.ln_begin(), self.$begin.col_begin(), self.$end.ln_end(), self.$end.col_end()) { + (Some(lb), Some(cb), Some(le), Some(ce)) => Location::range(lb, cb, le, ce), + (Some(lb), _, Some(le), _) => Location::LineRange(lb, le), + (Some(l), _, _, _) | (_, _, Some(l), _) => Location::Line(l), + _ => Location::Unknown, + } + } + } + }; +} + +pub trait NestedDisplay { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result; +} + +/// `impl Display for T NestedDisplay`はorphan-ruleに違反するので個別定義する +#[macro_export] +macro_rules! impl_display_from_nested { + ($T: ty) => { + impl std::fmt::Display for $T { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt_nest(f, 0) + } + } + }; +} + +#[macro_export] +macro_rules! impl_nested_display_for_enum { + ($Enum: ident; $($Variant: ident $(,)?)*) => { + impl NestedDisplay for $Enum { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + write!(f, "{}", " ".repeat(level))?; + match self { + $($Enum::$Variant(v) => v.fmt_nest(f, level),)* + } + } + } + } +} + +/// cloneのコストがあるためなるべく.ref_tを使うようにすること +/// いくつかの構造体は直接Typeを保持していないので、その場合は.tを使う +#[allow(unused_variables)] +pub trait HasType { + fn ref_t(&self) -> &Type; + // 関数呼び出しの場合、.ref_t()は戻り値を返し、signature_t()は関数全体の型を返す + fn signature_t(&self) -> Option<&Type>; + #[inline] + fn t(&self) -> Type { self.ref_t().clone() } + #[inline] + fn inner_ts(&self) -> Vec { self.ref_t().inner_ts() } + #[inline] + fn lhs_t(&self) -> &Type { &self.ref_t().non_default_params().unwrap()[0].ty } + #[inline] + fn rhs_t(&self) -> &Type { &self.ref_t().non_default_params().unwrap()[1].ty } +} + +#[macro_export] +macro_rules! impl_t { + ($T: ty, $t: ident) => { + impl common::traits::HasType for $T { + #[inline] + fn ref_t(&self) -> &common::ty::Type { &common::ty::Type::$t } + } + }; +} + +/// Pythonではis演算子に相当 +pub trait AddrEq { + #[inline] + fn addr_eq(&self, other: &Self) -> bool { addr_eq!(self, other) } +} + +pub trait __Str__ { + fn __str__(&self) -> String; +} diff --git a/src/common/tsort.rs b/src/common/tsort.rs new file mode 100644 index 00000000..e476e1d9 --- /dev/null +++ b/src/common/tsort.rs @@ -0,0 +1,64 @@ +use crate::dict::Dict; + +type NodeIdx = usize; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Node { + _id: T, + depends_on: Vec, +} + +impl Node { + pub const fn new(id: T, depends_on: Vec) -> Self { + Node { _id: id, depends_on,} + } +} + +pub type Graph = Vec>; + +fn reorder_by_idx(mut v: Vec, idx: Vec) -> Vec { + let mut swap_table = Dict::new(); + for (node_id, mut sort_i) in idx.into_iter().enumerate() { + if node_id == sort_i { continue; } + while let Some(moved_to) = swap_table.get(&sort_i) { + sort_i = *moved_to; + } + v.swap(node_id, sort_i); + swap_table.insert(node_id, sort_i); + } + v +} + +fn dfs(g: &Graph, v: NodeIdx, used: &mut Vec, idx: &mut Vec) { + used[v] = true; + for &node_id in g[v].depends_on.iter() { + if !used[node_id] { dfs(g, node_id, used, idx); } + } + idx.push(v); +} + +/// perform topological sort on a graph +pub fn tsort(g: Graph) -> Graph { + let n = g.len(); + let mut idx = Vec::with_capacity(n); + let mut used = vec![false; n]; + for v in 0..n { + if !used[v] { dfs(&g, v, &mut used, &mut idx); } + } + reorder_by_idx(g, idx) +} + +fn _test() { + let v = vec!["e", "d", "b", "a", "c"]; + let idx = vec![3, 2, 4, 1, 0]; + assert_eq!(vec!["a", "b", "c", "d", "e"], reorder_by_idx(v, idx)); + + let en_0 = Node::new("even n", vec![4, 1]); + let o0_1 = Node::new("odd 0", vec![]); + let on_2 = Node::new("odd n", vec![3, 0]); + let e0_3 = Node::new("even 0", vec![]); + let ond_4 = Node::new("odd n (decl)", vec![]); + let sorted = vec![ond_4.clone(), o0_1.clone(), en_0.clone(), e0_3.clone(), on_2.clone()]; + let dag = vec![en_0, o0_1, on_2, e0_3, ond_4]; + assert_eq!(sorted, tsort(dag)); +} diff --git a/src/common/ty.rs b/src/common/ty.rs new file mode 100644 index 00000000..7717878e --- /dev/null +++ b/src/common/ty.rs @@ -0,0 +1,2575 @@ +//! defines `Type` (type kind). +//! +//! Type(コンパイラ等で使われる「型」を表現する)を定義する +use std::cell::{Ref, RefMut}; +use std::cmp::Ordering; +use std::fmt; +use std::mem; +use std::ops::{Add, Sub, Mul, Div, Neg, Range, RangeInclusive}; + +use crate::{Str, fmt_vec, fmt_vec_split_with, fmt_set_split_with, set}; +use crate::codeobj::CodeObj; +use crate::value::ValueObj; +use crate::dict::Dict; +use crate::rccell::RcCell; +use crate::set::Set; +use crate::traits::HasType; +use crate::ty::ValueObj::{NegInf, Inf}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum OpKind { + Add, + Sub, + Mul, + Div, + Pos, + Neg, + Invert, + Gt, + Lt, + Ge, + Le, + Eq, + Ne, + Mutate, +} + +impl fmt::Display for OpKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Add => write!(f, "+"), + Self::Sub => write!(f, "-"), + Self::Mul => write!(f, "*"), + Self::Div => write!(f, "/"), + Self::Pos => write!(f, "+"), + Self::Neg => write!(f, "-"), + Self::Invert => write!(f, "~"), + Self::Gt => write!(f, ">"), + Self::Lt => write!(f, "<"), + Self::Ge => write!(f, ">="), + Self::Le => write!(f, "<="), + Self::Eq => write!(f, "=="), + Self::Ne => write!(f, "!="), + Self::Mutate => write!(f, "!"), + } + } +} + +pub type Level = usize; +pub type Id = usize; + +thread_local! { + static UNBOUND_ID: RcCell = RcCell::new(0); + static REFINEMENT_VAR_ID: RcCell = RcCell::new(0); +} + +pub fn fresh_varname() -> String { + REFINEMENT_VAR_ID.with(|id| { + *id.borrow_mut() += 1; + let i = id.borrow().clone(); + format!("%v{i}") + }) +} + +pub fn fresh_param_name() -> String { + REFINEMENT_VAR_ID.with(|id| { + *id.borrow_mut() += 1; + let i = id.borrow().clone(); + format!("%p{i}") + }) +} + +pub trait HasLevel { + fn level(&self) -> Option; + fn update_level(&self, level: Level); + fn lift(&self); +} + +// REVIEW: TyBoundと微妙に役割が被っている +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Constraint { + SubtypeOf(Type), + TypeOf(Type), +} + +impl fmt::Display for Constraint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::SubtypeOf(ty) => write!(f, "<: {}", ty), + Self::TypeOf(ty) => write!(f, ": {}", ty), + } + } +} + +impl Constraint { + pub fn typ(&self) -> Option<&Type> { + match self { + Self::TypeOf(ty) => Some(ty), + _ => None, + } + } + + pub fn super_type(&self) -> Option<&Type> { + match self { + Self::SubtypeOf(ty) => Some(ty), + _ => None, + } + } + + pub fn super_type_mut(&mut self) -> Option<&mut Type> { + match self { + Self::SubtypeOf(ty) => Some(ty), + _ => None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum FreeKind { + Linked(T), + Unbound{ id: Id, lev: Level, constraint: Constraint }, + NamedUnbound{ name: Str, lev: Level, constraint: Constraint }, +} + +impl fmt::Display for FreeKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Linked(t) => write!(f, "({t})"), + Self::NamedUnbound{ name, lev, constraint } => write!(f, "?{name}({constraint})[{lev}]"), + Self::Unbound{ id, lev, constraint }=> write!(f, "?{id}({constraint})[{lev}]"), + } + } +} + +impl FreeKind { + pub const fn unbound(id: Id, lev: Level, constraint: Constraint) -> Self { + Self::Unbound{ id, lev, constraint } + } + + pub const fn named_unbound(name: Str, lev: Level, constraint: Constraint) -> Self { + Self::NamedUnbound{ name, lev, constraint } + } + + pub const fn constraint(&self) -> Option<&Constraint> { + match self { + Self::Unbound{ constraint, .. } + | Self::NamedUnbound{ constraint, .. } => Some(constraint), + _ => None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Free(RcCell>); + +impl fmt::Display for Free { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.borrow()) + } +} + +impl Free { + pub fn new(f: FreeKind) -> Self { Self(RcCell::new(f)) } + + pub fn new_unbound(level: Level, constraint: Constraint) -> Self { + UNBOUND_ID.with(|id| { + *id.borrow_mut() += 1; + Self(RcCell::new(FreeKind::unbound(*id.borrow(), level, constraint))) + }) + } + + pub fn new_named_unbound(name: Str, level: Level, constraint: Constraint) -> Self { + Self(RcCell::new(FreeKind::named_unbound(name, level, constraint))) + } + + pub fn new_linked(t: T) -> Self { + Self(RcCell::new(FreeKind::Linked(t))) + } + + pub fn link(&self, to: &T) { + *self.0.borrow_mut() = FreeKind::Linked(to.clone()); + } + + pub fn update_level(&self, level: Level) { + match &mut *self.0.borrow_mut() { + FreeKind::Unbound{ lev, .. } + | FreeKind::NamedUnbound{ lev, .. } if level < *lev => { *lev = level; }, + FreeKind::Linked(t) => { t.update_level(level); }, + _ => {} + } + } + + pub fn lift(&self) { + match &mut *self.0.borrow_mut() { + FreeKind::Unbound{ lev, .. } + | FreeKind::NamedUnbound{ lev, .. } => { *lev += 1; }, + FreeKind::Linked(t) => { if let Some(lev) = t.level() { t.update_level(lev+1); } }, + } + } + + pub fn level(&self) -> Option { + match &*self.0.borrow() { + FreeKind::Unbound{ lev, .. } + | FreeKind::NamedUnbound{ lev, .. } => Some(*lev), + FreeKind::Linked(t) => t.level(), + } + } + + pub fn update_constraint(&self, new_constraint: Constraint) { + match &mut *self.0.borrow_mut() { + FreeKind::Unbound{ constraint, .. } + | FreeKind::NamedUnbound{ constraint, .. } => { + *constraint = new_constraint; + }, + _ => {} + } + } + + pub fn unwrap(self) -> T { + match self.0.clone_inner() { + FreeKind::Linked(t) => t, + FreeKind::Unbound{ .. } + | FreeKind::NamedUnbound{ .. } => panic!("the value is unbounded"), + } + } + + /// returns linked type (panic if self is unbounded) + /// NOTE: check by `.is_linked` before call + pub fn crack(&self) -> Ref<'_, T> { + Ref::map(self.0.borrow(), |f| match f { + FreeKind::Linked(t) => t, + FreeKind::Unbound{ .. } + | FreeKind::NamedUnbound{ .. } => panic!("the value is unbounded"), + }) + } + + pub fn type_of(&self) -> Option { + self.0.borrow().constraint() + .and_then(|c| c.typ().map(|t| t.clone())) + } + + pub fn subtype_of(&self) -> Option { + self.0.borrow().constraint() + .and_then(|c| c.super_type().map(|t| t.clone())) + } + + pub fn is_unbound(&self) -> bool { + matches!(&*self.0.borrow(), FreeKind::Unbound{ .. } | FreeKind::NamedUnbound{ .. }) + } + + pub fn is_linked(&self) -> bool { + matches!(&*self.0.borrow(), FreeKind::Linked(_)) + } + + pub fn unbound_name(&self) -> Option { + match &*self.0.borrow() { + FreeKind::NamedUnbound{ name, .. } => Some(name.clone()), + _ => None, + } + } + + pub fn borrow(&self) -> Ref<'_, FreeKind> { self.0.borrow() } + pub fn borrow_mut(&self) -> RefMut<'_, FreeKind> { self.0.borrow_mut() } +} + +impl Free { + pub fn map(&self, f: F) where F: Fn(TyParam) -> TyParam { + match &mut *self.0.borrow_mut() { + FreeKind::Unbound{ .. } + | FreeKind::NamedUnbound{ .. } => panic!("the value is unbounded"), + FreeKind::Linked(t) => { *t = f(mem::take(t)); }, + } + } +} + +pub type FreeTyVar = Free; +pub type FreeTyParam = Free; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct UserConstSubr { + code: CodeObj, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BuiltinConstSubr { + subr: fn(Vec) -> ValueObj, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ConstSubr { + User(UserConstSubr), + Builtin(BuiltinConstSubr), +} + +impl ConstSubr { + pub fn call(&self, args: Vec) -> ValueObj { + match self { + ConstSubr::User(_user) => todo!(), + ConstSubr::Builtin(builtin) => (builtin.subr)(args), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ConstObj { + Value(ValueObj), + MutValue(RcCell), + Subr(ConstSubr), + Record(Dict), + Type(Box), +} + +impl fmt::Display for ConstObj { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ConstObj::Value(v) => write!(f, "{v}"), + ConstObj::MutValue(v) => write!(f, "!{v}"), + ConstObj::Subr(s) => write!(f, "{s:?}"), + ConstObj::Record(r) => write!(f, "{r}"), + ConstObj::Type(t) => write!(f, "{t}"), + } + } +} + +impl ConstObj { + pub fn t(t: Type) -> Self { Self::Type(Box::new(t)) } + + pub fn try_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Self::Value(l), Self::Value(r)) => l.try_cmp(r), + (Self::MutValue(l), Self::MutValue(r)) => l.borrow().try_cmp(&r.borrow()), + (Self::Value(l), Self::MutValue(r)) => l.try_cmp(&r.borrow()), + (Self::MutValue(l), Self::Value(r)) => l.borrow().try_cmp(r), + // TODO: cmp with str + (_s, _o) => None, + } + } +} + +/// 型引数 +/// データのみ、その評価結果は別に持つ +/// __Info__: 連携型パラメータがあるので、比較には`deep_eq`を使うこと +/// * Literal: 1, "aa", True, None, ... (don't use container literals, they can only hold literals) +/// * Type: Int, Add(?R, ?O), ... +/// * Mono: I, N, ... +/// * Attr: math.PI, ... +/// * Array: `[1, 2, N]` +/// * Tuple: (1, N, True) +/// * App: Array(Int), Fib(10), ... +/// * QuantVar: N: Nat, ... +/// * FreeVar: ?I: Int, ... +/// * UnaryOp: -N, ~B, ... +/// * BinOp: 1 + 1, N * 2, ... +/// * Erased: _: Type, _: Nat, ... +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum TyParam { + ConstObj(ConstObj), + Type(Box), + Array(Vec), + Tuple(Vec), + Mono(Str), + MonoProj{ obj: Box, attr: Str }, + App{ name: Str, args: Vec }, + UnaryOp{ op: OpKind, val: Box }, + BinOp{ op: OpKind, lhs: Box, rhs: Box }, + Erased(Box), + MonoQVar(Str), + PolyQVar{ name: Str, args: Vec }, + FreeVar(FreeTyParam), + Failure, +} + +impl fmt::Display for TyParam { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ConstObj(c) => write!(f, "{c}"), + Self::Type(t) => write!(f, "{t}"), + Self::Mono(c) => write!(f, "{c}"), + Self::MonoProj{ obj, attr } => write!(f, "{obj}.{attr}"), + Self::Array(a) => write!(f, "[{}]", fmt_vec(a)), + Self::Tuple(t) => write!(f, "({})", fmt_vec(t)), + Self::App{ name, args } => write!(f, "{name}({})", fmt_vec(args)), + Self::MonoQVar(name) => write!(f, "'{name}"), + Self::PolyQVar{ name, args } => write!(f, "'{name}({})", fmt_vec(args)), + Self::FreeVar(fv) => write!(f, "{fv}"), + Self::UnaryOp{ op, val } => write!(f, "{op}{val}"), + Self::BinOp{ op, lhs, rhs } => write!(f, "{lhs} {op} {rhs}"), + Self::Erased(t) => write!(f, "_: {t}"), + Self::Failure => write!(f, ""), + } + } +} + +impl Default for TyParam { + #[inline] + fn default() -> Self { Self::Failure } +} + +impl Add for TyParam { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { Self::bin(OpKind::Add, self, rhs)} +} + +impl Sub for TyParam { + type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { Self::bin(OpKind::Sub, self, rhs) } +} + +impl Mul for TyParam { + type Output = Self; + fn mul(self, rhs: Self) -> Self::Output { Self::bin(OpKind::Mul, self, rhs) } +} + +impl Div for TyParam { + type Output = Self; + fn div(self, rhs: Self) -> Self::Output { Self::bin(OpKind::Div, self, rhs) } +} + +impl Neg for TyParam { + type Output = Self; + fn neg(self) -> Self::Output { Self::unary(OpKind::Neg, self) } +} + +impl From> for TyParam { + fn from(r: Range) -> Self { + Self::t(Type::int_interval(IntervalOp::RightOpen, r.start, r.end)) + } +} + +impl From> for TyParam { + fn from(r: Range<&TyParam>) -> Self { + Self::t(Type::int_interval(IntervalOp::RightOpen, r.start.clone(), r.end.clone())) + } +} + +impl From> for TyParam { + fn from(r: RangeInclusive) -> Self { + let (start, end) = r.into_inner(); + Self::t(Type::int_interval(IntervalOp::Closed, start, end)) + } +} + +impl From> for TyParam { + fn from(r: RangeInclusive<&TyParam>) -> Self { + let (start, end) = r.into_inner(); + Self::t(Type::int_interval(IntervalOp::Closed, start.clone(), end.clone())) + } +} + +impl> From for TyParam { + fn from(v: V) -> Self { Self::ConstObj(ConstObj::Value(v.into())) } +} + +impl HasLevel for TyParam { + fn level(&self) -> Option { + match self { + Self::Type(t) => t.level(), + Self::FreeVar(fv) => fv.level(), + Self::UnaryOp{ val, .. } => val.level(), + Self::BinOp{ lhs, rhs, .. } => lhs.level().and_then(|l| rhs.level().map(|r| l.max(r))), + _ => None, + } + } + + fn update_level(&self, level: Level) { + match self { + Self::FreeVar(fv) => fv.update_level(level), + Self::UnaryOp{ val, .. } => val.update_level(level), + Self::BinOp{ lhs, rhs, .. } => { + lhs.update_level(level); + rhs.update_level(level); + }, + Self::App{ args, .. } + | Self::PolyQVar{ args, .. } => { + for arg in args.iter() { + arg.update_level(level); + } + }, + _ => {} + } + } + + fn lift(&self) { + match self { + Self::FreeVar(fv) => fv.lift(), + Self::UnaryOp{ val, .. } => val.lift(), + Self::BinOp{ lhs, rhs, .. } => { + lhs.lift(); + rhs.lift(); + }, + Self::App{ args, .. } + | Self::PolyQVar{ args, .. } => { + for arg in args.iter() { + arg.lift(); + } + }, + _ => {} + } + } +} + +impl TyParam { + pub fn t(t: Type) -> Self { Self::Type(Box::new(t)) } + + pub fn mono>(name: S) -> Self { Self::Mono(name.into()) } + + pub fn mono_q>(name: S) -> Self { Self::MonoQVar(name.into()) } + + pub fn mono_proj>(obj: TyParam, attr: S) -> Self { + Self::MonoProj{ obj: Box::new(obj), attr: attr.into() } + } + + // TODO: polymorphic type + pub fn array_t(t: Str, len: TyParam) -> Self { Self::Array(vec![TyParam::t(Type::mono(t)), len]) } + + pub fn free_var(level: usize, t: Type) -> Self { + Self::FreeVar(FreeTyParam::new_unbound(level, Constraint::TypeOf(t))) + } + + pub fn named_free_var(name: Str, level: usize, t: Type) -> Self { + Self::FreeVar(FreeTyParam::new_named_unbound(name, level, Constraint::TypeOf(t))) + } + + #[inline] + pub fn value>(v: V) -> Self { Self::ConstObj(ConstObj::Value(v.into())) } + + #[inline] + pub fn cons>(l: C) -> Self { Self::ConstObj(l.into()) } + + #[inline] + pub fn unary(op: OpKind, val: TyParam) -> Self { Self::UnaryOp{ op, val: Box::new(val) } } + + #[inline] + pub fn mutate(self) -> Self { Self::unary(OpKind::Mutate, self) } + + #[inline] + pub fn bin(op: OpKind, lhs: TyParam, rhs: TyParam) -> Self { + Self::BinOp{ op, lhs: Box::new(lhs), rhs: Box::new(rhs) } + } + + pub fn app(name: &'static str, args: Vec) -> Self { + Self::App{ name: Str::ever(name), args } + } + + #[inline] + pub fn erased(t: Type) -> Self { Self::Erased(Box::new(t)) } + + // if self: Ratio, Succ(self) => self+ε + pub fn succ(self) -> Self { Self::app("Succ", vec![self]) } + + // if self: Ratio, Pred(self) => self-ε + pub fn pred(self) -> Self { Self::app("Pred", vec![self]) } + + /// 型変数の内容を考慮した比較を行う + pub fn deep_eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Type(l), Self::Type(r)) => l.deep_eq(r), + (Self::FreeVar(fv), o) + | (o, Self::FreeVar(fv)) => match &*fv.borrow() { + FreeKind::Linked(tp) => tp.deep_eq(o), + _ => self == o, + }, + ( + Self::MonoProj{ obj: lo, attr: la }, + Self::MonoProj{ obj: ro, attr: ra} + ) => lo.deep_eq(ro) && la == ra, + (Self::Array(l), Self::Array(r)) + | (Self::Tuple(l), Self::Tuple(r)) => + l.iter().zip(r.iter()).all(|(l, r)| l.deep_eq(r)), + ( + Self::App{ name: ln, args: lps }, + Self::App{ name: rn, args: rps} + ) => ln == rn && lps.iter().zip(rps.iter()).all(|(l, r)| l.deep_eq(r)), + ( + Self::UnaryOp{ op: lop, val: lv }, + Self::UnaryOp{ op: rop, val: rv } + ) => lop == rop && lv.deep_eq(rv), + ( + Self::BinOp{ op: lop, lhs: ll, rhs: lr }, + Self::BinOp{ op: rop, lhs: rl, rhs: rr } + ) => lop == rop && ll.deep_eq(rl) && lr.deep_eq(rr), + (Self::Erased(l), Self::Erased(r)) => l.deep_eq(r), + _ => self == other, + } + } + + // 定数の比較など環境が必要な場合はSymbolTable::try_cmpを使う + pub fn cheap_cmp(&self, r: &TyParam) -> Option { + match (self, r) { + (Self::Type(l), Self::Type(r)) => + if l.deep_eq(r) { Some(TyParamOrdering::Equal) } else { Some(TyParamOrdering::NotEqual) }, + (Self::ConstObj(l), Self::ConstObj(r)) => + l.try_cmp(r).map(Into::into), + (Self::FreeVar(fv), p) if fv.is_linked() => + fv.crack().cheap_cmp(p), + (p, Self::FreeVar(fv)) if fv.is_linked() => + p.cheap_cmp(&*fv.crack()), + (Self::FreeVar{ .. } | Self::Erased(_), Self::FreeVar{ .. } | Self::Erased(_)) + /* if v.is_unbound() */ => Some(Any), + (Self::App{ name, args }, Self::App{ name: rname, args: rargs }) + | (Self::PolyQVar{ name, args }, Self::PolyQVar{ name: rname, args: rargs }) => + if name == rname + && args.len() == rargs.len() + && args.iter().zip(rargs.iter()).all(|(l, r)| l.cheap_cmp(r) == Some(Equal)) { + Some(TyParamOrdering::Equal) + } else { + Some(TyParamOrdering::NotEqual) + }, + (l, r @ (Self::Erased(_) | Self::Mono{ .. } | Self::FreeVar{ .. })) => + r.cheap_cmp(l).map(|ord| ord.reverse()), + _ => None, + } + } + + pub fn has_unbound_var(&self) -> bool { + match self { + Self::FreeVar(fv) => + if fv.is_unbound() { true } else { fv.crack().has_unbound_var() }, + Self::Type(t) => t.has_unbound_var(), + Self::MonoProj{ obj, .. } => obj.has_unbound_var(), + Self::Array(ts) + | Self::Tuple(ts) => ts.iter().any(|t| t.has_unbound_var()), + Self::UnaryOp{ val, .. } => val.has_unbound_var(), + Self::BinOp{ lhs, rhs, .. } => + lhs.has_unbound_var() || rhs.has_unbound_var(), + Self::App{ args, .. } + | Self::PolyQVar{ args, .. } => args.iter().any(|p| p.has_unbound_var()), + Self::Erased(t) => t.has_unbound_var(), + _ => false, + } + } + + pub fn has_no_unbound_var(&self) -> bool { !self.has_unbound_var() } + + pub fn has_upper_bound(&self) -> bool { + match self { + // TODO: 型によっては上限がある + // また、上限がないもの同士の加算等も上限はない + Self::Erased(_) | Self::MonoQVar(_) => false, + Self::FreeVar(fv) => !fv.is_unbound(), // != fv.is_linked(), + _ => true, + } + } + + pub fn has_lower_bound(&self) -> bool { + match self { + Self::Erased(_) | Self::MonoQVar(_) => false, + Self::FreeVar(fv) => !fv.is_unbound(), + _ => true, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum TyParamOrdering { + Less, + Equal, + Greater, + LessEqual, // Less or Equal + NotEqual, // Less or Greater + GreaterEqual, // Greater or Equal + Any, +} + +use TyParamOrdering::*; + +impl From for TyParamOrdering { + fn from(o: Ordering) -> Self { + match o { + Ordering::Less => Less, + Ordering::Equal => Equal, + Ordering::Greater => Greater, + } + } +} + +impl TyParamOrdering { + pub const fn is_lt(&self) -> bool { matches!(self, Less | LessEqual | Any) } + pub const fn is_le(&self) -> bool { matches!(self, Less | Equal | LessEqual | Any) } + pub const fn is_gt(&self) -> bool { matches!(self, Greater | GreaterEqual | Any) } + pub const fn is_ge(&self) -> bool { matches!(self, Greater | Equal | GreaterEqual | Any) } + pub const fn is_eq(&self) -> bool { matches!(self, Equal | Any) } + pub const fn is_ne(&self) -> bool { matches!(self, Less | Greater | NotEqual | Any) } + pub const fn reverse(&self) -> Self { + match self { + Less => Greater, + Greater => Less, + LessEqual => GreaterEqual, + GreaterEqual => LessEqual, + Equal => NotEqual, + NotEqual => Equal, + Any => Any, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum TyBound { + // e.g. A <: Add => Subtype{sub: A, sup: Add}, A <: {a: Int} => Subtype{sub: A, sup: {a: Int}} + Subtype{ sub: Type, sup: Type }, + // TODO: Supertype{ sup: Type, sub: Type }, + // TyParam::MonoQuantVarに型の情報が含まれているので、boundsからは除去される + // e.g. N: Nat => Instance{name: N, t: Nat} + Instance{ name: Str, t: Type }, +} + +impl fmt::Display for TyBound { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Subtype{ sub, sup } => write!(f, "{sub} <: {sup}"), + Self::Instance{ name, t } => write!(f, "'{name}: {t}"), + } + } +} + +impl HasLevel for TyBound { + fn level(&self) -> Option { + todo!() + } + + fn update_level(&self, level: usize) { + match self { + Self::Subtype{ sub, sup } => { + sub.update_level(level); + sup.update_level(level); + } + Self::Instance{ t, .. } => { t.update_level(level); }, + } + } + + fn lift(&self) { + match self { + Self::Subtype{ sub, sup } => { + sub.lift(); + sup.lift(); + } + Self::Instance{ t, .. } => { t.lift(); }, + } + } +} + +impl TyBound { + pub const fn subtype(sub: Type, sup: Type) -> Self { Self::Subtype{ sub, sup } } + + pub const fn static_instance(name: &'static str, t: Type) -> Self { + Self::Instance{ name: Str::ever(name), t } + } + + pub const fn instance(name: Str, t: Type) -> Self { Self::Instance{ name, t } } + + pub fn mentions_as_instance(&self, name: &str) -> bool { + matches!(self, Self::Instance{ name: n, .. } if &n[..] == name) + } + + pub fn mentions_as_subtype(&self, name: &str) -> bool { + matches!(self, Self::Subtype{ sub, .. } if sub.name() == name) + } + + pub fn has_unbound_var(&self) -> bool { + match self { + Self::Subtype{ sub, sup } => sub.has_unbound_var() || sup.has_unbound_var(), + Self::Instance{ t, .. } => t.has_unbound_var(), + } + } + + pub const fn t(&self) -> &Type { + match self { + Self::Subtype{ sup, .. } => sup, + Self::Instance{ t, .. } => t, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Predicate { + Value(ValueObj), // True/False + Const(Str), + /// i == 0 => Eq{ lhs: "i", rhs: 0 } + Equal{ lhs: Str, rhs: TyParam }, + /// i > 0 => i >= 0+ε => GreaterEqual{ lhs: "i", rhs: 0+ε } + GreaterEqual{ lhs: Str, rhs: TyParam }, + LessEqual{ lhs: Str, rhs: TyParam }, + NotEqual{ lhs: Str, rhs: TyParam }, + Or(Box, Box), + And(Box, Box), + Not(Box, Box), +} + +impl fmt::Display for Predicate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Value(v) => write!(f, "{v}"), + Self::Const(c) => write!(f, "{c}"), + Self::Equal{ lhs, rhs } => write!(f, "{lhs} == {rhs}"), + Self::GreaterEqual{ lhs, rhs } => write!(f, "{lhs} >= {rhs}"), + Self::LessEqual{ lhs, rhs } => write!(f, "{lhs} <= {rhs}"), + Self::NotEqual{ lhs, rhs } => write!(f, "{lhs} != {rhs}"), + Self::Or(l, r) => write!(f, "({l}) or ({r})"), + Self::And(l, r) => write!(f, "({l}) and ({r})"), + Self::Not(l, r) => write!(f, "({l}) not ({r})"), + } + } +} + +impl HasLevel for Predicate { + fn level(&self) -> Option { + match self { + Self::Value(_) | Self::Const(_) => None, + Self::Equal{ rhs, .. } + | Self::GreaterEqual{ rhs, .. } + | Self::LessEqual{ rhs, .. } + | Self::NotEqual{ rhs, .. } => rhs.level(), + Self::And(_lhs, _rhs) + | Self::Or(_lhs, _rhs) + | Self::Not(_lhs, _rhs) => todo!(), + } + } + + fn update_level(&self, level: usize) { + match self { + Self::Value(_) | Self::Const(_) => {}, + Self::Equal{ rhs, .. } + | Self::GreaterEqual{ rhs, .. } + | Self::LessEqual{ rhs, .. } + | Self::NotEqual{ rhs, .. } => { + rhs.update_level(level); + }, + Self::And(lhs, rhs) + | Self::Or(lhs, rhs) + | Self::Not(lhs, rhs) => { + lhs.update_level(level); + rhs.update_level(level); + }, + } + } + + fn lift(&self) { + match self { + Self::Value(_) | Self::Const(_) => {}, + Self::Equal{ rhs, .. } + | Self::GreaterEqual{ rhs, .. } + | Self::LessEqual{ rhs, .. } + | Self::NotEqual{ rhs, .. } => { + rhs.lift(); + }, + Self::And(lhs, rhs) + | Self::Or(lhs, rhs) + | Self::Not(lhs, rhs) => { + lhs.lift(); + rhs.lift(); + }, + } + } +} + +impl Predicate { + pub const fn eq(lhs: Str, rhs: TyParam) -> Self { Self::Equal{ lhs, rhs } } + pub const fn ne(lhs: Str, rhs: TyParam) -> Self { Self::NotEqual{ lhs, rhs } } + /// >= + pub const fn ge(lhs: Str, rhs: TyParam) -> Self { Self::GreaterEqual{ lhs, rhs } } + /// <= + pub const fn le(lhs: Str, rhs: TyParam) -> Self { Self::LessEqual{ lhs, rhs } } + + pub fn and(lhs: Predicate, rhs: Predicate) -> Self { + Self::And(Box::new(lhs), Box::new(rhs)) + } + + pub fn or(lhs: Predicate, rhs: Predicate) -> Self { + Self::Or(Box::new(lhs), Box::new(rhs)) + } + + pub fn not(lhs: Predicate, rhs: Predicate) -> Self { + Self::Not(Box::new(lhs), Box::new(rhs)) + } + + pub fn subject(&self) -> Option<&str> { + match self { + Self::Equal{ lhs, .. } + | Self::LessEqual{ lhs, .. } + | Self::GreaterEqual{ lhs, .. } + | Self::NotEqual{ lhs, .. } => Some(&lhs[..]), + Self::And(lhs, rhs) + | Self::Or(lhs, rhs) + | Self::Not(lhs, rhs) => { + let l = lhs.subject(); + let r = rhs.subject(); + if l != r { todo!() } + else { l } + }, + _ => None, + } + } + + pub fn change_subject_name(self, name: Str) -> Self { + match self { + Self::Equal{ rhs, .. } => Self::eq(name, rhs), + Self::GreaterEqual{ rhs, .. } => Self::ge(name, rhs), + Self::LessEqual{ rhs, .. } => Self::le(name, rhs), + Self::NotEqual{ rhs, .. } => Self::ne(name, rhs), + Self::And(lhs, rhs) => Self::and(lhs.change_subject_name(name.clone()), rhs.change_subject_name(name)), + Self::Or(lhs, rhs) => Self::or(lhs.change_subject_name(name.clone()), rhs.change_subject_name(name)), + Self::Not(lhs, rhs) => Self::not(lhs.change_subject_name(name.clone()), rhs.change_subject_name(name)), + _ => self, + } + } + + pub fn mentions(&self, name: &str) -> bool { + match self { + Self::Const(n) => &n[..] == name, + Self::Equal{ lhs, .. } + | Self::LessEqual{ lhs, .. } + | Self::GreaterEqual{ lhs, .. } + | Self::NotEqual{ lhs, .. } => &lhs[..] == name, + Self::And(lhs, rhs) + | Self::Or(lhs, rhs) + | Self::Not(lhs, rhs) => lhs.mentions(name) || rhs.mentions(name), + _ => false, + } + } + + pub fn can_be_false(&self) -> bool { + match self { + Self::Value(l) => matches!(l, ValueObj::False), + Self::Const(_) => todo!(), + Self::Or(lhs, rhs) => lhs.can_be_false() || rhs.can_be_false(), + Self::And(lhs, rhs) => lhs.can_be_false() && rhs.can_be_false(), + Self::Not(lhs, rhs) => lhs.can_be_false() && !rhs.can_be_false(), + _ => true, + } + } + + pub fn has_unbound_var(&self) -> bool { + match self { + Self::Value(_) => false, + Self::Const(_) => false, + Self::Equal{ rhs, .. } + |Self::GreaterEqual{ rhs, .. } + | Self::LessEqual{ rhs, .. } + | Self::NotEqual{ rhs, .. } => rhs.has_unbound_var(), + Self::Or(lhs, rhs) + | Self::And(lhs, rhs) + | Self::Not(lhs, rhs) => lhs.has_unbound_var() || rhs.has_unbound_var(), + } + } + + pub fn min_max<'a>(&'a self, min: Option<&'a TyParam>, max: Option<&'a TyParam>) -> (Option<&'a TyParam>, Option<&'a TyParam>) { + match self { + Predicate::Equal{ rhs: _, .. } => todo!(), + // {I | I <= 1; I <= 2} + Predicate::LessEqual { rhs, .. } => { + (min, max.map(|l: &TyParam| match l.cheap_cmp(rhs) { + Some(c) if c.is_ge() => l, + Some(_) => rhs, + _ => l, + }).or(Some(rhs))) + }, + // {I | I >= 1; I >= 2} + Predicate::GreaterEqual { rhs, .. } => { + (min.map(|l: &TyParam| match l.cheap_cmp(rhs) { + Some(c) if c.is_le() => l, + Some(_) => rhs, + _ => l, + }).or(Some(rhs)), max) + }, + Predicate::And(_l, _r) => todo!(), + _ => todo!(), + } + } + + pub fn typarams(&self) -> Vec<&TyParam> { + match self { + Self::Value(_) | Self::Const(_) => vec![], + Self::Equal{ rhs, .. } + | Self::GreaterEqual{ rhs, .. } + | Self::LessEqual{ rhs, .. } + | Self::NotEqual{ rhs, .. } => vec![rhs], + Self::And(lhs, rhs) + | Self::Or(lhs, rhs) + | Self::Not(lhs, rhs) => + lhs.typarams().into_iter().chain(rhs.typarams()).collect(), + } + } + + pub fn typarams_mut(&mut self) -> Vec<&mut TyParam> { + match self { + Self::Value(_) | Self::Const(_) => vec![], + Self::Equal{ rhs, .. } + | Self::GreaterEqual{ rhs, .. } + | Self::LessEqual{ rhs, .. } + | Self::NotEqual{ rhs, .. } => vec![rhs], + Self::And(lhs, rhs) + | Self::Or(lhs, rhs) + | Self::Not(lhs, rhs) => + lhs.typarams_mut().into_iter().chain(rhs.typarams_mut()).collect(), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum IntervalOp { + /// .. + Closed, + /// <.. + LeftOpen, + /// ..< + RightOpen, + /// <..< + Open, +} + +impl IntervalOp { + pub const fn is_closed(&self) -> bool { matches!(self, Self::Closed) } + pub const fn is_left_open(&self) -> bool { matches!(self, Self::LeftOpen | Self::Open) } + pub const fn is_right_open(&self) -> bool { matches!(self, Self::RightOpen | Self::Open) } + pub const fn is_open(&self) -> bool { matches!(self, Self::Open) } +} + +impl fmt::Display for IntervalOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Closed => write!(f, ".."), + Self::LeftOpen => write!(f, "<.."), + Self::RightOpen => write!(f, "..<"), + Self::Open => write!(f, "<..<"), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParamTy { + pub name: Option, + pub ty: Type, +} + +impl fmt::Display for ParamTy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(name) = &self.name { + write!(f, "{}: {}", name, self.ty) + } else { + write!(f, "{}", self.ty) + } + } +} + +impl ParamTy { + pub const fn new(name: Option, ty: Type) -> Self { Self { name, ty } } + + pub const fn anonymous(ty: Type) -> Self { Self::new(None, ty) } +} + +/// e.g. +/// (x: Int, ?base: Int) -> Int +/// => SubrTy{ kind: Func, non_default_params: [x: Int], default_params: [base: Int] return_t: Int } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SubrType { + pub kind: SubrKind, + pub non_default_params: Vec, + pub default_params: Vec, + pub return_t: Box, +} + +impl fmt::Display for SubrType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.default_params.is_empty() { + write!( + f, + "{}({}) {} {}", + self.kind.prefix(), + fmt_vec(&self.non_default_params), + self.kind.arrow(), + self.return_t, + ) + } else { + write!( + f, + "{}({} |= {}) {} {}", + self.kind.prefix(), + fmt_vec(&self.non_default_params), + fmt_vec(&self.default_params), + self.kind.arrow(), + self.return_t, + ) + } + } +} + +impl SubrType { + pub fn new(kind: SubrKind, non_default_params: Vec, default_params: Vec, return_t: Type) -> Self { + Self{ kind, non_default_params, default_params, return_t: Box::new(return_t) } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum RefineKind { + Interval{ min: TyParam, max: TyParam }, // e.g. {I: Int | I >= 2; I <= 10} 2..10 + Enum(Set), // e.g. {I: Int | I == 1 or I == 2} {1, 2} + Complex, +} + +/// e.g. +/// ``` +/// {I: Int | I >= 0} +/// {_: StrWithLen N | N >= 0} +/// {T: (Int, Int) | T.0 >= 0, T.1 >= 0} +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RefinementType { + pub var: Str, + pub t: Box, + pub preds: Set, +} + +impl fmt::Display for RefinementType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{{{}: {} | {}}}", self.var, self.t, fmt_set_split_with(&self.preds, "; ")) + } +} + +impl RefinementType { + pub fn new(var: Str, t: Type, preds: Set) -> Self { + Self { var, t: Box::new(t), preds } + } + + pub fn bound(&self) -> TyBound { + TyBound::instance(self.var.clone(), *self.t.clone()) + } +} + +/// e.g. +/// ``` +/// |T: Type| T -> T == Quantified{ unbound_t: (T -> T), bounds: [T: Type] } +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct QuantifiedType { + pub unbound_callable: Box, + pub bounds: Set, +} + +impl fmt::Display for QuantifiedType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "|{}| {}", &self.bounds, self.unbound_callable) + } +} + +impl QuantifiedType { + pub fn new(unbound_callable: Type, bounds: Set) -> Self { + Self { unbound_callable: Box::new(unbound_callable), bounds } + } +} + +type SelfType = Type; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum SubrKind { + Func, + Proc, + FuncMethod(Box), + ProcMethod{ before: Box, after: Option> }, +} + +impl HasLevel for SubrKind { + fn level(&self) -> Option { todo!() } + + fn update_level(&self, level: usize) { + match self { + Self::FuncMethod(t) => t.update_level(level), + Self::ProcMethod{ before, after } => { + before.update_level(level); + after.as_ref().map(|t| { t.update_level(level); }); + } + _ => {} + } + } + + fn lift(&self) { + match self { + Self::FuncMethod(t) => t.lift(), + Self::ProcMethod{ before, after } => { + before.lift(); + after.as_ref().map(|t| { t.lift(); }); + } + _ => {} + } + } +} + +impl SubrKind { + pub fn fn_met(t: SelfType) -> Self { SubrKind::FuncMethod(Box::new(t)) } + + pub fn pr_met(before: SelfType, after: Option) -> Self { + Self::ProcMethod{ before: Box::new(before), after: after.map(Box::new) } + } + + pub const fn arrow(&self) -> &str { + match self { + Self::Func | Self::FuncMethod(_) => "->", + Self::Proc | Self::ProcMethod{ .. } => "=>", + } + } + + pub const fn inner_len(&self) -> usize { + match self { + Self::Func | Self::Proc => 0, + Self::FuncMethod(_) | Self::ProcMethod{ .. } => 1, + } + } + + pub fn prefix(&self) -> String { + match self { + Self::Func | Self::Proc => "".to_string(), + Self::FuncMethod(t) => format!("{t}."), + Self::ProcMethod{ before, after } => + if let Some(after) = after { format!("({before} ~> {after}).") } + else { format!("{before}.") }, + } + } + + pub fn has_unbound_var(&self) -> bool { + match self { + Self::Func | Self::Proc => false, + Self::FuncMethod(t) => t.has_unbound_var(), + Self::ProcMethod{ before, after } => + before.has_unbound_var() || after.as_ref().map(|t| t.has_unbound_var()).unwrap_or(false), + } + } + + pub fn same_kind_as(&self, other: &Self) -> bool { + match (self, other) { + (Self::Func, Self::Func) | (Self::Proc, Self::Proc) + | (Self::FuncMethod(_), Self::FuncMethod(_)) | (Self::ProcMethod{ .. }, Self::ProcMethod{ .. }) => true, + _ => false, + } + } + + pub fn self_t(&self) -> Option<&SelfType> { + match self { + Self::FuncMethod(t) | Self::ProcMethod{ before: t, .. } => Some(t), + _ => None, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Ownership { + Owned, + Ref, + RefMut, +} + +impl Ownership { + pub const fn is_owned(&self) -> bool { matches!(self, Self::Owned) } + pub const fn is_ref(&self) -> bool { matches!(self, Self::Ref) } + pub const fn is_refmut(&self) -> bool { matches!(self, Self::RefMut) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ArgsOwnership { + Args{ self_: Option, non_defaults: Vec, defaults: Vec }, + VarArgs(Ownership), // TODO: defaults + VarArgsDefault(Ownership), +} + +impl ArgsOwnership { + pub const fn args(self_: Option, non_defaults: Vec, defaults: Vec) -> Self { + Self::Args{ self_, non_defaults, defaults } + } +} + +/// NOTE: 連携型変数があるので、比較には`ref_eq`を使うこと +/// Commonが付く型は多相だが中の型をなんでも受け入れるバージョン +/// TODO: MonoArray Int, 3 == PolyArray Int, Int, Int +/// Mut型を作ろうとすると、name() -> &strがうまくいかないので +/// 組み込みMut型は全て書き下す +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Type { + /* Monomorphic (builtin) types */ + Obj, // {=} + ObjMut, + Int, + IntMut, + Nat, + NatMut, + Ratio, + RatioMut, + Float, + FloatMut, + Bool, + BoolMut, + Str, + StrMut, + NoneType, + Code, + Module, + Frame, + Error, + Inf, // {∞} + NegInf, // {-∞} + // TODO: PolyType/Class + Type, + Class, + Trait, + Patch, + RangeCommon, + FuncCommon, + ProcCommon, + FuncMethodCommon, + ProcMethodCommon, + CallableCommon, + ArrayCommon, + DictCommon, + NotImplemented, + Ellipsis, // これはクラスのほうで型推論用のマーカーではない + Never, // {} + Mono(Str), // others + /* Polymorphic types */ + Range(Box), + Iter(Box), + Ref(Box), + RefMut(Box), + Option(Box), + OptionMut(Box), + Subr(SubrType), + // CallableはProcの上位型なので、変数に!をつける + Callable{ param_ts: Vec, return_t: Box }, + // e.g. [Int] == Array{ t: Int, len: _ }, [Int; 3] == Array { t: Int, len: 3 } + Array{ t: Box, len: TyParam }, + // TODO: heterogeneous dict + Dict{ k: Box, v: Box }, + Tuple(Vec), + Record(Dict), // e.g. {x = Int} + // e.g. {T -> T | T: Type}, {I: Int | I > 0}, {S | N: Nat; S: Str N; N > 1} + // 区間型と列挙型は篩型に変換される + // f 0 = ...はf _: {0} == {I: Int | I == 0}のシンタックスシュガー + // e.g. + // {0, 1, 2} => {I: Int | I == 0 or I == 1 or I == 2} + // 1..10 => {I: Int | I >= 1 and I <= 10} + Refinement(RefinementType), + // e.g. |T: Type| T -> T + Quantified(QuantifiedType), + And(Vec), + Not(Vec), + Or(Vec), + VarArgs(Box), // ...T + Poly{ name: Str, params: Vec }, // T(params) + /* Special types (inference-time types) */ + MonoQVar(Str), // QuantifiedTyの中で使う一般化型変数、利便性のためMonoとは区別する + PolyQVar{ name: Str, params: Vec }, + FreeVar(FreeTyVar), // a reference to the type of other expression, see docs/compiler/inference.md + MonoProj{ lhs: Box, rhs: Str }, // e.g. T.U + ASTOmitted, // call中のcalleeの型など、不要な部分に付ける + Failure, // when failed to infer +} + +impl fmt::Display for Type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Mono(name) => write!(f, "{name}"), + Self::Range(t) => write!(f, "Range({t})"), + Self::RangeCommon => write!(f, "Range(Int)"), + Self::Iter(t) => write!(f, "Iter({t})"), + Self::Ref(t) => write!(f, "Ref({t})"), + Self::RefMut(t) => write!(f, "Ref!({t})"), + Self::Option(t) => write!(f, "Option({t})"), + Self::OptionMut(t) => write!(f, "Option!({t})"), + Self::Subr(sub) => write!(f, "{sub}"), + Self::Callable{ param_ts, return_t } => { + write!(f, "Callable(({}), {return_t})", fmt_vec(param_ts)) + } + Self::Array{ t, len } => write!(f, "[{t}; {len}]"), + Self::Dict{ k, v } => write!(f, "{{{k}: {v}}}"), + Self::Tuple(ts) => write!(f, "({})", fmt_vec(ts)), + Self::Record(attrs) => write!(f, "{{{attrs}}}"), + Self::Refinement(refinement) => write!(f, "{}", refinement), + Self::Quantified(quantified) => write!(f, "{}", quantified), + Self::And(types) => write!(f, "{}", fmt_vec_split_with(types, " and ")), + Self::Not(types) => write!(f, "{}", fmt_vec_split_with(types, " not ")), + Self::Or(types) => write!(f, "{}", fmt_vec_split_with(types, " or ")), + Self::VarArgs(t) => write!(f, "...{t}"), + Self::Poly{ name, params } => write!(f, "{name}({})", fmt_vec(params)), + Self::MonoQVar(name) => write!(f, "'{name}"), + Self::FreeVar(v) => write!(f, "{v}"), + Self::MonoProj{ lhs, rhs } => write!(f, "{lhs}.{rhs}"), + _ => write!(f, "{}", self.name()), + } + } +} + +impl Default for Type { + fn default() -> Self { Self::Failure } +} + +impl From> for Type { + fn from(r: Range) -> Self { + Type::int_interval(IntervalOp::RightOpen, r.start, r.end) + } +} + +impl From> for Type { + fn from(r: Range<&TyParam>) -> Self { + Type::int_interval(IntervalOp::RightOpen, r.start.clone(), r.end.clone()) + } +} + +impl From> for Type { + fn from(r: RangeInclusive) -> Self { + let (start, end) = r.into_inner(); + Type::int_interval(IntervalOp::Closed, start, end) + } +} + +impl From> for Type { + fn from(r: RangeInclusive<&TyParam>) -> Self { + let (start, end) = r.into_inner(); + Type::int_interval(IntervalOp::Closed, start.clone(), end.clone()) + } +} + +impl From<&str> for Type { + fn from(item: &str) -> Self { + match item { + "Obj" => Self::Obj, + "Obj!" => Self::ObjMut, + "Int" => Self::Int, + "Int!" => Self::IntMut, + "Nat" => Self::Nat, + "Nat!" => Self::NatMut, + "Ratio" => Self::Ratio, + "Ratio!" => Self::RatioMut, + "Float" => Self::Float, + "Float!" => Self::FloatMut, + "Bool" => Self::Bool, + "Bool!" => Self::BoolMut, + "Str" => Self::Str, + "Str!" => Self::StrMut, + "NoneType" => Self::NoneType, + "Type" => Self::Type, + "Class" => Self::Class, + "Trait" => Self::Trait, + "Patch" => Self::Patch, + "Code" => Self::Code, + "Module" => Self::Module, + "Frame" => Self::Frame, + "Error" => Self::Error, + // "Array" => Self::Array(Box::new(Type::Illegal)), + "Ellipsis" => Self::Ellipsis, + "NotImplemented" => Self::NotImplemented, + "Never" => Self::Never, + "Inf" => Self::Inf, + "_" => Self::Top(), + _ => todo!(), + } + } +} + +impl HasType for Type { + #[inline] + fn ref_t(&self) -> &Type { self } + fn inner_ts(&self) -> Vec { + match self { + Self::RangeCommon => vec![Type::Int], + Self::Dict{k, v} => vec![ + k.as_ref().clone(), + v.as_ref().clone() + ], + Self::Ref(t) + | Self::RefMut(t) + | Self::Option(t) + | Self::OptionMut(t) + | Self::Range(t) + | Self::Iter(t) + | Self::Array{ t, .. } + | Self::VarArgs(t) => vec![t.as_ref().clone()], + // Self::And(ts) | Self::Or(ts) => , + Self::Subr(_sub) => todo!(), + | Self::Callable{ param_ts, .. } + | Self::Tuple(param_ts) => param_ts.clone(), + Self::Poly{ .. } => { + todo!() + }, + _ => vec![], + } + } + fn signature_t(&self) -> Option<&Type> { None } +} + +impl HasLevel for Type { + // FIXME: 複合型のレベル + fn level(&self) -> Option { + match self { + Self::FreeVar(v) => v.level(), + _ => None, + } + } + + fn update_level(&self, level: Level) { + match self { + Self::FreeVar(v) => v.update_level(level), + Self::Ref(t) + | Self::RefMut(t) + | Self::Option(t) + | Self::OptionMut(t) + | Self::Range(t) + | Self::Iter(t) + | Self::Array{ t, .. } + | Self::VarArgs(t) => t.update_level(level), + Self::Callable{ param_ts, return_t } => { + for p in param_ts.iter() { + p.update_level(level); + } + return_t.update_level(level); + } + Self::Subr(subr) => { + subr.kind.update_level(level); + for p in subr.non_default_params.iter() + .chain(subr.default_params.iter()) { + p.ty.update_level(level); + } + subr.return_t.update_level(level); + } + Self::And(ts) + | Self::Or(ts) + | Self::Not(ts) + | Self::Tuple(ts) => { + for t in ts.iter() { + t.update_level(level); + } + }, + Self::Dict{ k, v } => { + k.update_level(level); + v.update_level(level); + }, + Self::Record(attrs) => { + for t in attrs.values() { + t.update_level(level); + } + }, + Self::Poly{ params, .. } => { + for p in params.iter() { + p.update_level(level); + } + }, + Self::MonoProj{ lhs, .. } => { + lhs.update_level(level); + }, + Self::Refinement(refine) => { + refine.t.update_level(level); + for pred in refine.preds.iter() { + pred.update_level(level); + } + } + Self::Quantified(quant) => { + quant.unbound_callable.update_level(level); + for bound in quant.bounds.iter() { + bound.update_level(level); + } + } + _ => {}, + } + } + + fn lift(&self) { + match self { + Self::FreeVar(v) => v.lift(), + Self::Ref(t) + | Self::RefMut(t) + | Self::Option(t) + | Self::OptionMut(t) + | Self::Range(t) + | Self::Iter(t) + | Self::Array{ t, .. } + | Self::VarArgs(t) => t.lift(), + Self::Callable{ param_ts, return_t } => { + for p in param_ts.iter() { + p.lift(); + } + return_t.lift(); + } + Self::Subr(subr) => { + subr.kind.lift(); + for p in subr.non_default_params.iter() + .chain(subr.default_params.iter()) { + p.ty.lift(); + } + subr.return_t.lift(); + } + Self::And(ts) + | Self::Or(ts) + | Self::Not(ts) + | Self::Tuple(ts) => { + for t in ts.iter() { + t.lift(); + } + }, + Self::Dict{ k, v } => { + k.lift(); + v.lift(); + }, + Self::Record(attrs) => { + for t in attrs.values() { + t.lift(); + } + }, + Self::Poly{ params, .. } => { + for p in params.iter() { + p.lift(); + } + }, + Self::MonoProj{ lhs, .. } => { lhs.lift(); }, + Self::Refinement(refine) => { + refine.t.lift(); + for pred in refine.preds.iter() { + pred.lift(); + } + } + Self::Quantified(quant) => { + quant.unbound_callable.lift(); + for bound in quant.bounds.iter() { + bound.lift(); + } + } + _ => {}, + } + } +} + +impl Type { + pub const OBJ: &'static Self = &Self::Obj; + pub const NONE: &'static Self = &Self::NoneType; + pub const NOT_IMPLEMENTED: &'static Self = &Self::NotImplemented; + pub const ELLIPSIS: &'static Self = &Self::Ellipsis; + pub const INF: &'static Self = &Self::Inf; + pub const NEG_INF: &'static Self = &Self::NegInf; + pub const NEVER: &'static Self = &Self::Never; + pub const FAILURE: &'static Self = &Self::Failure; + + /// Top := {=} + #[allow(non_snake_case)] + pub const fn Top() -> Self { Self::Mono(Str::ever("Top")) } + /// Bottom := {} + #[allow(non_snake_case)] + pub const fn Bottom() -> Self { Self::Mono(Str::ever("Bottom")) } + + #[inline] + pub fn free_var(level: usize, constraint: Constraint) -> Self { + Self::FreeVar(Free::new_unbound(level, constraint)) + } + + #[inline] + pub fn named_free_var(name: Str, level: usize, constraint: Constraint) -> Self { + Self::FreeVar(Free::new_named_unbound(name, level, constraint)) + } + + #[inline] + pub fn array(elem_t: Type, len: TyParam) -> Self { Self::Array{ t: Box::new(elem_t), len } } + + #[inline] + pub fn dict(k_t: Type, v_t: Type) -> Self { + Self::Dict{ k: Box::new(k_t), v: Box::new(v_t) } + } + + #[inline] + pub fn var_args(elem_t: Type) -> Self { Self::VarArgs(Box::new(elem_t)) } + + #[inline] + pub fn range(t: Type) -> Self { Self::Range(Box::new(t)) } + + pub fn enum_t(s: Set) -> Self { + assert!(s.is_homogeneous()); + let name = Str::from(fresh_varname()); + let preds = s.iter() + .map(|o| Predicate::eq(name.clone(), TyParam::value(o.clone()))) + .collect(); + let refine = RefinementType::new(name, s.inner_class(), preds); + Self::Refinement(refine) + } + + #[inline] + pub fn int_interval, Q: Into>(op: IntervalOp, l: P, r: Q) -> Self { + let l = l.into(); + let r = r.into(); + let l = l.try_into().unwrap_or_else(|l| todo!("{l}")); + let r = r.try_into().unwrap_or_else(|r| todo!("{r}")); + let name = Str::from(fresh_varname()); + let pred = match op { + IntervalOp::LeftOpen if l == TyParam::value(NegInf) => Predicate::le(name.clone(), r), + // l<..r => {I: classof(l) | I >= l+ε and I <= r} + IntervalOp::LeftOpen => Predicate::and( + Predicate::ge(name.clone(), TyParam::succ(l)), + Predicate::le(name.clone(), r) + ), + IntervalOp::RightOpen if r == TyParam::value(Inf) => Predicate::ge(name.clone(), l), + // l.. {I: classof(l) | I >= l and I <= r-ε} + IntervalOp::RightOpen => Predicate::and( + Predicate::ge(name.clone(), l), + Predicate::le(name.clone(), TyParam::pred(r)) + ), + // l..r => {I: classof(l) | I >= l and I <= r} + IntervalOp::Closed => Predicate::and( + Predicate::ge(name.clone(), l), + Predicate::le(name.clone(), r) + ), + IntervalOp::Open if l == TyParam::value(NegInf) && r == TyParam::value(Inf) => { + return Type::refinement(name.clone(), Type::Int, set!{}) + }, + // l<.. {I: classof(l) | I >= l+ε and I <= r-ε} + IntervalOp::Open => Predicate::and( + Predicate::ge(name.clone(), TyParam::succ(l)), + Predicate::le(name.clone(), TyParam::pred(r)) + ), + }; + Type::refinement(name.clone(), Type::Int, set!{pred}) + } + + pub fn iter(t: Type) -> Self { Self::Iter(Box::new(t)) } + + pub fn refer(t: Type) -> Self { Self::Ref(Box::new(t)) } + + pub fn ref_mut(t: Type) -> Self { Self::RefMut(Box::new(t)) } + + pub fn option(t: Type) -> Self { Self::Option(Box::new(t)) } + + pub fn option_mut(t: Type) -> Self { Self::OptionMut(Box::new(t)) } + + pub fn subr(kind: SubrKind, non_default_params: Vec, default_params: Vec, return_t: Type) -> Self { + Self::Subr(SubrType::new(kind, non_default_params, default_params, return_t)) + } + + pub fn func(non_default_params: Vec, default_params: Vec, return_t: Type) -> Self { + Self::Subr(SubrType::new(SubrKind::Func, non_default_params, default_params, return_t)) + } + + pub fn func1(param_t: Type, return_t: Type) -> Self { Self::func(vec![ParamTy::anonymous(param_t)], vec![], return_t) } + + pub fn kind1(param: Type) -> Self { Self::func1(param, Type::Type) } + + pub fn func2(l: Type, r: Type, return_t: Type) -> Self { + Self::func(vec![ParamTy::anonymous(l), ParamTy::anonymous(r)], vec![], return_t) + } + + pub fn anon_param_func(non_default_params: Vec, default_params: Vec, return_t: Type) -> Self { + let non_default_params = non_default_params.into_iter().map(ParamTy::anonymous).collect(); + let default_params = default_params.into_iter().map(ParamTy::anonymous).collect(); + Self::func(non_default_params, default_params, return_t) + } + + pub fn proc(non_default_params: Vec, default_params: Vec, return_t: Type) -> Self { + Self::Subr(SubrType::new(SubrKind::Proc, non_default_params, default_params, return_t)) + } + + pub fn proc1(param_t: Type, return_t: Type) -> Self { + Self::proc(vec![ParamTy::anonymous(param_t)], vec![], return_t) + } + + pub fn proc2(l: Type, r: Type, return_t: Type) -> Self { + Self::proc(vec![ParamTy::anonymous(l), ParamTy::anonymous(r)], vec![], return_t) + } + + pub fn anon_param_proc(non_default_params: Vec, default_params: Vec, return_t: Type) -> Self { + let non_default_params = non_default_params.into_iter().map(ParamTy::anonymous).collect(); + let default_params = default_params.into_iter().map(ParamTy::anonymous).collect(); + Self::proc(non_default_params, default_params, return_t) + } + + pub fn fn_met(self_t: Type, non_default_params: Vec, default_params: Vec, return_t: Type) -> Self { + Self::Subr(SubrType::new(SubrKind::FuncMethod(Box::new(self_t)), non_default_params, default_params, return_t)) + } + + pub fn fn0_met(self_t: Type, return_t: Type) -> Self { + Self::fn_met(self_t, vec![], vec![], return_t) + } + + pub fn fn1_met(self_t: Type, input_t: Type, return_t: Type) -> Self { + Self::fn_met(self_t, vec![ParamTy::anonymous(input_t)], vec![], return_t) + } + + pub fn anon_param_fn_met(self_t: Type, non_default_params: Vec, default_params: Vec, return_t: Type) -> Self { + let non_default_params = non_default_params.into_iter().map(ParamTy::anonymous).collect(); + let default_params = default_params.into_iter().map(ParamTy::anonymous).collect(); + Self::fn_met(self_t, non_default_params, default_params, return_t) + } + + pub fn pr_met(self_before: Type, self_after: Option, non_default_params: Vec, default_params: Vec, return_t: Type) -> Self { + Self::Subr(SubrType::new(SubrKind::pr_met(self_before, self_after), non_default_params, default_params, return_t)) + } + + pub fn pr0_met(self_before: Type, self_after: Option, return_t: Type) -> Self { + Self::pr_met(self_before, self_after, vec![], vec![], return_t) + } + + pub fn pr1_met(self_before: Type, self_after: Option, input_t: Type, return_t: Type) -> Self { + Self::pr_met(self_before, self_after, vec![ParamTy::anonymous(input_t)], vec![], return_t) + } + + pub fn anon_param_pr_met(self_before: Type, self_after: Option, non_default_params: Vec, default_params: Vec, return_t: Type) -> Self { + let non_default_params = non_default_params.into_iter().map(ParamTy::anonymous).collect(); + let default_params = default_params.into_iter().map(ParamTy::anonymous).collect(); + Self::pr_met(self_before, self_after, non_default_params, default_params, return_t) + } + + /// function type with non-default parameters + #[inline] + pub fn nd_func(params: Vec, ret: Type) -> Type { + Type::func(params, vec![], ret) + } + + #[inline] + pub fn nd_proc(params: Vec, ret: Type) -> Type { + Type::proc(params, vec![], ret) + } + + pub fn callable(param_ts: Vec, return_t: Type) -> Self { + Self::Callable{ param_ts, return_t: Box::new(return_t) } + } + + #[inline] + pub fn mono>(name: S) -> Self { Self::Mono(name.into()) } + + #[inline] + pub fn mono_q>(name: S) -> Self { Self::MonoQVar(name.into()) } + + #[inline] + pub fn poly>(name: S, params: Vec) -> Self { + Self::Poly{ name: name.into(), params } + } + + #[inline] + pub fn poly_q>(name: S, params: Vec) -> Self { + Self::PolyQVar{ name: name.into(), params } + } + + #[inline] + pub fn mono_proj>(lhs: Type, rhs: S) -> Self { + Self::MonoProj{ lhs: Box::new(lhs), rhs: rhs.into() } + } + + /// ```rust + /// {I: Int | I >= 0} + /// => Refinement{ + /// layout: TyParam::MonoQ "I", + /// bounds: [TyBound::Instance("I", "Int")], + /// preds: [Predicate::GreaterEqual("I", 0)] + /// } + /// ``` + #[inline] + pub fn refinement(var: Str, t: Type, preds: Set) -> Self { + Self::Refinement(RefinementType::new(var, t, preds)) + } + + /// quantified((T -> T), T: Type) => |T: Type| T -> T + pub fn quantified(unbound_t: Type, bounds: Set) -> Self { + Self::Quantified(QuantifiedType::new(unbound_t, bounds)) + } + + pub fn mutate(self) -> Self { + match self { + Self::Int => Self::IntMut, + Self::Nat => Self::NatMut, + Self::Ratio => Self::RatioMut, + Self::Float => Self::FloatMut, + Self::Bool => Self::BoolMut, + Self::Str => Self::StrMut, + Self::Option(t) => Self::OptionMut(t), + Self::Array{ t, len } => + Self::poly("Array!", vec![TyParam::t(*t), len.mutate()]), + _ => todo!(), + } + } + + pub fn is_mut(&self) -> bool { + match self { + Self::FreeVar(fv) => if fv.is_linked() { + fv.crack().is_mut() + } else { + fv.unbound_name().unwrap().ends_with("!") + }, + Self::IntMut + | Self::NatMut + | Self::RatioMut + | Self::FloatMut + | Self::BoolMut + | Self::StrMut + | Self::OptionMut(_) => true, + Self::Mono(name) + | Self::MonoQVar(name) + | Self::Poly { name, .. } + | Self::PolyQVar { name, .. } + | Self::MonoProj { rhs: name, .. } => name.ends_with("!"), + _ => false, + } + } + + pub fn is_nonelike(&self) -> bool { + match self { + Self::NoneType => true, + Self::Option(t) + | Self::OptionMut(t) => t.is_nonelike(), + Self::Tuple(ts) => ts.len() == 0, + _ => false, + } + } + + pub fn args_ownership(&self) -> ArgsOwnership { + match self { + Self::Subr(subr) => { + let self_ = subr.kind.self_t().map(|t| t.ownership()); + let mut nd_args = vec![]; + for nd_param in subr.non_default_params.iter() { + let ownership = match &nd_param.ty { + Self::Ref(_) => Ownership::Ref, + Self::RefMut(_) => Ownership::RefMut, + Self::VarArgs(t) => { return ArgsOwnership::VarArgs(t.ownership()) }, + _ => Ownership::Owned, + }; + nd_args.push(ownership); + } + let mut d_args = vec![]; + for d_param in subr.default_params.iter() { + let ownership = match &d_param.ty { + Self::Ref(_) => Ownership::Ref, + Self::RefMut(_) => Ownership::RefMut, + Self::VarArgs(t) => { return ArgsOwnership::VarArgsDefault(t.ownership()) }, + _ => Ownership::Owned, + }; + d_args.push(ownership); + } + ArgsOwnership::args(self_, nd_args, d_args) + }, + _ => todo!(), + } + } + + pub fn ownership(&self) -> Ownership { + match self { + Self::Ref(_) => Ownership::Ref, + Self::RefMut(_) => Ownership::RefMut, + _ => Ownership::Owned, + } + } + + pub fn deep_eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::FreeVar(v), other) + | (other, Self::FreeVar(v)) => match &*v.borrow() { + FreeKind::Linked(t) => t.deep_eq(other), + _ => self == other, + }, + (Self::Range(l), Self::Range(r)) + | (Self::Iter(l), Self::Iter(r)) + | (Self::Ref(l), Self::Ref(r)) + | (Self::RefMut(l), Self::RefMut(r)) + | (Self::Option(l), Self::Option(r)) + | (Self::OptionMut(l), Self::OptionMut(r)) => l.deep_eq(r), + (Self::Subr(l), Self::Subr(r)) => { + match (&l.kind, &r.kind) { + (SubrKind::Func, SubrKind::Func) + | (SubrKind::Proc, SubrKind::Proc) => {}, + (SubrKind::FuncMethod(l), SubrKind::FuncMethod(r)) if !l.deep_eq(r.as_ref()) => { return false }, + (SubrKind::ProcMethod{ before, after }, SubrKind::ProcMethod{ before: rbefore, after: rafter }) + if !before.deep_eq(rbefore.as_ref()) + || !after.as_ref().zip(rafter.as_ref()).map(|(l, r)| l.deep_eq(r)).unwrap_or(false) => { return false }, + _ => { return false }, + } + if !l.default_params.iter().zip(r.default_params.iter()).all(|(l, r)| { + l.name == r.name && l.ty.deep_eq(&r.ty) + }) { return false } + if !l.non_default_params.iter().zip(r.non_default_params.iter()).all(|(l, r)| { + l.name == r.name && l.ty.deep_eq(&r.ty) + }) { return false } + l.return_t.deep_eq(&r.return_t) + }, + ( + Self::Callable{ param_ts: _lps, return_t: _lr }, + Self::Callable{ param_ts: _rps, return_t: _rr }, + ) => todo!(), + ( + Self::Array{ t: lt, len: ll}, + Self::Array{ t: rt, len: rl } + ) => lt.deep_eq(rt) && ll.deep_eq(rl), + ( + Self::Dict{ k: lk, v: lv }, + Self::Dict{ k: rk, v: rv } + ) => lk.deep_eq(rk) && lv.deep_eq(rv), + (Self::Record(_l), Self::Record(_r)) => todo!(), + (Self::Refinement(l), Self::Refinement(r)) => { + l.t.deep_eq(&r.t) && &l.preds == &r.preds + }, + (Self::Quantified(l), Self::Quantified(r)) => { + l.unbound_callable.deep_eq(&r.unbound_callable) && &l.bounds == &r.bounds + }, + (Self::Tuple(l), Self::Tuple(r)) + | (Self::And(l), Self::And(r)) + | (Self::Not(l), Self::Not(r)) + | (Self::Or(l), Self::Or(r)) => l.iter().zip(r.iter()).all(|(l, r)| l.deep_eq(r)), + (Self::VarArgs(l), Self::VarArgs(r)) => l.deep_eq(r), + ( + Self::Poly{ name: ln, params: lps } | Self::PolyQVar{ name: ln, params: lps }, + Self::Poly{ name: rn, params: rps } | Self::PolyQVar{ name: rn, params: rps }, + ) => ln == rn && lps.iter().zip(rps.iter()).all(|(l, r)| l.deep_eq(r)), + (Self::MonoProj{ lhs, rhs }, Self::MonoProj{ lhs: rlhs, rhs: rrhs }) => { + lhs.deep_eq(rlhs) && rhs == rrhs + }, + _ => self == other, + } + } + + /// 共通部分(A and B)を返す + /// 型同士の包含関係はここでは検査しない(TypeCheckerでする) + pub fn intersection(lhs: &Self, rhs: &Self) -> Self { + if lhs == rhs { return lhs.clone() } + match (lhs, rhs) { + // { .i: Int } and { .s: Str } == { .i: Int, .s: Str } + (Self::Record(l), Self::Record(r)) => { + Self::Record(l.clone().concat(r.clone())) + } + (Self::And(ts), t) + | (t, Self::And(ts)) => Self::And([vec![t.clone()], ts.clone()].concat()), + (t, Self::Obj) | (Self::Obj, t) => t.clone(), + (_, Self::Never) | (Self::Never, _) => Self::Never, + (l, r) => Self::And(vec![l.clone(), r.clone()]), + } + } + + pub fn name(&self) -> &str { + match self { + Self::Obj => "Obj", + Self::ObjMut => "Obj!", + Self::Int => "Int", + Self::IntMut => "Int!", + Self::Nat => "Nat", + Self::NatMut => "Nat!", + Self::Ratio => "Ratio", + Self::RatioMut => "Ratio!", + Self::Float => "Float", + Self::FloatMut => "Float!", + Self::Bool => "Bool", + Self::BoolMut => "Bool!", + Self::Str => "Str", + Self::StrMut => "Str!", + Self::NoneType => "None", + Self::Type => "Type", + Self::Class => "Class", + Self::Trait => "Trait", + Self::Patch => "Patch", + Self::Code => "Code", + Self::Module => "Module", + Self::Frame => "Frame", + Self::Error => "Error", + Self::Inf => "Inf", + Self::NegInf => "NegInf", + Self::Mono(name) + | Self::MonoQVar(name) => name, + Self::Range(_) | Self::RangeCommon => "Range", + Self::Iter(_) => "Iter", + Self::And(_) => "And", + Self::Not(_) => "Not", + Self::Or(_) => "Or", + Self::Ref(_) => "Ref", + Self::RefMut(_) => "Ref!", + Self::Option(_) => "Option", + Self::OptionMut(_) => "Option!", + Self::Subr(SubrType{ kind: SubrKind::Func, .. }) | Self::FuncCommon => "Func", + Self::Subr(SubrType{ kind: SubrKind::Proc, .. }) | Self::ProcCommon => "Proc", + Self::Subr(SubrType{ kind: SubrKind::FuncMethod(_), .. }) | Self::FuncMethodCommon => "FuncMethod", + Self::Subr(SubrType{ kind: SubrKind::ProcMethod{ .. }, .. }) | Self::ProcMethodCommon => "ProcMethod", + Self::Callable{ .. } | Self::CallableCommon => "Callable", + Self::Array{ .. } | Self::ArrayCommon => "Array", + Self::Dict{ .. } | Self::DictCommon => "Dict", + Self::Tuple(..) => "Tuple", + Self::Record(_) => "Record", + Self::VarArgs(_) => "VarArgs", + Self::Poly{ name, .. } + | Self::PolyQVar{ name, .. } => &*name, + // NOTE: compiler/codegen/convert_to_python_methodでクラス名を使うため、こうすると都合が良い + Self::Refinement(refine)=> refine.t.name(), + Self::Quantified(_) => "Quantified", + Self::Ellipsis => "Ellipsis", + Self::NotImplemented => "NotImplemented", + Self::Never => "Never", + Self::FreeVar(_) => "?", // TODO: 中身がSomeなら表示したい + Self::MonoProj{ .. } => "MonoProj", + Self::ASTOmitted => "ASTOmitted", + Self::Failure => "", + } + } + + pub const fn is_free_var(&self) -> bool { + matches!(self, Self::FreeVar(_)) + } + + pub const fn is_varargs(&self) -> bool { matches!(self, Self::VarArgs(_)) } + + pub fn is_monomorphic(&self) -> bool { self.typaram_len() == 0 } + + pub const fn is_callable(&self) -> bool { + matches!(self, Self::Subr{ .. } | Self::Callable{ .. }) + } + + pub fn has_unbound_var(&self) -> bool { + match self { + Self::FreeVar(fv) => + if fv.is_unbound() { true } else { fv.crack().has_unbound_var() }, + Self::Range(t) + | Self::Iter(t) + | Self::Ref(t) + | Self::RefMut(t) + | Self::Option(t) + | Self::OptionMut(t) + | Self::VarArgs(t) => t.has_unbound_var(), + Self::And(param_ts) + | Self::Not(param_ts) + | Self::Or(param_ts) => param_ts.iter().any(|t| t.has_unbound_var()), + Self::Array{ t, len } => t.has_unbound_var() || len.has_unbound_var(), + Self::Dict{ k, v } => k.has_unbound_var() || v.has_unbound_var(), + Self::Callable{ param_ts, return_t } => { + param_ts.iter().any(|t| t.has_unbound_var()) || return_t.has_unbound_var() + }, + Self::Subr(subr) => { + subr.kind.has_unbound_var() + || subr.non_default_params.iter().any(|p| p.ty.has_unbound_var()) + || subr.default_params.iter().any(|p| p.ty.has_unbound_var()) + || subr.return_t.has_unbound_var() + }, + Self::Record(r) => r.values().any(|t| t.has_unbound_var()), + Self::Refinement(refine) => + refine.t.has_unbound_var() + || refine.preds.iter().any(|p| p.has_unbound_var()), + Self::Quantified(quant) => + quant.unbound_callable.has_unbound_var() + || quant.bounds.iter().any(|b| b.has_unbound_var()), + Self::Poly{ params, .. } + | Self::PolyQVar{ params, .. }=> params.iter().any(|p| p.has_unbound_var()), + Self::MonoProj{ lhs, .. } => lhs.has_no_unbound_var(), + _ => false, + } + } + + pub fn has_no_unbound_var(&self) -> bool { !self.has_unbound_var() } + + pub fn typaram_len(&self) -> usize { + match self { + Self::Range(_) + | Self::Iter(_) + | Self::Option(_) + | Self::OptionMut(_) => 1, + Self::Array{ .. } | Self::Dict{ .. } => 2, + Self::And(param_ts) + | Self::Or(param_ts) + | Self::Tuple(param_ts) => param_ts.len() + 1, + Self::Subr(subr) => + subr.kind.inner_len() + subr.non_default_params.len() + subr.default_params.len() + 1, + Self::Callable{ param_ts, .. } => param_ts.len() + 1, + Self::Poly{ params, .. } + | Self::PolyQVar{ params, .. } => params.len(), + _ => 0, + } + } + + pub fn typarams(&self) -> Vec { + match self { + Self::FreeVar(f) if f.is_linked() => f.crack().typarams(), + Self::FreeVar(_unbound) => todo!(), + Self::Range(t) + | Self::Iter(t) + | Self::Ref(t) + | Self::RefMut(t) + | Self::Option(t) + | Self::OptionMut(t) => vec![TyParam::t(*t.clone())], + Self::Array{ t, len } => vec![TyParam::t(*t.clone()), len.clone()], + Self::Dict{ k, v } => vec![TyParam::t(*k.clone()), TyParam::t(*v.clone())], + Self::And(param_ts) + | Self::Or(param_ts) + | Self::Not(param_ts) + | Self::Tuple(param_ts) => param_ts.iter().map(|t| TyParam::t(t.clone())).collect(), + Self::Subr(subr) => if let Some(self_t) = subr.kind.self_t() { + [ + vec![TyParam::t(self_t.clone())], + subr.non_default_params.iter().map(|t| TyParam::t(t.ty.clone())).collect(), + subr.default_params.iter().map(|t| TyParam::t(t.ty.clone())).collect(), + ].concat() + } else { + [ + subr.non_default_params.iter().map(|t| TyParam::t(t.ty.clone())).collect::>(), + subr.default_params.iter().map(|t| TyParam::t(t.ty.clone())).collect(), + ].concat() + }, + Self::Callable{ param_ts: _, .. } => todo!(), + Self::Poly{ params, .. } + | Self::PolyQVar{ params, .. } => params.clone(), + _ => vec![], + } + } + + pub const fn self_t(&self) -> Option<&Type> { + match self { + Self::Subr(SubrType{ kind: + SubrKind::FuncMethod(self_t) | SubrKind::ProcMethod{ before: self_t, .. }, + .. + }) => Some(self_t), + _ => None, + } + } + + pub const fn non_default_params(&self) -> Option<&Vec> { + match self { + Self::Subr(SubrType{ non_default_params, .. }) => Some(non_default_params), + Self::Callable{ param_ts: _, .. } => todo!(), + _ => None, + } + } + + pub const fn default_params(&self) -> Option<&Vec> { + match self { + Self::Subr(SubrType{ default_params, .. }) => Some(default_params), + _ => None, + } + } + + pub const fn return_t(&self) -> Option<&Type> { + match self { + Self::Subr(SubrType{ return_t, .. }) + | Self::Callable{ return_t, .. } => Some(return_t), + _ => None, + } + } + + pub fn mut_return_t(&mut self) -> Option<&mut Type> { + match self { + Self::Subr(SubrType{ return_t, .. }) + | Self::Callable{ return_t, .. } => Some(return_t), + _ => None, + } + } +} + +pub mod type_constrs { + use crate::ty::*; + + #[inline] + pub const fn param_t(name: &'static str, ty: Type) -> ParamTy { + ParamTy::new(Some(Str::ever(name)), ty) + } + + #[inline] + pub const fn anon(ty: Type) -> ParamTy { ParamTy::anonymous(ty) } + + #[inline] + pub fn mono>(name: S) -> Type { Type::mono(name) } + + #[inline] + pub fn mono_q>(name: S) -> Type { Type::mono_q(name) } + + #[inline] + pub fn poly>(name: S, params: Vec) -> Type { Type::poly(name, params) } + + #[inline] + pub fn poly_q>(name: S, params: Vec) -> Type { Type::poly_q(name, params) } + + #[inline] + pub fn func(non_default_params: Vec, default_params: Vec, ret: Type) -> Type { + Type::func(non_default_params, default_params, ret) + } + + #[inline] + pub fn proc(non_default_params: Vec, default_params: Vec, ret: Type) -> Type { + Type::proc(non_default_params, default_params, ret) + } + + #[inline] + pub fn nd_func(params: Vec, ret: Type) -> Type { Type::nd_func(params, ret) } + + #[inline] + pub fn nd_proc(params: Vec, ret: Type) -> Type { Type::nd_proc(params, ret) } + + #[inline] + pub fn fn0_met(self_t: Type, return_t: Type) -> Type { Type::fn0_met(self_t, return_t) } + + #[inline] + pub fn fn1_met(self_t: Type, input_t: Type, return_t: Type) -> Type { Type::fn1_met(self_t, input_t, return_t) } + + #[inline] + pub fn quant(unbound_t: Type, bounds: Set) -> Type { Type::quantified(unbound_t, bounds) } + + #[inline] + pub fn instance(name: Str, t: Type) -> TyBound { TyBound::instance(name, t) } + + #[inline] + pub fn static_instance(name: &'static str, t: Type) -> TyBound { TyBound::static_instance(name, t) } + + #[inline] + pub fn subtype(sub: Type, sup: Type) -> TyBound { TyBound::subtype(sub, sup) } + + #[inline] + pub fn mono_q_tp>(name: S) -> TyParam { TyParam::mono_q(name) } + + #[inline] + pub fn mono_tp>(name: S) -> TyParam { TyParam::mono(name) } + + #[inline] + pub fn ty_tp(t: Type) -> TyParam { TyParam::t(t) } + + #[inline] + pub fn value>(v: V) -> TyParam { TyParam::value(v) } +} + +/// バイトコード命令で、in-place型付けをするオブジェクト +/// MaybeBigがついている場合、固定長でない可能性あり(実行時検査が必要) +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum TypeCode { + Int32 = 1, + Nat64, + Float64, + Bool, + Str, + StrMut, + Array, // 要素数は検査済みなので、気にする必要はない + ArrayMut, + // Dict, + Func, + Proc, + MaybeBigInt, + MaybeBigNat, + MaybeBigFloat, + MaybeBigStr, + Other, + Illegal, +} + +// TODO: +impl From<&Type> for TypeCode { + fn from(arg: &Type) -> Self { + match arg { + Type::Int => Self::Int32, + Type::Nat => Self::Nat64, + Type::Float => Self::Float64, + Type::Bool => Self::Bool, + Type::Str => Self::Str, + Type::Array{ .. } => Self::Array, + Type::FuncCommon => Self::Func, + Type::ProcCommon => Self::Proc, + _ => Self::Other, + } + } +} + +/// バイトコード命令で、in-place型付けをするオブジェクトペア +/// とりあえずは必要性の高いペアから登録する +/// 全ての式の型が確認されているので、戻り値の型は不要 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum TypePair { + IntInt = 1, + IntNat, + IntFloat, + IntStr, + IntBool, + IntArray, + IntFunc, + IntProc, + NatInt, + NatNat, + NatFloat, + NatStr, + NatBool, + NatArray, + NatFunc, + NatProc, + FloatInt, + FloatNat, + FloatFloat, + FloatStr, + FloatBool, + FloatArray, + FloatFunc, + FloatProc, + BoolInt, + BoolNat, + BoolFloat, + BoolStr, + BoolBool, + BoolArray, + BoolFunc, + BoolProc, + StrInt, + StrNat, + StrFloat, + StrBool, + StrStr, + StrArray, + StrFunc, + StrProc, + // 要素数は検査済みなので、気にする必要はない + ArrayInt, + ArrayNat, + ArrayFloat, + ArrayStr, + ArrayBool, + ArrayArray, + ArrayFunc, + ArrayProc, + FuncInt, + FuncNat, + FuncFloat, + FuncStr, + FuncBool, + FuncArray, + FuncFunc, + FuncProc, + ProcInt, + ProcNat, + ProcFloat, + ProcStr, + ProcBool, + ProcArray, + ProcProc, + Others, + Illegals, +} + +impl From for TypePair { + fn from(code: u8) -> Self { + match code { + 1 => Self::IntInt, + 2 => Self::IntNat, + 3 => Self::IntFloat, + 4 => Self::IntStr, + 5 => Self::IntBool, + 6 => Self::IntArray, + 7 => Self::IntFunc, + 8 => Self::IntProc, + 9 => Self::NatInt, + 10 => Self::NatNat, + 11 => Self::NatFloat, + 12 => Self::NatStr, + 13 => Self::NatBool, + 14 => Self::NatArray, + 15 => Self::NatFunc, + 16 => Self::NatProc, + 17 => Self::FloatInt, + 18 => Self::FloatNat, + 19 => Self::FloatFloat, + 20 => Self::FloatStr, + 21 => Self::FloatBool, + 22 => Self::FloatArray, + 23 => Self::FloatFunc, + 24 => Self::FloatProc, + 25 => Self::BoolInt, + 26 => Self::BoolNat, + 27 => Self::BoolFloat, + 28 => Self::BoolStr, + 29 => Self::BoolBool, + 30 => Self::BoolArray, + 31 => Self::BoolFunc, + 32 => Self::BoolProc, + 33 => Self::StrInt, + 34 => Self::StrNat, + 35 => Self::StrFloat, + 36 => Self::StrBool, + 37 => Self::StrStr, + 38 => Self::StrArray, + 39 => Self::StrFunc, + 40 => Self::StrProc, + // 要素数は検査済みなので、気にする必要はない + 41 => Self::ArrayInt, + 42 => Self::ArrayNat, + 43 => Self::ArrayFloat, + 44 => Self::ArrayStr, + 45 => Self::ArrayBool, + 46 => Self::ArrayArray, + 47 => Self::ArrayFunc, + 48 => Self::ArrayProc, + 49 => Self::FuncInt, + 50 => Self::FuncNat, + 51 => Self::FuncFloat, + 52 => Self::FuncStr, + 53 => Self::FuncBool, + 54 => Self::FuncArray, + 55 => Self::FuncFunc, + 56 => Self::FuncProc, + 57 => Self::ProcInt, + 58 => Self::ProcNat, + 59 => Self::ProcFloat, + 60 => Self::ProcStr, + 61 => Self::ProcBool, + 62 => Self::ProcArray, + 63 => Self::ProcProc, + 64 => Self::Others, + 65 | _ => Self::Illegals, + } + } +} + +// TODO: +impl TypePair { + pub fn new(lhs: &Type, rhs: &Type) -> Self { + match (lhs, rhs) { + (Type::Int, Type::Int) => Self::IntInt, + (Type::Int, Type::Nat) => Self::IntNat, + (Type::Int, Type::Float) => Self::IntFloat, + (Type::Int, Type::Str) => Self::IntStr, + (Type::Int, Type::Bool) => Self::IntBool, + (Type::Int, Type::Array{ .. }) => Self::IntArray, + (Type::Int, Type::FuncCommon) => Self::IntFunc, + (Type::Int, Type::ProcCommon) => Self::IntProc, + (Type::Nat, Type::Int) => Self::NatInt, + (Type::Nat, Type::Nat) => Self::NatNat, + (Type::Nat, Type::Float) => Self::NatFloat, + (Type::Nat, Type::Str) => Self::NatStr, + (Type::Nat, Type::Bool) => Self::NatBool, + (Type::Nat, Type::Array{ .. }) => Self::NatArray, + (Type::Nat, Type::FuncCommon) => Self::NatFunc, + (Type::Nat, Type::ProcCommon) => Self::NatProc, + (Type::Float, Type::Int) => Self::FloatInt, + (Type::Float, Type::Nat) => Self::FloatNat, + (Type::Float, Type::Float) => Self::FloatFloat, + (Type::Float, Type::Str) => Self::FloatStr, + (Type::Float, Type::Bool) => Self::FloatBool, + (Type::Float, Type::Array{ .. }) => Self::FloatArray, + (Type::Float, Type::FuncCommon) => Self::FloatFunc, + (Type::Float, Type::ProcCommon) => Self::FloatProc, + (Type::Bool, Type::Int) => Self::BoolInt, + (Type::Bool, Type::Nat) => Self::BoolNat, + (Type::Bool, Type::Float) => Self::BoolFloat, + (Type::Bool, Type::Str) => Self::BoolStr, + (Type::Bool, Type::Bool) => Self::BoolBool, + (Type::Bool, Type::Array{ .. }) => Self::BoolArray, + (Type::Bool, Type::FuncCommon) => Self::BoolFunc, + (Type::Bool, Type::ProcCommon) => Self::BoolProc, + (Type::Str, Type::Int) => Self::StrInt, + (Type::Str, Type::Nat) => Self::StrNat, + (Type::Str, Type::Float) => Self::StrFloat, + (Type::Str, Type::Bool) => Self::StrBool, + (Type::Str, Type::Str) => Self::StrStr, + (Type::Str, Type::Array{ .. }) => Self::StrArray, + (Type::Str, Type::FuncCommon) => Self::StrFunc, + (Type::Str, Type::ProcCommon) => Self::StrProc, + // 要素数は検査済みなので、気にする必要はない + (Type::Array{ .. }, Type::Int) => Self::ArrayInt, + (Type::Array{ .. }, Type::Nat) => Self::ArrayNat, + (Type::Array{ .. }, Type::Float) => Self::ArrayFloat, + (Type::Array{ .. }, Type::Str) => Self::ArrayStr, + (Type::Array{ .. }, Type::Bool) => Self::ArrayBool, + (Type::Array{ .. }, Type::Array{ .. }) => Self::ArrayArray, + (Type::Array{ .. }, Type::FuncCommon) => Self::ArrayFunc, + (Type::Array{ .. }, Type::ProcCommon) => Self::ArrayProc, + (Type::FuncCommon, Type::Int) => Self::FuncInt, + (Type::FuncCommon, Type::Nat) => Self::FuncNat, + (Type::FuncCommon, Type::Float) => Self::FuncFloat, + (Type::FuncCommon, Type::Str) => Self::FuncStr, + (Type::FuncCommon, Type::Bool) => Self::FuncBool, + (Type::FuncCommon, Type::Array{ .. }) => Self::FuncArray, + (Type::FuncCommon, Type::FuncCommon) => Self::FuncFunc, + (Type::FuncCommon, Type::ProcCommon) => Self::FuncProc, + (Type::ProcCommon, Type::Int) => Self::ProcInt, + (Type::ProcCommon, Type::Nat) => Self::ProcNat, + (Type::ProcCommon, Type::Float) => Self::ProcFloat, + (Type::ProcCommon, Type::Str) => Self::ProcStr, + (Type::ProcCommon, Type::Bool) => Self::ProcBool, + (Type::ProcCommon, Type::Array{ .. }) => Self::ProcArray, + (Type::ProcCommon, Type::ProcCommon) => Self::ProcProc, + (_, _) => Self::Others, + } + } +} diff --git a/src/common/value.rs b/src/common/value.rs new file mode 100644 index 00000000..2ceee5d6 --- /dev/null +++ b/src/common/value.rs @@ -0,0 +1,448 @@ +//! defines `ValueObj` (used in the compiler, VM). +//! +//! コンパイラ、VM等で使われる(データも保持した)値オブジェクトを定義する +use std::fmt; +use std::hash::{Hash, Hasher}; +use std::ops::Neg; +use std::rc::Rc; +use std::cmp::Ordering; + +use crate::{Str, RcArray}; +use crate::set; +use crate::{fmt_iter, switch_lang, impl_display_from_debug}; +use crate::codeobj::CodeObj; +use crate::serialize::*; +use crate::traits::HasType; +use crate::ty::{Type, TyParam, Predicate, ConstObj, fresh_varname}; + +/// 値オブジェクト +/// コンパイル時評価ができ、シリアライズも可能 +#[derive(Clone, PartialEq)] +pub enum ValueObj { + Int(i32), + Nat(u64), + Float(f64), + Str(Str), + True, + False, + Array(Rc<[ValueObj]>), + Dict(Rc<[(ValueObj, ValueObj)]>), + Code(Box), + None, + Ellipsis, + NotImplemented, + NegInf, + Inf, + Illegal, // to avoid conversions with TryFrom +} + +impl fmt::Debug for ValueObj { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Int(i) => write!(f, "{i}"), + Self::Nat(n) => write!(f, "{n}"), + Self::Float(fl) => { + // In Rust, .0 is shown omitted. + if fl.fract() < 1e-10 { write!(f, "{fl:.1}") } + else { write!(f, "{fl}") } + }, + Self::Str(s) => write!(f, "\"{s}\""), + Self::True => write!(f, "True"), + Self::False => write!(f, "False"), + Self::Array(arr) => write!(f, "[{}]", fmt_iter(arr.iter())), + Self::Dict(dict) => { + let mut s = "".to_string(); + for (k, v) in dict.iter() { + s += &format!("{k}: {v}, "); + } + s.pop(); + s.pop(); + write!(f, "[{s}]") + }, + Self::Code(code) => write!(f, "{code}"), + Self::None => write!(f, "None"), + Self::Ellipsis => write!(f, "Ellipsis"), + Self::NotImplemented => write!(f, "NotImplemented"), + Self::NegInf => write!(f, "-Inf"), + Self::Inf => write!(f, "Inf"), + Self::Illegal => write!(f, ""), + } + } +} + +impl_display_from_debug!(ValueObj); + +impl Eq for ValueObj {} + +impl Neg for ValueObj { + type Output = Self; + #[inline] + fn neg(self) -> Self { + match self { + Self::Int(i) => Self::Int(-i), + Self::Nat(n) => Self::Int(-(n as i32)), + Self::Float(fl) => Self::Float(-fl), + Self::Inf => Self::NegInf, + Self::NegInf => Self::Inf, + other => panic!("cannot negate {other}"), + } + } +} + +// FIXME: +impl Hash for ValueObj { + fn hash(&self, state: &mut H) { + match self { + Self::Int(i) => i.hash(state), + Self::Nat(n) => n.hash(state), + // TODO: + Self::Float(f) => f.to_bits().hash(state), + Self::Str(s) => s.hash(state), + Self::True => true.hash(state), + Self::False => false.hash(state), + Self::Array(arr) => arr.hash(state), + Self::Dict(dict) => dict.hash(state), + Self::Code(code) => code.hash(state), + Self::None => { + "literal".hash(state); + "None".hash(state) + } + Self::Ellipsis => { + "literal".hash(state); + "Ellipsis".hash(state) + } + Self::NotImplemented => { + "literal".hash(state); + "NotImplemented".hash(state) + } + Self::NegInf => { + "literal".hash(state); + "NegInf".hash(state) + } + Self::Inf => { + "literal".hash(state); + "Inf".hash(state) + } + Self::Illegal => { + "literal".hash(state); + "illegal".hash(state) + } + } + } +} + +impl From for ValueObj { + fn from(item: i32) -> Self { ValueObj::Int(item) } +} + +impl From for ValueObj { + fn from(item: u64) -> Self { ValueObj::Nat(item) } +} + +impl From for ValueObj { + fn from(item: usize) -> Self { ValueObj::Nat(item as u64) } +} + +impl From for ValueObj { + fn from(item: f64) -> Self { ValueObj::Float(item) } +} + +impl From<&str> for ValueObj { + fn from(item: &str) -> Self { ValueObj::Str(Str::rc(item)) } +} + +impl From for ValueObj { + fn from(item: Str) -> Self { ValueObj::Str(item) } +} + +impl From for ValueObj { + fn from(item: bool) -> Self { if item { ValueObj::True } else { ValueObj::False } } +} + +impl From for ValueObj { + fn from(item: CodeObj) -> Self { ValueObj::Code(Box::new(item)) } +} + +impl From> for ValueObj { + fn from(item: Vec) -> Self { ValueObj::Array(RcArray::from(&item[..])) } +} + +impl TryFrom<&ValueObj> for f64 { + type Error = (); + fn try_from(val: &ValueObj) -> Result { + match val { + ValueObj::Int(i) => Ok(*i as f64), + ValueObj::Nat(n) => Ok(*n as f64), + ValueObj::Float(f) => Ok(*f), + ValueObj::Inf => Ok(f64::INFINITY), + ValueObj::NegInf => Ok(f64::NEG_INFINITY), + _ => Err(()), + } + } +} + +impl HasType for ValueObj { + fn ref_t(&self) -> &Type { panic!("cannot get reference of the const") } + /// その要素だけの集合型を返す、クラスが欲しい場合は.classで + #[inline] + fn t(&self) -> Type { + let name = Str::from(fresh_varname()); + let pred = Predicate::eq(name.clone(), TyParam::ConstObj(ConstObj::Value(self.clone()))); + Type::refinement(name, self.class(), set!{pred}) + } + fn signature_t(&self) -> Option<&Type> { None } +} + +impl ValueObj { + pub const fn is_num(&self) -> bool { + matches!(self, Self::Int(_) | Self::Nat(_) | Self::Float(_)) + } + + pub fn from_str(t: Type, content: Str) -> Self { + match t { + Type::Int => Self::Int(content.replace("_", "").parse::().unwrap()), + Type::Nat => Self::Nat(content.replace("_", "").parse::().unwrap()), + Type::Float => Self::Float(content.replace("_", "").parse::().unwrap()), + Type::Str => { + if &content[..] == "\"\"" { + Self::Str(Str::from("")) + } else { + let replaced = content.trim_start_matches('\"').trim_end_matches('\"'); + Self::Str(Str::rc(replaced)) + } + }, + Type::Bool => { + if &content[..] == "True" { Self::True } else { Self::False } + }, + Type::NoneType => Self::None, + Type::Ellipsis => Self::Ellipsis, + Type::NotImplemented => Self::NotImplemented, + Type::Inf => Self::Inf, + Type::NegInf => Self::NegInf, + _ => todo!("{t} {content}"), + } + } + + pub fn into_bytes(self) -> Vec { + match self { + Self::Int(i) => { + [vec![DataTypePrefix::Int32 as u8], i32::from(i).to_le_bytes().to_vec()].concat() + }, + // TODO: Natとしてシリアライズ + Self::Nat(n) => { + [vec![DataTypePrefix::Int32 as u8], i32::from(n as i32).to_le_bytes().to_vec()].concat() + }, + Self::Float(f) => { + [vec![DataTypePrefix::BinFloat as u8], f64::from(f).to_le_bytes().to_vec()].concat() + }, + Self::Str(s) => { str_into_bytes(s, false) }, + Self::True => vec![DataTypePrefix::True as u8], + Self::False => vec![DataTypePrefix::False as u8], + // TODO: SmallTuple + Self::Array(arr) => { + let mut bytes = Vec::with_capacity(arr.len()); + bytes.push(DataTypePrefix::Tuple as u8); + bytes.append(&mut (arr.len() as u32).to_le_bytes().to_vec()); + for obj in arr.to_vec().into_iter() { + bytes.append(&mut obj.into_bytes()); + } + bytes + }, + Self::None => { vec![DataTypePrefix::None as u8] }, + Self::Code(c) => c.into_bytes(3425), + // Dict + other => { panic!("{}", switch_lang!( + format!("this object cannot be serialized: {other}"), + format!("このオブジェクトはシリアライズできません: {other}") + )) }, + } + } + + pub fn class(&self) -> Type { + match self { + Self::Int(_) => Type::Int, + Self::Nat(_) => Type::Nat, + Self::Float(_) => Type::Float, + Self::Str(_) => Type::Str, + Self::True | Self::False => Type::Bool, + // TODO: + Self::Array(arr) =>Type::array( + arr.iter().next().unwrap().class(), + TyParam::value(arr.len()) + ), + Self::Dict(_dict) => todo!(), + Self::Code(_) => Type::Code, + Self::None => Type::NoneType, + Self::Ellipsis => Type::Ellipsis, + Self::NotImplemented => Type::NotImplemented, + Self::Inf => Type::Inf, + Self::NegInf => Type::NegInf, + Self::Illegal => todo!(), + } + } + + pub fn try_cmp(&self, other: &Self) -> Option { + match (self, other) { + (l, r) if l.is_num() && r.is_num() => { + f64::try_from(l).unwrap().partial_cmp(&f64::try_from(r).unwrap()) + } + (Self::Inf, n) + | (n, Self::NegInf) if n.is_num() => Some(Ordering::Greater), + (n, Self::Inf) + | (Self::NegInf, n) if n.is_num() => Some(Ordering::Less), + (Self::NegInf, Self::Inf) => Some(Ordering::Less), + (Self::Inf, Self::NegInf) => Some(Ordering::Greater), + // REVIEW: 等しいとみなしてよいのか? + (Self::Inf, Self::Inf) | (Self::NegInf, Self::NegInf) => Some(Ordering::Equal), + /* (Self::PlusEpsilon(l), r) => l.try_cmp(r) + .map(|o| if matches!(o, Ordering::Equal) { Ordering::Less } else { o }), + (l, Self::PlusEpsilon(r)) => l.try_cmp(r) + .map(|o| if matches!(o, Ordering::Equal) { Ordering::Greater } else { o }), + */ + // TODO: cmp with str + (_s, _o) => None, + } + } + + // REVIEW: allow_divergenceオプションを付けるべきか? + pub fn try_add(self, other: Self) -> Option { + match (self, other) { + (Self::Int(l), Self::Int(r)) => Some(Self::Int(l + r)), + (Self::Nat(l), Self::Nat(r)) => Some(Self::Nat(l + r)), + (Self::Float(l), Self::Float(r)) => Some(Self::Float(l + r)), + (Self::Int(l), Self::Nat(r)) => Some(Self::Int(l + r as i32)), + (Self::Nat(l), Self::Int(r)) => Some(Self::Int(l as i32 + r)), + (Self::Float(l), Self::Nat(r)) => Some(Self::Float(l + r as f64)), + (Self::Nat(l), Self::Float(r)) => Some(Self::Float(l as f64 + r)), + (Self::Float(l), Self::Int(r)) => Some(Self::Float(l + r as f64)), + (Self::Int(l), Self::Float(r)) => Some(Self::Float(l as f64 + r)), + (Self::Str(l), Self::Str(r)) => Some(Self::Str(Str::from(format!("{}{}", l, r)))), + (inf @ (Self::Inf | Self::NegInf), _) + | (_, inf @ (Self::Inf | Self::NegInf)) => Some(inf), + _ => None, + } + } + + pub fn try_sub(self, other: Self) -> Option { + match (self, other) { + (Self::Int(l), Self::Int(r)) => Some(Self::Int(l - r)), + (Self::Nat(l), Self::Nat(r)) => Some(Self::Nat(l - r)), + (Self::Float(l), Self::Float(r)) => Some(Self::Float(l - r)), + (Self::Int(l), Self::Nat(r)) => Some(Self::Int(l - r as i32)), + (Self::Nat(l), Self::Int(r)) => Some(Self::Int(l as i32 - r)), + (Self::Float(l), Self::Nat(r)) => Some(Self::Float(l - r as f64)), + (Self::Nat(l), Self::Float(r)) => Some(Self::Float(l as f64 - r)), + (Self::Float(l), Self::Int(r)) => Some(Self::Float(l - r as f64)), + (Self::Int(l), Self::Float(r)) => Some(Self::Float(l as f64 - r)), + (inf @ (Self::Inf | Self::NegInf), other) + | (other, inf @ (Self::Inf | Self::NegInf)) + if other != Self::Inf && other != Self::NegInf => Some(inf), + _ => None, + } + } + + pub fn try_mul(self, other: Self) -> Option { + match (self, other) { + (Self::Int(l), Self::Int(r)) => Some(Self::Int(l * r)), + (Self::Nat(l), Self::Nat(r)) => Some(Self::Nat(l * r)), + (Self::Float(l), Self::Float(r)) => Some(Self::Float(l * r)), + (Self::Int(l), Self::Nat(r)) => Some(Self::Int(l * r as i32)), + (Self::Nat(l), Self::Int(r)) => Some(Self::Int(l as i32 * r)), + (Self::Float(l), Self::Nat(r)) => Some(Self::Float(l * r as f64)), + (Self::Nat(l), Self::Float(r)) => Some(Self::Float(l as f64 * r)), + (Self::Float(l), Self::Int(r)) => Some(Self::Float(l * r as f64)), + (Self::Int(l), Self::Float(r)) => Some(Self::Float(l as f64 * r)), + (Self::Str(l), Self::Nat(r)) => Some(Self::Str(Str::from(l.repeat(r as usize)))), + (inf @ (Self::Inf | Self::NegInf), _) + | (_, inf @ (Self::Inf | Self::NegInf)) => Some(inf), + _ => None, + } + } + + pub fn try_div(self, other: Self) -> Option { + match (self, other) { + (Self::Int(l), Self::Int(r)) => Some(Self::Int(l / r)), + (Self::Nat(l), Self::Nat(r)) => Some(Self::Nat(l / r)), + (Self::Float(l), Self::Float(r)) => Some(Self::Float(l / r)), + (Self::Int(l), Self::Nat(r)) => Some(Self::Int(l / r as i32)), + (Self::Nat(l), Self::Int(r)) => Some(Self::Int(l as i32 / r)), + (Self::Float(l), Self::Nat(r)) => Some(Self::Float(l / r as f64)), + (Self::Nat(l), Self::Float(r)) => Some(Self::Float(l as f64 / r)), + (Self::Float(l), Self::Int(r)) => Some(Self::Float(l / r as f64)), + (Self::Int(l), Self::Float(r)) => Some(Self::Float(l as f64 / r)), + // TODO: x/±Inf = 0 + _ => None, + } + } + + pub fn try_gt(self, other: Self) -> Option { + match (self, other) { + (Self::Int(l), Self::Int(r)) => Some(Self::from(l > r)), + (Self::Nat(l), Self::Nat(r)) => Some(Self::from(l > r)), + (Self::Float(l), Self::Float(r)) => Some(Self::from(l > r)), + (Self::Int(l), Self::Nat(r)) => Some(Self::from(l > r as i32)), + (Self::Nat(l), Self::Int(r)) => Some(Self::from(l as i32 > r)), + (Self::Float(l), Self::Nat(r)) => Some(Self::from(l > r as f64)), + (Self::Nat(l), Self::Float(r)) => Some(Self::from(l as f64 > r)), + (Self::Float(l), Self::Int(r)) => Some(Self::from(l > r as f64)), + (Self::Int(l), Self::Float(r)) => Some(Self::from(l as f64 > r)), + _ => None, + } + } + + pub fn try_ge(self, other: Self) -> Option { + match (self, other) { + (Self::Int(l), Self::Int(r)) => Some(Self::from(l >= r)), + (Self::Nat(l), Self::Nat(r)) => Some(Self::from(l >= r)), + (Self::Float(l), Self::Float(r)) => Some(Self::from(l >= r)), + (Self::Int(l), Self::Nat(r)) => Some(Self::from(l >= r as i32)), + (Self::Nat(l), Self::Int(r)) => Some(Self::from(l as i32 >= r)), + (Self::Float(l), Self::Nat(r)) => Some(Self::from(l >= r as f64)), + (Self::Nat(l), Self::Float(r)) => Some(Self::from(l as f64 >= r)), + (Self::Float(l), Self::Int(r)) => Some(Self::from(l >= r as f64)), + (Self::Int(l), Self::Float(r)) => Some(Self::from(l as f64 >= r)), + _ => None, + } + } + + pub fn try_eq(self, other: Self) -> Option { + match (self, other) { + (Self::Int(l), Self::Int(r)) => Some(Self::from(l == r)), + (Self::Nat(l), Self::Nat(r)) => Some(Self::from(l == r)), + (Self::Float(l), Self::Float(r)) => Some(Self::from(l == r)), + (Self::Int(l), Self::Nat(r)) => Some(Self::from(l == r as i32)), + (Self::Nat(l), Self::Int(r)) => Some(Self::from(l as i32 == r)), + (Self::Float(l), Self::Nat(r)) => Some(Self::from(l == r as f64)), + (Self::Nat(l), Self::Float(r)) => Some(Self::from(l as f64 == r)), + (Self::Float(l), Self::Int(r)) => Some(Self::from(l == r as f64)), + (Self::Int(l), Self::Float(r)) => Some(Self::from(l as f64 == r)), + (Self::Str(l), Self::Str(r)) => Some(Self::from(l == r)), + (Self::True, Self::True) | + (Self::False, Self::False) => Some(Self::True), + (Self::True, Self::False) | + (Self::False, Self::True) => Some(Self::False), + // TODO: + _ => None, + } + } + + pub fn try_ne(self, other: Self) -> Option { + match (self, other) { + (Self::Int(l), Self::Int(r)) => Some(Self::from(l != r)), + (Self::Nat(l), Self::Nat(r)) => Some(Self::from(l != r)), + (Self::Float(l), Self::Float(r)) => Some(Self::from(l != r)), + (Self::Int(l), Self::Nat(r)) => Some(Self::from(l != r as i32)), + (Self::Nat(l), Self::Int(r)) => Some(Self::from(l as i32 != r)), + (Self::Float(l), Self::Nat(r)) => Some(Self::from(l != r as f64)), + (Self::Nat(l), Self::Float(r)) => Some(Self::from(l as f64 != r)), + (Self::Float(l), Self::Int(r)) => Some(Self::from(l != r as f64)), + (Self::Int(l), Self::Float(r)) => Some(Self::from(l as f64 != r)), + (Self::Str(l), Self::Str(r)) => Some(Self::from(l != r)), + (Self::True, Self::True) | + (Self::False, Self::False) => Some(Self::False), + (Self::True, Self::False) | + (Self::False, Self::True) => Some(Self::True), + _ => None, + } + } +} diff --git a/src/compiler/.gitignore b/src/compiler/.gitignore new file mode 100644 index 00000000..efb24520 --- /dev/null +++ b/src/compiler/.gitignore @@ -0,0 +1,2 @@ +/.vscode/ +/target/ \ No newline at end of file diff --git a/src/compiler/Cargo.toml b/src/compiler/Cargo.toml new file mode 100644 index 00000000..ccd2a498 --- /dev/null +++ b/src/compiler/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "compiler" +version = "0.1.0" +description = "Centimetre: the Erg compiler" +authors = ["Shunsuke Shibayama "] +license = "MIT OR Apache-2.0" +edition = "2021" + +[features] +# when "debug" feature is turned on, that of parser will also be turned on. +debug = [ "common/debug", "parser/debug" ] +japanese = [ "common/japanese", "parser/japanese" ] + +[dependencies] +common = { path = "../common" } +parser = { path = "parser" } + +[lib] +path = "lib.rs" + +[[bin]] +name = "cm" +path = "main.rs" diff --git a/src/compiler/README.md b/src/compiler/README.md new file mode 100644 index 00000000..7c085b62 --- /dev/null +++ b/src/compiler/README.md @@ -0,0 +1,3 @@ +# The Erg compiler (codename: Centimetre) + +The overall structure is described in detail in [architecture.md](../../doc/compiler/architecture.md). diff --git a/src/compiler/codegen.rs b/src/compiler/codegen.rs new file mode 100644 index 00000000..9a9e6a14 --- /dev/null +++ b/src/compiler/codegen.rs @@ -0,0 +1,1181 @@ +//! generates `CodeObj` (equivalent to PyCodeObject of CPython) from `AST`. +//! +//! ASTからPythonバイトコード(コードオブジェクト)を生成する +use std::fmt; +use std::process; + +use common::Str; +use common::cache::Cache; +use common::{fn_name_full, enum_unwrap, switch_unreachable, debug_power_assert, log, impl_stream_for_wrapper}; +use common::codeobj::{CodeObj, CodeObjFlags}; +use common::color::{GREEN, RESET}; +use common::config::{ErgConfig, Input}; +use common::error::{Location, MultiErrorDisplay}; +use common::value::ValueObj; +use common::opcode::Opcode; +use Opcode::*; +use common::traits::{HasType, Locational, Stream}; +use common::ty::{TypeCode, TypePair}; + +use parser::token::{Token, TokenKind, TokenCategory}; +use parser::ast::{VarPattern, ParamPattern, Params}; + +use crate::compile::{AccessKind, Name, StoreLoadKind}; +use crate::error::{CompileError, CompileErrors, CompileResult}; +use crate::hir::{Args, Expr, Signature, VarSignature, SubrSignature, DefBody, Accessor, Block, HIR}; +use AccessKind::*; + +fn obj_name(obj: &Expr) -> Option { + match obj { + Expr::Accessor(Accessor::Local(n)) => Some(n.inspect().to_string()), + Expr::Accessor(Accessor::Attr(a)) => + Some(obj_name(&a.obj)? + "." + a.name.inspect()), + Expr::Accessor(Accessor::SelfDot(n)) => Some(format!(".{}", n.inspect())), + _ => None, + } +} + +fn convert_to_python_attr(class: &str, name: Str) -> Str { + match (class, &name[..]) { + ("Array!", "push!") => Str::ever("append"), + ("Complex" | "Real"| "Int" | "Nat" | "Float", "Real") => Str::ever("real"), + ("Complex" | "Real"| "Int" | "Nat" | "Float", "Imag") => Str::ever("imag"), + _ => name, + } +} + +fn escape_attr(class: &str, name: Str) -> Str { + let mut name = convert_to_python_attr(class, name).to_string(); + name = name.replace("!", "__erg_proc__"); + name = name.replace("$", "__erg_shared__"); + Str::rc(&name) +} + +fn convert_to_python_name(name: Str) -> Str { + match &name[..] { + "abs" => Str::ever("abs"), + // assert is implemented in bytecode + "classof" => Str::ever("type"), + "compile" => Str::ever("compile"), + // discard is implemented in bytecode + // for is implemented in bytecode + "id" => Str::ever("id"), + // if is implemented in bytecode + "import" => Str::ever("__import__"), + "input!" => Str::ever("input"), + "log" => Str::ever("print"), // TODO: log != print (prints after executing) + "print!" => Str::ever("print"), + "pyimport" => Str::ever("__import__"), + "quit" | "exit" => Str::ever("quit"), + _ => name, + } +} + +fn escape_name(name: Str) -> Str { + let mut name = convert_to_python_name(name).to_string(); + name = name.replace("!", "__erg_proc__"); + name = name.replace("$", "__erg_shared__"); + Str::rc(&name) +} + +#[derive(Debug, Clone)] +pub struct CodeGenUnit { + pub(crate) id: usize, + pub(crate) codeobj: CodeObj, + pub(crate) stack_len: u32, // the maximum stack size + pub(crate) prev_lineno: usize, + pub(crate) lasti: usize, + pub(crate) prev_lasti: usize, + pub(crate) _refs: Vec, // ref-counted objects +} + +impl PartialEq for CodeGenUnit { + #[inline] + fn eq(&self, other: &Self) -> bool { self.id == other.id } +} + +impl fmt::Display for CodeGenUnit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "CompilerUnit{{\nid: {}\ncode:\n{}\n}}", + self.id, + self.codeobj.code_info() + ) + } +} + +impl CodeGenUnit { + pub fn new, T: Into>( + id: usize, + params: Vec, + filename: S, + name: T, + firstlineno: usize, + ) -> Self { + Self { + id, + codeobj: CodeObj::empty(params, filename, name, firstlineno as u32), + stack_len: 0, + prev_lineno: firstlineno, + lasti: 0, + prev_lasti: 0, + _refs: vec![], + } + } +} + +#[derive(Debug, Clone)] +pub struct CodeGenStack(Vec); + +impl_stream_for_wrapper!(CodeGenStack, CodeGenUnit); + +#[derive(Debug)] +pub struct CodeGenerator { + cfg: ErgConfig, + str_cache: Cache, + unit_size: usize, + units: CodeGenStack, + pub(crate) errs: CompileErrors, +} + +impl CodeGenerator { + pub fn new(cfg: ErgConfig) -> Self { + Self { + cfg, + str_cache: Cache::new(), + unit_size: 0, + units: CodeGenStack::empty(), + errs: CompileErrors::empty(), + } + } + + pub fn clear(&mut self) { + self.units.clear(); + self.errs.clear(); + } + + #[inline] + fn input(&self) -> &Input { &self.cfg.input } + + fn get_cached(&self, s: &str) -> Str { + self.str_cache.get(s) + } + + /// 大抵の場合はモジュールのブロックが返る + #[inline] + fn toplevel_block(&self) -> &CodeGenUnit { + self.units.first().unwrap() + } + + #[inline] + fn cur_block(&self) -> &CodeGenUnit { + self.units.last().unwrap() + } + + #[inline] + fn mut_cur_block(&mut self) -> &mut CodeGenUnit { + self.units.last_mut().unwrap() + } + + #[inline] + fn cur_block_codeobj(&self) -> &CodeObj { + &self.cur_block().codeobj + } + + #[inline] + fn mut_cur_block_codeobj(&mut self) -> &mut CodeObj { + &mut self.mut_cur_block().codeobj + } + + #[inline] + fn toplevel_block_codeobj(&self) -> &CodeObj { + &self.toplevel_block().codeobj + } + + #[inline] + fn edit_code(&mut self, idx: usize, code: usize) { + *self.mut_cur_block_codeobj().code.get_mut(idx).unwrap() = code as u8; + } + + fn write_instr(&mut self, code: Opcode) { + self.mut_cur_block_codeobj().code.push(code as u8); + self.mut_cur_block().lasti += 1; + // log!("wrote: {}", code); + } + + fn write_arg(&mut self, code: u8) { + self.mut_cur_block_codeobj().code.push(code); + self.mut_cur_block().lasti += 1; + // log!("wrote: {}", code); + } + + fn stack_inc(&mut self) { + self.mut_cur_block().stack_len += 1; + if self.cur_block().stack_len > self.cur_block_codeobj().stacksize { + self.mut_cur_block_codeobj().stacksize = self.cur_block().stack_len; + } + } + + fn stack_dec(&mut self) { + if self.cur_block().stack_len == 0 { + println!("current block: {}", self.cur_block()); + self.crash("the stack size becomes -1"); + } else { + self.mut_cur_block().stack_len -= 1; + } + } + + fn stack_inc_n(&mut self, n: usize) { + self.mut_cur_block().stack_len += n as u32; + if self.cur_block().stack_len > self.cur_block_codeobj().stacksize { + self.mut_cur_block_codeobj().stacksize = self.cur_block().stack_len; + } + } + + fn stack_dec_n(&mut self, n: usize) { + if n > 0 && self.cur_block().stack_len == 0 { + self.crash("the stack size becomes -1"); + } else { + self.mut_cur_block().stack_len -= n as u32; + } + } + + fn emit_load_const>(&mut self, cons: C) { + let cons = cons.into(); + let idx = self + .mut_cur_block_codeobj() + .consts + .iter() + .position(|c| c == &cons) + .unwrap_or_else(|| { + self.mut_cur_block_codeobj().consts.push(cons); + self.mut_cur_block_codeobj().consts.len() - 1 + }); + self.write_instr(Opcode::LOAD_CONST); + self.write_arg(idx as u8); + self.stack_inc(); + } + + fn local_search(&self, name: &str, acc_kind: AccessKind) -> Option { + let current_is_toplevel = self.cur_block() == self.toplevel_block(); + if let Some(idx) = self + .cur_block_codeobj() + .names + .iter() + .position(|n| &**n == name) { + if current_is_toplevel || !acc_kind.is_local() { Some(Name::local(idx)) } + else { Some(Name::global(idx)) } + } else if let Some(idx) = self + .cur_block_codeobj() + .varnames + .iter() + .position(|v| &**v == name) { + if current_is_toplevel { Some(Name::local(idx)) } + else { Some(Name::fast(idx)) } + } else if let Some(idx) = self + .cur_block_codeobj() + .freevars + .iter() + .position(|f| &**f == name) { + Some(Name::deref(idx)) + } else { + None + } + } + + // local_searchで見つからなかった変数を探索する + fn deep_search(&mut self, name: &str) -> Option { + // search_name()を実行した後なのでcur_blockはskipする + for (nth_from_toplevel, block) in self.units.iter_mut().enumerate().rev().skip(1) { + let block_is_toplevel = nth_from_toplevel == 0; + if let Some(_) = block.codeobj.cellvars.iter().position(|c| &**c == name) { + return Some(StoreLoadKind::Deref) + } else if let Some(idx) = block.codeobj.varnames.iter().position(|v| &**v == name) { + if block_is_toplevel { + return Some(StoreLoadKind::Global) + } else { + // the outer scope variable + let cellvar_name = block.codeobj.varnames.get(idx).unwrap().clone(); + block.codeobj.cellvars.push(cellvar_name); + return Some(StoreLoadKind::Deref) + } + } + if block_is_toplevel { + if let Some(_) = block.codeobj.names.iter().position(|n| &**n == name) { + return Some(StoreLoadKind::Global) + } + } + } + // 見つからなかった変数(前方参照変数など)はグローバル + Some(StoreLoadKind::Global) + } + + fn register_name(&mut self, name: Str) -> Name { + let current_is_toplevel = self.cur_block() == self.toplevel_block(); + let name = escape_name(name); + match self.deep_search(&name) { + Some(st @ (StoreLoadKind::Local | StoreLoadKind::Global)) => { + self.mut_cur_block_codeobj().names.push(name); + Name::new(st, self.cur_block_codeobj().names.len() - 1) + } + Some(StoreLoadKind::Deref) => { + self.mut_cur_block_codeobj().freevars.push(name); + Name::deref(self.cur_block_codeobj().freevars.len() - 1) + } + None => { + // new variable + if current_is_toplevel { + self.mut_cur_block_codeobj().names.push(name); + Name::local(self.cur_block_codeobj().names.len() - 1) + } else { + self.mut_cur_block_codeobj().varnames.push(name); + Name::fast(self.cur_block_codeobj().varnames.len() - 1) + } + } + Some(_) => { + switch_unreachable!() + } + } + } + + fn register_attr(&mut self, class: &str, name: Str) -> Name { + let name = Str::rc(name.split(".").last().unwrap()); + let name = escape_attr(class, name); + self.mut_cur_block_codeobj().names.push(name); + Name::local(self.cur_block_codeobj().names.len() - 1) + } + + fn register_method(&mut self, class: &str, name: Str) -> Name { + let name = Str::rc(name.split(".").last().unwrap()); + let name = escape_attr(class, name); + self.mut_cur_block_codeobj().names.push(name); + Name::local(self.cur_block_codeobj().names.len() - 1) + } + + fn emit_load_name_instr(&mut self, name: Str) -> CompileResult<()> { + let name = self.local_search(&name, Name).unwrap_or_else(|| { + self.register_name(name) + }); + let instr = match name.kind { + StoreLoadKind::Fast | StoreLoadKind::FastConst => Opcode::LOAD_FAST, + StoreLoadKind::Global | StoreLoadKind::GlobalConst => Opcode::LOAD_GLOBAL, + StoreLoadKind::Deref | StoreLoadKind::DerefConst => Opcode::LOAD_DEREF, + StoreLoadKind::Local | StoreLoadKind::LocalConst => Opcode::LOAD_NAME, + }; + self.write_instr(instr); + self.write_arg(name.idx as u8); + self.stack_inc(); + Ok(()) + } + + fn emit_load_attr_instr(&mut self, class: &str, name: Str) -> CompileResult<()> { + let name = self.local_search(&name, Attr).unwrap_or_else(|| { + self.register_attr(class, name) + }); + let instr = match name.kind { + StoreLoadKind::Fast | StoreLoadKind::FastConst => Opcode::LOAD_FAST, + StoreLoadKind::Global | StoreLoadKind::GlobalConst => Opcode::LOAD_GLOBAL, + StoreLoadKind::Deref | StoreLoadKind::DerefConst => Opcode::LOAD_DEREF, + StoreLoadKind::Local | StoreLoadKind::LocalConst => Opcode::LOAD_ATTR, + }; + self.write_instr(instr); + self.write_arg(name.idx as u8); + Ok(()) + } + + fn emit_load_method_instr(&mut self, class: &str, name: Str) -> CompileResult<()> { + let name = self.local_search(&name, Method).unwrap_or_else(|| { + self.register_method(class, name) + }); + let instr = match name.kind { + StoreLoadKind::Fast | StoreLoadKind::FastConst => Opcode::LOAD_FAST, + StoreLoadKind::Global | StoreLoadKind::GlobalConst => Opcode::LOAD_GLOBAL, + StoreLoadKind::Deref | StoreLoadKind::DerefConst => Opcode::LOAD_DEREF, + StoreLoadKind::Local | StoreLoadKind::LocalConst => Opcode::LOAD_METHOD, + }; + self.write_instr(instr); + self.write_arg(name.idx as u8); + Ok(()) + } + + fn emit_store_instr(&mut self, name: Str, acc_kind: AccessKind) { + let name = self.local_search(&name, acc_kind).unwrap_or_else(|| { + self.register_name(name) + }); + let instr = match name.kind { + StoreLoadKind::Fast => Opcode::STORE_FAST, + StoreLoadKind::FastConst => Opcode::ERG_STORE_FAST_IMMUT, + StoreLoadKind::Global | StoreLoadKind::GlobalConst => Opcode::STORE_GLOBAL, + StoreLoadKind::Deref | StoreLoadKind::DerefConst => Opcode::STORE_DEREF, + StoreLoadKind::Local | StoreLoadKind::LocalConst => { + match acc_kind { + AccessKind::Name => Opcode::STORE_NAME, + AccessKind::Attr => Opcode::STORE_ATTR, + // cannot overwrite methods directly + AccessKind::Method => Opcode::STORE_ATTR, + } + } + }; + self.write_instr(instr); + self.write_arg(name.idx as u8); + self.stack_dec(); + } + + fn emit_pop_top(&mut self) { + self.write_instr(Opcode::POP_TOP); + self.write_arg(0u8); + self.stack_dec(); + } + + fn cancel_pop_top(&mut self) { + let lasop_t_idx = self.cur_block_codeobj().code.len() - 2; + if self.cur_block_codeobj().code.get(lasop_t_idx) == Some(&(Opcode::POP_TOP as u8)) { + self.mut_cur_block_codeobj().code.pop(); + self.mut_cur_block_codeobj().code.pop(); + self.mut_cur_block().lasti -= 2; + self.stack_inc(); + } + } + + /// Compileが継続不能になった際呼び出す + /// 極力使わないこと + fn crash(&mut self, description: &'static str) -> ! { + self.errs.fmt_all_stderr(); + if cfg!(feature = "debug") { + panic!("internal error: {description}"); + } else { + process::exit(1); + } + } + + fn gen_param_names(&self, params: &Params) -> Vec { + params + .non_defaults.iter().map(|p| p.inspect().map(|s| &s[..]).unwrap_or("_")) + .chain(params.defaults.iter().map(|p| p.inspect().map(|s| &s[..]).unwrap_or("_"))) + .map(|s| self.get_cached(&s)) + .collect() + } + + fn emit_var_pat(&mut self, pat: &VarPattern, op: &Token) { + match pat { + VarPattern::VarName(var) => { + if op.category_is(TokenCategory::DefOp) { + self.emit_store_instr(var.inspect().clone(), Name); + } else { + todo!() + } + } + VarPattern::Array(a) => { + if op.category_is(TokenCategory::DefOp) { + // TODO: UNPACK_EX + self.write_instr(UNPACK_SEQUENCE); + self.write_arg(a.len() as u8); + self.stack_inc_n(a.len() - 1); + for sig in a.iter() { + self.emit_var_pat(&sig.pat, op); + } + } else { + switch_unreachable!() + } + } + _ => todo!(), + } + } + + fn emit_mono_type_def(&mut self, sig: VarSignature, body: DefBody) { + self.write_instr(Opcode::LOAD_BUILD_CLASS); + self.write_arg(0); + self.stack_inc(); + let name = sig.inspect().unwrap(); + let code = self.codegen_typedef_block(name.clone(), body.block); + self.emit_load_const(code); + self.emit_load_const(name.clone()); + self.write_instr(Opcode::MAKE_FUNCTION); + self.write_arg(0); + self.emit_load_const(name.clone()); + self.write_instr(Opcode::CALL_FUNCTION); + self.write_arg(2); + self.stack_dec_n((1 + 2) - 1); + self.emit_store_instr(name.clone(), Name); + } + + fn emit_var_def(&mut self, sig: VarSignature, mut body: DefBody) { + if body.is_type() { + return self.emit_mono_type_def(sig, body) + } + if body.block.len() == 1 { + self.codegen_expr(body.block.remove(0)); + } else { + self.codegen_frameless_block(body.block, vec![]); + } + self.emit_var_pat(&sig.pat, &body.op); + } + + fn emit_subr_def(&mut self, sig: SubrSignature, body: DefBody) { + let name = sig.name.inspect().clone(); + let mut opcode_flag = 0u8; + let params = self.gen_param_names(&sig.params); + let code = self.codegen_block(body.block, Some(name.clone()), params); + self.emit_load_const(code); + if !self.cur_block_codeobj().cellvars.is_empty() { + let cellvars_len = self.cur_block_codeobj().cellvars.len() as u8; + for i in 0..cellvars_len { + self.write_instr(LOAD_CLOSURE); + self.write_arg(i); + } + self.write_instr(BUILD_TUPLE); + self.write_arg(cellvars_len); + opcode_flag += 8; + } + self.emit_load_const(name.clone()); + self.write_instr(MAKE_FUNCTION); + self.write_arg(opcode_flag); + // stack_dec: + -> + self.stack_dec(); + self.emit_store_instr(name, Name); + } + + fn emit_discard_instr(&mut self, mut args: Args) -> CompileResult<()> { + while let Some(arg) = args.try_remove(0) { + self.codegen_expr(arg); + self.emit_pop_top(); + } + Ok(()) + } + + fn emit_if_instr(&mut self, mut args: Args) -> CompileResult<()> { + let cond = args.remove(0); + self.codegen_expr(cond); + let idx_pop_jump_if_false = self.cur_block().lasti; + self.write_instr(POP_JUMP_IF_FALSE); + // cannot detect where to jump to at this moment, so put as 0 + self.write_arg(0 as u8); + match args.remove(0) { + // then block + Expr::Lambda(lambda) => { + let params = self.gen_param_names(&lambda.params); + self.codegen_frameless_block(lambda.body, params); + } + other => { + self.codegen_expr(other); + } + } + if args.get(0).is_some() { + self.write_instr(JUMP_FORWARD); // jump to end + self.write_arg(0 as u8); + // else block + let idx_else_begin = self.cur_block().lasti; + self.edit_code(idx_pop_jump_if_false + 1, idx_else_begin / 2); + match args.remove(0) { + Expr::Lambda(lambda) => { + let params = self.gen_param_names(&lambda.params); + self.codegen_frameless_block(lambda.body, params); + } + other => { + self.codegen_expr(other); + } + } + let idx_jump_forward = idx_else_begin - 2; + let idx_end = self.cur_block().lasti; + self.edit_code(idx_jump_forward + 1, (idx_end - idx_jump_forward - 2) / 2); + self.stack_dec(); + self.stack_dec(); + } else { + // no else block + let idx_end = self.cur_block().lasti; + self.edit_code(idx_pop_jump_if_false + 1,idx_end / 2); + self.stack_dec(); + } + Ok(()) + } + + fn emit_for_instr(&mut self, mut args: Args) -> CompileResult<()> { + let iterable = args.remove(0); + self.codegen_expr(iterable); + self.write_instr(GET_ITER); + self.write_arg(0); + let idx_for_iter = self.cur_block().lasti; + self.write_instr(FOR_ITER); + // FOR_ITER pushes a value onto the stack, but we can't know how many + // but after executing this instruction, stack_len should be 1 + // cannot detect where to jump to at this moment, so put as 0 + self.write_arg(0); + let lambda = enum_unwrap!(args.remove(0), Expr::Lambda); + let params = self.gen_param_names(&lambda.params); + self.codegen_frameless_block(lambda.body, params); // ここでPOPされる + self.write_instr(JUMP_ABSOLUTE); + self.write_arg((idx_for_iter / 2) as u8); + let idx_end = self.cur_block().lasti; + self.edit_code(idx_for_iter + 1, (idx_end - idx_for_iter - 2) / 2); + self.emit_load_const(ValueObj::None); + Ok(()) + } + + fn emit_match_instr(&mut self, mut args: Args, _use_erg_specific: bool) -> CompileResult<()> { + let expr = args.remove(0); + self.codegen_expr(expr); + let len = args.len(); + let mut absolute_jump_points = vec![]; + while let Some(expr) = args.try_remove(0) { + // パターンが複数ある場合引数を複製する、ただし最後はしない + if len > 1 && args.len() > 0 { + self.write_instr(Opcode::DUP_TOP); + self.write_arg(0); + self.stack_inc(); + } + // compilerで型チェック済み(可読性が下がるため、matchでNamedは使えない) + let mut lambda = enum_unwrap!(expr, Expr::Lambda); + debug_power_assert!(lambda.params.len(), ==, 1); + if !lambda.params.defaults.is_empty() { + todo!("default values in match expression are not supported yet") + } + let pat = lambda.params.non_defaults.remove(0).pat; + let pop_jump_points = self.emit_match_pattern(pat)?; + self.codegen_frameless_block(lambda.body, Vec::new()); + for pop_jump_point in pop_jump_points.into_iter() { + let idx = self.cur_block().lasti + 2; + self.edit_code(pop_jump_point + 1, idx / 2); // jump to POP_TOP + absolute_jump_points.push(self.cur_block().lasti); + self.write_instr(Opcode::JUMP_ABSOLUTE); // jump to the end + self.write_arg(0); + } + } + let lasti = self.cur_block().lasti; + for absolute_jump_point in absolute_jump_points.into_iter() { + self.edit_code(absolute_jump_point + 1, lasti / 2); + } + Ok(()) + } + + fn emit_match_pattern(&mut self, pat: ParamPattern) -> CompileResult> { + let mut pop_jump_points = vec![]; + match pat { + ParamPattern::VarName(name) => { + self.emit_store_instr(name.inspect().clone(), AccessKind::Name); + } + ParamPattern::Lit(lit) => { + self.emit_load_const(ValueObj::from(&lit)); + self.write_instr(Opcode::COMPARE_OP); + self.write_arg(2); // == + self.stack_dec(); + pop_jump_points.push(self.cur_block().lasti); + self.write_instr(Opcode::POP_JUMP_IF_FALSE); // jump to the next case + self.write_arg(0); + self.emit_pop_top(); + self.stack_dec(); + } + ParamPattern::Array(arr) => { + let len = arr.len(); + self.write_instr(Opcode::MATCH_SEQUENCE); + self.write_arg(0); + pop_jump_points.push(self.cur_block().lasti); + self.write_instr(Opcode::POP_JUMP_IF_FALSE); + self.write_arg(0); + self.stack_dec(); + self.write_instr(Opcode::GET_LEN); + self.write_arg(0); + self.emit_load_const(len); + self.write_instr(Opcode::COMPARE_OP); + self.write_arg(2); // == + self.stack_dec(); + pop_jump_points.push(self.cur_block().lasti); + self.write_instr(Opcode::POP_JUMP_IF_FALSE); + self.write_arg(0); + self.stack_dec(); + self.write_instr(Opcode::UNPACK_SEQUENCE); + self.write_arg(len as u8); + self.stack_inc_n(len - 1); + for elem in arr.elems.non_defaults { + pop_jump_points.append(&mut self.emit_match_pattern(elem.pat)?); + } + if !arr.elems.defaults.is_empty() { + todo!("default values in match are not supported yet") + } + } + _other => { + todo!() + } + } + Ok(pop_jump_points) + } + + fn emit_call_name(&mut self, name: Str, mut args: Args) -> CompileResult<()> { + match &name[..] { + "assert" => self.emit_assert_instr(args), + "discard" => self.emit_discard_instr(args), + "for" | "for!" => self.emit_for_instr(args), + "if" | "if!" => self.emit_if_instr(args), + "match" | "match!" => self.emit_match_instr(args, true), + _ => { + self.emit_load_name_instr(name).unwrap_or_else(|e| { + self.errs.push(e); + }); + let argc = args.len(); + let mut kws = Vec::with_capacity(args.kw_len()); + while let Some(arg) = args.try_remove_pos(0) { + self.codegen_expr(arg.expr); + } + while let Some(arg) = args.try_remove_kw(0) { + kws.push(ValueObj::Str(arg.keyword.content.clone())); + self.codegen_expr(arg.expr); + } + let kwsc = if !kws.is_empty() { + let kws_tuple = ValueObj::from(kws); + self.emit_load_const(kws_tuple); + self.write_instr(CALL_FUNCTION_KW); + 1 + } else { + self.write_instr(CALL_FUNCTION); + 0 + }; + self.write_arg(argc as u8); + // (1 (subroutine) + argc + kwsc) input objects -> 1 return object + self.stack_dec_n((1 + argc + kwsc) - 1); + Ok(()) + } + } + } + + fn emit_call_method(&mut self, obj: Expr, name: Str, mut args: Args, is_static: bool) { + if is_static { + self.emit_load_name_instr(name).unwrap_or_else(|err| { + self.errs.push(err); + }); + let argc = args.len(); + let mut kws = Vec::with_capacity(args.kw_len()); + while let Some(arg) = args.try_remove_pos(0) { + self.codegen_expr(arg.expr); + } + while let Some(arg) = args.try_remove_kw(0) { + kws.push(ValueObj::Str(arg.keyword.content.clone())); + self.codegen_expr(arg.expr); + } + let kwsc = if !kws.is_empty() { + let kws_tuple = ValueObj::from(kws); + self.emit_load_const(kws_tuple); + self.write_instr(CALL_FUNCTION_KW); + 1 + } else { + self.write_instr(CALL_FUNCTION); + 0 + }; + self.write_arg(1 + argc as u8); + // (1 (method as subroutine) + 1 (obj) + argc + kwsc) input objects -> 1 return object + self.stack_dec_n((1 + 1 + argc + kwsc) - 1); + } else { + let class = Str::rc(obj.ref_t().name()); + self.codegen_expr(obj); + self.emit_load_method_instr(&class, name).unwrap_or_else(|err| { + self.errs.push(err); + }); + let argc = args.len(); + let mut kws = Vec::with_capacity(args.kw_len()); + while let Some(arg) = args.try_remove_pos(0) { + self.codegen_expr(arg.expr); + } + while let Some(arg) = args.try_remove_kw(0) { + kws.push(ValueObj::Str(arg.keyword.content.clone())); + self.codegen_expr(arg.expr); + } + let kwsc = if !kws.is_empty() { + let kws_tuple = ValueObj::from(kws); + self.emit_load_const(kws_tuple); + self.write_instr(CALL_FUNCTION_KW); + 1 + } else { + self.write_instr(CALL_METHOD); + 0 + }; + self.write_arg(argc as u8); + // (1 (method) + argc + kwsc) input objects -> 1 return object + self.stack_dec_n((1 + argc + kwsc) - 1); + } + } + + fn emit_call_callable_obj(&mut self, obj: Expr, mut args: Args) { + self.codegen_expr(obj); + let argc = args.len(); + let mut kws = Vec::with_capacity(args.kw_len()); + while let Some(arg) = args.try_remove_pos(0) { + self.codegen_expr(arg.expr); + } + while let Some(arg) = args.try_remove_kw(0) { + kws.push(ValueObj::Str(arg.keyword.content.clone())); + self.codegen_expr(arg.expr); + } + let kwsc = if !kws.is_empty() { + let kws_tuple = ValueObj::from(kws); + self.emit_load_const(kws_tuple); + self.write_instr(CALL_FUNCTION_KW); + 1 + } else { + self.write_instr(CALL_FUNCTION); + 0 + }; + self.write_arg(argc as u8); + // (1 (name) + argc + kwsc) objects -> 1 return object + self.stack_dec_n((1 + argc + kwsc) - 1); + } + + // assert takes 1 or 2 arguments (0: cond, 1: message) + fn emit_assert_instr(&mut self, mut args: Args) -> CompileResult<()> { + self.codegen_expr(args.remove(0)); + let pop_jump_point = self.cur_block().lasti; + self.write_instr(Opcode::POP_JUMP_IF_TRUE); + self.write_arg(0); + self.stack_dec(); + self.write_instr(Opcode::LOAD_ASSERTION_ERROR); + self.write_arg(0); + if let Some(expr) = args.try_remove(0) { + self.codegen_expr(expr); + self.write_instr(Opcode::CALL_FUNCTION); + self.write_arg(1); + } + self.write_instr(Opcode::RAISE_VARARGS); + self.write_arg(1); + let idx = self.cur_block().lasti; + self.edit_code(pop_jump_point + 1, idx / 2); // jump to POP_TOP + Ok(()) + } + + fn codegen_expr(&mut self, expr: Expr) { + if expr.ln_begin().unwrap() > self.cur_block().prev_lineno { + let sd = self.cur_block().lasti - self.cur_block().prev_lasti; + let ld = expr.ln_begin().unwrap() - self.cur_block().prev_lineno; + if ld != 0 { + if sd != 0 { + self.mut_cur_block_codeobj().lnotab.push(sd as u8); + self.mut_cur_block_codeobj().lnotab.push(ld as u8); + } else { + // empty lines + if let Some(last_ld) = self.mut_cur_block_codeobj().lnotab.last_mut() { + *last_ld += ld as u8; + } else { + // a block starts with an empty line + self.mut_cur_block_codeobj().lnotab.push(0); + self.mut_cur_block_codeobj().lnotab.push(ld as u8); + } + } + self.mut_cur_block().prev_lineno += ld; + self.mut_cur_block().prev_lasti = self.cur_block().lasti; + } else { + self.errs.push(CompileError::compiler_bug( + 0, + self.cfg.input.clone(), + expr.loc(), + fn_name_full!(), + line!(), + )); + self.crash("codegen failed: invalid bytecode format"); + } + } + match expr { + Expr::Lit(lit) => { + self.emit_load_const(lit.data); + } + Expr::Accessor(Accessor::Local(l)) => { + self.emit_load_name_instr(l.inspect().clone()).unwrap_or_else(|err| { + self.errs.push(err); + }); + } + Expr::Accessor(Accessor::Attr(a)) => { + let class = Str::rc(a.obj.ref_t().name()); + self.codegen_expr(*a.obj); + self.emit_load_attr_instr(&class, a.name.content.clone()).unwrap_or_else(|err| { + self.errs.push(err); + }); + } + Expr::Def(def) => { + match def.sig { + Signature::Subr(sig) => { self.emit_subr_def(sig, def.body) } + Signature::Var(sig) => { self.emit_var_def(sig, def.body) } + } + } + // TODO: + Expr::Lambda(lambda) => { + let params = self.gen_param_names(&lambda.params); + self.codegen_block(lambda.body, Some("".into()), params); + self.emit_load_const(""); + self.write_instr(MAKE_FUNCTION); + self.write_arg(0u8); + // stack_dec: + "> -> + self.stack_dec(); + } + Expr::UnaryOp(unary) => { + let tycode = TypeCode::from(unary.lhs_t()); + self.codegen_expr(*unary.expr); + let instr = match &unary.op.kind { + // TODO: + TokenKind::PrePlus => UNARY_POSITIVE, + TokenKind::PreMinus => UNARY_NEGATIVE, + TokenKind::Mutate => NOP, // ERG_MUTATE, + // TokenKind::PreStar =>, + // TokenKind::PreRng =>, + _ => { + self.errs.push(CompileError::feature_error( + self.cfg.input.clone(), + unary.op.loc(), + "", + unary.op.content.clone(), + )); + NOT_IMPLEMENTED + } + }; + self.write_instr(instr); + self.write_arg(tycode as u8); + } + Expr::BinOp(bin) => { + // TODO: and/orのプリミティブ命令の実装 + let type_pair = TypePair::new(bin.lhs_t(), bin.rhs_t()); + self.codegen_expr(*bin.lhs); + self.codegen_expr(*bin.rhs); + let instr = match &bin.op.kind { + TokenKind::Plus => BINARY_ADD, + TokenKind::Minus => BINARY_SUBTRACT, + TokenKind::Star => BINARY_MULTIPLY, + TokenKind::Slash => BINARY_TRUE_DIVIDE, + TokenKind::Pow => BINARY_POWER, + TokenKind::Mod => BINARY_MODULO, + TokenKind::AndOp => BINARY_AND, + TokenKind::OrOp => BINARY_OR, + TokenKind::Less + | TokenKind::LessEq + | TokenKind::DblEq + | TokenKind::NotEq + | TokenKind::Gre + | TokenKind::GreEq => COMPARE_OP, + TokenKind::Closed => ERG_BINARY_RANGE, + _ => { + self.errs.push(CompileError::feature_error( + self.cfg.input.clone(), + bin.op.loc(), + "", + bin.op.content.clone() + )); + NOT_IMPLEMENTED + } + }; + let arg = match &bin.op.kind { + TokenKind::Less => 0, + TokenKind::LessEq => 1, + TokenKind::DblEq => 2, + TokenKind::NotEq => 3, + TokenKind::Gre => 4, + TokenKind::GreEq => 5, + _ => type_pair as u8, + }; + self.write_instr(instr); + self.write_arg(arg); + self.stack_dec(); + } + Expr::Call(call) => { + // TODO: unwrap + let name = Str::from(obj_name(&call.obj).unwrap()); + match *call.obj { + Expr::Accessor(Accessor::Local(_)) => { + self.emit_call_name(name, call.args).unwrap(); + } + Expr::Accessor(Accessor::Attr(a)) => { + // TODO: impl static dispatch mode + self.emit_call_method(*a.obj, name, call.args, false); + } + obj => { + self.emit_call_callable_obj(obj, call.args); + } + } + }, + // TODO: list comprehension + Expr::Array(mut arr) => { + let len = arr.elems.len(); + while let Some(arg) = arr.elems.try_remove_pos(0) { + self.codegen_expr(arg.expr); + } + self.write_instr(BUILD_LIST); + self.write_arg(len as u8); + if len == 0 { + self.stack_inc(); + } else { + self.stack_dec_n(len - 1); + } + } + other => { + self.errs.push(CompileError::feature_error(self.cfg.input.clone(), other.loc(), "", "".into())); + self.crash("cannot compile this expression at this time"); + } + } + } + + /// forブロックなどで使う + fn codegen_frameless_block(&mut self, block: Block, params: Vec) { + for param in params { + self.emit_store_instr(param, Name); + } + for expr in block.into_iter() { + self.codegen_expr(expr); + // TODO: discard + // 最終的に帳尻を合わせる(コード生成の順番的にスタックの整合性が一時的に崩れる場合がある) + if self.cur_block().stack_len == 1 { + self.emit_pop_top(); + } + } + self.cancel_pop_top(); + } + + fn codegen_typedef_block(&mut self, name: Str, block: Block) -> CodeObj { + self.unit_size += 1; + self.units.push(CodeGenUnit::new( + self.unit_size, + vec![], + Str::rc(self.cfg.input.enclosed_name()), + &name, + block[0].ln_begin().unwrap(), + )); + let mod_name = self.toplevel_block_codeobj().name.clone(); + self.emit_load_const(mod_name); + self.emit_store_instr(Str::from("__module__"), Attr); + self.emit_load_const(name); + self.emit_store_instr(Str::from("__qualname__"), Attr); + // TODO: サブルーチンはT.subという書式でSTORE + for expr in block.into_iter() { + self.codegen_expr(expr); + // TODO: discard + if self.cur_block().stack_len == 1 { + self.emit_pop_top(); + } + } + self.emit_load_const(ValueObj::None); + self.write_instr(RETURN_VALUE); + self.write_arg(0u8); + if self.cur_block().stack_len > 1 { + let block_id = self.cur_block().id; + let stack_len = self.cur_block().stack_len; + self.errs.push(CompileError::stack_bug( + self.input().clone(), + Location::Unknown, + stack_len, + block_id, + fn_name_full!() + )); + self.crash("error in codegen_typedef_block: invalid stack size"); + } + // flagging + if !self.cur_block_codeobj().varnames.is_empty() { + self.mut_cur_block_codeobj().flags += CodeObjFlags::NewLocals as u32; + } + // end of flagging + let unit = self.units.pop().unwrap(); + if !self.units.is_empty() { + let ld = unit.prev_lineno - self.cur_block().prev_lineno; + if ld != 0 { + self.mut_cur_block_codeobj().lnotab.last_mut().map(|l| { *l += ld as u8; }); + self.mut_cur_block().prev_lineno += ld; + } + } + unit.codeobj + } + + fn codegen_block(&mut self, block: Block, opt_name: Option, params: Vec) -> CodeObj { + self.unit_size += 1; + let name = if let Some(name) = opt_name { name } else { "".into() }; + let firstlineno = block.first().unwrap().ln_begin().unwrap(); + self.units.push(CodeGenUnit::new( + self.unit_size, + params, + Str::rc(self.cfg.input.enclosed_name()), + &name, + firstlineno, + )); + for expr in block.into_iter() { + self.codegen_expr(expr); + // NOTE: 各行のトップレベルでは0個または1個のオブジェクトが残っている + // Pythonの場合使わなかったオブジェクトはそのまま捨てられるが、Ergではdiscardを使う必要がある + // TODO: discard + if self.cur_block().stack_len == 1 { + self.emit_pop_top(); + } + } + self.cancel_pop_top(); // 最後の値は戻り値として取っておく + if self.cur_block().stack_len == 0 { + self.emit_load_const(ValueObj::None); + } else if self.cur_block().stack_len > 1 { + let block_id = self.cur_block().id; + let stack_len = self.cur_block().stack_len; + self.errs.push(CompileError::stack_bug( + self.input().clone(), + Location::Unknown, + stack_len, + block_id, + fn_name_full!() + )); + self.crash("error in codegen_block: invalid stack size"); + } + self.write_instr(RETURN_VALUE); + self.write_arg(0u8); + // flagging + if !self.cur_block_codeobj().varnames.is_empty() { + self.mut_cur_block_codeobj().flags += CodeObjFlags::NewLocals as u32; + } + // end of flagging + let unit = self.units.pop().unwrap(); + if !self.units.is_empty() { + let ld = unit.prev_lineno - self.cur_block().prev_lineno; + if ld != 0 { + self.mut_cur_block_codeobj().lnotab.last_mut().map(|l| { *l += ld as u8; }); + self.mut_cur_block().prev_lineno += ld; + } + } + unit.codeobj + } + + pub fn codegen(&mut self, hir: HIR) -> CodeObj { + log!("{GREEN}[DEBUG] the code-generating process has started.{RESET}"); + self.unit_size += 1; + self.units.push(CodeGenUnit::new( + self.unit_size, + vec![], + Str::rc(self.cfg.input.enclosed_name()), + "", + 1 + )); + for expr in hir.module.into_iter() { + self.codegen_expr(expr); + // TODO: discard + if self.cur_block().stack_len == 1 { + self.emit_pop_top(); + } + } + self.cancel_pop_top(); // 最後の値は戻り値として取っておく + if self.cur_block().stack_len == 0 { + self.emit_load_const(ValueObj::None); + } else if self.cur_block().stack_len > 1 { + let block_id = self.cur_block().id; + let stack_len = self.cur_block().stack_len; + self.errs.push(CompileError::stack_bug( + self.input().clone(), + Location::Unknown, + stack_len, + block_id, + fn_name_full!() + )); + self.crash("error in codegen_module: invalid stack size"); + } + self.write_instr(RETURN_VALUE); + self.write_arg(0u8); + // flagging + if !self.cur_block_codeobj().varnames.is_empty() { + self.mut_cur_block_codeobj().flags += CodeObjFlags::NewLocals as u32; + } + // end of flagging + let unit = self.units.pop().unwrap(); + if !self.units.is_empty() { + let ld = unit.prev_lineno - self.cur_block().prev_lineno; + if ld != 0 { + self.mut_cur_block_codeobj().lnotab.last_mut().map(|l| { *l += ld as u8; }); + self.mut_cur_block().prev_lineno += ld; + } + } + log!("{GREEN}[DEBUG] the code-generating process has completed.{RESET}"); + unit.codeobj + } +} diff --git a/src/compiler/compile.rs b/src/compiler/compile.rs new file mode 100644 index 00000000..30498d1e --- /dev/null +++ b/src/compiler/compile.rs @@ -0,0 +1,145 @@ +//! defines `Compiler`. +//! +//! コンパイラーを定義する +use std::path::Path; + +use common::Str; +use common::{log}; +use common::codeobj::{CodeObj, CodeObjFlags}; +use common::color::{GREEN, RESET}; +use common::config::{Input, ErgConfig, SEMVER, BUILD_INFO}; +use common::error::MultiErrorDisplay; +use common::traits::{Runnable, Stream}; + +use parser::ParserRunner; + +use crate::codegen::CodeGenerator; +use crate::effectcheck::SideEffectChecker; +use crate::error::{TyCheckErrors, CompileError, CompileErrors}; +use crate::lower::ASTLowerer; +use crate::ownercheck::OwnershipChecker; + +/// * registered as global -> Global +/// * defined in the toplevel scope (and called in the inner scope) -> Global +/// * defined and called in the toplevel scope -> Local +/// * not defined in the toplevel and called in the inner scope -> Deref +/// * defined and called in the current scope (except the toplevel) -> Fast +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum StoreLoadKind { + Local, + LocalConst, + Global, + GlobalConst, + Deref, + DerefConst, + Fast, + FastConst, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Name { + pub kind: StoreLoadKind, + pub idx: usize, +} + +impl Name { + pub const fn new(kind: StoreLoadKind, idx: usize) -> Self { Self{ kind, idx } } + + pub const fn local(idx: usize) -> Self { Self{ kind: StoreLoadKind::Local, idx } } + pub const fn global(idx: usize) -> Self { Self{ kind: StoreLoadKind::Global, idx } } + pub const fn deref(idx: usize) -> Self { Self{ kind: StoreLoadKind::Deref, idx } } + pub const fn fast(idx: usize) -> Self { Self{ kind: StoreLoadKind::Fast, idx } } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AccessKind { + Name, + Attr, + Method, +} + +impl AccessKind { + pub const fn is_local(&self) -> bool { matches!(self, Self::Name) } + pub const fn is_attr(&self) -> bool { matches!(self, Self::Attr) } + pub const fn is_method(&self) -> bool { matches!(self, Self::Method) } +} + +/// Generates a `CodeObj` from an `AST`. +/// The input AST is not typed, so it's typed by `ASTLowerer` according to the cfg.opt_level. +#[derive(Debug)] +pub struct Compiler { + cfg: ErgConfig, + lowerer: ASTLowerer, + code_generator: CodeGenerator, +} + +impl Runnable for Compiler { + type Err = CompileError; + type Errs = CompileErrors; + + fn new(cfg: ErgConfig) -> Self { + Self { + code_generator: CodeGenerator::new(cfg.copy()), + lowerer: ASTLowerer::new(), + cfg, + } + } + + #[inline] + fn input(&self) -> &Input { &self.cfg.input } + + #[inline] + fn start_message(&self) -> String { format!("Erg compiler {} {}\n", SEMVER, &*BUILD_INFO) } + + fn clear(&mut self) { + self.code_generator.clear(); + } + + fn eval(&mut self, src: Str) -> Result { + let codeobj = self.compile(src, "eval")?; + Ok(codeobj.code_info()) + } +} + +impl Compiler { + fn convert(&self, errs: TyCheckErrors) -> CompileErrors { + errs.into_iter().map(|e| CompileError::new(e.core, self.input().clone(), e.caused_by)).collect::>().into() + } + + pub fn compile_and_dump_as_pyc>(&mut self, src: Str, path: P) -> Result<(), CompileErrors> { + let code = self.compile(src, "exec")?; + code.dump_as_pyc(path, self.cfg.python_ver).expect("failed to dump a .pyc file"); + Ok(()) + } + + pub fn compile(&mut self, src: Str, mode: &str) -> Result { + log!("{GREEN}[DEBUG] the compiling process has started.{RESET}"); + let mut dynamic = true; + let mut parser = ParserRunner::new(self.cfg.copy()); + let ast = parser.parse_from_str(src)?; + if ast.is_empty() { + return Ok(CodeObj::empty(vec![], Str::rc(self.input().enclosed_name()), "", 1)) + } + let (hir, warns) = self.lowerer.lower(ast, mode).map_err(|errs| self.convert(errs))?; + if warns.is_empty() { dynamic = false; } + if self.cfg.verbose >= 2 { + let warns = self.convert(warns); + warns.fmt_all_stderr(); + } + let effect_checker = SideEffectChecker::new(); + let hir = effect_checker.check(hir).map_err(|errs| self.convert(errs))?; + let ownership_checker = OwnershipChecker::new(); + let hir = ownership_checker.check(hir).map_err(|errs| self.convert(errs))?; + let mut codeobj = self.code_generator.codegen(hir); + if dynamic { + codeobj.flags += CodeObjFlags::EvmDynamic as u32; + } + log!("{GREEN}code object:\n{}", codeobj.code_info()); + log!("[DEBUG] the compiling process has completed, found errors: {}{RESET}", self.code_generator.errs.len()); + if self.code_generator.errs.is_empty() { + Ok(codeobj) + } else { + Err(self.code_generator.errs.flush()) + } + } +} diff --git a/src/compiler/effectcheck.rs b/src/compiler/effectcheck.rs new file mode 100644 index 00000000..ed68dac7 --- /dev/null +++ b/src/compiler/effectcheck.rs @@ -0,0 +1,186 @@ +//! implements SideEffectChecker +//! SideEffectCheckerを実装 +//! 関数や不変型に副作用がないかチェックする + +use common::Str; +use common::color::{GREEN, RESET}; +use common::log; +use common::traits::Stream; + +use crate::varinfo::Visibility; +use Visibility::*; +use crate::error::{EffectError, EffectErrors, EffectResult}; +use crate::hir::{HIR, Expr, Def, Accessor, Signature}; + +#[derive(Debug)] +pub struct SideEffectChecker { + path_stack: Vec<(Str, Visibility)>, + errs: EffectErrors, +} + +impl SideEffectChecker { + pub fn new() -> Self { Self { path_stack: vec![], errs: EffectErrors::empty() } } + + fn full_path(&self) -> String { + self.path_stack.iter().fold(String::new(), |acc, (path, vis)| { + if vis.is_public() { acc + "." + &path[..] } else { acc + "::" + &path[..] } + }) + } + + pub fn check(mut self, hir: HIR) -> EffectResult { + self.path_stack.push((hir.name.clone(), Private)); + log!("{GREEN}[DEBUG] the side-effect checking process has started.{RESET}"); + // トップレベルでは副作用があっても問題なく、純粋性違反がないかのみチェックする + for expr in hir.module.iter() { + match expr { + Expr::Def(def) => { self.check_def(def, true); }, + Expr::Call(call) => { + for parg in call.args.pos_args().iter() { + self.check_expr(&parg.expr, true); + } + for kwarg in call.args.kw_args().iter() { + self.check_expr(&kwarg.expr, true); + } + }, + other => todo!("{other}"), + } + } + log!("{GREEN}[DEBUG] the side-effect checking process has completed, found errors: {}{RESET}", self.errs.len()); + if self.errs.is_empty() { + Ok(hir) + } else { + Err(self.errs) + } + } + + fn check_def(&mut self, def: &Def, allow_inner_effect: bool) { + self.path_stack.push(match &def.sig { + Signature::Var(var) => + // TODO: visibility + if let Some(name) = var.inspect() { (name.clone(), Private) } + else { (Str::ever("::"), Private) }, + Signature::Subr(subr) => (subr.name.inspect().clone(), Private), + }); + // TODO: support raw identifier (``) + let is_procedural = def.sig.is_procedural(); + let is_subr = def.sig.is_subr(); + let is_const = def.sig.is_const(); + let is_type = def.body.is_type(); + match (is_procedural, is_subr) { + (true, _) => { + if !allow_inner_effect { + let expr = Expr::Def(def.clone()); + self.errs.push( + EffectError::has_effect(&expr, self.full_path()) + ); + } + for chunk in def.body.block.iter() { + self.check_expr(chunk, allow_inner_effect); + } + }, + (false, false) => { + for chunk in def.body.block.iter() { + self.check_expr(chunk, allow_inner_effect); + } + }, + (false, true) => { self.check_func(def); }, + } + if is_const { self.check_const(def); } + if !is_procedural && is_type { self.check_immut_type(def); } + self.path_stack.pop(); + } + + fn check_func(&mut self, funcdef: &Def) { + for chunk in funcdef.body.block.iter() { + self.check_expr(chunk, false); + } + } + + fn check_immut_type(&mut self, _typedef: &Def) { + todo!() + } + + fn check_const(&mut self, _constdef: &Def) { + todo!() + } + + /// check if `expr` has side-effects / purity violations. + /// + /// returns effects, purity violations will be appended to `self.errs`. + /// + /// causes side-effects: + /// ``` + /// p!() // 1 side-effect + /// p!(q!()) // 2 side-effects + /// x = + /// y = r!() + /// y + 1 // 1 side-effect + /// ``` + /// causes no side-effects: + /// ``` + /// q! = p! + /// y = f(p!) + /// ``` + /// purity violation: + /// ``` + /// for iter, i -> print! i + /// ``` + fn check_expr(&mut self, expr: &Expr, allow_self_effect: bool) { + match expr { + Expr::Def(def) => { self.check_def(def, allow_self_effect); }, + // 引数がproceduralでも関数呼び出しなら副作用なし + Expr::Call(call) => { + if self.is_procedural(&call.obj) && !allow_self_effect { + self.errs.push( + EffectError::has_effect(expr, self.full_path()) + ); + } + call.args + .pos_args() + .iter() + .for_each(|parg|self.check_expr(&parg.expr, allow_self_effect)); + call.args + .kw_args() + .iter() + .for_each(|kwarg| self.check_expr(&kwarg.expr, allow_self_effect)); + }, + Expr::UnaryOp(unary) => { + self.check_expr(&unary.expr, allow_self_effect); + }, + Expr::BinOp(bin) => { + self.check_expr(&bin.lhs, allow_self_effect); + self.check_expr(&bin.rhs, allow_self_effect); + }, + Expr::Lambda(lambda) => { + let is_proc = lambda.is_procedural(); + if is_proc { + self.path_stack.push((Str::ever(""), Private)); + } else { + self.path_stack.push((Str::ever(""), Private)); + } + if !allow_self_effect && is_proc { + self.errs.push(EffectError::has_effect(expr, self.full_path())); + } + lambda.body + .iter() + .for_each(|chunk| self.check_expr(chunk, allow_self_effect && is_proc)); + self.path_stack.pop(); + }, + _ => {}, + } + } + + fn is_procedural(&self, expr: &Expr) -> bool { + match expr { + Expr::Lambda(lambda) => lambda.is_procedural(), + // 引数がproceduralでも関数呼び出しなら副作用なし + Expr::Call(call) => self.is_procedural(&call.obj), + Expr::Accessor(Accessor::Local(local)) => local.name.is_procedural(), + // procedural: x.y! (e.g. Array.sample!) + // !procedural: !x.y + Expr::Accessor(Accessor::Attr(attr)) => attr.name.is_procedural(), + Expr::Accessor(_) => todo!(), + _ => false, + } + } +} diff --git a/src/compiler/error.rs b/src/compiler/error.rs new file mode 100644 index 00000000..107e7b70 --- /dev/null +++ b/src/compiler/error.rs @@ -0,0 +1,488 @@ +use std::fmt::Display; +use std::ops::Add; + +use common::color::{GREEN, RED, YELLOW, RESET}; +use common::config::Input; +use common::error::{ErrorCore, ErrorKind::*, ErrorDisplay, MultiErrorDisplay, Location}; +use common::traits::{Stream, Locational}; +use common::ty::{Type, Predicate}; +use common::{Str, fmt_iter}; +use common::{impl_stream_for_wrapper, switch_lang}; + +use parser::error::{ParserRunnerError, ParserRunnerErrors}; + +use crate::hir::Expr; + +/// dname is for "double under name" +pub fn binop_to_dname(op: &str) -> &str { + match op { + "+" => "__add__", + "-" => "__sub__", + "*" => "__mul__", + "/" => "__div__", + "**" => "__pow__", + "%" => "__mod__", + ".." => "__rng__", + "<.." => "__lrng__", + "..<" => "__rrng__", + "<..<" => "__lrrng__", + "and" => "__and__", + "or" => "__or__", + "in" => "__in__", + "contains" => "__contains__", + "subof" => "__subof__", + "supof" => "__supof__", + "is" => "__is__", + "isnot" => "__isnot__", + "==" => "__eq__", + "!=" => "__ne__", + "<" => "__lt__", + "<=" => "__le__", + ">" => "__gt__", + ">=" => "__ge__", + _ => todo!(), + } +} + +pub fn unaryop_to_dname(op: &str) -> &str { + match op { + "+" => "__pos__", + "-" => "__neg__", + "~" => "__invert__", + "!" => "__mutate__", + "..." => "__spread__", + _ => todo!(), + } +} + +pub fn readable_name(name: &str) -> &str { + match name { + "__add__" => "`+`", + "__sub__" => "`-`", + "__mul__" => "`*`", + "__div__" => "`/`", + "__pow__" => "`**`", + "__mod__" => "`%`", + "__rng__" => "`..`", + "__lrng__" => "`<..`", + "__rrng__" => "`..<`", + "__lrrng__" => "`<..<`", + "__and__" => "`and`", + "__or__" => "`or`", + "__in__" => "`in`", + "__contains__" => "`contains`", + "__subof__" => "`subof`", + "__supof__" => "`supof`", + "__is__" => "`is`", + "__isnot__" => "`isnot`", + "__eq__" => "`==`", + "__ne__" => "`!=`", + "__lt__" => "`<`", + "__le__" => "`<=`", + "__gt__" => "`>`", + "__ge__" => "`>=`", + "__pos__" => "`+`", + "__neg__" => "`-`", + "__invert__" => "`~`", + "__mutate__" => "`!`", + "__spread__" => "`...`", + other => other, + } +} + +#[derive(Debug)] +pub struct CompileError { + pub core: ErrorCore, + pub input: Input, + pub caused_by: Str, +} + +impl From for CompileError { + fn from(err: ParserRunnerError) -> Self { + Self { core: err.core, input: err.input, caused_by: "".into() } + } +} + +impl ErrorDisplay for CompileError { + fn core(&self) -> &ErrorCore { &self.core } + fn input(&self) -> &Input { &self.input } + fn caused_by(&self) -> &str { &self.caused_by } + fn ref_inner(&self) -> Option<&Box> { None } +} + +impl CompileError { + pub const fn new(core: ErrorCore, input: Input, caused_by: Str) -> Self { Self{ core, input, caused_by } } + + pub fn compiler_bug(errno: usize, input: Input, loc: Location, fn_name: &str, line: u32) -> Self { + Self::new(ErrorCore::new(errno, CompilerSystemError, loc, switch_lang!( + format!("this is a bug of the Erg compiler, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"), + format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生") + ), None), input, "".into()) + } + + pub fn stack_bug(input: Input, loc: Location, stack_len: u32, block_id: usize, fn_name: &str) -> Self { + Self::new(ErrorCore::new(0, CompilerSystemError, loc, switch_lang!( + format!("the number of elements in the stack is invalid (num of elems: {stack_len}, block id: {block_id})\n\ + this is a bug of the Erg compiler, please report it (https://github.com/...)\n\ + caused from: {fn_name}"), + format!("スタックの要素数が異常です (要素数: {stack_len}, ブロックID: {block_id})\n\ + これはコンパイラのバグです、開発者に報告して下さい (https://github.com/...)\n\ + {fn_name}より発生") + ), None), input, "".into()) + } + + pub fn feature_error(input: Input, loc: Location, name: &str, caused_by: Str) -> Self { + Self::new(ErrorCore::new(0, FeatureError, loc, switch_lang!( + format!("this feature({name}) is not implemented yet"), + format!("この機能({name})はまだ正式に提供されていません") + ), None), input, caused_by) + } +} + +#[derive(Debug)] +pub struct TyCheckError { + pub core: ErrorCore, + pub caused_by: Str, +} + +impl TyCheckError { + pub const fn new(core: ErrorCore, caused_by: Str) -> Self { Self{ core, caused_by } } + + pub fn unreachable(fn_name: &str, line: u32) -> Self { Self::new(ErrorCore::unreachable(fn_name, line), "".into()) } + + pub fn checker_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self { + Self::new(ErrorCore::new(errno, CompilerSystemError, loc, switch_lang!( + format!("this is a bug of the Erg compiler, please report it to https://github.com/...\ncaused from: {fn_name}:{line}"), + format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/...)\n{fn_name}:{line}より発生") + ), None), "".into()) + } + + pub fn feature_error(loc: Location, name: &str, caused_by: Str) -> Self { + Self::new(ErrorCore::new(0, FeatureError, loc, switch_lang!( + format!("this feature({name}) is not implemented yet"), + format!("この機能({name})はまだ正式に提供されていません") + ), None), caused_by) + } + + pub fn syntax_error>(errno: usize, loc: Location, caused_by: Str, desc: S, hint: Option) -> Self { + Self::new(ErrorCore::new(errno, SyntaxError, loc, desc, hint), caused_by) + } + + pub fn duplicate_decl_error(loc: Location, caused_by: Str, name: &str) -> Self { + let name = readable_name(name); + Self::new(ErrorCore::new(0, NameError, loc, + switch_lang!( + format!("{name} is already declared"), + format!("{name}は既に宣言されています") + ), Option::::None), + caused_by + ) + } + + pub fn violate_decl_error(loc: Location, caused_by: Str, name: &str, spec_t: &Type, found_t: &Type) -> Self { + let name = readable_name(name); + Self::new(ErrorCore::new(0, TypeError, loc, + switch_lang!( + format!("{name} was declared as {GREEN}{spec_t}{RESET}, but an {RED}{found_t}{RESET} object is assigned"), + format!("{name}は{GREEN}{spec_t}{RESET}型として宣言されましたが、{RED}{found_t}{RESET}型のオブジェクトが代入されています") + ), Option::::None), + caused_by + ) + } + + pub fn no_type_spec_error(loc: Location, caused_by: Str, name: &str) -> Self { + let name = readable_name(name); + Self::new(ErrorCore::new(0, TypeError, loc, + switch_lang!( + format!("the type of {name} is not specified"), + format!("{name}の型が指定されていません") + ), None), + caused_by + ) + } + + pub fn no_var_error(loc: Location, caused_by: Str, name: &str, similar_name: Option<&Str>) -> Self { + let name = readable_name(name); + let hint = similar_name.map(|n| { + let n = readable_name(n); + switch_lang!( + format!("exists a similar name variable: {n}"), + format!("似た名前の変数があります: {n}") + ).into() + }); + Self::new(ErrorCore::new(0, NameError, loc, switch_lang!( + format!("{RED}{name}{RESET} is not defined"), + format!("{RED}{name}{RESET}という変数は定義されていません") + ), hint), caused_by) + } + + pub fn no_attr_error( + loc: Location, + caused_by: Str, + obj_t: &Type, + name: &str, + similar_name: Option<&Str>, + ) -> Self { + let hint = similar_name.map(|n| { + let n = readable_name(n); + switch_lang!( + format!("has a similar name attribute: {n}"), + format!("似た名前の属性があります: {n}") + ).into() + }); + Self::new(ErrorCore::new( 0, AttributeError, loc, switch_lang!( + format!("{obj_t} object has no attribute {RED}{name}{RESET}"), + format!("{obj_t}型オブジェクトに{RED}{name}{RESET}という属性はありません") + ), hint), + caused_by, + ) + } + + pub fn callable_impl_error<'a, C: Locational + Display>(callee: &C, param_ts: impl Iterator, caused_by: Str) -> Self { + let param_ts = fmt_iter(param_ts); + Self::new(ErrorCore::new(0, NotImplementedError, callee.loc(), switch_lang!( + format!("{callee} is not a Callable object that takes {param_ts} as an argument"), + format!("{callee}は{param_ts}を引数に取る呼び出し可能オブジェクトではありません") + ), None), caused_by) + } + + pub fn type_mismatch_error( + loc: Location, + caused_by: Str, + name: &str, + expect: &Type, + found: &Type, + ) -> Self { + let name = readable_name(name); + Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!( + format!("the type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"), + format!("{name}の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}") + ), None), caused_by) + } + + pub fn return_type_error( + loc: Location, + caused_by: Str, + name: &str, + expect: &Type, + found: &Type, + ) -> Self { + let name = readable_name(name); + Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!( + format!("the return type of {name} is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"), + format!("{name}の戻り値の型が違います。\n予期した型: {GREEN}{expect}{RESET}\n与えられた型: {RED}{found}{RESET}") + ), None), caused_by) + } + + pub fn uninitialized_error(loc: Location, caused_by: Str, name: &str, t: &Type) -> Self { + let name = readable_name(name); + Self::new(ErrorCore::new(0, NameError, loc, switch_lang!( + format!("{name}: {t} is not initialized"), + format!("{name}: {t}は初期化されていません") + ), None), caused_by) + } + + pub fn argument_error(loc: Location, caused_by: Str, expect: usize, found: usize) -> Self { + Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!( + format!("the number of positional arguments is mismatched:\nexpected: {GREEN}{expect}{RESET}\nbut found: {RED}{found}{RESET}"), + format!("ポジショナル引数の数が違います。\n予期した個数: {GREEN}{expect}{RESET}\n与えられた個数: {RED}{found}{RESET}") + ), None), caused_by) + } + + pub fn match_error(loc: Location, caused_by: Str, expr_t: &Type) -> Self { + Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!( + format!("not all patterns of type {expr_t} are covered"), + format!("{expr_t}型の全パターンを網羅していません") + ), None), caused_by) + } + + pub fn infer_error(loc: Location, caused_by: Str, expr: &str) -> Self { + Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!( + format!("failed to infer the type of {expr}"), + format!("{expr}の型が推論できません") + ), None), caused_by) + } + + pub fn reassign_error(loc: Location, caused_by: Str, name: &str) -> Self { + let name = readable_name(name); + Self::new(ErrorCore::new(0, AssignError, loc, switch_lang!( + format!("cannot assign twice to the immutable variable {name}"), + format!("定数{name}には再代入できません") + ), None), caused_by) + } + + pub fn too_many_args_error( + loc: Location, + callee_name: &str, + caused_by: Str, + params_len: usize, + pos_args_len: usize, + kw_args_len: usize + ) -> Self { + let name = readable_name(callee_name); + Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!( + format!("too many arguments for {name}: +total expected params: {GREEN}{params_len}{RESET} +passed positional args: {RED}{pos_args_len}{RESET} +passed keyword args: {RED}{kw_args_len}{RESET}"), + format!("{name}に渡された引数の数が多すぎます。 +必要な引数の合計数: {GREEN}{params_len}{RESET}個 +渡された引数の数: {RED}{pos_args_len}{RESET}個 +キーワード引数の数: {RED}{kw_args_len}{RESET}個") + ), None), caused_by) + } + + pub fn multiple_args_error( + loc: Location, + callee_name: &str, + caused_by: Str, + arg_name: &str, + ) -> Self { + let name = readable_name(callee_name); + Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!( + format!("{name}'s argument {RED}{arg_name}{RESET} is passed multiple times"), + format!("{name}の引数{RED}{arg_name}{RESET}が複数回渡されています") + ), None), caused_by) + } + + pub fn unexpected_kw_arg_error( + loc: Location, + callee_name: &str, + caused_by: Str, + param_name: &str, + ) -> Self { + let name = readable_name(callee_name); + Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!( + format!("{name} got unexpected keyword argument {RED}{param_name}{RESET}"), + format!("{name}には予期しないキーワード引数{RED}{param_name}{RESET}が渡されています") + ), None), caused_by) + } + + pub fn unused_warning(loc: Location, name: &str, caused_by: Str) -> Self { + let name = readable_name(name); + Self::new(ErrorCore::new(0, UnusedWarning, loc, switch_lang!( + format!("{YELLOW}{name}{RESET} is not used"), + format!("{YELLOW}{name}{RESET}は使用されていません") + ), None), caused_by) + } + + pub fn unification_error(lhs_t: &Type, rhs_t: &Type, lhs_loc: Option, rhs_loc: Option, caused_by: Str) -> Self { + let loc = match (lhs_loc, rhs_loc) { + (Some(l), Some(r)) => Location::pair(l, r), + (Some(l), None) => l, + (None, Some(r)) => r, + (None, None) => Location::Unknown, + }; + Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!( + format!("unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"), + format!("型の単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}") + ), None), caused_by) + } + + pub fn re_unification_error(lhs_t: &Type, rhs_t: &Type, lhs_loc: Option, rhs_loc: Option, caused_by: Str) -> Self { + let loc = match (lhs_loc, rhs_loc) { + (Some(l), Some(r)) => Location::pair(l, r), + (Some(l), None) => l, + (None, Some(r)) => r, + (None, None) => Location::Unknown, + }; + Self::new(ErrorCore::new(0, TypeError, loc, switch_lang!( + format!("re-unification failed:\nlhs: {YELLOW}{lhs_t}{RESET}\nrhs: {YELLOW}{rhs_t}{RESET}"), + format!("型の再単一化に失敗しました:\n左辺: {YELLOW}{lhs_t}{RESET}\n右辺: {YELLOW}{rhs_t}{RESET}") + ), None), caused_by) + } + + pub fn pred_unification_error(lhs: &Predicate, rhs: &Predicate, caused_by: Str) -> Self { + Self::new(ErrorCore::new(0, TypeError, Location::Unknown, switch_lang!( + format!("predicate unification failed:\nlhs: {YELLOW}{lhs}{RESET}\nrhs: {YELLOW}{rhs}{RESET}"), + format!("述語式の単一化に失敗しました:\n左辺: {YELLOW}{lhs}{RESET}\n右辺: {YELLOW}{rhs}{RESET}") + ), None), caused_by) + } + + pub fn has_effect>(expr: &Expr, caused_by: S) -> Self { + Self::new(ErrorCore::new(0, HasEffect, expr.loc(), switch_lang!( + format!("this expression causes a side-effect"), + format!("この式には副作用があります") + ), None), caused_by.into()) + } + + pub fn move_error(name: &Str, name_loc: Location, moved_loc: Location, caused_by: Str) -> Self { + Self::new(ErrorCore::new(0, MoveError, name_loc, switch_lang!( + format!("{RED}{name}{RESET} was moved in line {}", moved_loc.ln_begin().unwrap()), + format!("{RED}{name}{RESET}は{}行目ですでに移動されています", moved_loc.ln_begin().unwrap()) + ), None), caused_by) + } +} + +#[derive(Debug)] +pub struct TyCheckErrors(Vec); + +impl_stream_for_wrapper!(TyCheckErrors, TyCheckError); + +impl From> for TyCheckErrors { + fn from(errs: Vec) -> Self { Self(errs) } +} + +impl Add for TyCheckErrors { + type Output = Self; + fn add(self, other: Self) -> Self { + Self(self.0.into_iter().chain(other.0.into_iter()).collect()) + } +} + +impl From for TyCheckErrors { + fn from(err: TyCheckError) -> Self { Self(vec![err]) } +} + +pub type TyCheckResult = Result; +pub type TyCheckWarning = TyCheckError; +pub type TyCheckWarnings = TyCheckErrors; + +pub type EvalError = TyCheckError; +pub type EvalErrors = TyCheckErrors; +pub type EvalResult = TyCheckResult; + +pub type EffectError = TyCheckError; +pub type EffectErrors = TyCheckErrors; +pub type EffectResult = Result; + +pub type OwnershipError = TyCheckError; +pub type OwnershipErrors = TyCheckErrors; +pub type OwnershipResult = Result; + +pub type LowerError = TyCheckError; +pub type LowerWarning = LowerError; +pub type LowerErrors = TyCheckErrors; +pub type LowerWarnings = LowerErrors; +pub type LowerResult = TyCheckResult; + +#[derive(Debug)] +pub struct CompileErrors(Vec); + +impl_stream_for_wrapper!(CompileErrors, CompileError); + +impl From for CompileErrors { + fn from(err: ParserRunnerErrors) -> Self { + Self(err.into_iter().map(CompileError::from).collect()) + } +} + +impl From> for CompileErrors { + fn from(errs: Vec) -> Self { Self(errs) } +} + +impl From for CompileErrors { + fn from(err: CompileError) -> Self { Self(vec![err]) } +} + +impl MultiErrorDisplay for CompileErrors {} + +impl CompileErrors { + pub fn flush(&mut self) -> Self { + Self(self.0.drain(..).collect()) + } +} + +pub type CompileResult = Result; +pub type CompileWarning = CompileError; +pub type CompileWarnings = CompileErrors; diff --git a/src/compiler/eval.rs b/src/compiler/eval.rs new file mode 100644 index 00000000..31eeb302 --- /dev/null +++ b/src/compiler/eval.rs @@ -0,0 +1,450 @@ +use std::mem; + +use common::Str; +use common::{fn_name, set}; +use common::value::ValueObj; +use common::dict::Dict; +use common::rccell::RcCell; +use common::set::{Set}; +use common::traits::Stream; +use common::ty::{OpKind, TyParam, Type, Predicate, TyBound, ConstObj, SubrKind}; +use OpKind::*; + +use parser::ast::*; +use parser::token::Token; + +use crate::table::{SymbolTable, TyVarTable}; +use crate::error::{EvalError, EvalResult, TyCheckResult}; + +/// SubstTable::new([?T; 0], SymbolTable(Array(T, N))) => SubstTable{ params: { T: ?T; N: 0 } } +/// SubstTable::substitute([T; !N], SymbolTable(Array(T, N))): [?T; !0] +#[derive(Debug)] +struct SubstTable { + params: Dict, +} + +impl SubstTable { + pub fn new(substituted: &Type, ty_table: &SymbolTable) -> Self { + let param_names = ty_table.impls.iter() + .filter(|(_, vi)| vi.kind.is_parameter()) + .map(|(name, _)| name.inspect().clone()); + let self_ = SubstTable{ + params: param_names.zip(substituted.typarams().into_iter()).collect(), + }; + // REVIEW: 順番は保証されるか? 引数がunnamed_paramsに入る可能性は? + self_ + } + + fn substitute(&self, quant_t: Type, ty_table: &SymbolTable, level: usize) -> TyCheckResult { + let bounds = ty_table.bounds(); + let tvtab = TyVarTable::new(level, bounds); + let (inst, _) = SymbolTable::instantiate_t(quant_t, tvtab); + for param in inst.typarams() { + self.substitute_tp(¶m, ty_table)?; + } + Ok(inst) + } + + fn substitute_tp(&self, param: &TyParam, ty_table: &SymbolTable) -> TyCheckResult<()> { + match param { + TyParam::FreeVar(fv) => { + if let Some(name) = fv.unbound_name() { + if let Some(v) = self.params.get(&name) { + ty_table.unify_tp(param, v, None, false)?; + } + } else { + if fv.is_unbound() { panic!() } + } + } + TyParam::BinOp{ lhs, rhs, .. } => { + self.substitute_tp(lhs, ty_table)?; + self.substitute_tp(rhs, ty_table)?; + } + TyParam::UnaryOp{ val, .. } => { + self.substitute_tp(val, ty_table)?; + } + TyParam::Array(args) + | TyParam::Tuple(args) + | TyParam::App{ args, .. } + | TyParam::PolyQVar{ args, .. } => { + for arg in args.iter() { + self.substitute_tp(arg, ty_table)?; + } + } + TyParam::Type(t) => { self.substitute_t(t, ty_table)?; }, + TyParam::MonoProj{ obj, attr } => todo!("{obj}.{attr}"), + _ => {} + } + Ok(()) + } + + fn substitute_t(&self, t: &Type, ty_table: &SymbolTable) -> TyCheckResult<()> { + match t { + Type::FreeVar(fv) => { + if let Some(name) = fv.unbound_name() { + if let Some(v) = self.params.get(&name) { + if let TyParam::Type(v) = v { + ty_table.unify(t, v, None, None)?; + } else { + panic!() + } + } + } + } + t => todo!("{t}"), + } + Ok(()) + } +} + +#[derive(Debug, Default)] +pub struct Evaluator { +} + +impl Evaluator { + #[inline] + pub fn new() -> Self { Self::default() } + + #[inline] + pub(crate) fn eval_const_lit(&self, lit: &Literal) -> ValueObj { ValueObj::from(lit) } + + fn eval_const_acc(&self, _acc: &Accessor) -> Option { + todo!() + } + + fn eval_const_bin(&self, _bin: &BinOp) -> Option { + todo!() + } + + fn eval_const_unary(&self, _unary: &UnaryOp) -> Option { + todo!() + } + + // TODO: kw args + fn eval_args(&self, _args: &Args) -> Option> { + todo!() + } + + fn eval_const_call(&self, call: &Call, table: &SymbolTable) -> Option { + if let Expr::Accessor(acc) = call.obj.as_ref() { + match acc { + Accessor::Local(name) if name.is_const() => { + if let Some(ConstObj::Subr(subr)) = table.consts.get(name.inspect()) { + let args = self.eval_args(&call.args)?; + Some(subr.call(args)) + } else { + None + } + } + Accessor::Local(_) => None, + Accessor::Attr(_attr) => todo!(), + Accessor::TupleAttr(_attr) => todo!(), + Accessor::SelfDot(_name) => todo!(), + Accessor::Subscr(_subscr) => todo!(), + } + } else { None } + } + + fn eval_const_def(&self, def: &Def) -> Option { + if def.is_const() { + todo!() + } + None + } + + // ConstExprを評価するのではなく、コンパイル時関数の式(AST上ではただのExpr)を評価する + // コンパイル時評価できないならNoneを返す + pub(crate) fn eval_const_expr(&self, expr: &Expr, table: &SymbolTable) -> Option { + match expr { + Expr::Lit(lit) => Some(self.eval_const_lit(lit)), + Expr::Accessor(acc) => self.eval_const_acc(acc), + Expr::BinOp(bin) => self.eval_const_bin(bin), + Expr::UnaryOp(unary) => self.eval_const_unary(unary), + Expr::Call(call) => self.eval_const_call(call, table), + Expr::Def(def) => self.eval_const_def(def), + other => todo!("{other}"), + } + } + + pub(crate) fn eval_const_block(&self, block: &Block, table: &SymbolTable) -> Option { + for chunk in block.iter().rev().skip(1).rev() { + self.eval_const_expr(chunk, table)?; + } + self.eval_const_expr(block.last().unwrap(), table) + } + + fn eval_bin_lit(&self, op: OpKind, lhs: ValueObj, rhs: ValueObj) -> EvalResult { + match op { + Add => lhs.try_add(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())), + Sub => lhs.try_sub(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())), + Mul => lhs.try_mul(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())), + Div => lhs.try_div(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())), + Gt => lhs.try_gt(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())), + Ge => lhs.try_ge(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())), + Eq => lhs.try_eq(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())), + Ne => lhs.try_ne(rhs).ok_or(EvalError::unreachable(fn_name!(), line!())), + other => todo!("{other}"), + } + } + + pub(crate) fn eval_bin_tp(&self, op: OpKind, lhs: &TyParam, rhs: &TyParam) -> EvalResult { + match (lhs, rhs) { + (TyParam::ConstObj(ConstObj::Value(lhs)), TyParam::ConstObj(ConstObj::Value(rhs))) => + self.eval_bin_lit(op, lhs.clone(), rhs.clone()) + .map(|v| TyParam::value(v)), + (TyParam::ConstObj(ConstObj::MutValue(lhs)), TyParam::ConstObj(ConstObj::Value(rhs))) => + self.eval_bin_lit(op, lhs.borrow().clone(), rhs.clone()) + .map(|v| TyParam::ConstObj(ConstObj::MutValue(RcCell::new(v)))), + (TyParam::FreeVar(fv), r) => { + if fv.is_linked() { + self.eval_bin_tp(op, &*fv.crack(), r) + } else { + Err(EvalError::unreachable(fn_name!(), line!())) + } + }, + (l, TyParam::FreeVar(fv)) => { + if fv.is_linked() { + self.eval_bin_tp(op, l, &*fv.crack()) + } else { + Err(EvalError::unreachable(fn_name!(), line!())) + } + }, + (e @ TyParam::Erased(_), _) + | (_, e @ TyParam::Erased(_)) => Ok(e.clone()), + (l, r) => todo!("{l} {op} {r}"), + } + } + + fn eval_unary_lit(&self, op: OpKind, val: ConstObj) -> EvalResult { + match op { + Pos => todo!(), + Neg => todo!(), + Invert => todo!(), + Mutate => if let ConstObj::Value(v) = val { + Ok(ConstObj::MutValue(RcCell::new(v))) + } else { todo!() }, + other => todo!("{other}"), + } + } + + fn eval_unary_tp(&self, op: OpKind, val: &TyParam) -> EvalResult { + match val { + TyParam::ConstObj(c) => + self.eval_unary_lit(op, c.clone()).map(|c| TyParam::cons(c)), + TyParam::FreeVar(fv) if fv.is_linked() => { + self.eval_unary_tp(op, &*fv.crack()) + }, + e @ TyParam::Erased(_) => Ok(e.clone()), + other => todo!("{op} {other}"), + } + } + + fn eval_app(&self, _name: &Str, _args: &Vec) -> EvalResult { + todo!() + } + + /// 量化変数などはそのまま返す + pub(crate) fn eval_tp(&self, p: &TyParam, table: &SymbolTable) -> EvalResult { + match p { + TyParam::FreeVar(fv) if fv.is_linked() => + self.eval_tp(&fv.crack(), table), + TyParam::Mono(name) => + table.consts.get(name) + .and_then(|c| match c { + ConstObj::Value(v) => Some(TyParam::value(v.clone())), + _ => None, + }).ok_or(EvalError::unreachable(fn_name!(), line!())), + TyParam::BinOp{ op, lhs, rhs } => + self.eval_bin_tp(*op, lhs, rhs), + TyParam::UnaryOp{ op, val } => + self.eval_unary_tp(*op, val), + TyParam::App{ name, args } => + self.eval_app(name, args), + p @ ( + TyParam::Type(_) | TyParam::Erased(_) | TyParam::ConstObj(_) | TyParam::FreeVar(_) | TyParam::MonoQVar(_) + ) => Ok(p.clone()), + other => todo!("{other}"), + } + } + + pub(crate) fn eval_t(&self, substituted: Type, table: &SymbolTable, level: usize) -> EvalResult { + match substituted { + Type::FreeVar(fv) if fv.is_linked() => + self.eval_t(fv.crack().clone(), table, level), + Type::Subr(mut subr) => { + let kind = match subr.kind { + SubrKind::FuncMethod(self_t) => { + SubrKind::fn_met(self.eval_t(*self_t, table, level)?) + } + SubrKind::ProcMethod{ before, after } => { + let before = self.eval_t(*before, table, level)?; + if let Some(after) = after { + let after = self.eval_t(*after, table, level)?; + SubrKind::pr_met(before, Some(after)) + } else { + SubrKind::pr_met(before, None) + } + } + other => other, + }; + for p in subr.non_default_params.iter_mut() { + p.ty = self.eval_t(mem::take(&mut p.ty), table, level)?; + } + for p in subr.default_params.iter_mut() { + p.ty = self.eval_t(mem::take(&mut p.ty), table, level)?; + } + let return_t = self.eval_t(*subr.return_t, table, level)?; + Ok(Type::subr(kind, subr.non_default_params, subr.default_params, return_t)) + }, + Type::Array{ t, len } => { + let t = self.eval_t(*t, table, level)?; + let len = self.eval_tp(&len, table)?; + Ok(Type::array(t, len)) + }, + Type::Refinement(refine) => { + let mut preds = Set::with_capacity(refine.preds.len()); + for pred in refine.preds.into_iter() { + preds.insert(self.eval_pred(pred, table)?); + } + Ok(Type::refinement(refine.var, *refine.t, preds)) + }, + // [?T; 0].MutType! == [?T; !0] + Type::MonoProj{ lhs, rhs } => { + for ty_table in table.get_sorted_supertype_tables(&lhs) { + if let Ok(obj) = ty_table.get_local(&Token::symbol(&rhs), &table.name) { + if let ConstObj::Type(quant_t) = obj { + let subst_tab = SubstTable::new(&lhs, ty_table); + let t = subst_tab.substitute(*quant_t, ty_table, level)?; + let t = self.eval_t(t, &table, level)?; + return Ok(t) + } else { todo!() } + } + } + todo!() + }, + Type::Range(l) => Ok(Type::range(self.eval_t(*l, table, level)?)), + Type::Iter(l) => Ok(Type::iter(self.eval_t(*l, table, level)?)), + Type::Ref(l) => Ok(Type::refer(self.eval_t(*l, table, level)?)), + Type::RefMut(l) => Ok(Type::ref_mut(self.eval_t(*l, table, level)?)), + Type::Option(l) => Ok(Type::option_mut(self.eval_t(*l, table, level)?)), + Type::OptionMut(l) => Ok(Type::option_mut(self.eval_t(*l, table, level)?)), + Type::VarArgs(l) => Ok(Type::var_args(self.eval_t(*l, table, level)?)), + Type::Poly{ name, mut params } => { + for p in params.iter_mut() { + *p = self.eval_tp(&mem::take(p), table)?; + } + Ok(Type::poly(name, params)) + }, + other if other.is_monomorphic() => Ok(other), + other => todo!("{other}"), + } + } + + pub(crate) fn _eval_bound(&self, bound: TyBound, table: &SymbolTable, level: usize) -> EvalResult { + match bound { + TyBound::Subtype{ sub, sup } => + Ok(TyBound::subtype( + self.eval_t(sub, table, level)?, + self.eval_t(sup, table, level)? + )), + TyBound::Instance{ name: inst, t } => + Ok(TyBound::instance(inst, self.eval_t(t, table, level)?)), + } + } + + pub(crate) fn eval_pred(&self, p: Predicate, table: &SymbolTable) -> EvalResult { + match p { + Predicate::Value(_) | Predicate::Const(_) => Ok(p), + Predicate::Equal{ lhs, rhs } => + Ok(Predicate::eq(lhs, self.eval_tp(&rhs, table)?)), + Predicate::NotEqual{ lhs, rhs } => + Ok(Predicate::ne(lhs, self.eval_tp(&rhs, table)?)), + Predicate::LessEqual{ lhs, rhs } => + Ok(Predicate::le(lhs, self.eval_tp(&rhs, table)?)), + Predicate::GreaterEqual{ lhs, rhs } => + Ok(Predicate::ge(lhs, self.eval_tp(&rhs, table)?)), + Predicate::And(l, r) => + Ok(Predicate::and(self.eval_pred(*l, table)?, self.eval_pred(*r, table)?)), + Predicate::Or(l, r) => + Ok(Predicate::or(self.eval_pred(*l, table)?, self.eval_pred(*r, table)?)), + Predicate::Not(l, r) => + Ok(Predicate::not(self.eval_pred(*l, table)?, self.eval_pred(*r, table)?)), + } + } + + pub(crate) fn get_tp_t(&self, p: &TyParam, bounds: Option<&Set>, table: &SymbolTable) -> EvalResult { + let p = self.eval_tp(p, table)?; + match p { + TyParam::ConstObj(ConstObj::Value(v)) => Ok(Type::enum_t(set![v])), + TyParam::ConstObj(ConstObj::MutValue(v)) => Ok(v.borrow().class().mutate()), + TyParam::Erased(t) => Ok((&*t).clone()), + TyParam::FreeVar(fv) => + if let Some(t) = fv.type_of() { Ok(t) } else { todo!() }, + TyParam::Type(_) => Ok(Type::Type), + TyParam::Mono(name) => + table.consts.get(&name) + .and_then(|c| match c { + ConstObj::Value(v) => Some(Type::enum_t(set![v.clone()])), + _ => None, + }).ok_or(EvalError::unreachable(fn_name!(), line!())), + TyParam::MonoQVar(name) => { + if let Some(bs) = bounds { + if let Some(bound) = bs.iter().find(|b| b.mentions_as_instance(&name)) { + Ok(bound.t().clone()) + } else { todo!() } + } else { todo!() } + }, + TyParam::UnaryOp{ op, val } => { + match op { + OpKind::Mutate => Ok(self.get_tp_t(&val, bounds, table)?.mutate()), + _ => todo!(), + } + }, + other => todo!("{other}"), + } + } + + pub(crate) fn _get_tp_class(&self, p: &TyParam, table: &SymbolTable) -> EvalResult { + let p = self.eval_tp(p, table)?; + match p { + TyParam::ConstObj(ConstObj::Value(v)) => Ok(v.class()), + TyParam::Erased(t) => Ok((&*t).clone()), + | TyParam::FreeVar(fv) => + if let Some(t) = fv.type_of() { Ok(t) } else { todo!() }, + TyParam::Type(_) => Ok(Type::Type), + TyParam::Mono(name) => + table.consts.get(&name) + .and_then(|c| match c { + ConstObj::Value(v) => Some(v.class()), + _ => None, + }).ok_or(EvalError::unreachable(fn_name!(), line!())), + other => todo!("{other}"), + } + } + + /// NOTE: lとrが型の場合はSymbolTableの方で判定する + pub(crate) fn shallow_eq_tp(&self, lhs: &TyParam, rhs: &TyParam, table: &SymbolTable) -> bool { + match (lhs, rhs) { + (TyParam::Type(l), TyParam::Type(r)) => l == r, + (TyParam::ConstObj(l), TyParam::ConstObj(r)) => l == r, + (TyParam::Erased(l), TyParam::Erased(r)) => l == r, + (TyParam::FreeVar{ .. }, TyParam::FreeVar{ .. }) => true, + (TyParam::Mono(l), TyParam::Mono(r)) => { + if l == r { true } + else if let (Some(l), Some(r)) = (table.consts.get(l), table.consts.get(r)) { l == r } + else { + // lとrが型の場合は... + false + } + }, + (TyParam::BinOp{ .. }, TyParam::BinOp{ .. }) => todo!(), + (TyParam::UnaryOp{ .. }, TyParam::UnaryOp{ .. }) => todo!(), + (TyParam::App{ .. }, TyParam::App{ .. }) => todo!(), + (TyParam::Mono(m), TyParam::ConstObj(l)) + | (TyParam::ConstObj(l), TyParam::Mono(m)) => + if let Some(o) = table.consts.get(m) { o == l } else { true }, + (TyParam::MonoQVar(_), _) | (_, TyParam::MonoQVar(_)) => false, + (l, r) => todo!("l: {l}, r: {r}"), + } + } +} diff --git a/src/compiler/hir.rs b/src/compiler/hir.rs new file mode 100644 index 00000000..60023efa --- /dev/null +++ b/src/compiler/hir.rs @@ -0,0 +1,829 @@ +/// defines High-level Intermediate Representation +use std::fmt; + +use common::{Str}; +use common::value::ValueObj; +use common::error::Location; +use common::traits::{HasType, Locational, Stream, NestedDisplay}; +use common::ty::{Type, TyParam, Constraint}; +use common::{ + impl_locational, impl_locational_for_enum, + impl_stream_for_wrapper, impl_display_for_enum, + impl_nested_display_for_enum, impl_display_from_nested, +}; + +use parser::token::{Token, TokenKind}; +use parser::ast::{VarName, VarPattern, Params, DefId, fmt_lines}; + +#[derive(Debug, Clone)] +pub struct Literal { + pub data: ValueObj, // for constant folding + pub token: Token, // for Locational + t: Type, +} + +impl HasType for Literal { + #[inline] + fn ref_t(&self) -> &Type { &self.t } + #[inline] + fn signature_t(&self) -> Option<&Type> { None } +} + +impl NestedDisplay for Literal { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{}", self.token) + } +} + +impl_display_from_nested!(Literal); + +impl Locational for Literal { + #[inline] + fn loc(&self) -> Location { self.token.loc() } +} + +impl From for Literal { + fn from(token: Token) -> Self { + let data = ValueObj::from_str(Type::from(token.kind), token.content.clone()); + Self { t: data.t(), data, token } + } +} + +impl Literal { + pub fn new(c: ValueObj, lineno: usize, col: usize) -> Self { + let kind = TokenKind::from(&c); + let token = Token::new(kind, c.to_string(), lineno, col); + Self { t: c.t(), data: c, token } + } + + #[inline] + pub fn is(&self, kind: TokenKind) -> bool { + self.token.is(kind) + } +} + +#[derive(Debug, Clone)] +pub struct PosArg { + pub expr: Expr, +} + +impl NestedDisplay for PosArg { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + self.expr.fmt_nest(f, level) + } +} + +impl_display_from_nested!(PosArg); + +impl Locational for PosArg { + fn loc(&self) -> Location { self.expr.loc() } +} + +impl PosArg { + pub const fn new(expr: Expr) -> Self { Self { expr } } +} + +#[derive(Debug, Clone)] +pub struct KwArg { + pub keyword: Token, + pub expr: Expr, +} + +impl NestedDisplay for KwArg { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + write!(f, "{}:\n", self.keyword)?; + self.expr.fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(KwArg); + +impl Locational for KwArg { + fn loc(&self) -> Location { + Location::concat(&self.keyword, &self.expr) + } +} + +impl KwArg { + pub const fn new(keyword: Token, expr: Expr) -> Self { Self { keyword, expr } } +} + +#[derive(Debug, Clone)] +pub struct Args { + pos_args: Vec, + kw_args: Vec, + paren: Option<(Token, Token)>, +} + +impl NestedDisplay for Args { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + if !self.pos_args.is_empty() { fmt_lines(self.pos_args.iter(), f, level)?; } + if !self.kw_args.is_empty() { fmt_lines(self.kw_args.iter(), f, level)?; } + Ok(()) + } +} + +impl_display_from_nested!(Args); + +impl Locational for Args { + fn loc(&self) -> Location { + if let Some((l, r)) = &self.paren { + Location::concat(l, r) + } else if !self.kw_args.is_empty() { + Location::concat(self.kw_args.first().unwrap(), self.kw_args.last().unwrap()) + } else if !self.pos_args.is_empty() { + Location::concat(self.pos_args.first().unwrap(), self.pos_args.last().unwrap()) + } else { + Location::Unknown + } + } +} + +// impl_stream!(Args, KwArg, kw_args); + +impl Args { + pub const fn new(pos_args: Vec, kw_args: Vec, paren: Option<(Token, Token)>) -> Self { + Self { pos_args, kw_args, paren } + } + + pub const fn empty() -> Self { + Self::new(vec![], vec![], None) + } + + #[inline] + pub fn len(&self) -> usize { + self.pos_args.len() + self.kw_args.len() + } + + #[inline] + pub fn kw_len(&self) -> usize { self.kw_args.len() } + + pub fn pos_args(&self) -> &[PosArg] { &self.pos_args[..] } + + pub fn kw_args(&self) -> &[KwArg] { &self.kw_args[..] } + + pub fn push_pos(&mut self, pos: PosArg) { + self.pos_args.push(pos); + } + + pub fn push_kw(&mut self, kw: KwArg) { + self.kw_args.push(kw); + } + + pub fn remove(&mut self, index: usize) -> Expr { + if self.pos_args.get(index).is_some() { + self.pos_args.remove(index).expr + } else { + self.kw_args.remove(index-self.pos_args.len()).expr + } + } + + /// try_remove((1, 2, z: 3), 2) == Some(3) + pub fn try_remove(&mut self, index: usize) -> Option { + if self.pos_args.get(index).is_some() { + Some(self.pos_args.remove(index).expr) + } else { + self.kw_args.get(index-self.pos_args.len())?; + Some(self.kw_args.remove(index-self.pos_args.len()).expr) + } + } + + pub fn try_remove_pos(&mut self, index: usize) -> Option { + self.pos_args.get(index)?; + Some(self.pos_args.remove(index)) + } + + pub fn try_remove_kw(&mut self, index: usize) -> Option { + self.kw_args.get(index)?; + Some(self.kw_args.remove(index)) + } + + pub fn get(&self, index: usize) -> Option<&Expr> { + if self.pos_args.get(index).is_some() { + self.pos_args.get(index).map(|a| &a.expr) + } else { + self.kw_args.get(index-self.pos_args.len()).map(|a| &a.expr) + } + } +} + +/// represents local variables +#[derive(Debug, Clone)] +pub struct Local { + pub name: Token, + t: Type, +} + +impl fmt::Display for Local { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.t != Type::ASTOmitted { + write!(f, "{} (: {})", self.name.content, self.t) + } else { + write!(f, "{}", self.name.content) + } + } +} + +impl HasType for Local { + #[inline] + fn ref_t(&self) -> &Type { &self.t } + #[inline] + fn signature_t(&self) -> Option<&Type> { None } +} + +impl Locational for Local { + #[inline] + fn loc(&self) -> Location { self.name.loc() } +} + +impl Local { + pub const fn new(name: Token, t: Type) -> Self { Self{ name, t } } + + // &strにするとクローンしたいときにアロケーションコストがかかるので&Strのままで + #[inline] + pub fn inspect(&self) -> &Str { &self.name.content } +} + +#[derive(Debug, Clone)] +pub struct Attribute { + pub obj: Box, + pub name: Token, + t: Type +} + +impl fmt::Display for Attribute { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({}).{}", self.obj, self.name) + } +} + +impl_locational!(Attribute, obj, name); + +impl HasType for Attribute { + #[inline] + fn ref_t(&self) -> &Type { &self.t } + #[inline] + fn signature_t(&self) -> Option<&Type> { None } +} + +impl Attribute { + pub fn new(obj: Expr, name: Token, t: Type) -> Self { + Self { obj: Box::new(obj), name, t } + } +} + +#[derive(Debug, Clone)] +pub struct Subscript { + obj: Box, + index: Box, + t: Type +} + +impl fmt::Display for Subscript { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({})[{}]", self.obj, self.index) + } +} + +impl_locational!(Subscript, obj, index); + +impl HasType for Subscript { + #[inline] + fn ref_t(&self) -> &Type { &self.t } + #[inline] + fn signature_t(&self) -> Option<&Type> { None } +} + +impl Subscript { + pub fn new(obj: Expr, index: Expr, t: Type) -> Self { + Self { obj: Box::new(obj), index: Box::new(index), t } + } +} + +#[derive(Debug, Clone)] +pub enum Accessor { + Local(Local), + SelfDot(Local), + Attr(Attribute), + Subscr(Subscript), +} + +impl NestedDisplay for Accessor { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + match self { + Self::Local(name) => write!(f, "{}", name), + Self::SelfDot(attr) => write!(f, "self.{}", attr), + Self::Attr(attr) => write!(f, "{}", attr), + Self::Subscr(subscr) => write!(f, "{}", subscr), + } + } +} + +impl_display_from_nested!(Accessor); +impl_locational_for_enum!(Accessor; Local, SelfDot, Attr, Subscr); + +impl HasType for Accessor { + #[inline] + fn ref_t(&self) -> &Type { + match self { + Self::Local(n) | Self::SelfDot(n) => n.ref_t(), + Self::Attr(a) => a.ref_t(), + Self::Subscr(s) => s.ref_t(), + } + } + #[inline] + fn signature_t(&self) -> Option<&Type> { None } +} + +impl Accessor { + pub const fn local(symbol: Token, t: Type) -> Self { Self::Local(Local::new(symbol, t)) } + + pub const fn self_dot(name: Token, t: Type) -> Self { Self::SelfDot(Local::new(name, t)) } + + pub fn attr(obj: Expr, name: Token, t: Type) -> Self { + Self::Attr(Attribute::new(obj, name, t)) + } + + pub fn subscr(obj: Expr, index: Expr, t: Type) -> Self { + Self::Subscr(Subscript::new(obj, index, t)) + } +} + +#[derive(Debug, Clone)] +pub struct Array { + pub l_sqbr: Token, + pub r_sqbr: Token, + t: Type, + pub elems: Args, + pub guard: Option>, +} + +impl HasType for Array { + #[inline] + fn ref_t(&self) -> &Type { &self.t } + #[inline] + fn signature_t(&self) -> Option<&Type> { None } +} + +impl NestedDisplay for Array { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + if let Some(guard) = &self.guard { + write!(f, "[{} | {}]", self.elems, guard) + } else { + write!(f, "[{}]", self.elems) + } + } +} + +impl_display_from_nested!(Array); +impl_locational!(Array, l_sqbr, r_sqbr); + +impl Array { + pub fn new(l_sqbr: Token, r_sqbr: Token, level: usize, elems: Args, guard: Option) -> Self { + let elem_t = elems.pos_args().first() + .map(|a| a.expr.t()) + .unwrap_or_else(|| Type::free_var(level, Constraint::TypeOf(Type::Type))); + let t = Type::array(elem_t, TyParam::value(elems.len())); + Self { l_sqbr, r_sqbr, t, elems, guard: guard.map(Box::new) } + } + + pub fn push(&mut self, elem: Expr) { + self.elems.push_pos(PosArg::new(elem)); + } +} + +#[derive(Debug, Clone)] +pub struct Dict { + pub l_brace: Token, + pub r_brace: Token, + pub attrs: Args, // TODO: keyをTokenではなくExprにする +} + +impl HasType for Dict { + fn ref_t(&self) -> &Type { todo!() } + fn t(&self) -> Type { todo!() } + #[inline] + fn signature_t(&self) -> Option<&Type> { None } +} + +impl NestedDisplay for Dict { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{{{}}}", self.attrs) + } +} + +impl_display_from_nested!(Dict); +impl_locational!(Dict, l_brace, r_brace); + +impl Dict { + pub const fn new(l_brace: Token, r_brace: Token, attrs: Args) -> Self { + Self { l_brace, r_brace, attrs } + } +} + +#[derive(Debug, Clone)] +pub struct BinOp { + pub op: Token, + pub lhs: Box, + pub rhs: Box, + pub sig_t: Type, // e.g. (Int, Int) -> Int +} + +impl NestedDisplay for BinOp { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + write!(f, "`{}`:\n", self.op.content)?; + self.lhs.fmt_nest(f, level + 1)?; + write!(f, "\n")?; + self.rhs.fmt_nest(f, level + 1) + } +} + +impl HasType for BinOp { + #[inline] + fn ref_t(&self) -> &Type { &self.sig_t.return_t().unwrap() } + #[inline] + fn lhs_t(&self) -> &Type { &self.sig_t.lhs_t() } + #[inline] + fn rhs_t(&self) -> &Type { &self.sig_t.rhs_t() } + #[inline] + fn signature_t(&self) -> Option<&Type> { Some(&self.sig_t) } +} + +impl_display_from_nested!(BinOp); +impl_locational!(BinOp, lhs, rhs); + +impl BinOp { + pub fn new(op: Token, lhs: Expr, rhs: Expr, sig_t: Type) -> Self { + Self { op, lhs: Box::new(lhs), rhs: Box::new(rhs), sig_t } + } +} + +#[derive(Debug, Clone)] +pub struct UnaryOp { + pub op: Token, + pub expr: Box, + pub sig_t: Type, // e.g. Neg -> Nat +} + +impl HasType for UnaryOp { + #[inline] + fn ref_t(&self) -> &Type { &self.sig_t.return_t().unwrap() } + #[inline] + fn lhs_t(&self) -> &Type { self.expr.ref_t() } + #[inline] + fn rhs_t(&self) -> &Type { panic!("invalid operation") } + #[inline] + fn signature_t(&self) -> Option<&Type> { Some(&self.sig_t) } +} + +impl NestedDisplay for UnaryOp { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + write!(f, "`{}`: {}:\n", self.op, self.sig_t)?; + self.expr.fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(UnaryOp); +impl_locational!(UnaryOp, op, expr); + +impl UnaryOp { + pub fn new(op: Token, expr: Expr, sig_t: Type) -> Self { + Self { op, expr: Box::new(expr), sig_t } + } +} + +#[derive(Debug, Clone)] +pub struct Call { + pub obj: Box, + pub args: Args, + /// 全体の型、e.g. `abs(-1)` -> `Neg -> Nat` + /// necessary for mangling + pub sig_t: Type, +} + +impl NestedDisplay for Call { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + write!(f, "({}): {}:\n", self.obj, self.sig_t)?; + self.args.fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(Call); + +impl HasType for Call { + #[inline] + fn ref_t(&self) -> &Type { &self.sig_t.return_t().unwrap() } + #[inline] + fn lhs_t(&self) -> &Type { self.sig_t.lhs_t() } + #[inline] + fn rhs_t(&self) -> &Type { self.sig_t.rhs_t() } + #[inline] + fn signature_t(&self) -> Option<&Type> { Some(&self.sig_t) } +} + +impl Locational for Call { + fn loc(&self) -> Location { + Location::concat(self.obj.as_ref(), &self.args) + } +} + +impl Call { + pub fn new(obj: Expr, args: Args, sig_t: Type) -> Self { + Self { obj: Box::new(obj), args, sig_t } + } +} + +#[derive(Debug, Clone)] +pub struct Block(Vec); + +impl HasType for Block { + #[inline] + fn ref_t(&self) -> &Type { self.last().unwrap().ref_t() } + #[inline] + fn t(&self) -> Type { self.last().unwrap().t() } + #[inline] + fn signature_t(&self) -> Option<&Type> { self.last().unwrap().signature_t() } +} + +impl NestedDisplay for Block { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + fmt_lines(self.0.iter(), f, level) + } +} + +impl_display_from_nested!(Block); +impl_stream_for_wrapper!(Block, Expr); + +impl Locational for Block { + fn loc(&self) -> Location { + Location::concat(self.0.first().unwrap(), self.0.last().unwrap()) + } +} + +#[derive(Debug, Clone, Hash)] +pub struct VarSignature { + pub pat: VarPattern, + pub t: Type, +} + +impl fmt::Display for VarSignature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} (: {})", self.pat, self.t) + } +} + +impl Locational for VarSignature { + fn loc(&self) -> Location { self.pat.loc() } +} + +impl VarSignature { + pub const fn new(pat: VarPattern, t: Type) -> Self { Self{ pat, t } } + + pub fn inspect(&self) -> Option<&Str> { self.pat.inspect() } +} + +#[derive(Debug, Clone)] +pub struct SubrSignature { + pub name: VarName, + pub params: Params, + pub t: Type, +} + +impl fmt::Display for SubrSignature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}{} (: {})", self.name, self.params, self.t) + } +} + +impl Locational for SubrSignature { + fn loc(&self) -> Location { + Location::concat(&self.name, &self.params) + } +} + +impl SubrSignature { + pub const fn new(name: VarName, params: Params, t: Type) -> Self { + Self{ name, params, t } + } + + pub fn is_procedural(&self) -> bool { self.name.is_procedural() } +} + +#[derive(Debug, Clone)] +pub struct Lambda { + pub params: Params, + op: Token, + pub body: Block, + pub id: usize, + t: Type +} + +impl HasType for Lambda { + #[inline] + fn ref_t(&self) -> &Type { &self.t } + #[inline] + fn signature_t(&self) -> Option<&Type> { None } +} + +impl NestedDisplay for Lambda { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + write!(f, "{} {}\n", self.params, self.op.content)?; + self.body.fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(Lambda); +impl_locational!(Lambda, params, body); + +impl Lambda { + pub const fn new(id: usize, params: Params, op: Token, body: Block, t: Type) -> Self { + Self { id, params, op, body, t } + } + + pub fn is_procedural(&self) -> bool { self.op.is(TokenKind::ProcArrow) } +} + +#[derive(Debug, Clone)] +pub enum Signature { + Var(VarSignature), + Subr(SubrSignature), +} + +impl_display_for_enum!(Signature; Var, Subr,); +impl_locational_for_enum!(Signature; Var, Subr,); + +impl Signature { + pub const fn is_subr(&self) -> bool { matches!(self, Self::Subr(_)) } + + pub fn is_const(&self) -> bool { + match self { + Self::Var(v) => v.pat.is_const(), + Self::Subr(s) => s.name.is_const(), + } + } + + pub fn is_procedural(&self) -> bool { + match self { + Self::Var(v) => v.pat.is_procedural(), + Self::Subr(s) => s.name.is_procedural(), + } + } +} + +/// represents a declaration of a variable +/// necessary for type field declaration +#[derive(Debug, Clone)] +pub struct Decl { + pub sig: Signature, + t: Type, +} + +impl NestedDisplay for Decl { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{}: {}", self.sig, self.t) + } +} + +impl_display_from_nested!(Decl); + +impl Locational for Decl { + #[inline] + fn loc(&self) -> Location { self.sig.loc() } +} + +impl Decl { + pub const fn spec_t(&self) -> &Type { &self.t } + + pub const fn is_sub(&self) -> bool { self.sig.is_subr() } +} + +#[derive(Clone, Debug)] +pub struct DefBody { + pub op: Token, + pub block: Block, + pub id : DefId, +} + +impl_locational!(DefBody, op, block); + +impl DefBody { + pub const fn new(op: Token, block: Block, id: DefId) -> Self { Self { op, block, id } } + + pub fn is_type(&self) -> bool { + match self.block.first().unwrap() { + Expr::Call(call) => { + if let Expr::Accessor(Accessor::Local(local)) = call.obj.as_ref() { + &local.inspect()[..] == "Type" + } else { false } + }, + _ => false, + } + } +} + +#[derive(Debug, Clone)] +pub struct Def { + pub sig: Signature, + pub body: DefBody, +} + +impl NestedDisplay for Def { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + write!(f, "{} {}\n", self.sig, self.body.op.content)?; + self.body.block.fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(Def); +impl_locational!(Def, sig, body); + +impl Def { + pub const fn new(sig: Signature, body: DefBody) -> Self { Self { sig, body } } +} + +#[derive(Debug, Clone)] +pub enum Expr { + Lit(Literal), + Accessor(Accessor), + Array(Array), + // Dict(Dict), + // Set(Set), + Dict(Dict), + BinOp(BinOp), + UnaryOp(UnaryOp), + Call(Call), + Lambda(Lambda), + Decl(Decl), + Def(Def), +} + +impl_nested_display_for_enum!(Expr; Lit, Accessor, Array, Dict, BinOp, UnaryOp, Call, Lambda, Decl, Def); +impl_display_from_nested!(Expr); +impl_locational_for_enum!(Expr; Lit, Accessor, Array, Dict, BinOp, UnaryOp, Call, Lambda, Decl, Def); + +impl HasType for Expr { + fn ref_t(&self) -> &Type { + match self { + Expr::Lit(lit) => lit.ref_t(), + Expr::Accessor(accessor) => accessor.ref_t(), + Expr::Array(array) => array.ref_t(), + Expr::Dict(dict) => dict.ref_t(), + Expr::BinOp(bin) => bin.ref_t(), + Expr::UnaryOp(unary) => unary.ref_t(), + Expr::Call(call) => call.ref_t(), + Expr::Lambda(lambda) => lambda.ref_t(), + _ => &Type::NoneType, + } + } + fn signature_t(&self) -> Option<&Type> { + match self { + Expr::BinOp(bin) => bin.signature_t(), + Expr::UnaryOp(unary) => unary.signature_t(), + Expr::Call(call) => call.signature_t(), + _ => None, + } + } +} + +impl Expr { + pub fn receiver_t(&self) -> Option<&Type> { + match self { + Self::Accessor(Accessor::Attr(attr)) => Some(attr.obj.ref_t()), + _other => None, + } + } +} + +/// Toplevel grammar unit +#[derive(Debug, Clone)] +pub struct Module(Vec); + +impl fmt::Display for Module { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt_lines(self.0.iter(), f, 0) + } +} + +impl Locational for Module { + fn loc(&self) -> Location { + Location::concat(self.0.first().unwrap(), self.0.last().unwrap()) + } +} + +impl_stream_for_wrapper!(Module, Expr); + +#[derive(Debug)] +pub struct HIR { + pub name: Str, + pub module: Module, +} + +impl std::fmt::Display for HIR { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.module) + } +} + +impl HIR { + pub const fn new(name: Str, module: Module) -> Self { Self { name, module } } +} diff --git a/src/compiler/initialize.rs b/src/compiler/initialize.rs new file mode 100644 index 00000000..45dfb9bc --- /dev/null +++ b/src/compiler/initialize.rs @@ -0,0 +1,463 @@ +//! defines type information for builtin objects (in `SymbolTable`) +//! +//! 組み込みオブジェクトの型情報を(記号表に)定義 +use common::{Str}; +use common::{set, debug_power_assert}; +use common::ty::{Type, TyParam, ConstObj}; +use Type::*; +use common::ty::type_constrs::*; +use ParamSpec as PS; + +use parser::ast::{VarName}; + +use crate::varinfo::{Mutability, Visibility, VarInfo, VarKind}; +use crate::table::{SymbolTable, ParamSpec, DefaultInfo}; +use Visibility::*; +use Mutability::*; +use VarKind::*; +use DefaultInfo::*; + +// NOTE: TyParam::MonoQuantVarは生成時に型を指定する必要があるが、逆にそちらがあれば型境界を指定しなくてもよい +impl SymbolTable { + fn register_decl(&mut self, name: &'static str, t: Type, vis: Visibility) { + let name = VarName::from_static(name); + if self.decls.get(&name).is_some() { + panic!("already registered: {name}"); + } else { + self.decls.insert(name, VarInfo::new(t, Immutable, vis, Builtin)); + } + } + + fn register_impl(&mut self, name: &'static str, t: Type, muty: Mutability, vis: Visibility) { + let name = VarName::from_static(name); + if self.impls.get(&name).is_some() { + panic!("already registered: {name}"); + } else { + self.impls.insert(name, VarInfo::new(t, muty, vis, Builtin)); + } + } + + fn register_const(&mut self, name: &'static str, obj: ConstObj) { + if self.consts.get(name).is_some() { + panic!("already registered: {name}"); + } else { + self.consts.insert(Str::ever(name), obj); + } + } + + fn register_type(&mut self, t: Type, table: Self, muty: Mutability) { + if self.types.contains_key(&t) { + panic!("{} has already been registered", t.name()); + } else { + let name = VarName::from_str(Str::rc(t.name())); + self.impls.insert(name, VarInfo::new(Type, muty, Private, Builtin)); + self.types.insert(t, table); + } + } + + fn register_patch(&mut self, name: &'static str, table: Self, muty: Mutability) { + if self.patches.contains_key(name) { + panic!("{} has already been registered", name); + } else { + let name = VarName::from_static(name); + self.impls.insert(name.clone(), VarInfo::new(Type, muty, Private, Builtin)); + for method_name in table.impls.keys() { + if let Some(patches) = self._method_impl_patches.get_mut(method_name) { + patches.push(name.clone()); + } else { + self._method_impl_patches.insert(method_name.clone(), vec![name.clone()]); + } + } + debug_power_assert!(table.super_classes.len(), ==, 1); + if let Some(target_type) = table.super_classes.first() { + for impl_trait in table.super_traits.iter() { + self.glue_patch_and_types.push( + (VarName::from_str(table.name.clone()), target_type.clone(), impl_trait.clone()) + ); + } + } + self.patches.insert(name, table); + } + } + + /// see std/prelude.er + // 型境界はすべて各サブルーチンで定義する + // push_subtype_boundなどはユーザー定義APIの型境界決定のために使用する + fn init_builtin_traits(&mut self) { + let mut eq = Self::poly_trait("Eq", vec![PS::t("R", WithDefault)], vec![], Self::TOP_LEVEL); + // __eq__: |Self <: Eq; R <: Eq()| Self(R).(R) -> Bool + let op_t = fn1_met(poly("Self", vec![mono_q_tp("R")]), mono_q("R"), Bool); + let op_t = quant(op_t, set!{subtype(mono_q("Self"), mono("Eq")), subtype(mono_q("R"), poly("Eq", vec![]))}); + eq.register_decl("__eq__", op_t.clone(), Public); + let mut ord = Self::poly_trait("Ord", vec![PS::t("R", WithDefault)], vec![mono("Eq")], Self::TOP_LEVEL); + let op_t = fn1_met(poly("Self", vec![mono_q_tp("R")]), mono_q("R"), Bool); + let op_t = quant(op_t, set!{subtype(mono_q("Self"), mono("Ord")), subtype(mono_q("R"), poly("Ord", vec![]))}); + ord.register_decl("__lt__", op_t.clone(), Public); + let mut seq = Self::poly_trait("Seq", vec![PS::t("T", NonDefault)], vec![], Self::TOP_LEVEL); + let self_t = poly_q("Self", vec![TyParam::t(mono_q("T"))]); + let t = fn0_met(self_t.clone(), Nat); + let t = quant(t, set!{subtype(self_t.clone(), mono("Seq"))}); + seq.register_decl("__len__", t, Public); + let t = Type::fn1_met(self_t.clone(), Nat, mono_q("T")); + let t = quant(t, set!{subtype(self_t, mono("Seq")), static_instance("T", Type)}); + seq.register_decl("get", t, Public); + let (r, o) = (mono_q("R"), mono_q("O")); + let (r_bound, o_bound) = (static_instance("R", Type), static_instance("O", Type)); + let params = vec![PS::t("R", WithDefault), PS::t("O", WithDefault)]; + let ty_params = vec![mono_q_tp("R"), mono_q_tp("O")]; + let mut add_ro = Self::poly_trait("Add", params.clone(), vec![], Self::TOP_LEVEL); + let self_bound = subtype(poly_q("Self", ty_params.clone()), poly("Add", ty_params.clone())); + let op_t = fn1_met(poly_q("Self", ty_params.clone()), r.clone(), o.clone()); + let op_t = quant(op_t, set!{r_bound.clone(), o_bound.clone(), self_bound}); + add_ro.register_decl("__add__", op_t, Public); + let mut sub_ro = Self::poly_trait("Sub", params.clone(), vec![], Self::TOP_LEVEL); + let self_bound = subtype(poly_q("Self", ty_params.clone()), poly("Sub", ty_params.clone())); + let op_t = fn1_met(poly_q("Self", ty_params.clone()), r.clone(), o.clone()); + let op_t = quant(op_t, set!{r_bound.clone(), o_bound.clone(), self_bound}); + sub_ro.register_decl("__sub__", op_t, Public); + let mut mul_ro = Self::poly_trait("Mul", params.clone(), vec![], Self::TOP_LEVEL); + let op_t = fn1_met(poly("Mul", ty_params.clone()), r.clone(), o.clone()); + mul_ro.register_decl("__mul__", op_t, Public); + let mut div_ro = Self::poly_trait("Div", params.clone(), vec![], Self::TOP_LEVEL); + let op_t = fn1_met(poly("Div", ty_params.clone()), r, o); + div_ro.register_decl("__div__", op_t, Public); + let sup = poly("Add", vec![mono_q_tp("Self"), TyParam::mono_proj(mono_q_tp("Self"), "AddO")]); + let mut add = Self::mono_trait("Add", vec![sup], Self::TOP_LEVEL); + add.register_decl("AddO", Type, Public); + let sup = poly("Sub", vec![mono_q_tp("Self"), TyParam::mono_proj(mono_q_tp("Self"), "SubO")]); + let mut sub = Self::mono_trait("Sub", vec![sup], Self::TOP_LEVEL); + sub.register_decl("SubO", Type, Public); + let sup = poly("Mul", vec![mono_q_tp("Self"), TyParam::mono_proj(mono_q_tp("Self"), "MulO")]); + let mut mul = Self::mono_trait("Mul", vec![sup], Self::TOP_LEVEL); + mul.register_decl("MulO", Type, Public); + let sup = Type::poly("Div", vec![mono_q_tp("Self"), TyParam::mono_proj(mono_q_tp("Self"), "DivO")]); + let mut div = Self::mono_trait("Div", vec![sup], Self::TOP_LEVEL); + div.register_decl("DivO", Type, Public); + let num = Self::mono_trait("Num", vec![ + mono("Eq"), + mono("Add"), mono("Sub"), mono("Mul"), + ], Self::TOP_LEVEL); + self.register_type(mono("Eq"), eq, Const); + self.register_type(mono("Ord"), ord, Const); + self.register_type(mono("Seq"), seq, Const); + self.register_type(poly("Add", ty_params.clone()), add_ro, Const); + self.register_type(poly("Sub", ty_params.clone()), sub_ro, Const); + self.register_type(poly("Mul", ty_params.clone()), mul_ro, Const); + self.register_type(poly("Div", ty_params), div_ro, Const); + self.register_type(mono("Num"), num, Const); + } + + fn init_builtin_classes(&mut self) { + let mut obj = Self::mono_class("Obj", vec![], vec![], Self::TOP_LEVEL); + let t = fn0_met(mono_q("Self"), mono_q("Self")); + let t = quant(t, set!{subtype(mono_q("Self"), mono("Obj"))}); + obj.register_impl("clone", t, Const, Public); + obj.register_impl("__module__", Str, Const, Public); + obj.register_impl("__sizeof__", fn0_met(Obj, Nat), Const, Public); + obj.register_impl("__repr__", fn0_met(Obj, Str), Immutable, Public); + obj.register_impl("__str__", fn0_met(Obj, Str), Immutable, Public); + obj.register_impl( + "__dict__", + fn0_met(Obj, Type::dict(Str, Obj)), + Immutable, + Public, + ); + obj.register_impl( + "__bytes__", + fn0_met(Obj, Type::mono("Bytes")), + Immutable, + Public, + ); + // let mut record = Self::mono_trait("Record", vec![Obj], Self::TOP_LEVEL); + // let mut class = Self::mono_class("Class", vec![Type, Obj], Self::TOP_LEVEL); + let mut float = Self::mono_class("Float", vec![Obj], vec![ + mono("Num"), + mono("Ord"), mono("Eq"), + mono("Add"), mono("Sub"), mono("Mul"), mono("Div"), + mono("Mutate"), + ], Self::TOP_LEVEL); + let op_t = fn1_met(Float, Float, Float); + float.register_impl("__add__", op_t.clone(), Const, Public); + float.register_impl("__sub__", op_t.clone(), Const, Public); + float.register_impl("__mul__", op_t.clone(), Const, Public); + float.register_impl("__div__", op_t , Const, Public); + float.register_impl("Real", Int, Const, Public); + float.register_impl("Imag", Int, Const, Public); + let mut int = Self::mono_class("Int", vec![Obj], vec![ + mono("Num"), + mono("Rational"), mono("Integral"), + mono("Ord"), mono("Eq"), + mono("Add"), mono("Sub"), mono("Mul"), mono("Div"), + mono("Mutate"), + ], Self::TOP_LEVEL); + int.register_impl("abs", fn0_met(Int, Nat), Immutable, Public); + // __div__ is not included in Int (cast to Float) + let op_t = fn1_met(Int, Int, Int); + int.register_impl("__add__", op_t.clone(), Const, Public); + int.register_impl("__sub__", op_t.clone(), Const, Public); + int.register_impl("__mul__", op_t, Const, Public); + int.register_impl("Real", Int, Const, Public); + int.register_impl("Imag", Int, Const, Public); + int.super_traits.push(poly("Add", vec![ty_tp(Int), ty_tp(Int)])); + int.super_traits.push(poly("Sub", vec![ty_tp(Int), ty_tp(Int)])); + int.super_traits.push(poly("Mul", vec![ty_tp(Int), ty_tp(Int)])); + int.super_traits.push(poly("Div", vec![ty_tp(Int), ty_tp(Ratio)])); + let mut nat = Self::mono_class("Nat", vec![Int, Obj], vec![ + mono("Num"), + mono("Rational"), mono("Integral"), + mono("Ord"), mono("Eq"), + mono("Add"), mono("Sub"), mono("Mul"), mono("Div"), + mono("Mutate"), + Obj, + ], Self::TOP_LEVEL); + // __sub__, __div__ is not included in Nat (cast to Int) + let op_t = fn1_met(Nat, Nat, Nat); + nat.register_impl("__add__", op_t.clone(), Const, Public); + nat.register_impl("__mul__", op_t, Const, Public); + nat.register_impl( + "times!", + Type::pr_met(Nat, None, vec![param_t("p", nd_proc(vec![], NoneType))], vec![], NoneType), + Immutable, + Public + ); + nat.register_impl("Real", Int, Const, Public); + nat.register_impl("Imag", Int, Const, Public); + nat.super_traits.push(poly("Add", vec![ty_tp(Nat), ty_tp(Nat)])); + nat.super_traits.push(poly("Sub", vec![ty_tp(Nat), ty_tp(Nat)])); + nat.super_traits.push(poly("Mul", vec![ty_tp(Nat), ty_tp(Nat)])); + nat.super_traits.push(poly("Div", vec![ty_tp(Nat), ty_tp(Ratio)])); + let mut bool_ = Self::mono_class("Bool", vec![Nat, Int, Obj], vec![ + mono("Num"), + mono("Rational"), mono("Integral"), + mono("Ord"), mono("Eq"), + mono("Add"), mono("Sub"), mono("Mul"), mono("Div"), + mono("Mutate"), + Obj, + ], Self::TOP_LEVEL); + bool_.register_impl("__and__", fn1_met(Bool, Bool, Bool), Const, Public); + bool_.register_impl("__or__", fn1_met(Bool, Bool, Bool), Const, Public); + let mut str_ = Self::mono_class("Str", vec![Obj], vec![ + mono("Eq"), mono("Seq"), mono("Mutate"), + ], Self::TOP_LEVEL); + str_.register_impl("__add__", fn1_met(Str, Str, Str), Const, Public); + str_.register_impl("replace", Type::fn_met( + Str, + vec![param_t("pat", Str), param_t("into", Str)], + vec![], Str), + Immutable, + Public + ); + str_.super_traits.push(poly("Add", vec![ty_tp(Str), ty_tp(Str)])); + let mut array = Self::poly_class("Array", vec![PS::t_nd("T"), PS::named_nd("N", Nat)], vec![Obj], vec![ + mono("Eq"), mono("Seq"), mono("Mutate"), poly("Output", vec![ty_tp(mono_q("T"))]) + ], Self::TOP_LEVEL); + let n = mono_q_tp("N"); + let m = mono_q_tp("M"); + let array_t = Type::array(mono_q("T"), n.clone()); + let t = Type::fn_met( + array_t.clone(), + vec![param_t("rhs", Type::array(mono_q("T"), m.clone()))], + vec![], + Type::array(mono_q("T"), n + m) + ); + let t = quant(t, set!{static_instance("N", Nat), static_instance("M", Nat)}); + array.register_impl("concat", t, Immutable, Public); + let mut_type = ConstObj::t(Type::poly("Array!", vec![ + TyParam::t(mono_q("T")), + TyParam::mono_q("N").mutate(), + ])); + // [T; N].MutType! = [T; !N] (neither [T!; N] nor [T; N]!) + array.register_const("MutType!", mut_type); + let mut type_ = Self::mono_class("Type", vec![Obj], vec![mono("Eq")], Self::TOP_LEVEL); + type_.register_impl("mro", Type::array(Type, TyParam::erased(Nat)), Immutable, Public); + let array_mut_t = Type::poly("Array!", vec![TyParam::t(mono_q("T")), mono_q_tp("N")]); + let mut array_mut = Self::poly_class("Array!", vec![PS::t_nd("T"), PS::named_nd("N", NatMut)], vec![Obj], vec![ + mono("Eq"), mono("Seq"), mono("Mutate"), + ], Self::TOP_LEVEL); + let t = Type::pr_met( + Type::ref_mut(array_mut_t.clone()), + Some(Type::ref_mut(poly("Array!", vec![TyParam::t(mono_q("T")), mono_q_tp("N") + value(1)]))), + vec![param_t("elem", mono_q("T"))], + vec![], + Type::NoneType, + ); + let t = quant(t, set!{static_instance("T", Type), static_instance("N", NatMut)}); + array_mut.register_impl("push!", t, Immutable, Public); + self.register_type(Obj, obj, Const); + // self.register_type(Type::mono("Record"), vec![], record, Const); + // self.register_type(Type::mono("Class"), vec![], class, Const); + self.register_type(Float, float, Const); + self.register_type(Int, int, Const); + self.register_type(Nat, nat, Const); + self.register_type(Bool, bool_, Const); + self.register_type(Str, str_, Const); + self.register_type(array_t, array, Const); + self.register_type(array_mut_t, array_mut, Const); + self.register_type(Type, type_, Const); + } + + fn init_builtin_funcs(&mut self) { + let t_abs = nd_func(vec![param_t("n", mono("Num"))], Nat); + let t_assert = func(vec![param_t("condition", Bool)], vec![param_t("err_message", Str)], NoneType); + let t_classof = nd_func(vec![param_t("o", Obj)], Type::option(Class)); + let t_compile = nd_func(vec![param_t("src", Str)], Code); + let t_cond = nd_func( + vec![param_t("condition", Bool), param_t("then", mono_q("T")), param_t("else", mono_q("T"))], + mono_q("T"), + ); + let t_cond = quant(t_cond, set!{static_instance("T", Type)}); + let t_discard = nd_func(vec![param_t("o", Obj)], NoneType); + let t_id = nd_func(vec![param_t("o", Obj)], Nat); + // FIXME: quantify + let t_if = func( + vec![param_t("cond", Bool), param_t("then", nd_func(vec![], mono_q("T")))], + vec![param_t("else", nd_func(vec![], mono_q("T")))], + Type::option(mono_q("T")), + ); + let t_if = quant(t_if, set!{static_instance("T", Type)}); + let t_import = nd_func(vec![param_t("path", Str)], Module); + let t_log = nd_func(vec![param_t("objs", Type::var_args(Obj))], NoneType); + let t_pyimport = nd_func(vec![param_t("path", Str)], Module); + let t_quit = func(vec![], vec![param_t("code", Int)], NoneType); + self.register_impl("abs", t_abs, Const, Private); + self.register_impl("assert", t_assert, Const, Private); + self.register_impl("classof", t_classof, Const, Private); + self.register_impl("compile", t_compile, Const, Private); + self.register_impl("cond", t_cond, Const, Private); + self.register_impl("discard", t_discard, Const, Private); + self.register_impl("id" , t_id, Const, Private); + self.register_impl("if", t_if, Const, Private); + self.register_impl("log", t_log, Const, Private); + self.register_impl("import", t_import, Const, Private); + self.register_impl("pyimport", t_pyimport, Const, Private); + self.register_impl("quit", t_quit, Const, Private); + } + + fn init_builtin_procs(&mut self) { + let t_print = nd_proc(vec![param_t("objs", Type::var_args(Type::refer(Obj)))], NoneType); + let t_input = nd_proc(vec![param_t("msg", Str)], Str); + let t_if = proc( + vec![param_t("cond", Bool), param_t("then", nd_proc(vec![], mono_q("T")))], + vec![param_t("else", nd_proc(vec![], mono_q("T")))], + Type::option(mono_q("T")) + ); + let t_if = quant(t_if, set!{static_instance("T", Type)}); + let t_for = nd_proc(vec![param_t("iter", Type::iter(mono_q("T"))), param_t("p", nd_proc(vec![anon(mono_q("T"))], NoneType))], NoneType); + let t_for = quant(t_for, set!{static_instance("T", Type)}); + let t_while = nd_proc(vec![param_t("cond", BoolMut), param_t("p", nd_proc(vec![], NoneType))], NoneType); + self.register_impl("print!", t_print, Const, Private); + self.register_impl("input!", t_input, Const, Private); + self.register_impl("if!", t_if, Const, Private); + self.register_impl("for!", t_for, Const, Private); + self.register_impl("while!", t_while, Const, Private); + } + + fn init_builtin_operators(&mut self) { + /* binary */ + let l = mono_q("L"); + let r = mono_q("R"); + let o = mono_q("O"); + let params = vec![mono_q_tp("R"), mono_q_tp("O")]; + let op_t = Type::func2(l.clone(), r.clone(), o.clone()); + let op_t = quant(op_t, set!{ + static_instance("R", Type), + static_instance("O", Type), + subtype(l.clone(), poly("Add", params.clone())) + }); + self.register_impl("__add__", op_t, Const, Private); + let op_t = Type::func2(l.clone(), r.clone(), o.clone()); + let op_t = quant(op_t, set!{ + static_instance("R", Type), + static_instance("O", Type), + subtype(l.clone(), poly("Sub", params.clone())) + }); + self.register_impl("__sub__", op_t, Const, Private); + let op_t = Type::func2(l.clone(), r.clone(), o.clone()); + let op_t = quant(op_t, set!{subtype(l.clone(), poly("Mul", params.clone()))}); + self.register_impl("__mul__", op_t, Const, Private); + let op_t = Type::func2(l.clone(), r.clone(), o.clone()); + let op_t = quant(op_t, set!{subtype(l, poly("Mul", params.clone()))}); + self.register_impl("__div__", op_t, Const, Private); + let m = mono_q("M"); + let op_t = Type::func2(m.clone(), m.clone(), m.clone()); + let op_t = quant(op_t, set!{subtype(m, poly("Mul", vec![]))}); + self.register_impl("__pow__", op_t, Const, Private); + let d = mono_q("D"); + let op_t = Type::func2(d.clone(), d.clone(), d.clone()); + let op_t = quant(op_t, set!{subtype(d, poly("Div", vec![]))}); + self.register_impl("__mod__", op_t, Const, Private); + let e = mono_q("E"); + let op_t = Type::func2(e.clone(), e.clone(), Bool); + let op_t = quant(op_t, set!{subtype(e, poly("Eq", vec![]))}); + self.register_impl("__eq__", op_t.clone(), Const, Private); + self.register_impl("__ne__", op_t, Const, Private); + let o = mono_q("O"); + let op_t = Type::func2(o.clone(), o.clone(), Bool); + let op_t = quant(op_t, set!{subtype(o, poly("Ord", vec![]))}); + self.register_impl("__lt__", op_t.clone(), Const, Private); + self.register_impl("__le__", op_t.clone(), Const, Private); + self.register_impl("__gt__", op_t.clone(), Const, Private); + self.register_impl("__ge__", op_t, Const, Private); + self.register_impl("__and__", Type::func2(Bool, Bool, Bool), Const, Private); + self.register_impl("__or__", Type::func2(Bool, Bool, Bool), Const, Private); + /* unary */ + // TODO: Boolの+/-は警告を出したい + let n = mono_q("N"); + let op_t = fn0_met(n.clone(), n.clone()); + let op_t = quant(op_t, set!{subtype(n, mono("Num"))}); + self.register_decl("__pos__", op_t.clone(), Private); + self.register_decl("__neg__", op_t, Private); + let t = mono_q("T"); + let op_t = fn1_met(t.clone(), t.clone(), Type::range(t.clone())); + let op_t = quant(op_t, set!{subtype(t, mono("Ord"))}); + self.register_decl("__rng__", op_t, Private); + let op_t = Type::func1(mono_q("T"), Type::mono_proj(mono_q("T"), "MutType!")); + let op_t = quant(op_t, set!{subtype(mono_q("T"), mono("Mutate"))}); + self.register_impl("__mutate__", op_t, Const, Private); + } + + fn init_builtin_patches(&mut self) { + let m = mono_q_tp("M"); + let n = mono_q_tp("N"); + let o = mono_q_tp("O"); + let p = mono_q_tp("P"); + let params = vec![ + PS::named_nd("M", Int), PS::named_nd("N", Int), + PS::named_nd("O", Int), PS::named_nd("P", Int), + ]; + // Interval is a bounding patch connecting M..N and (Add(O..P, M+O..N..P), Sub(O..P, M-P..N-O)) + let mut interval = Self::poly_patch("Interval", params, vec![Type::from(&m..=&n)], vec![ + poly("Add", vec![TyParam::from(&o..=&p), TyParam::from(m.clone() + o.clone() ..= n.clone() + p.clone())]), + poly("Sub", vec![TyParam::from(&o..=&p), TyParam::from(m.clone() - p.clone() ..= n.clone() - o.clone())]), + ], Self::TOP_LEVEL); + let op_t = fn1_met( + Type::from(&m..=&n), + Type::from(&o..=&p), + Type::from(m.clone() + o.clone() ..= n.clone() + p.clone()), + ); + interval.register_impl("__add__", op_t, Const, Public); + let op_t = fn1_met( + Type::from(&m..=&n), + Type::from(&o..=&p), + Type::from(m - p ..= n - o), + ); + interval.register_impl("__sub__", op_t, Const, Public); + self.register_patch("Interval", interval, Const); + // eq.register_impl("__ne__", op_t, Const, Public); + // ord.register_impl("__le__", op_t.clone(), Const, Public); + // ord.register_impl("__gt__", op_t.clone(), Const, Public); + // ord.register_impl("__ge__", op_t, Const, Public); + } + + pub(crate) fn init_builtins() -> Self { + // TODO: capacityを正確に把握する + let mut table = SymbolTable::module("".into(), 40); + table.init_builtin_funcs(); + table.init_builtin_procs(); + table.init_builtin_operators(); + table.init_builtin_traits(); + table.init_builtin_classes(); + table.init_builtin_patches(); + table + } +} diff --git a/src/compiler/lib.rs b/src/compiler/lib.rs new file mode 100644 index 00000000..84bd15a9 --- /dev/null +++ b/src/compiler/lib.rs @@ -0,0 +1,18 @@ +//! defines the compiler for Erg (ergc). +extern crate common; +pub extern crate parser; + +mod compile; +pub use compile::*; +mod codegen; +pub mod effectcheck; +pub mod error; +pub mod eval; +pub mod hir; +pub mod initialize; +pub mod lower; +pub use lower::ASTLowerer; +pub mod optimize; +pub mod ownercheck; +pub mod table; +pub mod varinfo; diff --git a/src/compiler/lower.rs b/src/compiler/lower.rs new file mode 100644 index 00000000..0f1d5952 --- /dev/null +++ b/src/compiler/lower.rs @@ -0,0 +1,349 @@ +//! implements `ASTLowerer`. +//! +//! ASTLowerer(ASTからHIRへの変換器)を実装 +use common::{switch_lang, log, fn_name}; +use common::color::{GREEN, RED, RESET}; +use common::error::Location; +use common::traits::{Locational, Stream, HasType}; +use common::ty::{Type, ParamTy}; +use common::get_hash; + +use parser::ast; +use parser::ast::{AST}; + +use crate::hir; +use crate::hir::{HIR}; +use crate::error::{LowerError, LowerErrors, LowerResult, LowerWarnings}; +use crate::table::{SymbolTable, TableKind, RegistrationMode}; +use crate::varinfo::{Visibility}; +use Visibility::*; + +/// Singleton that checks types of an AST, and convert (lower) it into a HIR +#[derive(Debug)] +pub struct ASTLowerer { + pub(crate) table: SymbolTable, + errs: LowerErrors, + warns: LowerWarnings, +} + +impl ASTLowerer { + pub fn new() -> Self { + Self { + table: SymbolTable::new( + "".into(), + TableKind::Module, + vec![], + Some(SymbolTable::init_builtins()), + vec![], + vec![], + 0 + ), + errs: LowerErrors::empty(), + warns: LowerWarnings::empty(), + } + } + + fn return_t_check(&self, loc: Location, name: &str, expect: &Type, found: &Type) -> LowerResult<()> { + self.table + .unify(expect, found, Some(loc), None) + .or_else(|_| Err(LowerError::type_mismatch_error( + loc, + self.table.caused_by(), + name, + expect, + found, + ))) + } + + fn use_check(&self, expr: hir::Expr, mode: &str) -> LowerResult { + if mode != "eval" && !expr.ref_t().is_nonelike() { + Err(LowerError::syntax_error( + 0, + expr.loc(), + self.table.name.clone(), + switch_lang!( + "the evaluation result of the expression is not used", + "式の評価結果が使われていません", + ), + Some(switch_lang!( + "if you don't use the value, use `discard` function", + "値を使わない場合は、discard関数を使用してください", + ).into()) + )) + } else { + Ok(expr) + } + } + + fn pop_append_errs(&mut self) { + if let Err(mut errs) = self.table.pop() { + self.errs.append(&mut errs); + } + } + + fn lower_array(&mut self, array: ast::Array, check: bool) -> LowerResult { + log!("[DEBUG] entered {}({array})", fn_name!()); + let mut hir_array = hir::Array::new(array.l_sqbr, array.r_sqbr, self.table.level, hir::Args::empty(), None); + for elem in array.elems.into_iters().0 { + hir_array.push(self.lower_expr(elem.expr, check)?); + } + Ok(hir_array) + } + + /// call全体で推論できる場合があり、そのときはcheck: falseにする + fn lower_acc(&mut self, acc: ast::Accessor, check: bool) -> LowerResult { + log!("[DEBUG] entered {}({acc})", fn_name!()); + match acc { + ast::Accessor::Local(n) => { + let t = if check { + self.table.get_local_t(&n.symbol, &self.table.name)? + } else { Type::ASTOmitted }; + let acc = hir::Accessor::Local(hir::Local::new(n.symbol, t)); + Ok(acc) + } + ast::Accessor::Attr(a) => { + let obj = self.lower_expr(*a.obj, true)?; + let t = if check { + self.table.get_attr_t(&obj, &a.name.symbol, &self.table.name)? + } else { Type::ASTOmitted }; + let acc = hir::Accessor::Attr(hir::Attribute::new(obj, a.name.symbol, t)); + Ok(acc) + } + _ => todo!() + } + } + + fn lower_bin(&mut self, bin: ast::BinOp) -> LowerResult { + log!("[DEBUG] entered {}({bin})", fn_name!()); + let mut args = bin.args.into_iter(); + let lhs = hir::PosArg::new(self.lower_expr(*args.next().unwrap(), true)?); + let rhs = hir::PosArg::new(self.lower_expr(*args.next().unwrap(), true)?); + let args = [lhs, rhs]; + let t = self.table.get_binop_t(&bin.op, &args, &self.table.name)?; + let mut args = args.into_iter(); + let lhs = args.next().unwrap().expr; + let rhs = args.next().unwrap().expr; + Ok(hir::BinOp::new(bin.op, lhs, rhs, t)) + } + + fn lower_unary(&mut self, unary: ast::UnaryOp) -> LowerResult { + log!("[DEBUG] entered {}({unary})", fn_name!()); + let mut args = unary.args.into_iter(); + let arg = hir::PosArg::new(self.lower_expr(*args.next().unwrap(), true)?); + let args = [arg]; + let t = self.table.get_unaryop_t(&unary.op, &args, &self.table.name)?; + let mut args = args.into_iter(); + let expr = args.next().unwrap().expr; + Ok(hir::UnaryOp::new(unary.op, expr, t)) + } + + fn lower_call(&mut self, call: ast::Call) -> LowerResult { + log!("[DEBUG] entered {}({}(...))", fn_name!(), call.obj); + let (pos_args, kw_args, paren) = call.args.deconstruct(); + let mut hir_args = hir::Args::new(Vec::with_capacity(pos_args.len()), Vec::with_capacity(kw_args.len()), paren); + for arg in pos_args.into_iter() { + hir_args.push_pos(hir::PosArg::new(self.lower_expr(arg.expr, true)?)); + } + for arg in kw_args.into_iter() { + hir_args.push_kw(hir::KwArg::new(arg.keyword, self.lower_expr(arg.expr, true)?)); + } + let mut obj = self.lower_expr(*call.obj, false)?; + let t = self.table.get_call_t(&mut obj, hir_args.pos_args(), hir_args.kw_args(), &self.table.name)?; + Ok(hir::Call::new(obj, hir_args, t)) + } + + fn lower_lambda(&mut self, lambda: ast::Lambda) -> LowerResult { + log!("[DEBUG] entered {}({lambda})", fn_name!()); + let is_procedural = lambda.is_procedural(); + let id = get_hash(&lambda.sig); + let name = format!(""); + let kind = if is_procedural { TableKind::Proc } else { TableKind::Func }; + self.table.grow(&name, kind, Private)?; + self.table.assign_params(&lambda.sig.params, None) + .map_err(|e| { self.pop_append_errs(); e })?; + self.table.preregister(lambda.body.ref_payload()) + .map_err(|e| { self.pop_append_errs(); e })?; + let body = self.lower_block(lambda.body) + .map_err(|e| { self.pop_append_errs(); e })?; + // impls => named non-default params + named default params + embedded params (will be discarded) + // unnnamed_params => unnamed non-default params + unnamed default params + // non default params = [unnamed non-default params + unnamed default params].sorted() // sort by pos + // default params = [unnamed default params, named default params].sorted() + let (named_non_default_params, named_default_params) = { + let (named_default_params, named_non_default_params_and_embeddeds): (Vec<_>, Vec<_>) = self.table.impls.iter() + .filter(|(_, v)| v.kind.is_parameter()) + .partition(|(_, v)| v.kind.has_default()); + let (_, named_non_default_params): (Vec<_>, Vec<_>)= named_non_default_params_and_embeddeds.into_iter() + .partition(|(_, v)| v.kind.is_embedded_param()); + ( + named_non_default_params.into_iter() + .map(|(n, v)| (v.kind.pos_as_param().unwrap(), ParamTy::new(Some(n.inspect().clone()), v.t()))) + .collect::>(), + named_default_params.into_iter() + .map(|(n, v)| (v.kind.pos_as_param().unwrap(), ParamTy::new(Some(n.inspect().clone()), v.t()))) + .collect::>() + ) + }; + let (unnamed_non_default_params, unnamed_default_params) = { + let (unnamed_default_params, unnamed_non_default_params): (Vec<_>, Vec<_>) = self.table.unnamed_params.iter() + .map(|v| (v, ParamTy::anonymous(v.t()))) + .partition(|(v, _)| v.kind.has_default()); + ( + unnamed_non_default_params.into_iter() + .map(|(v, pt)| (v.kind.pos_as_param().unwrap(), pt)).collect(), + unnamed_default_params.into_iter() + .map(|(v, pt)| (v.kind.pos_as_param().unwrap(), pt)).collect(), + ) + }; + let non_default_params = { + let mut a = [unnamed_non_default_params, named_non_default_params].concat(); + a.sort_by(|(l, _), (r, _)| l.cmp(r)); + a.into_iter().map(|(_, p)| p).collect::>() + }; + let default_params = { + let mut a = [unnamed_default_params, named_default_params].concat(); + a.sort_by(|(l, _), (r, _)| l.cmp(r)); + a.into_iter().map(|(_, p)| p).collect::>() + }; + let bounds = self.table.instantiate_ty_bounds(&lambda.sig.bounds, RegistrationMode::Normal) + .map_err(|e| { self.pop_append_errs(); e })?; + self.pop_append_errs(); + let t = if is_procedural { + Type::proc(non_default_params, default_params, body.t()) + } else { + Type::func(non_default_params, default_params, body.t()) + }; + let t = if bounds.is_empty() { t } else { Type::quantified(t, bounds) }; + Ok(hir::Lambda::new(id, lambda.sig.params, lambda.op, body, t)) + } + + fn lower_def(&mut self, def: ast::Def) -> LowerResult { + log!("[DEBUG] entered {}({})", fn_name!(), def.sig); + // FIXME: Instant + self.table.grow(def.sig.name_as_str(), TableKind::Instant, Private)?; + let res = match def.sig { + ast::Signature::Subr(sig) => self.lower_subr_def(sig, def.body), + ast::Signature::Var(sig) => self.lower_var_def(sig, def.body), + }; + // TODO: SymbolTable上の関数に型境界情報を追加 + // let bounds = self.table.bounds; + self.pop_append_errs(); + res + } + + fn lower_var_def(&mut self, sig: ast::VarSignature, body: ast::DefBody) -> LowerResult { + log!("[DEBUG] entered {}({sig})", fn_name!()); + self.table.preregister(body.block.ref_payload())?; + let block = self.lower_block(body.block)?; + let found_body_t = block.ref_t(); + let opt_expect_body_t = self.table + .outer.as_ref().unwrap() + .get_current_scope_local_var(sig.inspect().unwrap()) + .map(|vi| vi.t.clone()); + let name = sig.pat.inspect().unwrap(); + if let Some(expect_body_t) = opt_expect_body_t { + if let Err(e) = self.return_t_check(sig.loc(), name, &expect_body_t, &found_body_t) { + self.errs.push(e); + } + } + let id = body.id; + // TODO: cover all VarPatterns + self.table.outer.as_mut().unwrap().assign_var(&sig, id, found_body_t)?; + let sig = hir::VarSignature::new(sig.pat, found_body_t.clone()); + let body = hir::DefBody::new(body.op, block, body.id); + Ok(hir::Def::new(hir::Signature::Var(sig), body)) + } + + // NOTE: 呼ばれている間はinner scopeなので注意 + fn lower_subr_def(&mut self, sig: ast::SubrSignature, body: ast::DefBody) -> LowerResult { + log!("[DEBUG] entered {}({sig})", fn_name!()); + let t = self.table + .outer.as_ref().unwrap() + .get_current_scope_local_var(sig.name.inspect()) + .unwrap_or_else(|| { + log!("{}\n", sig.name.inspect()); + log!("{}\n", self.table.outer.as_ref().unwrap()); + panic!() + }) // FIXME: or instantiate + .t.clone(); + self.table.assign_params(&sig.params, None)?; + self.table.preregister(body.block.ref_payload())?; + let block = self.lower_block(body.block)?; + let found_body_t = block.ref_t(); + let expect_body_t = t.return_t().unwrap(); + if let Err(e) = self.return_t_check(sig.loc(), sig.name.inspect(), expect_body_t, found_body_t) { + self.errs.push(e); + } + let id = body.id; + self.table.outer.as_mut().unwrap().assign_subr(&sig, id, found_body_t)?; + let sig = hir::SubrSignature::new(sig.name, sig.params, t); + let body = hir::DefBody::new(body.op, block, body.id); + Ok(hir::Def::new(hir::Signature::Subr(sig), body)) + } + + // Call.obj == Accessor cannot be type inferred by itself (it can only be inferred with arguments) + // so turn off type checking (check=false) + fn lower_expr(&mut self, expr: ast::Expr, check: bool) -> LowerResult { + log!("[DEBUG] entered {}", fn_name!()); + match expr { + ast::Expr::Lit(lit) => { + Ok(hir::Expr::Lit(hir::Literal::from(lit.token))) + }, + ast::Expr::Array(arr) => { + Ok(hir::Expr::Array(self.lower_array(arr, check)?)) + } + ast::Expr::Accessor(acc) => { + Ok(hir::Expr::Accessor(self.lower_acc(acc, check)?)) + } + ast::Expr::BinOp(bin) => { + Ok(hir::Expr::BinOp(self.lower_bin(bin)?)) + } + ast::Expr::UnaryOp(unary) => { + Ok(hir::Expr::UnaryOp(self.lower_unary(unary)?)) + } + ast::Expr::Call(call) => { + Ok(hir::Expr::Call(self.lower_call(call)?)) + } + ast::Expr::Lambda(lambda) => { + Ok(hir::Expr::Lambda(self.lower_lambda(lambda)?)) + } + ast::Expr::Def(def) => { + Ok(hir::Expr::Def(self.lower_def(def)?)) + } + other => todo!("{other}"), + } + } + + fn lower_block(&mut self, ast_block: ast::Block) -> LowerResult { + log!("[DEBUG] entered {}", fn_name!()); + let mut hir_block = Vec::with_capacity(ast_block.len()); + for expr in ast_block.into_iter() { + let expr = self.lower_expr(expr, true)?; + hir_block.push(expr); + } + Ok(hir::Block::new(hir_block)) + } + + pub fn lower(&mut self, ast: AST, mode: &str) -> Result<(HIR, LowerWarnings), LowerErrors> { + log!("{GREEN}[DEBUG] the type-checking process has started."); + let mut module = hir::Module::with_capacity(ast.module.len()); + self.table.preregister(ast.module.ref_payload())?; + for expr in ast.module.into_iter() { + match self.lower_expr(expr, true) + .and_then(|e| self.use_check(e, mode)) { + Ok(expr) => { module.push(expr); } + Err(e) => { self.errs.push(e); }, + } + } + let hir = HIR::new(ast.name, module); + log!("[DEBUG] {}() has completed, found errors: {}", fn_name!(), self.errs.len()); + if self.errs.is_empty() { + log!("HIR:\n{hir}"); + log!("[DEBUG] the type-checking process has completed.{RESET}"); + Ok((hir, LowerWarnings::from(self.warns.take_all()))) + } else { + log!("{RED}[DEBUG] the type-checking process has failed.{RESET}"); + Err(LowerErrors::from(self.errs.take_all())) + } + } +} diff --git a/src/compiler/main.rs b/src/compiler/main.rs new file mode 100644 index 00000000..3340f8ef --- /dev/null +++ b/src/compiler/main.rs @@ -0,0 +1,28 @@ +extern crate common; +extern crate compiler; +extern crate parser; + +use std::process; + +use common::deserialize::Deserializer; +use common::config::{ErgConfig}; +use common::traits::Runnable; + +use compiler::Compiler; + +use parser::lex::LexerRunner; +use parser::ParserRunner; + +fn main() { + let cfg = ErgConfig::parse(); + match cfg.mode { + "lex" => { LexerRunner::run(cfg); } + "parse" => { ParserRunner::run(cfg); } + "compile" | "exec" => { Compiler::run(cfg); } + "read" => { Deserializer::run(cfg); } + other => { + println!("invalid mode: {other}"); + process::exit(1); + } + } +} diff --git a/src/compiler/optimize.rs b/src/compiler/optimize.rs new file mode 100644 index 00000000..6f421d6e --- /dev/null +++ b/src/compiler/optimize.rs @@ -0,0 +1,17 @@ +use crate::hir::HIR; +use crate::error::{CompileWarnings}; + +#[derive(Debug)] +pub struct HIROptimizer { + +} + +impl HIROptimizer { + pub fn fold_constants(&mut self, mut _hir: HIR) -> HIR { todo!() } + + pub fn eliminate_unused_variables(&mut self, mut _hir: HIR) -> (HIR, CompileWarnings) { todo!() } + + pub fn eliminate_dead_code(&mut self, mut _hir: HIR) -> (HIR, CompileWarnings) { + todo!() + } +} diff --git a/src/compiler/ownercheck.rs b/src/compiler/ownercheck.rs new file mode 100644 index 00000000..33244d36 --- /dev/null +++ b/src/compiler/ownercheck.rs @@ -0,0 +1,177 @@ +use common::Str; +use common::{debug_power_assert, log}; +use common::color::{GREEN, RESET}; +use common::dict::Dict; +use common::error::Location; +use common::set::Set; +use common::traits::{Stream, Locational, HasType}; +use common::ty::{Type, ArgsOwnership, Ownership}; + +use crate::error::{OwnershipError, OwnershipErrors, OwnershipResult}; +use crate::hir::{HIR, Def, Signature, Accessor, Block, Expr}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum WrapperKind { + Ref, + Rc, + Box, +} + +#[derive(Debug, Default)] +struct LocalVars { + alive_vars: Set, + dropped_vars: Dict, +} + +#[derive(Debug)] +pub struct OwnershipChecker { + current_scope_name: Str, + dict: Dict, + errs: OwnershipErrors, +} + +impl OwnershipChecker { + pub fn new() -> Self { + OwnershipChecker { + current_scope_name: Str::ever(""), + dict: Dict::new(), + errs: OwnershipErrors::empty(), + } + } + + // moveされた後の変数が使用されていないかチェックする + // ProceduralでないメソッドでRefMutが使われているかはSideEffectCheckerでチェックする + pub fn check(mut self, hir: HIR) -> OwnershipResult { + log!("{GREEN}[DEBUG] the ownership checking process has started.{RESET}"); + self.current_scope_name = hir.name.clone(); + self.dict.insert(hir.name.clone(), LocalVars::default()); + for chunk in hir.module.iter() { + self.check_expr(chunk, Ownership::Owned); + } + log!("{GREEN}[DEBUG] the ownership checking process has completed, found errors: {}{RESET}", self.errs.len()); + if self.errs.is_empty() { + Ok(hir) + } else { + Err(self.errs) + } + } + + fn check_block(&mut self, block: &Block) { + for chunk in block.iter() { + self.check_expr(chunk, Ownership::Owned); + } + } + + // 参照を取るMethod Call中ならばreferenced: trueとなり、所有権は移動しない + fn check_expr(&mut self, expr: &Expr, ownership: Ownership) { + match expr { + Expr::Def(def) => { + self.define(&def); + self.check_block(&def.body.block); + }, + Expr::Accessor(Accessor::Local(local)) => { + // TODO: スコープを再帰的にチェックする + if let Some(moved_loc) = self.current_scope().dropped_vars.get(local.inspect()) { + let moved_loc = *moved_loc; + self.errs.push(OwnershipError::move_error( + local.inspect(), + local.loc(), + moved_loc, + Str::from(""), + )); + } else { + if expr.ref_t().is_mut() && ownership.is_owned() { + log!("dropped: {}", local.inspect()); + self.drop(local.inspect(), expr.loc()); + } + } + }, + Expr::Accessor(Accessor::Attr(a)) => { + if a.ref_t() != &Type::ASTOmitted { todo!("{a}: {}", a.ref_t()) } + }, + Expr::Accessor(_a) => todo!(), + // TODO: referenced + Expr::Call(call) => { + self.check_expr(&call.obj, ownership); + let args_ownership = call.signature_t().unwrap().args_ownership(); + match args_ownership { + ArgsOwnership::Args{ self_, non_defaults, defaults } => { + if let Some(ownership) = self_ { + self.check_expr(&call.obj, ownership); + } + let (nd_ownerships, d_ownerships): (Vec<_>, Vec<_>) = non_defaults.iter() + .enumerate() + .partition(|(i, _)| *i == call.args.pos_args().len()); + for (parg, (_, ownership)) in call.args.pos_args() + .iter() + .zip(nd_ownerships.into_iter()) { + self.check_expr(&parg.expr, *ownership); + } + for (kwarg, (_, ownership)) in call.args.kw_args() + .iter() + .zip(d_ownerships.into_iter().chain(defaults.iter().enumerate())) { + self.check_expr(&kwarg.expr, *ownership); + } + }, + ArgsOwnership::VarArgs(ownership) => { + for parg in call.args.pos_args().iter() { + self.check_expr(&parg.expr, ownership); + } + for kwarg in call.args.kw_args().iter() { + self.check_expr(&kwarg.expr, ownership); + } + }, + other => todo!("{other:?}"), + } + }, + // TODO: referenced + Expr::BinOp(binop) => { + self.check_expr(&binop.lhs, ownership); + self.check_expr(&binop.rhs, ownership); + }, + Expr::UnaryOp(unary) => { + self.check_expr(&unary.expr, ownership); + }, + Expr::Array(arr) => { + for a in arr.elems.pos_args().iter() { + self.check_expr(&a.expr, ownership); + } + }, + Expr::Dict(dict) => { + for a in dict.attrs.kw_args().iter() { + // self.check_expr(&a.key); + self.check_expr(&a.expr, ownership); + } + }, + // TODO: capturing + Expr::Lambda(lambda) => { + self.check_block(&lambda.body); + }, + _ => {}, + } + } + + /// TODO: このメソッドを呼ぶとき、スコープを再帰的に検索する + #[inline] + fn current_scope(&mut self) -> &mut LocalVars { + self.dict.get_mut(&self.current_scope_name).unwrap() + } + + fn define(&mut self, def: &Def) { + match &def.sig { + Signature::Var(sig) => { + for name in sig.pat.inspects() { + self.current_scope().alive_vars.insert(name.clone()); + } + }, + Signature::Subr(sig) => { + self.current_scope().alive_vars.insert(sig.name.inspect().clone()); + }, + } + } + + fn drop(&mut self, name: &Str, moved_loc: Location) { + debug_power_assert!(self.current_scope().alive_vars.remove(name), ==, true); + self.current_scope().dropped_vars.insert(name.clone(), moved_loc); + } +} diff --git a/src/compiler/parser/.gitignore b/src/compiler/parser/.gitignore new file mode 100644 index 00000000..efb24520 --- /dev/null +++ b/src/compiler/parser/.gitignore @@ -0,0 +1,2 @@ +/.vscode/ +/target/ \ No newline at end of file diff --git a/src/compiler/parser/Cargo.toml b/src/compiler/parser/Cargo.toml new file mode 100644 index 00000000..6723e6c3 --- /dev/null +++ b/src/compiler/parser/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "parser" +version = "0.1.0" +description = "The Erg parser" +authors = ["mtshiba "] +license = "MIT OR Apache-2.0" +edition = "2021" + +[features] +debug = [ "common/debug" ] +japanese = [ "common/japanese" ] + +[dependencies] +common = { path = "../../common" } + +[lib] +path = "lib.rs" + +[[bin]] +name = "ergp" +path = "main.rs" diff --git a/src/compiler/parser/README.md b/src/compiler/parser/README.md new file mode 100644 index 00000000..4d18423f --- /dev/null +++ b/src/compiler/parser/README.md @@ -0,0 +1,5 @@ +# Erg parser + +## Why isn't this module but crate? + +For maintainability. This crate has tests. diff --git a/src/compiler/parser/ast.rs b/src/compiler/parser/ast.rs new file mode 100644 index 00000000..052d28a2 --- /dev/null +++ b/src/compiler/parser/ast.rs @@ -0,0 +1,1856 @@ +//! defines `Expr` (Expression, the minimum executing unit of Erg). +use std::borrow::Borrow; +use std::fmt; + +use common::{Str}; +use common::{ + impl_display_for_single_struct, + impl_locational, impl_locational_for_enum, + impl_displayable_stream_for_wrapper, + impl_stream, impl_stream_for_wrapper, + fmt_vec, fmt_option, impl_display_for_enum, + impl_display_from_nested, impl_nested_display_for_enum +}; +use common::value::ValueObj; +use common::error::Location; +use common::set::Set; +use common::traits::{Locational, Stream, NestedDisplay}; +use common::ty::SubrKind; + +use crate::token::{Token, TokenKind}; + +pub fn fmt_lines<'a, T: NestedDisplay + 'a>( + mut iter: impl Iterator, f: &mut fmt::Formatter<'_>, level: usize +) -> fmt::Result { + if let Some(line) = iter.next() { + line.fmt_nest(f, level)?; + } + for arg in iter { + write!(f, "\n")?; + arg.fmt_nest(f, level)?; + } + Ok(()) +} + +/// リテラルに実際の値が格納された構造体(定数畳み込み用) +/// ArrayやDictはまた別に +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Literal { + pub token: Token, +} + +impl NestedDisplay for Literal { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{}", self.token) + } +} + +impl_display_from_nested!(Literal); + +impl Locational for Literal { + #[inline] + fn loc(&self) -> Location { self.token.loc() } +} + +impl From for Literal { + #[inline] + fn from(token: Token) -> Self { Self { token } } +} + +impl From<&Literal> for ValueObj { + #[inline] + fn from(lit: &Literal) -> ValueObj { ValueObj::from(&lit.token) } +} + +impl Literal { + #[inline] + pub fn is(&self, kind: TokenKind) -> bool { self.token.is(kind) } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct PosArg { + pub expr: Expr, +} + +impl NestedDisplay for PosArg { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + self.expr.fmt_nest(f, level) + } +} + +impl_display_from_nested!(PosArg); + +impl Locational for PosArg { + fn loc(&self) -> Location { self.expr.loc() } +} + +impl PosArg { + pub const fn new(expr: Expr) -> Self { Self { expr } } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct KwArg { + pub keyword: Token, + pub expr: Expr, +} + +impl NestedDisplay for KwArg { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + write!(f, "{}:\n", self.keyword)?; + self.expr.fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(KwArg); + +impl Locational for KwArg { + fn loc(&self) -> Location { + Location::concat(&self.keyword, &self.expr) + } +} + +impl KwArg { + pub const fn new(keyword: Token, expr: Expr) -> Self { Self { keyword, expr } } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Args { + pos_args: Vec, + kw_args: Vec, + paren: Option<(Token, Token)>, +} + +impl NestedDisplay for Args { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + fmt_lines(self.pos_args.iter(), f, level) + } +} + +impl_display_from_nested!(Args); + +impl Locational for Args { + fn loc(&self) -> Location { + if let Some((l, r)) = &self.paren { Location::concat(l, r) } + else { Location::concat(&self.pos_args[0], self.pos_args.last().unwrap()) } + } +} + +// impl_stream!(Args, Arg, args); + +impl Args { + pub const fn new(pos_args: Vec, kw_args: Vec, paren: Option<(Token, Token)>) -> Self { + Self { pos_args, kw_args, paren } + } + + pub const fn empty() -> Self { Self::new(vec![], vec![], None) } + + // for replacing to hir::Args + pub fn deconstruct(self) -> (Vec, Vec, Option<(Token, Token)>) { + (self.pos_args, self.kw_args, self.paren) + } + + pub fn is_empty(&self) -> bool { + self.pos_args.is_empty() && self.kw_args.is_empty() + } + + pub fn kw_is_empty(&self) -> bool { + self.kw_args.is_empty() + } + + pub fn pos_args(&self) -> &[PosArg] { &self.pos_args[..] } + + pub fn kw_args(&self) -> &[KwArg] { &self.kw_args[..] } + + pub fn into_iters(self) -> (impl IntoIterator, impl IntoIterator) { + (self.pos_args.into_iter(), self.kw_args.into_iter()) + } + + pub fn push_pos(&mut self, arg: PosArg) { + self.pos_args.push(arg); + } + + pub fn remove_pos(&mut self, index: usize) -> PosArg { + self.pos_args.remove(index) + } + + pub fn insert_pos(&mut self, index: usize, arg: PosArg) { + self.pos_args.insert(index, arg); + } + + pub fn push_kw(&mut self, arg: KwArg) { + self.kw_args.push(arg); + } +} + +/// represents a local variable +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Local { + pub symbol: Token, +} + +impl_display_for_single_struct!(Local, symbol.content); + +impl Locational for Local { + #[inline] + fn loc(&self) -> Location { self.symbol.loc() } +} + +impl Local { + pub const fn new(symbol: Token) -> Self { Self{ symbol } } + + pub fn dummy(name: &'static str) -> Self { + Self::new(Token::from_str(TokenKind::Symbol, name)) + } + + // &strにするとクローンしたいときにアロケーションコストがかかるので&Strのままで + pub const fn inspect(&self) -> &Str { &self.symbol.content } + + pub fn is_const(&self) -> bool { self.symbol.inspect().chars().next().unwrap().is_uppercase() } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Attribute { + pub obj: Box, + pub name: Local, +} + +impl fmt::Display for Attribute { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({}).{}", self.obj, self.name) + } +} + +impl_locational!(Attribute, obj, name); + +impl Attribute { + pub fn new(obj: Expr, name: Local) -> Self { Self { obj: Box::new(obj), name } } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct TupleAttribute { + pub obj: Box, + pub index: Literal, +} + +impl fmt::Display for TupleAttribute { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({}).{}", self.obj, self.index) + } +} + +impl_locational!(TupleAttribute, obj, index); + +impl TupleAttribute { + pub fn new(obj: Expr, index: Literal) -> Self { Self { obj: Box::new(obj), index } } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Subscript { + obj: Box, + index: Box, +} + +impl fmt::Display for Subscript { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({})[{}]", self.obj, self.index) + } +} + +impl_locational!(Subscript, obj, index); + +impl Subscript { + pub fn new(obj: Expr, index: Expr) -> Self { + Self { obj: Box::new(obj), index: Box::new(index) } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum Accessor { + Local(Local), + SelfDot(Local), + Attr(Attribute), + TupleAttr(TupleAttribute), + Subscr(Subscript), +} + +impl NestedDisplay for Accessor { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + match self { + Self::Local(name) => write!(f, "{}", name), + Self::SelfDot(attr) => write!(f, "self.{}", attr), + Self::Attr(attr) => write!(f, "{}", attr), + Self::TupleAttr(attr) => write!(f, "{}", attr), + Self::Subscr(subscr) => write!(f, "{}", subscr), + } + } +} + +impl_display_from_nested!(Accessor); +impl_locational_for_enum!(Accessor; Local, SelfDot, Attr, TupleAttr, Subscr); + +impl Accessor { + pub const fn local(symbol: Token) -> Self { Self::Local(Local::new(symbol)) } + + pub const fn self_dot(attr: Token) -> Self { Self::SelfDot(Local::new(attr)) } + + pub fn attr(obj: Expr, name: Local) -> Self { Self::Attr(Attribute::new(obj, name)) } + + pub fn subscr(obj: Expr, index: Expr) -> Self { + Self::Subscr(Subscript::new(obj, index)) + } + + pub const fn name(&self) -> Option<&Str> { + match self { + Self::Local(local) => Some(local.inspect()), + Self::SelfDot(local) => Some(local.inspect()), + _ => None, + } + } + + pub fn is_const(&self) -> bool { + match self { + Self::Local(local) + | Self::SelfDot(local) => local.is_const(), + Self::Subscr(subscr) => subscr.obj.is_const_acc(), + Self::TupleAttr(attr) => attr.obj.is_const_acc(), + Self::Attr(attr) => attr.obj.is_const_acc() && attr.name.is_const(), + } + } +} + +/// DictはキーつきArray(型としては別物) +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Array { + pub l_sqbr: Token, + pub r_sqbr: Token, + pub elems: Args, + pub guard: Option>, +} + +impl NestedDisplay for Array { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + if let Some(guard) = &self.guard { + write!(f, "[{} | {}]", self.elems, guard) + } else { + write!(f, "[{}]", self.elems) + } + } +} + +impl_display_from_nested!(Array); +impl_locational!(Array, l_sqbr, r_sqbr); + +impl Array { + pub fn new(l_sqbr: Token, r_sqbr: Token, elems: Args, guard: Option) -> Self { + Self { l_sqbr, r_sqbr, elems, guard: guard.map(Box::new) } + } +} + +/// DictはキーつきArrayとして実現される +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Dict { + l_brace: Token, + r_brace: Token, + pub attrs: Args, + guard: Option>, +} + +impl NestedDisplay for Dict { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + if let Some(guard) = &self.guard { + write!(f, "{{{} | {guard}}}", self.attrs) + } else { + write!(f, "{{{}}}", self.attrs) + } + } +} + +impl_display_from_nested!(Dict); +impl_locational!(Dict, l_brace, r_brace); + +impl Dict { + pub fn new(l_brace: Token, r_brace: Token, attrs: Args, guard: Option) -> Self { + Self { l_brace, r_brace, attrs, guard: guard.map(Box::new) } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct BinOp { + pub op: Token, + pub args: [Box; 2], +} + +impl NestedDisplay for BinOp { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + write!(f, "`{}`:\n", self.op.content)?; + self.args[0].fmt_nest(f, level + 1)?; + write!(f, "\n")?; + self.args[1].fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(BinOp); + +impl Locational for BinOp { + fn loc(&self) -> Location { + Location::concat(&self.op, self.args[1].as_ref()) + } +} + +impl BinOp { + pub fn new(op: Token, lhs: Expr, rhs: Expr) -> Self { + Self { op, args: [Box::new(lhs), Box::new(rhs)] } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct UnaryOp { + pub op: Token, + pub args: [Box; 1], +} + +impl NestedDisplay for UnaryOp { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + write!(f, "`{}`:\n", self.op.content)?; + self.args[0].fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(UnaryOp); + +impl Locational for UnaryOp { + fn loc(&self) -> Location { + Location::concat(&self.op, self.args[0].as_ref()) + } +} + +impl UnaryOp { + pub fn new(op: Token, expr: Expr) -> Self { Self { op, args: [Box::new(expr)] } } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Call { + pub obj: Box, + pub args: Args, +} + +impl NestedDisplay for Call { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + write!(f, "({}):\n", self.obj)?; + self.args.fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(Call); + +impl Locational for Call { + fn loc(&self) -> Location { + if self.args.is_empty() { self.obj.loc() } + else { Location::concat(self.obj.as_ref(), &self.args) } + } +} + +impl Call { + pub fn new(obj: Expr, args: Args) -> Self { Self { obj: Box::new(obj), args } } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Block(Vec); + +impl NestedDisplay for Block { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + fmt_lines(self.0.iter(), f, level) + } +} + +impl_display_from_nested!(Block); + +impl Locational for Block { + fn loc(&self) -> Location { + Location::concat(self.0.first().unwrap(), self.0.last().unwrap()) + } +} + +impl_stream_for_wrapper!(Block, Expr); + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstLocal { + pub symbol: Token, +} + +impl NestedDisplay for ConstLocal { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{}", self.symbol) + } +} + +impl_display_from_nested!(ConstLocal); + +impl Locational for ConstLocal { + #[inline] + fn loc(&self) -> Location { self.symbol.loc() } +} + +impl ConstLocal { + pub const fn new(symbol: Token) -> Self { Self{ symbol } } + + pub fn dummy(name: &'static str) -> Self { + Self::new(Token::from_str(TokenKind::Symbol, name)) + } + + // &strにするとクローンしたいときにアロケーションコストがかかるので&Strのままで + pub const fn inspect(&self) -> &Str { &self.symbol.content } +} + +/// type variables +pub type ConstVar = ConstLocal; + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstAttribute { + pub obj: Box, + pub name: ConstLocal, +} + +impl NestedDisplay for ConstAttribute { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "({}).{}", self.obj, self.name) + } +} + +impl_display_from_nested!(ConstAttribute); +impl_locational!(ConstAttribute, obj, name); + +impl ConstAttribute { + pub fn new(expr: ConstExpr, name: ConstLocal) -> Self { Self { obj: Box::new(expr), name } } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstSubscript { + obj: Box, + index: Box, +} + +impl NestedDisplay for ConstSubscript { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "({})[{}]", self.obj, self.index) + } +} + +impl_display_from_nested!(ConstSubscript); +impl_locational!(ConstSubscript, obj, index); + +impl ConstSubscript { + pub fn new(obj: ConstExpr, index: ConstExpr) -> Self { + Self { obj: Box::new(obj), index: Box::new(index) } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum ConstAccessor { + Local(ConstLocal), + SelfDot(ConstLocal), + Attr(ConstAttribute), + Subscr(ConstSubscript), +} + +impl_nested_display_for_enum!(ConstAccessor; Local, SelfDot, Attr, Subscr); +impl_display_from_nested!(ConstAccessor); +impl_locational_for_enum!(ConstAccessor; Local, SelfDot, Attr, Subscr); + +impl ConstAccessor { + pub const fn local(symbol: Token) -> Self { Self::Local(ConstLocal::new(symbol)) } + + pub const fn dot_self(attr: Token) -> Self { Self::SelfDot(ConstLocal::new(attr)) } + + pub fn attr(obj: ConstExpr, name: ConstLocal) -> Self { + Self::Attr(ConstAttribute::new(obj, name)) + } + + pub fn subscr(obj: ConstExpr, index: ConstExpr) -> Self { + Self::Subscr(ConstSubscript::new(obj, index)) + } +} + +/// DictはキーつきArray(型としては別物) +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstArray { + pub l_sqbr: Token, + pub r_sqbr: Token, + pub elems: ConstArgs, + pub guard: Option>, +} + +impl NestedDisplay for ConstArray { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + if let Some(guard) = &self.guard { + write!(f, "[{} | {}]", self.elems, guard) + } else { + write!(f, "[{}]", self.elems) + } + } +} + +impl_display_from_nested!(ConstArray); +impl_locational!(ConstArray, l_sqbr, r_sqbr); + +impl ConstArray { + pub fn new(l_sqbr: Token, r_sqbr: Token, elems: ConstArgs, guard: Option) -> Self { + Self { l_sqbr, r_sqbr, elems, guard: guard.map(Box::new) } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstDict { + l_brace: Token, + r_brace: Token, + pub attrs: ConstArgs, +} + +impl NestedDisplay for ConstDict { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{{{}}}", self.attrs) + } +} + +impl_display_from_nested!(ConstDict); +impl_locational!(ConstDict, l_brace, r_brace); + +impl ConstDict { + pub fn new(l_brace: Token, r_brace: Token, attrs: ConstArgs) -> Self { + Self { l_brace, r_brace, attrs } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstBinOp { + pub op: Token, + pub lhs: Box, + pub rhs: Box, +} + +impl NestedDisplay for ConstBinOp { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "`{}`({}, {})", self.op.content, self.lhs, self.rhs) + } +} + +impl_display_from_nested!(ConstBinOp); +impl_locational!(ConstBinOp, lhs, rhs); + +impl ConstBinOp { + pub fn new(op: Token, lhs: ConstExpr, rhs: ConstExpr) -> Self { + Self { op, lhs: Box::new(lhs), rhs: Box::new(rhs) } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstUnaryOp { + pub op: Token, + pub expr: Box, +} + +impl NestedDisplay for ConstUnaryOp { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "`{}`({})", self.op.content, self.expr) + } +} + +impl_display_from_nested!(ConstUnaryOp); +impl_locational!(ConstUnaryOp, op, expr); + +impl ConstUnaryOp { + pub fn new(op: Token, expr: ConstExpr) -> Self { + Self { op, expr: Box::new(expr) } + } +} + +/// Application +/// ex. `Vec Int` of `Option Vec Int` +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstApp { + pub acc: ConstAccessor, + pub args: ConstArgs, +} + +impl NestedDisplay for ConstApp { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + write!(f, "({})", self.acc)?; + self.args.fmt_nest(f, level + 1) + } +} + +impl Locational for ConstApp { + fn loc(&self) -> Location { + if self.args.is_empty() { self.acc.loc() } + else { Location::concat(&self.acc, &self.args) } + } +} + +impl ConstApp { + pub const fn new(acc: ConstAccessor, args: ConstArgs) -> Self { Self { acc, args } } +} + +/// valid expression for an argument of polymorphic types +/// 多相型の実引数として有効な式 +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum ConstExpr { + Lit(Literal), + Erased(Literal), // _ + Accessor(ConstAccessor), + App(ConstApp), + Array(ConstArray), + // Dict(Dict), + // Set(Set), + Dict(ConstDict), + BinOp(ConstBinOp), + UnaryOp(ConstUnaryOp), +} + +impl_nested_display_for_enum!(ConstExpr; Lit, Accessor, App, Array, Dict, BinOp, UnaryOp, Erased); +impl_display_from_nested!(ConstExpr); +impl_locational_for_enum!(ConstExpr; Lit, Accessor, App, Array, Dict, BinOp, UnaryOp, Erased); + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstPosArg { + pub expr: ConstExpr, +} + +impl NestedDisplay for ConstPosArg { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + self.expr.fmt_nest(f, level) + } +} + +impl Locational for ConstPosArg { + fn loc(&self) -> Location { self.expr.loc() } +} + +impl ConstPosArg { + pub const fn new(expr: ConstExpr) -> Self { Self { expr } } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstKwArg { + pub keyword: Token, + pub expr: ConstExpr, +} + +impl NestedDisplay for ConstKwArg { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + write!(f, "{}: ", self.keyword.inspect())?; + self.expr.fmt_nest(f, level + 1) + } +} + +impl Locational for ConstKwArg { + fn loc(&self) -> Location { + Location::concat(&self.keyword, &self.expr) + } +} + +impl ConstKwArg { + pub const fn new(keyword: Token, expr: ConstExpr) -> Self { Self { keyword, expr } } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ConstArgs { + pos_args: Vec, + kw_args: Vec, + paren: Option<(Token, Token)>, +} + +impl NestedDisplay for ConstArgs { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, level: usize) -> std::fmt::Result { + for arg in self.pos_args.iter() { + arg.fmt_nest(f, level)?; + write!(f, ", ")?; + } + write!(f, "?")?; + for arg in self.kw_args.iter() { + arg.fmt_nest(f, level)?; + write!(f, ", ")?; + } + Ok(()) + } +} + +impl_display_from_nested!(ConstArgs); + +impl Locational for ConstArgs { + fn loc(&self) -> Location { + if let Some((l, r)) = &self.paren { + Location::concat(l, r) + } else if let Some(last) = self.kw_args.last() { + Location::concat(self.pos_args.first().unwrap(), last) + } else if let Some(last) = self.pos_args.last() { + Location::concat(self.pos_args.first().unwrap(), last) + } else { unreachable!() } + } +} + +// impl_stream!(ConstArgs, ConstKwArg, pos_args); + +impl ConstArgs { + pub const fn new(pos_args: Vec, kw_args: Vec, paren: Option<(Token, Token)>) -> Self { + Self { pos_args, kw_args, paren } + } + + pub const fn empty() -> Self { + Self::new(vec![], vec![], None) + } + + pub fn is_empty(&self) -> bool { + self.pos_args.is_empty() && self.kw_args.is_empty() + } + + pub fn pos_args(&self) -> impl Iterator { + self.pos_args.iter() + } + + pub fn kw_args(&self) -> impl Iterator { + self.kw_args.iter() + } + + pub fn into_iters(self) -> (impl IntoIterator, impl IntoIterator) { + (self.pos_args.into_iter(), self.kw_args.into_iter()) + } + + pub fn push_pos(&mut self, arg: ConstPosArg) { + self.pos_args.push(arg); + } + + pub fn push_kw(&mut self, arg: ConstKwArg) { + self.kw_args.push(arg); + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct SimpleTypeSpec { + pub name: VarName, + pub args: ConstArgs, // args can be nested (e.g. Vec Vec Int) +} + +impl fmt::Display for SimpleTypeSpec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.args.is_empty() { + write!(f, "{}", self.name) + } else { + write!(f, "{}{}", self.name, self.args) + } + } +} + +impl Locational for SimpleTypeSpec { + fn loc(&self) -> Location { + if let Some(last) = self.args.kw_args.last() { + Location::concat(&self.name, last) + } else if let Some(last) = self.args.pos_args.last() { + Location::concat(&self.name, last) + } else { + self.name.loc() + } + } +} + +impl SimpleTypeSpec { + pub const fn new(name: VarName, args: ConstArgs) -> Self { Self { name, args } } +} + +// OK: +// ts = [T, U]; x: ts[0] = ... +// ts = {.T: T, .U: U}; x: ts.T = ... +// ...; x: foo.bar.ts[0] = ... +// NG: +// ts = {"T": T, "U": U}; x: ts["T"] = ... +// f T = T; x: f(T) = ... +// ...; x: foo[0].T = ... +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum PreDeclTypeSpec { + Simple(SimpleTypeSpec), + Attr{ namespace: Vec, t: SimpleTypeSpec }, + Subscr{ namespace: Vec, name: VarName, index: Token }, +} + +impl fmt::Display for PreDeclTypeSpec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PreDeclTypeSpec::Simple(ts) => write!(f, "{}", ts), + PreDeclTypeSpec::Attr{ namespace, t } => { + write!(f, "{}.{}", namespace.join("."), t) + } + PreDeclTypeSpec::Subscr{ namespace, name, index } => { + write!(f, "{}.{}[{}]", namespace.join("."), name, index) + } + } + } +} + +impl Locational for PreDeclTypeSpec { + fn loc(&self) -> Location { + match self { + Self::Simple(s) => s.loc(), + Self::Attr{ namespace, t } => Location::concat( + &namespace[0], + t, + ), + Self::Subscr{ namespace, index, .. } => Location::concat( + &namespace[0], + index, + ), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParamTySpec { + pub name: Option, + pub ty: TypeSpec, +} + +impl fmt::Display for ParamTySpec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(name) = &self.name { + write!(f, "{}: {}", name.inspect(), self.ty) + } else { + write!(f, "{}", self.ty) + } + } +} + +impl Locational for ParamTySpec { + fn loc(&self) -> Location { + if let Some(name) = &self.name { + Location::concat(name, &self.ty) + } else { + self.ty.loc() + } + } +} + +impl ParamTySpec { + pub const fn new(name: Option, ty: TypeSpec) -> Self { Self { name, ty } } + + pub const fn anonymous(ty: TypeSpec) -> Self { Self::new(None, ty) } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct SubrTySpec { + pub kind: SubrKind, + pub lparen: Option, + pub non_defaults: Vec, + pub defaults: Vec, + pub return_t: Box, +} + +impl fmt::Display for SubrTySpec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({}, ? {}) {} {}", fmt_vec(&self.non_defaults), fmt_vec(&self.defaults), self.kind.arrow(), self.return_t) + } +} + +impl Locational for SubrTySpec { + fn loc(&self) -> Location { + if let Some(lparen) = &self.lparen { + Location::concat(lparen, self.return_t.as_ref()) + } else { + // FIXME: only default subrs + Location::concat(self.non_defaults.first().unwrap(), self.return_t.as_ref()) + } + } +} + +impl SubrTySpec { + pub fn new(kind: SubrKind, lparen: Option, non_defaults: Vec, defaults: Vec, return_t: TypeSpec) -> Self { + Self { kind, lparen, non_defaults, defaults, return_t: Box::new(return_t) } + } +} + +/// * Array: `[Int; 3]`, `[Int, Ratio, Complex]`, etc. +/// * Dict: `[Str: Str]`, etc. +/// * Option: `Int?`, etc. +/// * And (Intersection type): Add and Sub and Mul (== Num), etc. +/// * Not (Diff type): Pos == Nat not {0}, etc. +/// * Or (Union type): Int or None (== Option Int), etc. +/// * Enum: `{0, 1}` (== Binary), etc. +/// * Range: 1..12, 0.0<..1.0, etc. +/// * Record: {.into_s: Self.() -> Str }, etc. +/// * Func: Int -> Int, etc. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum TypeSpec { + PreDeclTy(PreDeclTypeSpec), + /* Composite types */ + Array{ t: PreDeclTypeSpec, len: ConstExpr }, + Tuple(Vec), + // Dict(), + // Option(), + And(Box, Box), + Not(Box, Box), + Or(Box, Box), + Enum(ConstArgs), + Interval{ op: Token, lhs: ConstExpr, rhs: ConstExpr }, + // Record(), + Subr(SubrTySpec), +} + +impl fmt::Display for TypeSpec { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::PreDeclTy(ty) => write!(f, "{ty}"), + Self::And(lhs, rhs) => write!(f, "{lhs} and {rhs}"), + Self::Not(lhs, rhs) => write!(f, "{lhs} not {rhs}"), + Self::Or(lhs, rhs) => write!(f, "{lhs} or {rhs}"), + Self::Array{ t, len } => write!(f, "[{t}; {len}]"), + Self::Tuple(tys) => write!(f, "({})", fmt_vec(&tys)), + Self::Enum(elems) => write!(f, "{{{elems}}}"), + Self::Interval{ op, lhs, rhs } => write!(f, "{lhs}{}{rhs}", op.inspect()), + Self::Subr(s) => write!(f, "{s}"), + } + } +} + +impl Locational for TypeSpec { + fn loc(&self) -> Location { + match self { + Self::PreDeclTy(sig) => sig.loc(), + Self::And(lhs, rhs) + | Self::Not(lhs, rhs) + | Self::Or(lhs, rhs) => Location::concat(lhs.as_ref(), rhs.as_ref()), + Self::Array{ t, len } => Location::concat(t, len), + // TODO: ユニット + Self::Tuple(tys) => Location::concat(tys.first().unwrap(), tys.last().unwrap()), + Self::Enum(set) => set.loc(), + Self::Interval{ lhs, rhs, .. } => Location::concat(lhs, rhs), + Self::Subr(s) => s.loc(), + } + } +} + +impl TypeSpec { + pub fn and(lhs: TypeSpec, rhs: TypeSpec) -> Self { + Self::And(Box::new(lhs), Box::new(rhs)) + } + + pub fn not(lhs: TypeSpec, rhs: TypeSpec) -> Self { + Self::Not(Box::new(lhs), Box::new(rhs)) + } + + pub fn or(lhs: TypeSpec, rhs: TypeSpec) -> Self { + Self::Or(Box::new(lhs), Box::new(rhs)) + } + + pub const fn interval(op: Token, lhs: ConstExpr, rhs: ConstExpr) -> Self { + Self::Interval{ op, lhs, rhs } + } + + pub fn func(lparen: Option, non_defaults: Vec, defaults: Vec, return_t: TypeSpec) -> Self { + Self::Subr(SubrTySpec::new(SubrKind::Func, lparen, non_defaults, defaults, return_t)) + } + + pub fn proc(lparen: Option, non_defaults: Vec, defaults: Vec, return_t: TypeSpec) -> Self { + Self::Subr(SubrTySpec::new(SubrKind::Proc, lparen, non_defaults, defaults, return_t)) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum TypeBoundSpec { + Subtype{ sub: VarName, sup: TypeSpec }, // e.g. S <: Show + Instance{ name: VarName, ty: TypeSpec }, // e.g. N: Nat + // Predicate, // TODO: e.g. N > 5 +} + +impl NestedDisplay for TypeBoundSpec { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + match self { + Self::Subtype{ sub, sup } => write!(f, "{} <: {}", sub, sup), + Self::Instance{ name, ty } => write!(f, "{} : {}", name, ty), + } + } +} + +impl_display_from_nested!(TypeBoundSpec); + +impl Locational for TypeBoundSpec { + fn loc(&self) -> Location { + match self { + Self::Subtype { sub: l, sup: r } + | Self::Instance { name: l, ty: r } => Location::concat(l, r), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct TypeBoundSpecs(Vec); + +impl_displayable_stream_for_wrapper!(TypeBoundSpecs, TypeBoundSpec); + +impl Locational for TypeBoundSpecs { + fn loc(&self) -> Location { + Location::concat(self.first().unwrap(), self.last().unwrap()) + } +} + +/// デコレータは関数を返す関数オブジェクトならば何でも指定できる +/// e.g. @(x -> x) +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Decorator(Expr); + +impl Decorator { + pub const fn new(expr: Expr) -> Self { Self(expr) } +} + +/// symbol as a left value +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct VarName(Token); + +impl Borrow for VarName { + #[inline] + fn borrow(&self) -> &str { &self.0.content[..] } +} + +impl Borrow for VarName { + #[inline] + fn borrow(&self) -> &Str { &self.0.content } +} + +impl Locational for VarName { + #[inline] + fn loc(&self) -> Location { self.0.loc() } +} + +impl fmt::Display for VarName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.inspect()) + } +} + +impl VarName { + pub const fn new(symbol: Token) -> Self { Self(symbol) } + + pub const fn from_static(symbol: &'static str) -> Self { Self(Token::static_symbol(symbol)) } + + pub fn from_str(symbol: Str) -> Self { Self(Token::from_str(TokenKind::Symbol, &symbol)) } + + #[inline] + pub fn is_const(&self) -> bool { + self.0.content.chars().next().map(|c| c.is_uppercase()).unwrap_or(false) + } + + #[inline] + pub fn is_procedural(&self) -> bool { + self.0.content.chars().last().map(|c| c == '!').unwrap_or(false) + } + + pub const fn token(&self) -> &Token { &self.0 } + + pub fn into_token(self) -> Token { self.0 } + + pub const fn inspect(&self) -> &Str { &self.0.content } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct VarArrayPattern { + l_sqbr: Token, + pub(crate) elems: Vars, + r_sqbr: Token, +} + +impl fmt::Display for VarArrayPattern { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "[{}]", self.elems) + } +} + +impl_locational!(VarArrayPattern, l_sqbr, r_sqbr); + +impl Stream for VarArrayPattern { + #[inline] + fn payload(self) -> Vec { self.elems.payload() } + #[inline] + fn ref_payload(&self) -> &Vec { self.elems.ref_payload() } + #[inline] + fn ref_mut_payload(&mut self) -> &mut Vec { self.elems.ref_mut_payload() } +} + +impl VarArrayPattern { + pub const fn new(l_sqbr: Token, elems: Vars, r_sqbr: Token) -> Self { + Self{ l_sqbr, elems, r_sqbr } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct VarTuplePattern { + paren: Option<(Token, Token)>, + pub(crate) elems: Vars, +} + +impl fmt::Display for VarTuplePattern { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({})", self.elems) + } +} + +impl Locational for VarTuplePattern { + fn loc(&self) -> Location { + match &self.paren { + Some((l, r)) => Location::concat(l, r), + None => Location::concat(&self.elems[0], self.elems.last().unwrap()), + } + } +} + +impl Stream for VarTuplePattern { + #[inline] + fn payload(self) -> Vec { self.elems.payload() } + #[inline] + fn ref_payload(&self) -> &Vec { self.elems.ref_payload() } + #[inline] + fn ref_mut_payload(&mut self) -> &mut Vec { self.elems.ref_mut_payload() } +} + +impl VarTuplePattern { + pub const fn new(paren: Option<(Token, Token)>, elems: Vars) -> Self { + Self{ paren, elems } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct VarRecordPattern { + l_brace: Token, + // TODO: レコード専用の構造体を作る + pub(crate) elems: Vars, + r_brace: Token, +} + +impl fmt::Display for VarRecordPattern { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{{{}}}", self.elems) + } +} + +impl_locational!(VarRecordPattern, l_brace, r_brace); + +impl VarRecordPattern { + pub const fn new(l_brace: Token, elems: Vars, r_brace: Token) -> Self { + Self{ l_brace, elems, r_brace } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum VarPattern { + Discard(Token), + VarName(VarName), + SelfDot(VarName), // only self-attribute can assign once + /// e.g. `[x, y, z]` of `[x, y, z] = [1, 2, 3]` + Array(VarArrayPattern), + /// e.g. `(x, y, z)` of `(x, y, z) = (1, 2, 3)` + Tuple(VarTuplePattern), + // e.g. `{name; age}`, `{_; [car, cdr]}` + Record(VarRecordPattern), +} + +impl NestedDisplay for VarPattern { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + match self { + Self::Discard(_) => write!(f, "_"), + Self::VarName(n) => write!(f, "{}", n), + Self::SelfDot(n) => write!(f, "self.{}", n), + Self::Array(a) => write!(f, "{}", a), + Self::Tuple(t) => write!(f, "{}", t), + Self::Record(r) => write!(f, "{}", r), + } + } +} + +impl_display_from_nested!(VarPattern); +impl_locational_for_enum!(VarPattern; Discard, VarName, SelfDot, Array, Tuple, Record); + +impl VarPattern { + pub const fn inspect(&self) -> Option<&Str> { + match self { + Self::VarName(n) | Self::SelfDot(n) => Some(n.inspect()), + _ => None, + } + } + + pub fn inspects(&self) -> Vec<&Str> { + match self { + Self::VarName(n) | Self::SelfDot(n) => vec![n.inspect()], + Self::Array(VarArrayPattern{ elems, .. }) + | Self::Tuple(VarTuplePattern{ elems, .. }) + | Self::Record(VarRecordPattern{ elems, .. }) => { + elems.iter().map(|s| s.pat.inspects()).flatten().collect() + } + _ => vec![], + } + } + + // _!(...) = ... is invalid + pub fn is_procedural(&self) -> bool { + match self { + Self::VarName(n) | Self::SelfDot(n) => n.is_procedural(), + _ => false, + } + } + + // _ = (type block) is invalid + pub fn is_const(&self) -> bool { + match self { + Self::VarName(n) | Self::SelfDot(n) => n.is_const(), + _ => false, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct VarSignature { + pub pat: VarPattern, + pub t_spec: Option, +} + +impl NestedDisplay for VarSignature { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{}{}", self.pat, fmt_option!(pre ": ", &self.t_spec)) + } +} + +impl_display_from_nested!(VarSignature); + +impl Locational for VarSignature { + fn loc(&self) -> Location { + if let Some(t_spec) = &self.t_spec { + Location::concat(&self.pat, t_spec) + } else { + self.pat.loc() + } + } +} + +impl VarSignature { + pub const fn new(pat: VarPattern, t_spec: Option) -> Self { Self{ pat, t_spec } } + + pub const fn inspect(&self) -> Option<&Str> { self.pat.inspect() } + + pub fn is_const(&self) -> bool { self.pat.is_const() } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Vars { + elems: Vec, +} + +impl NestedDisplay for Vars { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{}", fmt_vec(&self.elems)) + } +} + +impl_display_from_nested!(Vars); +impl_stream!(Vars, VarSignature, elems); + +impl Vars { + pub const fn new(elems: Vec) -> Self { Self { elems } } + + pub const fn empty() -> Self { Self::new(vec![]) } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ParamArrayPattern { + l_sqbr: Token, + pub elems: Params, + r_sqbr: Token, +} + +impl NestedDisplay for ParamArrayPattern { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "[{}]", self.elems) + } +} + +impl_display_from_nested!(ParamArrayPattern); +impl_locational!(ParamArrayPattern, l_sqbr, r_sqbr); + +impl ParamArrayPattern { + pub const fn new(l_sqbr: Token, elems: Params, r_sqbr: Token) -> Self { + Self{ l_sqbr, elems, r_sqbr } + } + + pub fn is_empty(&self) -> bool { self.elems.is_empty() } + pub fn len(&self) -> usize { self.elems.len() } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ParamRecordPattern { + l_brace: Token, + pub(crate) elems: Params, + r_brace: Token, +} + +impl NestedDisplay for ParamRecordPattern { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + write!(f, "{{{}}}", self.elems) + } +} + +impl_display_from_nested!(ParamRecordPattern); +impl_locational!(ParamRecordPattern, l_brace, r_brace); + +impl ParamRecordPattern { + pub const fn new(l_brace: Token, elems: Params, r_brace: Token) -> Self { + Self{ l_brace, elems, r_brace } + } +} + +/// 関数定義や無名関数で使えるパターン +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum ParamPattern { + Discard(Token), + VarName(VarName), + // TODO: ConstField(), + // e.g. `a` of `[*a, b] = [1, 2, 3]` (a == [1, 2], b == 3) + // `b` of `[a, *b] = [1, 2, 3]` (a == 1, b == [2, 3]) + VarArgsName(VarName), + Lit(Literal), + Array(ParamArrayPattern), + Record(ParamRecordPattern), +} + +impl_display_for_enum!(ParamPattern; Discard, VarName, VarArgsName, Lit, Array, Record); +impl_locational_for_enum!(ParamPattern; Discard, VarName, VarArgsName, Lit, Array, Record); + +impl ParamPattern { + pub const fn inspect(&self) -> Option<&Str> { + match self { + Self::VarName(n) + | Self::VarArgsName(n) => Some(n.inspect()), + _ => None, + } + } + + pub const fn is_lit(&self) -> bool { matches!(self, Self::Lit(_)) } + + pub fn is_procedural(&self) -> bool { + match self { + Self::Discard(_) => true, + Self::VarName(n) + | Self::VarArgsName(n) => n.is_procedural(), + _ => false, + } + } + + pub fn is_const(&self) -> bool { + match self { + Self::Discard(_) => true, + Self::VarName(n) + | Self::VarArgsName(n) => n.is_const(), + _ => false, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct NonDefaultParamSignature { + pub pat: ParamPattern, + pub t_spec: Option, +} + +impl NestedDisplay for NonDefaultParamSignature { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, _level: usize) -> std::fmt::Result { + write!(f, "{}{}", self.pat, fmt_option!(pre ": ", &self.t_spec)) + } +} + +impl_display_from_nested!(NonDefaultParamSignature); + +impl Locational for NonDefaultParamSignature { + fn loc(&self) -> Location { + if let Some(t_spec) = &self.t_spec { Location::concat(&self.pat, t_spec) } + else { self.pat.loc() } + } +} + +impl NonDefaultParamSignature { + pub const fn new(pat: ParamPattern, t_spec: Option) -> Self { + Self{ pat, t_spec } + } + + pub const fn inspect(&self) -> Option<&Str> { self.pat.inspect() } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct DefaultParamSignature { + pub pat: ParamPattern, + pub t_spec: Option, + pub val: ConstExpr, +} + +impl NestedDisplay for DefaultParamSignature { + fn fmt_nest(&self, f: &mut std::fmt::Formatter<'_>, _level: usize) -> std::fmt::Result { + write!(f, "{}{} |= {}", self.pat, fmt_option!(pre ": ", &self.t_spec), self.val) + } +} + +impl_display_from_nested!(DefaultParamSignature); + +impl Locational for DefaultParamSignature { + fn loc(&self) -> Location { + Location::concat(&self.pat, &self.val) + } +} + +impl DefaultParamSignature { + pub const fn new(pat: ParamPattern, t_spec: Option, val: ConstExpr) -> Self { + Self{ pat, t_spec, val } + } + + pub const fn inspect(&self) -> Option<&Str> { self.pat.inspect() } +} + +pub trait ParamSig { + fn pat(&self) -> &ParamPattern; + fn t_spec(&self) -> Option<&TypeSpec>; +} + +impl ParamSig for NonDefaultParamSignature { + fn pat(&self) -> &ParamPattern { &self.pat } + fn t_spec(&self) -> Option<&TypeSpec> { self.t_spec.as_ref() } +} + +impl ParamSig for DefaultParamSignature { + fn pat(&self) -> &ParamPattern { &self.pat } + fn t_spec(&self) -> Option<&TypeSpec> { self.t_spec.as_ref() } +} + +impl ParamSig for &NonDefaultParamSignature { + fn pat(&self) -> &ParamPattern { &self.pat } + fn t_spec(&self) -> Option<&TypeSpec> { self.t_spec.as_ref() } +} + +impl ParamSig for &DefaultParamSignature { + fn pat(&self) -> &ParamPattern { &self.pat } + fn t_spec(&self) -> Option<&TypeSpec> { self.t_spec.as_ref() } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Params { + pub non_defaults: Vec, + pub defaults: Vec, + pub parens: Option<(Token, Token)>, +} + +impl fmt::Display for Params { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "({}, {})", fmt_vec(&self.non_defaults), fmt_vec(&self.defaults)) + } +} + +impl Locational for Params { + fn loc(&self) -> Location { + if let Some((l, r)) = &self.parens { + Location::concat(l, r) + } else if !self.non_defaults.is_empty() { + Location::concat(&self.non_defaults[0], self.non_defaults.last().unwrap()) + } else if !self.defaults.is_empty() { + Location::concat(&self.defaults[0], self.defaults.last().unwrap()) + } else { + panic!() + } + } +} + +impl Params { + pub const fn new(non_defaults: Vec, defaults: Vec, parens: Option<(Token, Token)>) -> Self { + Self { non_defaults, defaults, parens } + } + + pub fn deconstruct(self) -> (Vec, Vec, Option<(Token, Token)>) { + (self.non_defaults, self.defaults, self.parens) + } + + #[inline] + pub fn len(&self) -> usize { self.non_defaults.len() + self.defaults.len() } + + #[inline] + pub fn is_empty(&self) -> bool { self.len() == 0 } +} + +/// 引数を取るならTypeでもSubr扱い +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct SubrSignature { + pub decorators: Set, + pub name: VarName, + pub params: Params, + pub return_t_spec: Option, + pub bounds: TypeBoundSpecs, +} + +impl NestedDisplay for SubrSignature { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { + if self.bounds.is_empty() { + write!(f, "{}{}{}", self.name, self.params, fmt_option!(pre ": ", &self.return_t_spec)) + } else { + write!(f, "{}|{}|{}{}", self.name, self.bounds, self.params, fmt_option!(pre ": ", &self.return_t_spec)) + } + } +} + +impl_display_from_nested!(SubrSignature); + +impl Locational for SubrSignature { + fn loc(&self) -> Location { + if !self.bounds.is_empty() { + Location::concat(&self.name, &self.bounds) + } else if let Some(return_t) = &self.return_t_spec { + Location::concat(&self.name, return_t) + } else { + Location::concat(&self.name, &self.params) + } + } +} + +impl SubrSignature { + pub const fn new( + decorators: Set, + name: VarName, + params: Params, + return_t: Option, + bounds: TypeBoundSpecs) -> Self { + Self{ decorators, name, params, return_t_spec: return_t, bounds } + } + + pub fn is_const(&self) -> bool { self.name.is_const() } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct LambdaSignature { + pub params: Params, + pub return_t_spec: Option, + pub bounds: TypeBoundSpecs, +} + +impl fmt::Display for LambdaSignature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.bounds.is_empty() { + write!(f, "{}{}", self.params, fmt_option!(pre ": ", &self.return_t_spec)) + } else { + write!(f, "|{}|{}{}", self.bounds, self.params, fmt_option!(pre ": ", &self.return_t_spec)) + } + } +} + +impl Locational for LambdaSignature { + fn loc(&self) -> Location { + if !self.bounds.is_empty() { + Location::concat(&self.params, &self.bounds) + } else if let Some(return_t) = &self.return_t_spec { + Location::concat(&self.params, return_t) + } else if self.params.is_empty() && self.params.parens.is_none() { + unreachable!() + } else { + self.params.loc() + } + } +} + +impl LambdaSignature { + pub const fn new(params: Params, return_t: Option, bounds: TypeBoundSpecs) -> Self { + Self { params, return_t_spec: return_t, bounds } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct DefId(pub usize); + +impl DefId { + pub fn inc(&mut self) { self.0 += 1; } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Lambda { + pub sig: LambdaSignature, + /// for detecting func/proc + pub op: Token, + pub body: Block, + pub id: DefId, +} + +impl NestedDisplay for Lambda { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + write!(f, "{} {}\n", self.sig, self.op.content)?; + self.body.fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(Lambda); + +impl Lambda { + pub const fn new(sig: LambdaSignature, op: Token, body: Block, id : DefId) -> Self { + Self { sig, op, body, id } + } + + pub fn is_procedural(&self) -> bool { self.op.is(TokenKind::ProcArrow) } +} + +impl_locational!(Lambda, sig, body); + +/// represents a declaration of a variable +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum Signature { + Var(VarSignature), + Subr(SubrSignature), +} + +impl_nested_display_for_enum!(Signature; Var, Subr); +impl_display_from_nested!(Signature); +impl_locational_for_enum!(Signature; Var, Subr); + +impl Signature { + pub fn name_as_str(&self) -> &Str { + match self { + Self::Var(v) => v.pat.inspect().unwrap(), + Self::Subr(s) => &s.name.inspect(), + } + } + + pub fn name(&self) -> Option<&VarName> { + match self { + Self::Var(v) => if let VarPattern::VarName(v) = &v.pat { Some(v) } else { None }, + Self::Subr(s) => Some(&s.name), + } + } + + pub fn t_spec(&self) -> Option<&TypeSpec> { + match self { + Self::Var(v) => v.t_spec.as_ref(), + Self::Subr(c) => c.return_t_spec.as_ref(), + } + } + + pub fn is_const(&self) -> bool { + match self { + Self::Var(v) => v.is_const(), + Self::Subr(s) => s.is_const(), + } + } +} + +pub type Decl = Signature; + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct DefBody { + pub op: Token, + pub block: Block, + pub id : DefId, +} + +impl_locational!(DefBody, op, block); + +impl DefBody { + pub const fn new(op: Token, block: Block, id: DefId) -> Self { Self { op, block, id } } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Def { + pub sig: Signature, + pub body: DefBody, +} + +impl NestedDisplay for Def { + fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { + write!(f, "{} {}\n", self.sig, self.body.op.content)?; + self.body.block.fmt_nest(f, level + 1) + } +} + +impl_display_from_nested!(Def); +impl_locational!(Def, sig, body); + +impl Def { + pub const fn new(sig: Signature, body: DefBody) -> Self { Self { sig, body } } + + pub fn is_const(&self) -> bool { self.sig.is_const() } + + pub const fn is_subr(&self) -> bool { + matches!(&self.sig, Signature::Subr(_)) + } +} + +/// Expression(式) +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Expr { + Lit(Literal), + Accessor(Accessor), + Array(Array), + // Dict(Dict), + // Set(Set), + Dict(Dict), + BinOp(BinOp), + UnaryOp(UnaryOp), + Call(Call), + Lambda(Lambda), + Decl(Decl), + Def(Def), +} + +impl_nested_display_for_enum!(Expr; Lit, Accessor, Array, Dict, BinOp, UnaryOp, Call, Lambda, Decl, Def); +impl_display_from_nested!(Expr); +impl_locational_for_enum!(Expr; Lit, Accessor, Array, Dict, BinOp, UnaryOp, Call, Lambda, Decl, Def); + +impl Expr { + pub fn is_match_call(&self) -> bool { + matches!(self, Expr::Call(Call{ obj, .. }) if obj.get_name().map(|s| &s[..] == "match").unwrap_or(false)) + } + + pub fn is_const_acc(&self) -> bool { + matches!(self, Expr::Accessor(acc) if acc.is_const()) + } + + pub fn get_name(&self) -> Option<&Str> { + match self { + Expr::Accessor(acc) => acc.name(), + _ => None, + } + } + + pub fn local(name: &str, lineno: usize, col_begin: usize) -> Self { + Self::Accessor(Accessor::local(Token::new(TokenKind::Symbol, Str::rc(name), lineno, col_begin))) + } + + pub fn dummy_local(name: &str) -> Self { + Self::Accessor(Accessor::local(Token::from_str(TokenKind::Symbol, name))) + } + + pub fn static_local(name: &'static str) -> Self { + Self::Accessor(Accessor::local(Token::static_symbol(name))) + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Module(Vec); + +impl fmt::Display for Module { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt_lines(self.0.iter(), f, 0) + } +} + +impl Locational for Module { + fn loc(&self) -> Location { + Location::concat(self.0.first().unwrap(), self.0.last().unwrap()) + } +} + +impl_stream_for_wrapper!(Module, Expr); + +#[derive(Debug)] +pub struct AST { + pub name: Str, + pub module: Module, +} + +impl_display_for_single_struct!(AST, module); + +impl AST { + pub const fn new(name: Str, module: Module) -> Self { Self { name, module } } + + pub fn is_empty(&self) -> bool { self.module.is_empty() } +} diff --git a/src/compiler/parser/desugar.rs b/src/compiler/parser/desugar.rs new file mode 100644 index 00000000..e33d092e --- /dev/null +++ b/src/compiler/parser/desugar.rs @@ -0,0 +1,152 @@ +//! Desugaring syntax sugars. +//! +//! Syntax sugarをdesugarする +//! e.g. Literal parameters, Multi assignment +//! 型チェックなどによる検証は行わない +#![allow(dead_code)] + +use common::{enum_unwrap, set}; +use common::{Str}; +use common::set::{Set}; +use common::traits::{Stream, Locational}; + +use crate::token::{Token, TokenKind}; +use crate::ast::{ + Module, Expr, Lambda, Call, Def, Accessor, + LambdaSignature, PosArg, Signature, SubrSignature, Args, + ParamPattern, NonDefaultParamSignature, Params, VarName, + TypeBoundSpecs, DefBody, Block, VarPattern +}; + +#[derive(Debug)] +pub struct Desugarer { + desugared: Set, +} + +impl Desugarer { + pub fn new() -> Desugarer { Self { desugared: Set::default() } } + + pub fn desugar(&mut self, module: Module) -> Module { + self.desugar_multiple_pattern_def(module) + } + + fn desugar_ubar_lambda(&self, _module: Module) -> Module { + todo!() + } + + /// `fib 0 = 0; fib 1 = 1; fib n = fib(n-1) + fib(n-2)` + /// -> `fib n = match n, (0 -> 0), (1 -> 1), n -> fib(n-1) + fib(n-2)` + fn desugar_multiple_pattern_def(&self, mut module: Module) -> Module { + let mut new = Module::with_capacity(module.len()); + while let Some(chunk) = module.lpop() { + match chunk { + Expr::Def(def) if def.is_subr() => { + if let Some(Expr::Def(previous)) = new.last() { + if previous.is_subr() && previous.sig.name_as_str() == def.sig.name_as_str() { + let mut previous = enum_unwrap!(new.pop().unwrap(), Expr::Def); + let name = def.sig.name().unwrap().clone(); + let op = Token::from_str(TokenKind::FuncArrow, "->"); + let (call, return_t_spec) = if previous.body.block.len() == 1 + && previous.body.block.first().unwrap().is_match_call() { + let mut call = enum_unwrap!(previous.body.block.remove(0), Expr::Call); + let sig = enum_unwrap!(def.sig, Signature::Subr); + let return_t_spec = sig.return_t_spec; + let first_arg = sig.params.non_defaults.first().unwrap(); + // 最後の定義の引数名を関数全体の引数名にする + if let Some(name) = first_arg.inspect() { + call.args.remove_pos(0); + let arg = PosArg::new(Expr::local(name, first_arg.ln_begin().unwrap(), first_arg.col_begin().unwrap())); + call.args.insert_pos(0, arg); + } + let sig = LambdaSignature::new(sig.params, return_t_spec.clone(), sig.bounds); + let new_branch = Lambda::new(sig, op, def.body.block, def.body.id); + call.args.push_pos(PosArg::new(Expr::Lambda(new_branch))); + (call, return_t_spec) + } else { + let sig = enum_unwrap!(previous.sig, Signature::Subr); + let match_symbol = Expr::static_local("match"); + let sig = LambdaSignature::new(sig.params, sig.return_t_spec, sig.bounds); + let first_branch = Lambda::new(sig, op.clone(), previous.body.block, previous.body.id); + let sig = enum_unwrap!(def.sig, Signature::Subr); + let return_t_spec = sig.return_t_spec; + let sig = LambdaSignature::new(sig.params, return_t_spec.clone(), sig.bounds); + let second_branch = Lambda::new(sig, op, def.body.block, def.body.id); + let args = Args::new(vec![ + PosArg::new(Expr::dummy_local("_")), // dummy argument, will be removed in line 56 + PosArg::new(Expr::Lambda(first_branch)), + PosArg::new(Expr::Lambda(second_branch)) + ], vec![], None); + let call = Call::new(match_symbol, args); + (call, return_t_spec) + }; + let param_name = enum_unwrap!(&call.args.pos_args().iter().next().unwrap().expr, Expr::Accessor:(Accessor::Local:(_))).inspect(); + // FIXME: multiple params + let param = VarName::new(Token::new( + TokenKind::Symbol, + param_name, + name.ln_begin().unwrap(), + name.col_end().unwrap() + 1 // HACK: `(name) %x = ...`という形を想定 + )); + let param = NonDefaultParamSignature::new(ParamPattern::VarName(param), None); + let params = Params::new(vec![param], vec![], None); + let sig = Signature::Subr(SubrSignature::new(set!{}, name, params, return_t_spec, TypeBoundSpecs::empty())); + let body = DefBody::new(def.body.op, Block::new(vec![Expr::Call(call)]), def.body.id); + let def = Def::new(sig, body); + new.push(Expr::Def(def)); + } else { + new.push(Expr::Def(def)); + } + } else { + new.push(Expr::Def(def)); + } + }, + other => { new.push(other); }, + } + } + new + } + + /// `f 0 = 1` -> `f _: {0} = 1` + fn desugar_literal_pattern(&self, _mod: Module) -> Module { + todo!() + } + + /// `[i, j] = [1, 2]` -> `i = 1; j = 2` + /// `[i, j] = l` -> `i = l[0]; j = l[1]` + /// `[i, [j, k]] = l` -> `i = l[0]; j = l[1][0]; k = l[1][1]` + /// `(i, j) = t` -> `i = t.0; j = t.1` + fn desugar_nest_vars_pattern(&self, mut module: Module) -> Module { + let mut new = Module::with_capacity(module.len()); + while let Some(chunk) = module.lpop() { + match chunk { + Expr::Def(def) => { + if let Signature::Var(v) = &def.sig { + match &v.pat { + VarPattern::Array(_a) => {} + VarPattern::Record(_r) => {}, + _ => {} + } + } + new.push(Expr::Def(def)); + }, + other => { new.push(other); }, + } + } + new + } + + /// `{i; j} = s` -> `i = s.i; j = s.j` + fn desugar_record_pattern(&self, _mod: Module) -> Module { + todo!() + } + + /// `F(I | I > 0)` -> `F(I: {I: Int | I > 0})` + fn desugar_refinement_pattern(&self, _mod: Module) -> Module { + todo!() + } + + /// `show! x: Show := print! x` -> `show! x: '1 | '1 <: Show := print! x` + fn desugar_trait_parameter(&self, _mod: Module) -> Module { + todo!() + } +} diff --git a/src/compiler/parser/error.rs b/src/compiler/parser/error.rs new file mode 100644 index 00000000..7b02d859 --- /dev/null +++ b/src/compiler/parser/error.rs @@ -0,0 +1,104 @@ +//! defines `ParseError` and others. +//! +//! パーサーが出すエラーを定義 +use common::{impl_stream_for_wrapper, switch_lang}; +use common::Str; +use common::config::Input; +use common::error::{ErrorCore, ErrorDisplay, MultiErrorDisplay, Location, ErrorKind::*}; +use common::traits::Stream; + +#[derive(Debug)] +pub struct LexError(ErrorCore); + +#[derive(Debug)] +pub struct LexErrors(Vec); + +impl_stream_for_wrapper!(LexErrors, LexError); + +impl LexError { + pub const fn new(core: ErrorCore) -> Self { Self(core) } + + pub fn compiler_bug(errno: usize, loc: Location, fn_name: &str, line: u32) -> Self { + Self::new(ErrorCore::new(errno, CompilerSystemError, loc, switch_lang!( + format!("this is a bug of the Erg compiler, please report it to https://github.com/mtshiba/erg\ncaused from: {fn_name}:{line}"), + format!("これはErg compilerのバグです、開発者に報告して下さい (https://github.com/mtshiba/erg)\n{fn_name}:{line}より発生") + ), None)) + } + + pub fn feature_error(errno: usize, loc: Location, name: &str) -> Self { + Self::new(ErrorCore::new(errno, FeatureError, loc, switch_lang!( + format!("this feature({name}) is not implemented yet"), + format!("この機能({name})はまだ正式に提供されていません") + ), None)) + } + + pub fn simple_syntax_error(errno: usize, loc: Location) -> Self { + Self::new(ErrorCore::new(errno, SyntaxError, loc, switch_lang!("invalid syntax", "不正な構文です"), None)) + } + + pub fn syntax_error>(errno: usize, loc: Location, desc: S, hint: Option) -> Self { + Self::new(ErrorCore::new(errno, SyntaxError, loc, desc, hint)) + } + + pub fn syntax_warning>(errno: usize,loc: Location, desc: S, hint: Option) -> Self { + Self::new(ErrorCore::new(errno, SyntaxWarning, loc, desc, hint)) + } +} + +pub type LexResult = Result; + +pub type ParseError = LexError; +pub type ParseErrors = LexErrors; +pub type ParseResult = Result; + +#[derive(Debug)] +pub struct DesugaringError { + pub core: ErrorCore, +} + +impl DesugaringError { + pub const fn new(core: ErrorCore) -> Self { Self{ core }} +} + +#[derive(Debug)] +pub struct DesugaringErrors(Vec); + +impl_stream_for_wrapper!(DesugaringErrors, DesugaringError); + +pub type DesugaringResult = Result; + +#[derive(Debug)] +pub struct ParserRunnerError { + pub core: ErrorCore, + pub input: Input, +} + +impl ErrorDisplay for ParserRunnerError { + fn core(&self) -> &ErrorCore { &self.core } + fn input(&self) -> &Input { &self.input } + fn caused_by(&self) -> &str { "" } + fn ref_inner(&self) -> Option<&Box> { None } +} + +impl ParserRunnerError { + pub const fn new(core: ErrorCore, input: Input) -> Self { Self{ core, input } } +} + +#[derive(Debug)] +pub struct ParserRunnerErrors(Vec); + +impl_stream_for_wrapper!(ParserRunnerErrors, ParserRunnerError); + +impl MultiErrorDisplay for ParserRunnerErrors {} + +impl ParserRunnerErrors { + pub fn convert(input: &Input, errs: ParseErrors) -> Self { + Self(errs.into_iter().map(|err| ParserRunnerError::new(err.0, input.clone())).collect()) + } +} + +pub type ParserRunnerResult = Result; + +pub type LexerRunnerError = ParserRunnerError; +pub type LexerRunnerErrors = ParserRunnerErrors; +pub type LexerRunnerResult = Result; diff --git a/src/compiler/parser/lex.rs b/src/compiler/parser/lex.rs new file mode 100644 index 00000000..13e2474d --- /dev/null +++ b/src/compiler/parser/lex.rs @@ -0,0 +1,729 @@ +//! defines and implements `Lexer` (Tokenizer). +use common::cache::Cache; +use common::Str; +use common::{fn_name_full, switch_lang, debug_power_assert, normalize_newline}; +use common::config::Input; +use common::config::ErgConfig; +use common::traits::{Locational, Runnable, Stream}; + +use crate::error::{LexerRunnerError, LexerRunnerErrors, LexError, LexErrors, LexResult}; +use crate::token::{Token, TokenCategory, TokenKind, TokenStream}; +use TokenKind::*; + +/// Lexerは使い捨てなので、Runnerを用意 +pub struct LexerRunner { + cfg: ErgConfig, +} + +impl Runnable for LexerRunner { + type Err = LexerRunnerError; + type Errs = LexerRunnerErrors; + + #[inline] + fn new(cfg: ErgConfig) -> Self { Self { cfg } } + + #[inline] + fn input(&self) -> &Input { &self.cfg.input } + + #[inline] + fn start_message(&self) -> String { "Erg lexer\n".to_string() } + + #[inline] + fn clear(&mut self) {} + + fn eval(&mut self, src: Str) -> Result { + let lexer = Lexer::from_str(src); + if cfg!(feature = "debug") { + let ts = lexer.lex().map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?; + println!("{ts}"); + Ok(ts.to_string()) + } else { + Ok(lexer.lex().map_err(|errs| LexerRunnerErrors::convert(self.input(), errs))?.to_string()) + } + } +} + +/// Lexes a source code and iterates tokens. +/// +/// This can be used as an iterator or to generate a `TokenStream`. +#[derive(Debug)] +pub struct Lexer /*<'a>*/ { + str_cache: Cache, + chars: Vec, + indent_stack: Vec, + /// indicates the position in the entire source code + cursor: usize, + /// to determine the type of operators, etc. + prev_token: Token, + /// 0-origin, but Token.lineno will 1-origin + lineno_token_starts: usize, + /// 0-origin, indicates the column number in which the token appears + col_token_starts: usize, +} + +impl Lexer /*<'a>*/ { + pub fn new(input: Input) -> Self { + let normed = normalize_newline(&input.read()); + Lexer { + str_cache: Cache::new(), + chars: normed.chars().collect::>(), + indent_stack: vec![], + cursor: 0, + prev_token: Token::new(TokenKind::BOF, "", 0, 0), + lineno_token_starts: 0, + col_token_starts: 0, + } + } + + pub fn from_str(src: Str) -> Self { + let escaped = normalize_newline(&src); + Lexer { + str_cache: Cache::new(), + chars: escaped.chars().collect::>(), + indent_stack: vec![], + cursor: 0, + prev_token: Token::new(TokenKind::BOF, "", 0, 0), + lineno_token_starts: 0, + col_token_starts: 0, + } + } + + pub fn lex(self) -> Result { + let mut result = TokenStream::empty(); + let mut errs = LexErrors::empty(); + for i in self.into_iter() { + match i { + Ok(token) => { result.push(token) } + Err(err) => { errs.push(err); } + } + } + if errs.is_empty() { Ok(result) } else { Err(errs) } + } + + fn emit_token(&mut self, kind: TokenKind, cont: &str) -> Token { + let cont = self.str_cache.get(cont); + // cannot use String::len() for multi-byte characters + let cont_len = cont.chars().count(); + let token = Token::new(kind, cont, self.lineno_token_starts + 1, self.col_token_starts); + self.prev_token = token.clone(); + self.col_token_starts += cont_len; + token + } + + #[inline] + fn accept(&mut self, kind: TokenKind, cont: &str) -> Option> { + Some(Ok(self.emit_token(kind, cont))) + } + + fn deny_feature(&mut self, cont: &str, feat_name: &str) -> Option> { + let token = self.emit_token(Illegal, cont); + Some(Err(LexError::feature_error(0, token.loc(), feat_name))) + } + + const fn is_valid_symbol_ch(c: char) -> bool { + match c { + '0'..='9' => true, + // control characters + '\0' | '\u{0009}'..='\u{001F}' => false, + // white spaces + ' ' | '\u{00A0}' => false, + '\u{007F}' | '\u{0085}' | '\u{05C1}' | '\u{05C2}' => false, + '\u{0701}'..='\u{070d}' => false, + '\u{07B2}'..='\u{07BF}' => false, + '\u{1680}' | '\u{180E}' => false, + '\u{2000}'..='\u{200F}' => false, + '\u{2028}'..='\u{202F}' => false, + '\u{205F}'..='\u{206F}' => false, + '\u{3000}' | '\u{3164}' | '\u{FEFF}' => false, + // operator characters + special markers + '<' | '>' | '$' | '%' | '.' | ',' | ':' | ';' | '+' | '-' | '*' | '/' | '=' | '#' + | '&' | '|' | '^' | '~' | '@' | '!' | '?' | '\\' => false, + // enclosures + '[' | ']' | '(' | ')' | '{' | '}' | '\"' | '\'' | '`' => false, + _ => true, + } + } + + /// Detect `c` is a bidirectional overriding character. + /// [CVE-2021-42574: homoglyph atack](https://blog.rust-lang.org/2021/11/01/cve-2021-42574.html) countermeasures. + pub fn is_bidi(c: char) -> bool { + match c { + '\u{200F}' | '\u{202B}' | '\u{202E}' | '\u{2067}' => true, + _ => false, + } + } + + #[inline] + fn is_definable_operator(s: &str) -> bool { + match s { + "+" | "-" | "*" | "/" | "//" | "**" | "%" | ".." | "..=" | "~" | "&&" | "||" | "^^" + | ">>" | "<<" | "==" | "!=" | ">" | "<" | ">=" | "<=" + | "dot" | "cross" => true, + _ => false, + } + } + + // +, -, * etc. may be pre/bin + // and, or, is, isnot, in, notin, as, dot, cross may be bin/function + const fn is_bin_position(&self) -> Option { + match self.prev_token.category() { + // unary: `[ +`, `= +`, `+ +`, `, +`, `:: +` + TokenCategory::LEnclosure + | TokenCategory::BinOp + | TokenCategory::UnaryOp + | TokenCategory::Separator + | TokenCategory::SpecialBinOp + | TokenCategory::DefOp + | TokenCategory::LambdaOp => Some(false), + // bin: `] +`, `1 +`, `true and[true]` + TokenCategory::REnclosure | TokenCategory::Literal => Some(true), + // bin: `fn +1` + // NOTE: if semantic analysis shows `fn` is a function, should this be rewritten to be unary? + TokenCategory::Symbol => Some(true), + _ => None, + } + } + + fn is_zero(s: &str) -> bool { s.replace("-0", "").replace("0", "").is_empty() } + + /// emit_tokenで一気にcol_token_startsを移動させるのでここでは移動させない + fn consume(&mut self) -> Option { + let now = self.cursor; + self.cursor += 1; + self.chars.get(now).map(|x| *x) + } + + fn peek_prev_ch(&self) -> Option { + if self.cursor == 0 { + None + } else { + self.chars.get(self.cursor - 1).map(|x| *x) + } + } + + #[inline] + fn peek_cur_ch(&self) -> Option { + self.chars.get(self.cursor).map(|x| *x) + } + + #[inline] + fn peek_next_ch(&self) -> Option { + self.chars.get(self.cursor + 1).map(|x| *x) + } + + fn lex_comment(&mut self) -> LexResult<()> { + // debug_power_assert!(self.consume(), ==, Some('#')); + let mut s = "".to_string(); + while self.peek_cur_ch().map(|cur| cur != '\n').unwrap_or(false) { + if Self::is_bidi(self.peek_cur_ch().unwrap()) { + let comment = self.emit_token(Illegal, &s); + return Err(LexError::syntax_error(0, comment.loc(), switch_lang!( + "invalid unicode character (bi-directional override) in comments", + "不正なユニコード文字(双方向オーバーライド)がコメント中に使用されています" + ), None)) + } + s.push(self.consume().unwrap()); + } + Ok(()) + } + + fn lex_space_indent_dedent(&mut self) -> Option> { + let is_toplevel = self.cursor > 0 + && !self.indent_stack.is_empty() + && self.peek_prev_ch() == Some('\n') + && self.peek_cur_ch() != Some(' '); + if is_toplevel { + let dedent = self.emit_token(Dedent, ""); + self.indent_stack.pop(); + self.col_token_starts = 0; + return Some(Ok(dedent)) + } + let mut spaces = "".to_string(); + while let Some(' ') = self.peek_cur_ch() { + spaces.push(self.consume().unwrap()); + } + // indent in the first line: error + if !spaces.is_empty() && self.cursor == 0 { + let space = self.emit_token(Illegal, &spaces); + Some(Err(LexError::syntax_error(0, space.loc(), switch_lang!( + "invalid indent", + "インデントが不正です" + ), None))) + } else if self.prev_token.is(Newline) { + self.lex_indent_dedent(spaces) + } else { + self.col_token_starts += spaces.len(); + None + } + } + + /// The semantic correctness of the use of indent/dedent will be analyzed with `Parser` + fn lex_indent_dedent(&mut self, spaces: String) -> Option> { + // same as the CPython's limit + if spaces.len() > 100 { + let token = self.emit_token(Indent, &spaces); + return Some(Err(LexError::syntax_error(0, token.loc(), switch_lang!( + "indentation is too deep", + "インデントが深すぎます" + ), Some(switch_lang!( + "The code is too complicated. Please split the process.", + "コードが複雑すぎます。処理を分割してください" + ).into())))) + } + // ignore indents if the current line is a comment + if let Some('#') = self.peek_cur_ch() { + if let Err(e) = self.lex_comment() { return Some(Err(e)) } + } + let mut is_valid_dedent = false; + let calc_indent_and_validate = |sum: usize, x: &usize| { + if sum + *x == spaces.len() { is_valid_dedent = true; } + sum + *x + }; + let sum_indent = self.indent_stack.iter().fold(0, calc_indent_and_validate); + if sum_indent < spaces.len() { + let indent_len = spaces.len() - sum_indent; + self.col_token_starts += sum_indent; + let indent = self.emit_token(Indent, &" ".repeat(indent_len)); + self.indent_stack.push(indent_len); + Some(Ok(indent)) + } else if sum_indent > spaces.len() { + if is_valid_dedent { + let dedent = self.emit_token(Dedent, ""); + self.indent_stack.pop(); + Some(Ok(dedent)) + } else { + let invalid_dedent = self.emit_token(Dedent, ""); + Some(Err(LexError::syntax_error(0, invalid_dedent.loc(), switch_lang!( + "invalid indent", + "インデントが不正です" + ), None))) + } + } else /* if indent_sum == space.len() */ { + self.col_token_starts += spaces.len(); + None + } + } + + fn lex_exponent(&mut self, mantissa: String) -> LexResult { + let mut num = mantissa; + debug_power_assert!(self.peek_cur_ch(), ==, Some('e')); + num.push(self.consume().unwrap()); // e + num.push(self.consume().unwrap()); // + | - + while let Some(cur) = self.peek_cur_ch() { + if cur.is_ascii_digit() || cur == '_' { + num.push(self.consume().unwrap()); + } else { + break; + } + } + Ok(self.emit_token(RatioLit, &num)) + } + + /// `_` will be removed at compiletime + fn lex_num(&mut self, first_ch: char) -> LexResult { + let mut num = first_ch.to_string(); + while let Some(ch) = self.peek_cur_ch() { + match ch { + // `.` may be a dot operator, don't consume + '.' => { return self.lex_num_dot(num) }, + n if n.is_ascii_digit() || n == '_' => { + num.push(self.consume().unwrap()); + } + c if Self::is_valid_symbol_ch(c) => { + // exponent (e.g. 10e+3) + if c == 'e' + && (self.peek_next_ch() == Some('+') || self.peek_next_ch() == Some('-')) { + return self.lex_exponent(num) + } else { + // IntLit * Symbol(e.g. 3x + 1) + let token = self.emit_token(Illegal, &(num + &c.to_string())); + return Err(LexError::feature_error(0, token.loc(), "*-less multiply")) + } + } + _ => { + break; + } + } + } + let kind = if num.starts_with('-') && !Self::is_zero(&num) { IntLit } else { NatLit }; + Ok(self.emit_token(kind, &num)) + } + + /// number '.' ~~ + /// Possibility: RatioLit or Int/NatLit call + fn lex_num_dot(&mut self, mut num: String) -> LexResult { + match self.peek_next_ch() { + // RatioLit + Some(n) if n.is_ascii_digit() => { + num.push(self.consume().unwrap()); + self.lex_ratio(num) + } + // method call of IntLit + // or range operator (e.g. 1..) + Some(c) if Self::is_valid_symbol_ch(c) || c == '.' => Ok(self.emit_token(IntLit, &num)), + Some('_') => { + self.consume(); + let token = self.emit_token(Illegal, &(num + "_")); + Err(LexError::simple_syntax_error(0, token.loc())) + } + // RatioLit without zero (e.g. 3.) + _ => { + num.push(self.consume().unwrap()); + self.lex_ratio(num) + } + } + } + + /// int_part_and_point must be like `12.` + fn lex_ratio(&mut self, intpart_and_point: String) -> LexResult { + let mut num = intpart_and_point; + while let Some(cur) = self.peek_cur_ch() { + if cur.is_ascii_digit() || cur == '_' { + num.push(self.consume().unwrap()); + } else if cur == 'e' { + return self.lex_exponent(num) + } else { + break; + } + } + Ok(self.emit_token(RatioLit, &num)) + } + + fn lex_symbol(&mut self, first_ch: char) -> LexResult { + let mut cont = first_ch.to_string(); + while let Some(c) = self.peek_cur_ch() { + if Self::is_valid_symbol_ch(c) { + cont.push(self.consume().unwrap()); + } else { + break; + } + } + if let Some('!') = self.peek_cur_ch() { + cont.push(self.consume().unwrap()); + } + if cont.is_empty() { + let token = self.emit_token(Illegal, &self.peek_cur_ch().unwrap().to_string()); + return Err(LexError::compiler_bug(0, token.loc(), fn_name_full!(), line!())) + } + // dot: scalar product, cross: vector product + // An alphabetical operator can also declare as a function, so checking is necessary + // e.g. and(true, true, true) = true + let kind = match &cont[..] { + "and" => AndOp, + "or" => OrOp, + "in" => InOp, + "notin" => NotInOp, + "is" => IsOp, + "isnot" => IsNotOp, + "dot" => DotOp, + "cross" => CrossOp, + // これらはリテラルというより定数だが便宜的にリテラルということにしておく + "True" | "False" => BoolLit, + "None" => NoneLit, + "NotImplemented" => NoImplLit, + "Ellipsis" => EllipsisLit, + "Inf" => InfLit, + "_" => UBar, + _ => Symbol, + }; + Ok(self.emit_token(kind, &cont)) + } + + fn lex_str(&mut self) -> LexResult { + let mut s = "\"".to_string(); + while let Some(c) = self.peek_cur_ch() { + if c == '\"' && s.chars().last() != Some('\\') { + s.push(self.consume().unwrap()); + let token = self.emit_token(StrLit, &s); + return Ok(token) + } else { + let c = self.consume().unwrap(); + s.push(c); + if Self::is_bidi(c) { + let token = self.emit_token(Illegal, &s); + return Err(LexError::syntax_error(0, token.loc(), switch_lang!( + "invalid unicode character (bi-directional override) in string literal", + "不正なユニコード文字(双方向オーバーライド)が文字列中に使用されています" + ), None)) + } + } + } + let token = self.emit_token(Illegal, &s); + Err(LexError::syntax_error(0, token.loc(), switch_lang!( + "the string is not closed by \"", + "文字列が\"によって閉じられていません" + ), None)) + } +} + +impl Iterator for Lexer /*<'a>*/ { + type Item = LexResult; + + fn next(&mut self) -> Option { + if self.prev_token.is(TokenKind::EOF) { + return None + } + let indent_dedent = self.lex_space_indent_dedent(); + if indent_dedent.is_some() { + return indent_dedent + } + if let Some('#') = self.peek_cur_ch() { + if let Err(e) = self.lex_comment() { return Some(Err(e)) } + } + match self.consume() { + Some('(') => self.accept(LParen, "("), + Some(')') => self.accept(RParen, ")"), + Some('[') => self.accept(LSqBr, "["), + Some(']') => self.accept(RSqBr, "]"), + Some('{') => self.accept(LBrace, "{"), + Some('}') => self.accept(RBrace, "}"), + Some('<') => match self.peek_cur_ch() { + Some('.') => { + self.consume(); + if let Some('.') = self.peek_cur_ch() { + self.consume(); + match self.peek_cur_ch() { + Some('<') => { + self.consume(); + self.accept(Open, "<..<") + } + _ => self.accept(LeftOpen, "<.."), + } + } else { + let token = self.emit_token(Illegal, "<."); + Some(Err(LexError::syntax_error(0, token.loc(), switch_lang!( + "no such operator: <.", + "<.という演算子はありません" + ), None))) + } + } + Some('=') => { + self.consume(); + self.accept(LessEq, "<=") + } + Some('<') => { + self.consume(); + self.accept(Shl, "<<") + } + _ => self.accept(Less, "<"), + }, + Some('>') => match self.peek_cur_ch() { + Some('=') => { + self.consume(); + self.accept(GreEq, ">=") + } + Some('>') => { + self.consume(); + self.accept(Shr, ">>") + } + _ => self.accept(Gre, ">"), + }, + Some('.') => { + match self.peek_cur_ch() { + Some('.') => { + self.consume(); + match self.peek_cur_ch() { + Some('<') => { + self.consume(); + self.accept(RightOpen, "..<") + }, + Some('.') => { + self.consume(); + self.accept(EllipsisLit, "...") + }, + _ => { + self.accept(Closed, "..") + } + } + } + Some(c) if c.is_ascii_digit() => { + Some(self.lex_ratio(".".into())) + } + _ => self.accept(Dot, ".") + } + } + Some(',') => self.accept(Comma, ","), + Some(':') => match self.peek_cur_ch() { + Some(':') => { + self.consume(); + self.accept(DblColon, "::") + } + Some('>') => { + self.consume(); + self.accept(SupertypeOf, ":>") + } + _ => self.accept(Colon, ":"), + }, + Some(';') => self.accept(Semi, ";"), + Some('&') => { + if let Some('&') = self.peek_cur_ch() { + self.consume(); + self.accept(BitAnd, "&&") + } else { + // let kind = if self.is_bin_position().unwrap() { Amper } else { PreAmp }; + self.accept(Amper, "&") + } + } + Some('|') => { + match self.peek_cur_ch() { + Some('|') => { + self.consume(); + self.accept(BitOr, "||") + } + Some('=') => { + self.consume(); + self.accept(OrEqual, "|=") + } + _ => { + self.accept(VBar, "|") + } + } + } + Some('^') => { + if let Some('^') = self.peek_cur_ch() { + self.consume(); + self.accept(BitXor, "^^") + } else { + self.accept(Caret, "^") + } + } + Some('~') => self.accept(PreBitNot, "~"), + // TODO: + Some('$') => self.deny_feature("$", "shared variables"), + Some('@') => self.accept(AtSign, "@"), + Some('=') => match self.peek_cur_ch() { + Some('=') => { + self.consume(); + self.accept(DblEq, "==") + } + Some('>') => { + self.consume(); + self.accept(ProcArrow, "=>") + } + _ => self.accept(Equal, "="), + }, + Some('!') => { + if let Some('=') = self.peek_cur_ch() { + self.consume(); + self.accept(NotEq, "!=") + } else { + self.accept(Mutate, "!") + } + } + Some('?') => self.accept(Try, "?"), + Some('+') => { + let kind = if self.is_bin_position().unwrap() { + Plus + } else { + PrePlus + }; + self.accept(kind, "+") + } + Some('-') => match self.peek_cur_ch() { + Some('>') => { + self.consume(); + self.accept(FuncArrow, "->") + } + _ => { + if self.is_bin_position().unwrap() { + self.accept(Minus, "-") + } else { + // IntLit (negative number) + if self.peek_cur_ch().map(|t| t.is_ascii_digit()).unwrap_or(false) { + Some(self.lex_num('-')) + } else { + self.accept(Minus, "-") + } + } + } + }, + Some('*') => match self.peek_cur_ch() { + Some('*') => { + self.consume(); + self.accept(Pow, "**") + } + _ => { + let kind = if self.is_bin_position().unwrap() { + Star + } else { + PreStar + }; + self.accept(kind, "*") + } + }, + Some('/') => match self.peek_cur_ch() { + Some('/') => { + self.consume(); + self.accept(FloorDiv, "//") + } + _ => self.accept(Slash, "/"), + }, + Some('%') => self.accept(Mod, "%"), + // Newline + // 改行記号はLexer新規生成時に全て\nにreplaceしてある + Some('\n') => { + let token = self.emit_token(Newline, "\n"); + self.lineno_token_starts += 1; + self.col_token_starts = 0; + Some(Ok(token)) + } + Some('\t') => { + let token = self.emit_token(Illegal, "\t"); + Some(Err(LexError::syntax_error(0, token.loc(), switch_lang!( + "cannot use a tab as a space", + "タブ文字は使用できません" + ), Some(switch_lang!("use spaces", "スペースを使用してください").into())))) + } + // TODO: + Some('\\') => self.deny_feature("\\", "ignoring line break"), + // StrLit + Some('\"') => Some(self.lex_str()), + // TODO: + Some('\'') => self.deny_feature("'", "raw identifier"), + // Symbolized operators (シンボル化された演算子) + // e.g. `-`(l, r) = l + (-r) + Some('`') => { + let mut op = "".to_string(); + while let Some(c) = self.consume() { + if c == '`' { + if Self::is_definable_operator(&op[..]) { + return self.accept(Symbol, &op) + } else { + let token = self.emit_token(Illegal, &op); + return Some(Err(LexError::syntax_error(0, token.loc(), switch_lang!( + format!("`{}` cannot be defined by user", &token.content), + format!("`{}`はユーザー定義できません", &token.content) + ), None))) + } + } + op.push(c); + } + let token = self.emit_token(Illegal, &op); + Some(Err(LexError::syntax_error(0, token.loc(), switch_lang!( + format!("back quotes (`) not closed"), + format!("バッククォート(`)が閉じられていません") + ), None))) + } + // IntLit or RatioLit + Some(n) if n.is_ascii_digit() => Some(self.lex_num(n)), + // Symbol (includes '_') + Some(c) if Self::is_valid_symbol_ch(c) => Some(self.lex_symbol(c)), + // Invalid character (e.g. space-like character) + Some(invalid) => { + let token = self.emit_token(Illegal, &invalid.to_string()); + Some(Err(LexError::syntax_error(0, token.loc(), switch_lang!( + format!("invalid character: '{invalid}'"), + format!("この文字は使用できません: '{invalid}'") + ), None))) + } + None => { + if self.indent_stack.len() == 0 { + self.accept(EOF, "") + } else { + self.indent_stack.pop(); + self.accept(Dedent, "") + } + } + } + } +} diff --git a/src/compiler/parser/lib.rs b/src/compiler/parser/lib.rs new file mode 100644 index 00000000..d8eb584b --- /dev/null +++ b/src/compiler/parser/lib.rs @@ -0,0 +1,12 @@ +//! Implements `Parser` for Erg. `Parser` parses the source code to generate `AST`, +//! and performs type checking and other optimizations if necessary. +extern crate common; + +pub mod desugar; +pub mod error; +pub mod ast; +pub mod lex; +pub mod parse; +pub mod token; + +pub use parse::{Parser, ParserRunner}; diff --git a/src/compiler/parser/main.rs b/src/compiler/parser/main.rs new file mode 100644 index 00000000..91847380 --- /dev/null +++ b/src/compiler/parser/main.rs @@ -0,0 +1,22 @@ +extern crate common; +extern crate parser; + +use std::process; + +use common::config::ErgConfig; +use common::traits::Runnable; + +use parser::lex::LexerRunner; +use parser::ParserRunner; + +fn main() { + let cfg = ErgConfig::parse(); + match cfg.mode { + "lex" => { LexerRunner::run(cfg); } + "parse" | "exec" => { ParserRunner::run(cfg); } + other => { + println!("invalid mode: {other}"); + process::exit(1); + } + } +} diff --git a/src/compiler/parser/parse.rs b/src/compiler/parser/parse.rs new file mode 100644 index 00000000..5904ba5d --- /dev/null +++ b/src/compiler/parser/parse.rs @@ -0,0 +1,1372 @@ +//! implements `Parser`. +//! +//! パーサーを実装する +//! +use std::fmt::Debug; +use std::mem; + +use common::Str; +use common::{debug_power_assert, enum_unwrap, fn_name, caused_by, switch_lang, switch_unreachable, log, set}; +use common::color::{GREEN, RED, RESET}; +use common::config::{Input, SEMVER, BUILD_INFO}; +use common::config::ErgConfig; +use common::error::{Location}; +use common::set::Set; +use common::traits::Runnable; +use common::traits::{Locational, Stream}; + +use crate::error::{ParseError, ParseErrors, ParseResult, ParserRunnerError, ParserRunnerErrors}; +use crate::ast::*; +use crate::lex::Lexer; +use crate::token::{TokenCategory, TokenKind, TokenStream, Token}; +use crate::desugar::Desugarer; + +use TokenCategory as TC; +use TokenKind::*; + +/// Display the name of the called function for debugging the parser +/// I thought about displaying the nesting level, but due to `?` operator there is not a single exit point for the function +/// So it is not possible to lower the level +macro_rules! debug_call_info { + ($self: ident) => { + log!("[DEBUG] entered {}, cur: {}", fn_name!(), $self.peek().unwrap()); + }; +} + +enum ExprOrOp { + Expr(Expr), + Op(Token), +} + +enum ParamSig { + NonDefault(NonDefaultParamSignature), + Default(DefaultParamSignature), +} + +enum PosOrKwArg { + Pos(PosArg), + Kw(KwArg), +} + +pub enum Side { + LhsAssign, + LhsLambda, + Rhs, +} + +/// To enhance error descriptions, the parsing process will continue as long as it's not fatal. +#[derive(Debug)] +pub struct Parser { + counter: DefId, + tokens: TokenStream, + warns: ParseErrors, + errs: ParseErrors, +} + +impl Parser { + const fn new(ts: TokenStream) -> Self { + Self { counter: DefId(0), tokens: ts, warns: ParseErrors::empty(), errs: ParseErrors::empty() } + } + + #[inline] + fn peek(&self) -> Option<&Token> { self.tokens.get(0) } + + #[inline] + fn nth(&self, idx: usize) -> Option<&Token> { self.tokens.get(idx) } + + #[inline] + fn skip(&mut self) { self.tokens.remove(0); } + + #[inline] + fn lpop(&mut self) -> Token { self.tokens.remove(0) } + + fn cur_category_is(&self, category: TokenCategory) -> bool { + self.peek() + .map(|t| t.category_is(category)) + .unwrap_or(false) + } + + fn cur_is(&self, kind: TokenKind) -> bool { + self.peek() + .map(|t| t.is(kind)) + .unwrap_or(false) + } + + fn nth_is(&self, idx: usize, kind: TokenKind) -> bool { + self.nth(idx) + .map(|t| t.is(kind)) + .unwrap_or(false) + } + + fn nth_category(&self, idx: usize) -> Option { + self.nth(idx).map(|t| t.category()) + } + + /// `+1`: true + /// `+ 1`: false + /// `F()`: true + /// `F ()`: false + fn cur_is_in_contact_with_next(&self) -> bool { + let cur_loc = self.peek().unwrap().ln_end().unwrap(); + let next_loc = self.nth(1).unwrap().ln_end().unwrap(); + cur_loc + 1 == next_loc + } + + /// returns if the current position is a left-hand side value. + /// + /// ``` + /// f(x: Int) = { y = x+1; z = [v: Int, w: Int] -> w + x } + /// LhsAssign | Rhs | LhsLambda | Rhs + /// ``` + /// `(Rhs) ; (LhsAssign) =` + /// `(Rhs) ; (LhsLambda) ->` + /// `(Rhs) , (LhsLambda) ->` + /// `(Rhs) (LhsLambda) -> (Rhs);` + fn cur_side(&self) -> Side { + // 以降に=, ->などがないならすべて右辺値 + let opt_equal_pos = self.tokens.iter().skip(1) + .position(|t| t.is(Equal)); + let opt_arrow_pos = self.tokens.iter().skip(1) + .position(|t| t.category_is(TC::LambdaOp)); + let opt_sep_pos = self.tokens.iter().skip(1) + .position(|t| t.category_is(TC::Separator)); + match (opt_equal_pos, opt_arrow_pos, opt_sep_pos) { + (Some(equal), Some(arrow), Some(sep)) => { + let min = [equal, arrow, sep].into_iter().min().unwrap(); + if min == sep { Side::Rhs } + else if min == equal { Side::LhsAssign } + else { + // (cur) -> ... = ... ; + if equal < sep { Side::LhsAssign } + // (cur) -> ... ; ... = + else { + if self.arrow_distance(0, 0) == 1 { + Side::LhsLambda + } else { + Side::Rhs + } + } + } + } + // (cur) = ... -> ... + // (cur) -> ... = ... + (Some(_eq), Some(_arrow), None) => Side::LhsAssign, + // (cur) = ... ; + // (cur) ; ... = + (Some(equal), None, Some(sep)) => + if equal < sep { Side::LhsAssign } else { Side::Rhs }, + (None, Some(arrow), Some(sep)) => { + // (cur) -> ... ; + if arrow < sep { + if self.arrow_distance(0, 0) == 1 { Side::LhsLambda } + else { Side::Rhs } + } + // (cur) ; ... -> + else { Side::Rhs } + } + (Some(_eq), None, None) => Side::LhsAssign, + (None, Some(_arrow), None) => + if self.arrow_distance(0, 0) == 1 { Side::LhsLambda } + else { Side::Rhs }, + (None, None, Some(_)) | (None, None, None) => Side::Rhs, + } + } + + /// `->`: 0 + /// `i ->`: 1 + /// `i: Int ->`: 1 + /// `a: Array(Int) ->`: 1 + /// `(i, j) ->`: 1 + /// `F () ->`: 2 + /// `F() ->`: 1 + /// `if True, () ->`: 3 + fn arrow_distance(&self, cur: usize, enc_nest_level: usize) -> usize { + match self.nth_category(cur).unwrap() { + TC::LambdaOp => 0, + TC::LEnclosure => { + if self.nth_category(cur+1).unwrap() == TC::REnclosure { + 1 + self.arrow_distance(cur+2, enc_nest_level) + } else { + self.arrow_distance(cur+1, enc_nest_level+1) + } + }, + TC::REnclosure => { + self.arrow_distance(cur+1, enc_nest_level-1) + } + _ => { + match self.nth_category(cur+1).unwrap() { + TC::SpecialBinOp => self.arrow_distance(cur+1, enc_nest_level), + TC::LEnclosure if self.cur_is_in_contact_with_next() => { + self.arrow_distance(cur+2, enc_nest_level+1) + }, + _ if enc_nest_level == 0 => { + 1 + self.arrow_distance(cur+1, enc_nest_level) + }, + _ => { + self.arrow_distance(cur+1, enc_nest_level) + }, + } + }, + } + } + + /// 解析を諦めて次の解析できる要素に移行する + fn next_expr(&mut self) { + while let Some(t) = self.peek() { + match t.category() { + TC::Separator | TC::DefOp | TC::LambdaOp => { + self.skip(); + return; + } + TC::EOF => { return; } + _ => { self.skip(); } + } + } + } + + fn skip_and_throw_syntax_err(&mut self, caused_by: &str) -> ParseError { + let loc = self.peek().unwrap().loc(); + log!("{RED}[DEBUG] error caused by: {caused_by}{GREEN}"); + self.next_expr(); + ParseError::simple_syntax_error(0, loc) + } + + #[inline] + fn restore(&mut self, token: Token) { self.tokens.insert(0, token); } +} + +#[derive(Debug)] +pub struct ParserRunner { + cfg: ErgConfig, +} + +impl Runnable for ParserRunner { + type Err = ParserRunnerError; + type Errs = ParserRunnerErrors; + + #[inline] + fn new(cfg: ErgConfig) -> Self { Self { cfg } } + + #[inline] + fn input(&self) -> &Input { &self.cfg.input } + + #[inline] + fn start_message(&self) -> String { format!("Erg parser {} {}\n", SEMVER, &*BUILD_INFO) } + + #[inline] + fn clear(&mut self) {} + + fn eval(&mut self, src: Str) -> Result { + let ast = self.parse_from_str(src)?; + Ok(format!("{ast}")) + } +} + +impl ParserRunner { + pub fn parse(&mut self, ts: TokenStream) -> Result { + Parser::new(ts).parse(Str::ever(self.cfg.module)) + .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs)) + } + + pub fn parse_from_str(&mut self, src: Str) -> Result { + let ts = Lexer::from_str(src).lex() + .map_err(|errs| ParserRunnerErrors::convert(self.input(), errs))?; + self.parse(ts) + } +} + +impl Parser { + pub fn parse(&mut self, mod_name: Str) -> Result { + if self.tokens.is_empty() { return Ok(AST::new(mod_name, Module::empty())) } + log!("{GREEN}[DEBUG] the parsing process has started."); + log!("token stream: {}", self.tokens); + let module = match self.try_reduce_module() { + Ok(module) => module, + Err(e) => { + self.errs.push(e); + return Err(mem::take(&mut self.errs)) + }, + }; + if !self.cur_is(EOF) { + let loc = self.peek().unwrap().loc(); + self.errs.push(ParseError::compiler_bug(0, loc, fn_name!(), line!())); + return Err(mem::take(&mut self.errs)) + } + log!("[DEBUG] the parsing process has completed."); + log!("AST:\n{module}"); + log!("[DEBUG] the desugaring process has started."); + let mut desugarer = Desugarer::new(); + let module = desugarer.desugar(module); + log!("AST (desugared):\n{module}"); + log!("[DEBUG] the desugaring process has completed.{RESET}"); + if self.errs.is_empty() { + Ok(AST::new(mod_name, module)) + } else { + Err(mem::take(&mut self.errs)) + } + } + + /// 構文の最大単位であるモジュールに還元する(これが呼ばれるのは一度きり) + /// プログラムのトップレベルに来てよいのは単独の式のみなので、例えばBinOpが先頭に来るのは構文エラー + #[inline] + fn try_reduce_module(&mut self) -> ParseResult { + debug_call_info!(self); + let mut chunks = Module::empty(); + loop { + match self.peek() { + Some(t) if t.category_is(TC::Separator) => { self.skip(); } + Some(t) if t.is(EOF) => { break; } + Some(t) if t.is(Indent) || t.is(Dedent) => { + switch_unreachable!() + } + Some(_) => { + match self.try_reduce_expr() { + Ok(expr) => { chunks.push(expr); } + Err(e) => { self.errs.push(e); } + } + } + _ => switch_unreachable!() + } + } + Ok(chunks) + } + + fn try_reduce_block(&mut self) -> ParseResult { + debug_call_info!(self); + let mut block = Block::with_capacity(2); + // single line block + if !self.cur_is(Newline) { + block.push(self.try_reduce_expr()?); + return Ok(block) + } + loop { + match self.peek() { + Some(t) if t.category_is(TC::Separator) => { self.skip(); } + Some(t) => { + if t.is(Indent) { + self.skip(); + while self.cur_is(Newline) { + self.skip(); + } + } else if self.cur_is(Dedent) { + self.skip(); + break; + } else if t.is(EOF) { break; } + match self.try_reduce_expr() { + Ok(expr) => { + block.push(expr); + if self.cur_is(Dedent) { + self.skip(); + break; + } + } + Err(e) => { self.errs.push(e); } + } + } + _ => switch_unreachable!() + } + } + if block.is_empty() { + let loc = if let Some(u) = self.peek() { u.loc() } else { Location::Unknown }; + let err = ParseError::syntax_error(0, loc, switch_lang!( + "failed to parse a block", + "ブロックの解析に失敗しました" + ), None); + Err(err) + } else { + Ok(block) + } + } + + #[inline] + fn opt_reduce_decorator(&mut self) -> ParseResult> { + if self.cur_is(TokenKind::AtSign) { + self.lpop(); + Ok(Some(Decorator::new(self.try_reduce_expr()?))) + } else { Ok(None) } + } + + #[inline] + fn opt_reduce_decorators(&mut self) -> ParseResult> { + let mut decs = set![]; + loop { + match self.opt_reduce_decorator()? { + Some(deco) => { decs.insert(deco); } + None => { break; } + } + } + Ok(decs) + } + + #[inline] + fn try_reduce_decl(&mut self) -> ParseResult { + debug_call_info!(self); + if self.peek().unwrap().category_is(TC::LEnclosure) { + return Ok(Signature::Var(self.try_reduce_var_sig()?)) + } + let decorators = self.opt_reduce_decorators()?; + let name = self.try_reduce_name()?; + // TODO: parse bounds |...| + let bounds = TypeBoundSpecs::empty(); + if self.cur_is(VBar) { + todo!("type bounds are not supported yet"); + } + if let Some(params) = self.opt_reduce_params().transpose()? { + let t_spec = if self.cur_is(Colon) { + self.skip(); + Some(self.try_reduce_type_spec()?) + } else { None }; + Ok(Signature::Subr(SubrSignature::new(decorators, name, params, t_spec, bounds))) + } else { + if !bounds.is_empty() { + let err = ParseError::syntax_error(0, self.peek().unwrap().loc(), switch_lang!( + "Cannot use type bounds in a declaration of a variable", + "変数宣言で型制約は使えません" + ), None); + self.next_expr(); + return Err(err) + } + let t_spec = if name.is_const() { + if self.cur_is(SubtypeOf) { + self.skip(); + Some(self.try_reduce_type_spec()?) + } else { + if self.cur_is(Colon) { + self.warns.push(ParseError::syntax_warning(0, name.loc(), switch_lang!( + "Since it is obvious that the variable is of type `Type`, there is no need to specify the type.", + "変数がType型であることは明らかなので、型指定は不要です。" + ), Some(switch_lang!( + "Are you sure you're not confusing it with subclass declaration (<:)?", + "サブクラスの宣言(<:)ではありませんか?" + ).into()) + )); + } + None + } + } else { + if self.cur_is(Colon) { + self.skip(); + Some(self.try_reduce_type_spec()?) + } else { None } + }; + Ok(Signature::Var(VarSignature::new(VarPattern::VarName(name), t_spec))) + } + } + + #[inline] + fn try_reduce_var_sig(&mut self) -> ParseResult { + debug_call_info!(self); + let pat = self.try_reduce_var_pattern()?; + let t_spec = if self.cur_is(Colon) { + self.skip(); + Some(self.try_reduce_type_spec()?) + } else { None }; + if self.cur_is(VBar) { + todo!() + } + Ok(VarSignature::new(pat, t_spec)) + } + + /// default_param ::= non_default_param `=` const_expr + fn try_reduce_param_sig(&mut self) -> ParseResult { + debug_call_info!(self); + let lhs = self.try_reduce_non_default_param_sig()?; + if self.cur_is(OrEqual) { + self.skip(); + let val = self.try_reduce_const_expr()?; + Ok(ParamSig::Default(DefaultParamSignature::new(lhs.pat, lhs.t_spec, val))) + } else { + Ok(ParamSig::NonDefault(lhs)) + } + } + + #[inline] + fn try_reduce_non_default_param_sig(&mut self) -> ParseResult { + debug_call_info!(self); + let pat = self.try_reduce_param_pattern()?; + let t_spec = if self.cur_is(Colon) { + self.skip(); + Some(self.try_reduce_type_spec()?) + } else { None }; + Ok(NonDefaultParamSignature::new(pat, t_spec)) + } + + #[inline] + fn try_reduce_default_param_sig(&mut self) -> ParseResult { + debug_call_info!(self); + let lhs = self.try_reduce_non_default_param_sig()?; + if self.cur_is(OrEqual) { + self.skip(); + let val = self.try_reduce_const_expr()?; + Ok(DefaultParamSignature::new(lhs.pat, lhs.t_spec, val)) + } else { + Err(ParseError::syntax_error(0, lhs.loc(), "non-default argument follows default argument", None)) + } + } + + #[inline] + fn try_reduce_lambda_sig(&mut self) -> ParseResult { + debug_call_info!(self); + // let lambda_mark = self.lpop(); + let params = self.try_reduce_params()?; + let return_t = match self.peek() { + Some(t) if t.is(SupertypeOf) => { + self.skip(); + Some(self.try_reduce_type_spec()?) + } + _ => None, + }; + let bounds = match self.peek() { + Some(t) if t.is(VBar) => { + self.skip(); + self.try_reduce_bounds()? + } + _ => TypeBoundSpecs::empty(), + }; + Ok(LambdaSignature::new(params, return_t, bounds)) + } + + fn try_reduce_acc(&mut self) -> ParseResult { + debug_call_info!(self); + let mut acc = match self.peek() { + Some(t) if t.is(Symbol) => { + Accessor::local(self.lpop()) + } + // TODO: SelfDot + _ => { + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + }; + loop { + match self.peek() { + Some(t) if t.is(Dot) => { + self.skip(); + let symbol = self.lpop(); + debug_power_assert!(symbol.is(Symbol)); + let attr = Local::new(symbol); + acc = Accessor::attr(Expr::Accessor(acc), attr); + } + Some(t) if t.is(LSqBr) => { + self.skip(); + let index = self.try_reduce_expr()?; + if self.cur_is(RSqBr) { self.skip(); } + else { return Err(self.skip_and_throw_syntax_err(caused_by!())) } + acc = Accessor::subscr(Expr::Accessor(acc), index); + if self.cur_is(RSqBr) { + self.lpop(); + } else { + // TODO: error report: RSqBr not found + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + _ => { break; } + } + } + Ok(acc) + } + + fn try_reduce_elems(&mut self) -> ParseResult { + debug_call_info!(self); + let mut elems = Vars::empty(); + match self.peek() { + Some(t) if t.is_block_op() => { + return Ok(Vars::empty()) + } + Some(_) => { + let elem = self.try_reduce_var_sig()?; + elems.push(elem); + } + _ => { + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + loop { + match self.peek() { + Some(t) if t.is(Comma) => { + self.skip(); + let elem = self.try_reduce_var_sig()?; + elems.push(elem); + } + Some(t) if t.category_is(TC::BinOp) => { + log!("[DEBUG] error caused by: {}", fn_name!()); + let err = ParseError::syntax_error(0, t.loc(), switch_lang!( + "Binary operators cannot be used in left-values", + "左辺値の中で中置演算子は使えません" + ), None); + self.next_expr(); + return Err(err) + } + _ => { break; } + } + } + Ok(elems) + } + + fn opt_reduce_params(&mut self) -> Option> { + debug_call_info!(self); + match self.peek() { + Some(t) if + t.category_is(TC::Literal) || t.is(Symbol) + || t.category_is(TC::UnaryOp) || t.is(Dot) || t.category_is(TC::Caret) + || t.is(LParen) || t.is(LSqBr) || t.is(LBrace) => { + Some(self.try_reduce_params()) + } + _ => None, + } + } + + fn try_reduce_params(&mut self) -> ParseResult { + debug_call_info!(self); + let lp = if self.cur_is(TokenKind::LParen) { + Some(self.lpop()) + } else { None }; + let mut non_default_params = vec![]; + let mut default_params = vec![]; + match self.peek() { + Some(t) if t.is(RParen) => { + let parens = (lp.unwrap(), self.lpop()); + return Ok(Params::new(non_default_params, default_params, Some(parens))) + }, + Some(t) if t.is_block_op() => { + if lp.is_none() { return Ok(Params::new(non_default_params, default_params, None)) } + // TODO: RParen not found + else { return Err(self.skip_and_throw_syntax_err(caused_by!())) } + } + Some(_) => { + match self.try_reduce_param_sig()? { + ParamSig::NonDefault(non_default) => { + non_default_params.push(non_default); + } + ParamSig::Default(default) => { + default_params.push(default); + } + } + } + _ => { + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + loop { + match self.peek() { + Some(t) if t.is(Comma) => { + self.skip(); + if default_params.is_empty() { + match self.try_reduce_param_sig()? { + ParamSig::NonDefault(non_default) => { + non_default_params.push(non_default); + } + ParamSig::Default(default) => { + default_params.push(default); + } + } + } else { + let default = self.try_reduce_default_param_sig()?; + default_params.push(default); + } + } + Some(t) if t.category_is(TC::BinOp) => { + let err = ParseError::syntax_error(0, t.loc(), switch_lang!( + "Binary operators cannot be used in parameters", + "仮引数の中で中置演算子は使えません" + ), None); + self.next_expr(); + return Err(err) + } + Some(t) if t.is(TokenKind::RParen) => { + let rp = self.lpop(); + if let Some(lp) = lp { + return Ok(Params::new(non_default_params, default_params, Some((lp, rp)))) + } else { + // LParen not found + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + _ if lp.is_none() => { + return Ok(Params::new(non_default_params, default_params, None)) + } + _ => { + // TODO: RParen not found + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + } + } + + fn try_reduce_var_pattern(&mut self) -> ParseResult { + debug_call_info!(self); + match self.peek() { + Some(t) if t.is(Symbol) => { + Ok(VarPattern::VarName(self.try_reduce_name()?)) + } + Some(t) if t.is(UBar) => { + Ok(VarPattern::Discard(self.lpop())) + } + Some(t) if t.is(LSqBr) => { + let l_sqbr = self.lpop(); + let elems = self.try_reduce_elems()?; + if self.cur_is(RSqBr) { + let r_sqbr = self.lpop(); + Ok(VarPattern::Array(VarArrayPattern::new(l_sqbr, elems, r_sqbr))) + } else { + // TODO: error report: RSqBr not found + Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + Some(t) if t.is(LParen) => { + self.skip(); + let pat = self.try_reduce_var_pattern()?; + if self.cur_is(RParen) { + self.skip(); + Ok(pat) + } else { + // TODO: error report: RParen not found + Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + _ => Err(self.skip_and_throw_syntax_err(caused_by!())), + } + } + + fn try_reduce_param_pattern(&mut self) -> ParseResult { + debug_call_info!(self); + match self.peek() { + Some(t) if t.is(Symbol) => { + Ok(ParamPattern::VarName(self.try_reduce_name()?)) + } + Some(t) if t.is(UBar) => { + Ok(ParamPattern::Discard(self.lpop())) + } + Some(t) if t.category_is(TC::Literal) => { + // TODO: range pattern + Ok(ParamPattern::Lit(self.try_reduce_lit()?)) + } + Some(t) if t.is(PreStar) => { + self.skip(); + Ok(ParamPattern::VarArgsName(self.try_reduce_name()?)) + } + Some(t) if t.is(LSqBr) => { + let l_sqbr = self.lpop(); + let elems = self.try_reduce_params()?; + if self.cur_is(RSqBr) { + let r_sqbr = self.lpop(); + Ok(ParamPattern::Array(ParamArrayPattern::new(l_sqbr, elems, r_sqbr))) + } else { + // TODO: error report: RSqBr not found + Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + Some(t) if t.is(LParen) => { + self.skip(); + let pat = self.try_reduce_param_pattern()?; + if self.cur_is(RParen) { + self.skip(); + Ok(pat) + } else { + // TODO: error report: RParen not found + Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + _ => Err(self.skip_and_throw_syntax_err(caused_by!())), + } + } + + // TODO: set type + fn try_reduce_type_spec(&mut self) -> ParseResult { + debug_call_info!(self); + let mut typ = match self.peek() { + Some(t) if t.is(Symbol) => { + TypeSpec::PreDeclTy(PreDeclTypeSpec::Simple(self.try_reduce_simple_type_spec()?)) + } + Some(t) if t.category_is(TC::Literal) => { + let lhs = ConstExpr::Lit(self.try_reduce_lit()?); + let maybe_op = self.lpop(); + let op = if + maybe_op.is(Closed) || maybe_op.is(LeftOpen) + || maybe_op.is(RightOpen) || maybe_op.is(Open) { maybe_op } else { + return Err(self.skip_and_throw_syntax_err(caused_by!())) + }; + // TODO: maybe Name + let rhs = ConstExpr::Lit(self.try_reduce_lit()?); + TypeSpec::interval(op, lhs, rhs) + } + Some(t) if t.is(LParen) => { + self.try_reduce_func_type()? + } + Some(t) if t.is(LSqBr) => { + self.skip(); + let mut tys = vec![self.try_reduce_type_spec()?]; + loop { + match self.peek() { + Some(t) if t.is(Comma) => { + self.skip(); + let t = self.try_reduce_type_spec()?; + tys.push(t); + } + Some(t) if t.is(RSqBr) => { + self.skip(); + break; + } + _ => { + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + } + TypeSpec::Tuple(tys) + } + _ => { + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + }; + loop { + match self.peek() { + Some(t) if t.is(AndOp) => { + let rhs = self.try_reduce_type_spec()?; + typ = TypeSpec::and(typ, rhs); + } + Some(t) if t.is(OrOp) => { + let rhs = self.try_reduce_type_spec()?; + typ = TypeSpec::or(typ, rhs); + } + Some(t) if t.category_is(TC::LambdaOp) => { + let is_func = t.is(FuncArrow); + self.skip(); + let rhs = self.try_reduce_type_spec()?; + typ = if is_func { + TypeSpec::func(None, vec![ParamTySpec::anonymous(typ)], vec![], rhs) + } else { + TypeSpec::proc(None, vec![ParamTySpec::anonymous(typ)], vec![], rhs) + }; + } + _ => { break; } + } + } + Ok(typ) + } + + fn try_reduce_func_type_param(&mut self) -> ParseResult { + debug_call_info!(self); + if self.cur_is(Symbol) && self.nth_is(1, Colon) { + let name = self.try_reduce_name()?; + self.skip(); + let typ = self.try_reduce_type_spec()?; + Ok(ParamTySpec::new(Some(name.into_token()), typ)) + } else { + Ok(ParamTySpec::anonymous(self.try_reduce_type_spec()?)) + } + } + + // TODO: default parameters + fn try_reduce_func_type(&mut self) -> ParseResult { + debug_call_info!(self); + let lparen = Some(self.lpop()); + let mut non_defaults = vec![self.try_reduce_func_type_param()?]; + loop { + match self.peek() { + Some(t) if t.is(Comma) => { + self.skip(); + non_defaults.push(self.try_reduce_func_type_param()?); + } + Some(t) if t.is(RParen) => { + self.skip(); + break; + } + _ => { + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + } + match self.peek() { + Some(t) if t.category_is(TC::LambdaOp) => { + let is_func = t.is(FuncArrow); + self.skip(); + let rhs = self.try_reduce_type_spec()?; + if is_func { + Ok(TypeSpec::func(lparen, non_defaults, vec![], rhs)) + } else { + Ok(TypeSpec::proc(lparen, non_defaults, vec![], rhs)) + } + } + _ => { Err(self.skip_and_throw_syntax_err(caused_by!())) } + } + } + + #[inline] + fn try_reduce_simple_type_spec(&mut self) -> ParseResult { + debug_call_info!(self); + match self.peek() { + Some(t) if t.is(Symbol) => { + let name = self.try_reduce_name()?; + if let Some(res) = self.opt_reduce_args() { + let args = self.validate_const_args(res?)?; + Ok(SimpleTypeSpec::new(name, args)) + } else { + Ok(SimpleTypeSpec::new(name, ConstArgs::empty())) + } + } + _ => Err(self.skip_and_throw_syntax_err(caused_by!())), + } + } + + fn try_reduce_bounds(&mut self) -> ParseResult { + todo!() + } + + fn validate_const_expr(&mut self, expr: Expr) -> ParseResult { + match expr { + Expr::Lit(l) => Ok(ConstExpr::Lit(l)), + Expr::Accessor(Accessor::Local(local)) => { + let local = ConstLocal::new(local.symbol); + Ok(ConstExpr::Accessor(ConstAccessor::Local(local))) + } + // TODO: App, Array, Record, BinOp, UnaryOp, + other => { + Err(ParseError::syntax_error(0, other.loc(), switch_lang!( + "this expression is not computable at the compile-time, so cannot used as a type-argument", + "この式はコンパイル時計算できないため、型引数には使用できません", + ), None)) + } + } + } + + fn validate_const_pos_arg(&mut self, arg: PosArg) -> ParseResult { + let expr = self.validate_const_expr(arg.expr)?; + Ok(ConstPosArg::new(expr)) + } + + fn validate_const_kw_arg(&mut self, arg: KwArg) -> ParseResult { + let expr = self.validate_const_expr(arg.expr)?; + Ok(ConstKwArg::new(arg.keyword, expr)) + } + + // exprが定数式か確認する + fn validate_const_args(&mut self, args: Args) -> ParseResult { + let (pos, kw, paren) = args.deconstruct(); + let mut const_args = ConstArgs::new(vec![], vec![], paren); + for arg in pos.into_iter() { + match self.validate_const_pos_arg(arg) { + Ok(arg) => { + const_args.push_pos(arg); + } + Err(e) => { return Err(e) } + } + } + for arg in kw.into_iter() { + match self.validate_const_kw_arg(arg) { + Ok(arg) => { + const_args.push_kw(arg); + } + Err(e) => { return Err(e) } + } + } + Ok(const_args) + } + + fn opt_reduce_args(&mut self) -> Option> { + debug_call_info!(self); + match self.peek() { + Some(t) if t.category_is(TC::Literal) + || t.is(Symbol) || t.category_is(TC::UnaryOp) + || t.is(Dot) || t.category_is(TC::Caret) + || t.is(LParen) || t.is(LSqBr) || t.is(LBrace) + || t.is(Colon) => { + Some(self.try_reduce_args()) + } + _ => None, + } + } + + /// 引数はインデントで区切ることができる(ただしコンマに戻すことはできない) + /// + /// ``` + /// x = if true, 1, 2 + /// # is equal to + /// x = if true: + /// 1 + /// 2 + /// ``` + fn try_reduce_args(&mut self) -> ParseResult { + debug_call_info!(self); + let mut lp = None; + let rp; + if self.cur_is(LParen) { + lp = Some(self.lpop()); + } + if self.cur_is(RParen) { + rp = Some(self.lpop()); + return Ok(Args::new(vec![], vec![], Some((lp.unwrap(), rp.unwrap())))); + } else if self.cur_category_is(TC::REnclosure) { + return Ok(Args::new(vec![], vec![], None)); + } + let mut args = match self.try_reduce_arg()? { + PosOrKwArg::Pos(arg) => Args::new(vec![arg], vec![], None), + PosOrKwArg::Kw(arg) => Args::new(vec![], vec![arg], None), + }; + let mut colon_style = false; + loop { + match self.peek() { + Some(t) if t.is(Colon) && colon_style => { + self.skip(); + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + Some(t) if t.is(Colon) => { + self.skip(); + colon_style = true; + while self.cur_is(Newline) { + self.skip(); + } + debug_power_assert!(self.cur_is(Indent)); + self.skip(); + if !args.kw_is_empty() { + args.push_kw(self.try_reduce_kw_arg()?); + } else { + match self.try_reduce_arg()? { + PosOrKwArg::Pos(arg) => { args.push_pos(arg); }, + PosOrKwArg::Kw(arg) => { args.push_kw(arg); }, + } + } + } + Some(t) if t.is(Comma) => { + self.skip(); + if colon_style || self.cur_is(Comma) { + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + if !args.kw_is_empty() { + args.push_kw(self.try_reduce_kw_arg()?); + } else { + match self.try_reduce_arg()? { + PosOrKwArg::Pos(arg) => { args.push_pos(arg); }, + PosOrKwArg::Kw(arg) => { args.push_kw(arg); }, + } + } + } + Some(t) if t.is(Newline) && colon_style => { + while self.cur_is(Newline) { + self.skip(); + } + if self.cur_is(Dedent) { + self.skip(); + break; + } + if !args.kw_is_empty() { + args.push_kw(self.try_reduce_kw_arg()?); + } else { + match self.try_reduce_arg()? { + PosOrKwArg::Pos(arg) => { args.push_pos(arg); }, + PosOrKwArg::Kw(arg) => { args.push_kw(arg); }, + } + } + } + Some(t) if t.is(RParen) => { + rp = Some(self.lpop()); + let (pos_args, kw_args, _) = args.deconstruct(); + args = Args::new(pos_args, kw_args, Some((lp.unwrap(), rp.unwrap()))); + break; + } + _ => { break; } + } + } + Ok(args) + } + + fn try_reduce_arg(&mut self) -> ParseResult { + debug_call_info!(self); + match self.peek() { + Some(t) if t.is(Symbol) => { + if self.nth_is(1, Colon) { + let acc = self.try_reduce_acc()?; + debug_power_assert!(self.cur_is(Colon)); + if self.nth_is(1, Newline) { // colon style call + Ok(PosOrKwArg::Pos(PosArg::new(Expr::Accessor(acc)))) + } else { + self.skip(); + let kw = if let Accessor::Local(n) = acc { + n.symbol + } else { + self.next_expr(); + return Err(ParseError::simple_syntax_error(0, acc.loc())) + }; + Ok(PosOrKwArg::Kw(KwArg::new(kw, self.try_reduce_expr()?))) + } + } else { + Ok(PosOrKwArg::Pos(PosArg::new(self.try_reduce_expr()?))) + } + } + Some(_) => { + Ok(PosOrKwArg::Pos(PosArg::new(self.try_reduce_expr()?))) + } + None => switch_unreachable!(), + } + } + + fn try_reduce_kw_arg(&mut self) -> ParseResult { + debug_call_info!(self); + match self.peek() { + Some(t) if t.is(Symbol) => { + if self.nth_is(1, Colon) { + let acc = self.try_reduce_acc()?; + debug_power_assert!(self.cur_is(Colon)); + self.skip(); + let keyword = if let Accessor::Local(n) = acc { + n.symbol + } else { + self.next_expr(); + return Err(ParseError::simple_syntax_error(0, acc.loc())) + }; + Ok(KwArg::new(keyword, self.try_reduce_expr()?)) + } else { + return Err(ParseError::simple_syntax_error(0, t.loc())) + } + } + Some(other) => { + Err(ParseError::simple_syntax_error(0, other.loc())) + } + None => switch_unreachable!(), + } + } + + fn try_reduce_const_expr(&mut self) -> ParseResult { + debug_call_info!(self); + let expr = self.try_reduce_expr()?; + self.validate_const_expr(expr) + } + + fn try_reduce_expr(&mut self) -> ParseResult { + debug_call_info!(self); + let mut stack = Vec::::new(); + match self.cur_side() { + Side::LhsAssign => { + let sig = self.try_reduce_decl()?; + match self.peek() { + Some(t) if t.is(Equal) => { + let op = self.lpop(); + self.counter.inc(); + let body = DefBody::new(op, self.try_reduce_block()?, self.counter); + Ok(Expr::Def(Def::new(sig, body))) + }, + _other => { + Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + }, + Side::LhsLambda => { + let params = self.try_reduce_params()?; + match self.peek() { + Some(t) if t.category_is(TC::LambdaOp) => { + let sig = LambdaSignature::new(params, None, TypeBoundSpecs::empty()); + let op = self.lpop(); + self.counter.inc(); + Ok(Expr::Lambda(Lambda::new(sig, op, self.try_reduce_block()?, self.counter))) + }, + Some(t) if t.is(Colon) => { + self.lpop(); + let spec_t = self.try_reduce_type_spec()?; + let sig = LambdaSignature::new(params, Some(spec_t), TypeBoundSpecs::empty()); + let op = self.lpop(); + self.counter.inc(); + Ok(Expr::Lambda(Lambda::new(sig, op, self.try_reduce_block()?, self.counter))) + }, + _other => { + Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + }, + Side::Rhs => { + stack.push(ExprOrOp::Expr(self.try_reduce_lhs()?)); + loop { + match self.peek() { + Some(op) if op.category_is(TC::BinOp) => { + let op_prec = op.kind.precedence(); + if stack.len() >= 2 { + while let Some(ExprOrOp::Op(prev_op)) = stack.get(stack.len() - 2) { + if prev_op.category_is(TC::BinOp) && + prev_op.kind.precedence() >= op_prec { + let rhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let prev_op = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Op:(_))); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let bin = BinOp::new(prev_op, lhs, rhs); + stack.push(ExprOrOp::Expr(Expr::BinOp(bin))); + } else { + break; + } + if stack.len() <= 1 { + break; + } + } + } + stack.push(ExprOrOp::Op(self.lpop())); + stack.push(ExprOrOp::Expr(self.try_reduce_lhs()?)); + } + Some(t) if t.category_is(TC::DefOp) => { switch_unreachable!() } + Some(t) if t.is(Dot) => { + self.skip(); + match self.lpop() { + symbol if symbol.is(Symbol) => { + let obj = if let Some(ExprOrOp::Expr(expr)) = stack.pop() { + expr + } else { + return Err(self.skip_and_throw_syntax_err(caused_by!())) + }; + let acc = Accessor::attr(obj, Local::new(symbol)); + if let Ok(args) = self.try_reduce_args() { + let call = Call::new(Expr::Accessor(acc), args); + stack.push(ExprOrOp::Expr(Expr::Call(call))); + } else { + stack.push(ExprOrOp::Expr(Expr::Accessor(acc))); + } + } + other => { + self.restore(other); + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + } + } + _ => { + if stack.len() <= 1 { + break; + } + // else if stack.len() == 2 { switch_unreachable!() } + else { + while stack.len() >= 3 { + let rhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let op = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Op:(_))); + let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))); + let bin = BinOp::new(op, lhs, rhs); + stack.push(ExprOrOp::Expr(Expr::BinOp(bin))); + } + } + } + } + } + match stack.pop() { + Some(ExprOrOp::Expr(expr)) if stack.is_empty() => Ok(expr), + Some(ExprOrOp::Expr(expr)) => { + let extra = stack.pop().unwrap(); + let loc = match extra { + ExprOrOp::Expr(expr) => expr.loc(), + ExprOrOp::Op(op) => op.loc(), + }; + self.warns.push(ParseError::compiler_bug(0, loc, fn_name!(), line!())); + Ok(expr) + } + Some(ExprOrOp::Op(op)) => { + Err(ParseError::compiler_bug(0, op.loc(), fn_name!(), line!())) + } + _ => switch_unreachable!(), + } + } + } + } + + /// "LHS" is the smallest unit that can be the left-hand side of an BinOp. + /// e.g. Call, Name, UnaryOp, Lambda + fn try_reduce_lhs(&mut self) -> ParseResult { + debug_call_info!(self); + match self.peek() { + Some(t) if t.category_is(TC::Literal) => { + // TODO: 10.times ...などメソッド呼び出しもある + Ok(Expr::Lit(self.try_reduce_lit()?)) + } + Some(t) if t.is(Symbol) || t.is(Dot) => { + Ok(self.try_reduce_call_or_acc()?) + } + Some(t) if t.category_is(TC::UnaryOp) => { + Ok(Expr::UnaryOp(self.try_reduce_unary()?)) + } + Some(t) if t.category_is(TC::Caret) => { + let lambda = self.try_reduce_lambda()?; + Ok(Expr::Lambda(lambda)) + } + Some(t) if t.is(LParen) => { + self.skip(); + let expr = self.try_reduce_expr()?; + if self.cur_is(RParen) { self.skip(); } + else { return Err(self.skip_and_throw_syntax_err(caused_by!())) } + Ok(expr) + } + Some(t) if t.is(LSqBr) => { + Ok(Expr::Array(self.try_reduce_array()?)) + } + Some(t) if t.is(LBrace) => { + todo!() + } + Some(t) if t.is(UBar) => { + let token = self.lpop(); + Err(ParseError::feature_error(0, token.loc(), "discard pattern")) + }, + _ => Err(self.skip_and_throw_syntax_err(caused_by!())), + } + } + + #[inline] + fn try_reduce_call_or_acc(&mut self) -> ParseResult { + debug_call_info!(self); + let acc = self.try_reduce_acc()?; + if let Some(res) = self.opt_reduce_args() { + let args = res?; + let call = Call::new(Expr::Accessor(acc), args); + Ok(Expr::Call(call)) + } else { + Ok(Expr::Accessor(acc)) + } + } + + #[inline] + fn try_reduce_lambda(&mut self) -> ParseResult { + debug_call_info!(self); + let sig = self.try_reduce_lambda_sig()?; + let op = self.lpop(); + if op.category() != TC::LambdaOp { + return Err(self.skip_and_throw_syntax_err(caused_by!())) + } + // REVIEW: この箇所必要か + while self.cur_is(Newline) { + self.skip(); + } + let body = self.try_reduce_block()?; + self.counter.inc(); + Ok(Lambda::new(sig, op, body, self.counter)) + } + + #[inline] + fn try_reduce_unary(&mut self) -> ParseResult { + debug_call_info!(self); + let op = self.lpop(); + let expr = self.try_reduce_expr()?; + Ok(UnaryOp::new(op, expr)) + } + + #[inline] + fn try_reduce_array(&mut self) -> ParseResult { + debug_call_info!(self); + let l_sqbr = self.lpop(); + let elems = self.try_reduce_args()?; + let r_sqbr = self.lpop(); + if !r_sqbr.is(RSqBr) { + return Err(ParseError::simple_syntax_error(0, r_sqbr.loc())) + } + let arr = Array::new(l_sqbr, r_sqbr, elems, None); + Ok(arr) + } + + #[inline] + fn try_reduce_name(&mut self) -> ParseResult { + debug_call_info!(self); + match self.peek() { + Some(t) if t.is(Symbol) => { + Ok(VarName::new(self.lpop())) + } + _ => Err(self.skip_and_throw_syntax_err(caused_by!())), + } + } + + #[inline] + fn try_reduce_lit(&mut self) -> ParseResult { + debug_call_info!(self); + match self.peek() { + Some(t) if t.category_is(TC::Literal) => { + Ok(Literal::from(self.lpop())) + } + _ => Err(self.skip_and_throw_syntax_err(caused_by!())), + } + } +} diff --git a/src/compiler/parser/tests/ast_example.txt b/src/compiler/parser/tests/ast_example.txt new file mode 100644 index 00000000..785a401b --- /dev/null +++ b/src/compiler/parser/tests/ast_example.txt @@ -0,0 +1,26 @@ +1 + 2 * 3 + +[1, 2, 3, *, +] + +x = + y = 1 + z = 2 + 3 + +[x y, 1, =, ;, z, 2, =, ;, 3, =] + +f + 1 + 2 + 3 + +# 直前が=, :=, ->, =>, do!なら改行はセミコロンと解釈し、それ以外ならコンマと解釈する +[1, 2, ',', 3, ',', f] + +add! x, y = + print! x, y + print! x + y + x + y + +add!(x, y) = (print! x, y;print! x + y; x + y) +[add(x,y), NewBlock, x, y, ',', print!, ;, x, y, +, print!, ;, x, y, +, BlockEnd, = \ No newline at end of file diff --git a/src/compiler/parser/tests/dependent.er b/src/compiler/parser/tests/dependent.er new file mode 100644 index 00000000..f34baabf --- /dev/null +++ b/src/compiler/parser/tests/dependent.er @@ -0,0 +1,4 @@ +concat|T: Type, M, N: Nat|(l: [T; M], r: [T; N]): [T; M + N] = l + r + +l: [Nat; 6] = concat [1, 2, 3], [4, 5, 6] +assert l == [1, 2, 3, 4, 5, 6] diff --git a/src/compiler/parser/tests/fib.er b/src/compiler/parser/tests/fib.er new file mode 100644 index 00000000..474fd6b6 --- /dev/null +++ b/src/compiler/parser/tests/fib.er @@ -0,0 +1,6 @@ +fib 0 = 0 +fib 1 = 1 +fib(n: 2.. ParseResult<()> { + let mut cfg = ErgConfig::default(); + cfg.set_input(Input::File(FILE1)); + let mut lexer = Lexer::new(cfg); + let newline = "\n"; + let /*mut*/ token_array = vec![ + (Symbol, "_a"), + (Equal, "="), + (IntLit, "1234"), + (Plus, "+"), + (RatioLit, "1113.0"), + (Plus, "+"), + (RatioLit, "0.30102"), + // (Symbol, "a"), + (Newline, newline), + (Symbol, "a"), + (Comma, ","), + (UBar, "_"), + (Comma, ","), + (Spread, "..."), + (Symbol, "b"), + (Let, "="), + (Symbol, "five_elem_tuple"), + (Newline, newline), + (Symbol, "if!"), + (Symbol, "True"), + (Comma, ","), + (Symbol, "do!"), + (Newline, newline), + (Indent, " "), + (Symbol, "print!"), + // (LParen, "("), + (StrLit, "\\\\hello, world\\\""), + // (RParen, ")"), + (Newline, newline), + (IntLit, "10"), + (Dot, "."), + (Symbol, "times!"), + // (LParen, "("), + // (RParen, ")"), + (Symbol, "do!"), + (Newline, newline), + (Indent, " "), + (Symbol, "if!"), + (Symbol, "True"), + (Comma, ","), + (Symbol, "do!"), + (Newline, newline), + (Indent, " "), + (Symbol, "print!"), + (StrLit, ""), + (Newline, newline), + // (Comment, " illegal indent"), + // (Illegal, "DEDENT"), + // (Symbol, "do_nothing"), + (Dedent, ""), + (Newline, newline), + (Newline, newline), + (Symbol, "Hello"), + (Let, "="), + (Symbol, "S2c"), + // (LParen, "("), + (StrLit, "hello"), + // (RParen, ")"), + (Newline, newline), + (Dedent, ""), + (Dedent, ""), + (Symbol, "aあ아"), + (Let, "="), + (Newline, newline), + (Indent, " "), + (Newline, newline), + (StrLit, "aaa"), + (Newline, newline), + (Dedent, ""), + (Symbol, "x"), + (Semi, ";"), + (Symbol, "x"), + (Semi, ";"), + (Semi, ";"), + (Symbol, "x"), + (Semi, ";"), + (Newline, newline), + (IntLit, "10"), + (Range, ".."), + (Symbol, "twelve"), + (Semi, ";"), + (Newline, newline), + (EOF, "EOF"), + ]; + + let mut tok: Token; + for i in token_array.into_iter() { + tok = lexer.next().unwrap().unwrap(); + assert_eq!(tok, Token::without_loc(i.0, i.1)); + println!("{tok}"); + } + Ok(()) + } + + #[test] + fn tesop_te_prec() { + assert_eq!(Mod.precedence(), Some(160)); + assert_eq!(LParen.precedence(), Some(0)); + assert_eq!(Illegal.precedence(), None); + } + + #[test] + fn test_parser1() -> Result<(), ParseErrors> { + let mut cfg = ErgConfig::default(); + cfg.input = Input::File(FILE1); + let lexer = Lexer::new(cfg); + let mut parser = ParserRunner::new(&cfg); + match parser.parse(lexer.lex()?) { + Ok(module) => { + println!("{module}"); + Ok(()) + } + Err(e) => { + e.fmt_all_stderr(); + Err(e) + } + } + } +} diff --git a/src/compiler/parser/tests/test1_basic_syntax.er b/src/compiler/parser/tests/test1_basic_syntax.er new file mode 100644 index 00000000..a5032eb9 --- /dev/null +++ b/src/compiler/parser/tests/test1_basic_syntax.er @@ -0,0 +1,20 @@ +# 基本的な構文をパーサーがパスできるかチェックする +# Check that a parser can pass the basic syntax + +_a = 1_234 + 1113.* 3_000.2e-4 ** 0003 * .4 +a, _, ...b = five_elem_tuple +f x, y = + x + y +if! True, do! + print! "\\hello, world\"" + 10.times! do! + if! x.y.z, do! + print! "" + # illegal indent + # do_nothing! + Hello = S2c "hello" +aあ아 = + # コメント + "aaa" +x; x;; x; +10..twelve; diff --git a/src/compiler/parser/tests/test2_advanced_syntax.er b/src/compiler/parser/tests/test2_advanced_syntax.er new file mode 100644 index 00000000..271bd621 --- /dev/null +++ b/src/compiler/parser/tests/test2_advanced_syntax.er @@ -0,0 +1,28 @@ +# Check that a parser can pass the advanced syntax +# 高度な文法をチェックする + +# overloading (多重定義) +f x = 1 + x + 2 +f x, y = + 1 + x + y +f x, y, z = + 1 + x + y + z +assert 4 == f 1 +assert 4 == f 1, 1 +assert 3 == f 1, 1, 1 + +# pattern overloading +fib 0 = 0 +fib 1 = 1 +fib(n: Nat) -> Nat = fib(n-1) + fib(n-2) + +# keyword arguments (キーワード引数) +t = if True: + then: 1 + else: 2 +assert t == 1 + +# import +math = import "math" +# {*} = "math" # use all +{pi} = import "math" diff --git a/src/compiler/parser/token.rs b/src/compiler/parser/token.rs new file mode 100644 index 00000000..9027737a --- /dev/null +++ b/src/compiler/parser/token.rs @@ -0,0 +1,418 @@ +//! defines `Token` (The minimum unit in the Erg source code that serves as input to the parser). +//! +//! Token(パーサーへの入力となる、Ergソースコードにおける最小単位)を定義する +use std::fmt; +use std::hash::{Hash, Hasher}; + +use common::str::Str; +use common::error::Location; +use common::impl_displayable_stream_for_wrapper; +use common::traits::{Stream, Locational}; +use common::value::ValueObj; +use common::ty::Type; + +/// 意味論的名前と記号自体の名前が混在しているが、Pythonの名残である +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum TokenKind { + /// e.g. i, p!, $s, T, `+`, `and`, 'd/dx' + Symbol, + // e.g. 0, 1 + NatLit, + // e.g. -1, -2 + IntLit, + RatioLit, + BoolLit, + StrLit, + NoneLit, + NoImplLit, + EllipsisLit, + InfLit, + /// `+` (unary) + PrePlus, + /// `-` (unary) + PreMinus, + /// `*` (unary) + PreStar, + /// ~ (unary) + PreBitNot, + // PreAmp, // & (unary) + // PreAt, // @ (unary) + /// ! (unary) + Mutate, + /// ? (postfix) + Try, + /// `+` + Plus, + /// `-` + Minus, + /// `*` + Star, + /// / + Slash, + /// // + FloorDiv, + /// ** + Pow, + /// % + Mod, + /// .. + Closed, + /// ..< + RightOpen, + /// <.. + LeftOpen, + /// <..< + Open, + /// && + BitAnd, + /// || + BitOr, + /// ^^ + BitXor, + /// << + Shl, + /// >> + Shr, + /// < + Less, + /// > + Gre, + /// <= + LessEq, + /// >= + GreEq, + /// == + DblEq, + /// != + NotEq, + /// `in` + InOp, + /// `notin` + NotInOp, + /// `sub` (subtype of) + SubOp, + /// `is` + IsOp, + /// `isnot` + IsNotOp, + /// `and` + AndOp, + /// `or` + OrOp, + /// `dot` (scalar product) + DotOp, + /// `cross` (vector product) + CrossOp, + /// = + Equal, + /// |= + OrEqual, + /// -> + FuncArrow, + /// => + ProcArrow, + /// ( + LParen, + /// ) + RParen, + /// [ + LSqBr, + /// ] + RSqBr, + /// { + LBrace, + /// } + RBrace, + Indent, + Dedent, + /// . + Dot, + /// |> + Pipe, + /// : + Colon, + /// :: + DblColon, + /// :> + SupertypeOf, + /// <: + SubtypeOf, + /// , + Comma, + /// ^ + Caret, + /// & + Amper, + /// @ + AtSign, + /// | + VBar, + /// _ + UBar, + /// ... + Spread, + /// \n + Newline, + /// ; + Semi, + Illegal, + /// Beginning Of File + BOF, + EOF, +} + +use TokenKind::*; + +impl From for Type { + #[inline] + fn from(tok: TokenKind) -> Type { + match tok { + NatLit => Type::Nat, + IntLit => Type::Int, + RatioLit => Type::Float, + StrLit => Type::Str, + BoolLit => Type::Bool, + NoneLit => Type::NoneType, + NoImplLit => Type::NotImplemented, + EllipsisLit => Type::Ellipsis, + InfLit => Type::Inf, + other => panic!("this has not type: {other}"), + } + } +} + +impl From<&ValueObj> for TokenKind { + fn from(c: &ValueObj) -> TokenKind { + match c { + ValueObj::Int(_) => TokenKind::IntLit, + ValueObj::Nat(_) => TokenKind::NatLit, + ValueObj::Float(_) => TokenKind::RatioLit, + ValueObj::Str(_) => TokenKind::StrLit, + ValueObj::True => TokenKind::BoolLit, + ValueObj::False => TokenKind::BoolLit, + ValueObj::None => TokenKind::NoneLit, + ValueObj::Ellipsis => TokenKind::EllipsisLit, + ValueObj::Inf => TokenKind::InfLit, + _ => TokenKind::Illegal, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TokenCategory { + Symbol, + Literal, + BinOp, + UnaryOp, + /// ? <.. .. + PostfixOp, + /// ( [ { Indent + LEnclosure, + /// ) } } Dedent + REnclosure, + /// , : :: :> <: . |> |= + SpecialBinOp, + /// = + DefOp, + /// -> => + LambdaOp, + /// \n ; + Separator, + /// ^ (reserved) + Caret, + /// & + Amper, + /// @ + AtSign, + /// | + VBar, + /// _ + UBar, + EOF, + Illegal, +} + +impl TokenCategory { + pub const fn is_block_op(&self) -> bool { + matches!(self, Self::DefOp | Self::LambdaOp) + } +} + +impl TokenKind { + pub const fn category(&self) -> TokenCategory { + match self { + Symbol => TokenCategory::Symbol, + NatLit | IntLit | RatioLit | StrLit | BoolLit + | NoneLit | EllipsisLit | NoImplLit | InfLit => TokenCategory::Literal, + PrePlus | PreMinus | PreStar | PreBitNot | Mutate => TokenCategory::UnaryOp, + Try => TokenCategory::PostfixOp, + Comma | Colon | DblColon | SupertypeOf | SubtypeOf | Dot | Pipe | OrEqual => TokenCategory::SpecialBinOp, + Equal => TokenCategory::DefOp, + FuncArrow | ProcArrow => TokenCategory::LambdaOp, + Semi | Newline => TokenCategory::Separator, + LParen | LBrace | LSqBr | Indent => TokenCategory::LEnclosure, + RParen | RBrace | RSqBr | Dedent => TokenCategory::REnclosure, + Caret => TokenCategory::Caret, + Amper => TokenCategory::Amper, + AtSign => TokenCategory::AtSign, + VBar => TokenCategory::VBar, + UBar => TokenCategory::UBar, + EOF => TokenCategory::EOF, + Illegal | BOF => TokenCategory::Illegal, + _ => TokenCategory::BinOp, + } + } + + pub const fn precedence(&self) -> Option { + let prec = match self { + Dot | DblColon => 200, // . + Pow => 190, // ** + PrePlus | PreMinus | PreBitNot => 180, // (unary) + - * ~ + Star | Slash | FloorDiv | Mod | CrossOp | DotOp => 170, // * / // % cross dot + Plus | Minus => 160, // + - + Shl | Shr => 150, // << >> + BitAnd => 140, // && + BitXor => 130, // ^^ + BitOr => 120, // || + Closed | LeftOpen | RightOpen | Open => 100, // range operators + Less | Gre | LessEq | GreEq | DblEq | NotEq + | InOp | NotInOp | IsOp | IsNotOp => 90, // < > <= >= == != in notin is isnot + AndOp => 80, // and + OrOp => 70, // or + FuncArrow | ProcArrow => 60, // -> => + Colon | SupertypeOf | SubtypeOf => 50, // : :> <: + Comma => 40, // , + Equal | OrEqual => 20, // = |= + Newline | Semi => 10, // \n ; + LParen | LBrace | LSqBr | Indent => 0, // ( { [ Indent + _ => { return None }, + }; + Some(prec) + } + + pub const fn is_right_associative(&self) -> bool { + match self { + FuncArrow | ProcArrow | Equal => true, + // PreDollar | PreAt => true, + _ => false, + } + } +} + +impl fmt::Display for TokenKind { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{self:?}") } +} + +#[derive(Clone, Eq)] +pub struct Token { + pub kind: TokenKind, + pub content: Str, + /// 1 origin + // TODO: 複数行文字列リテラルもあるのでタプルにするのが妥当? + pub lineno: usize, + /// a pointer from which the token starts (0 origin) + pub col_begin: usize, +} + +impl From for ValueObj { + #[inline] + fn from(tok: Token) -> ValueObj { + ValueObj::from_str(Type::from(tok.kind), tok.content) + } +} + +impl From<&Token> for ValueObj { + #[inline] + fn from(tok: &Token) -> ValueObj { + ValueObj::from_str(Type::from(tok.kind), tok.content.clone()) + } +} + +impl fmt::Debug for Token { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Token") + .field("kind", &self.kind) + .field("content", &self.content.replace("\n", "\\n")) + .field("lineno", &self.lineno) + .field("col_begin", &self.col_begin) + .finish() + } +} + +impl fmt::Display for Token { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?} {}", self.kind, self.content.replace("\n", "\\n")) + } +} + +// the values of lineno and col are not relevant for comparison +impl PartialEq for Token { + #[inline] + fn eq(&self, other: &Self) -> bool { self.is(other.kind) && self.content == other.content } +} + +impl Hash for Token { + fn hash(&self, state: &mut H) { + self.kind.hash(state); + self.content.hash(state); + } +} + +impl Locational for Token { + fn loc(&self) -> Location { + if self.lineno == 0 { Location::Unknown } else { + Location::range( + self.lineno, + self.col_begin, + self.lineno, + self.col_begin + self.content.len(), + ) + } + } + + #[inline] + fn col_end(&self) -> Option { Some(self.col_begin + self.content.len()) } +} + +impl Token { + #[inline] + pub fn dummy() -> Self { + Token{ kind: TokenKind::Illegal, content: "DUMMY".into(), lineno: 1, col_begin: 0 } + } + + #[inline] + pub fn new>(kind: TokenKind, cont: S, lineno: usize, col_begin: usize) -> Self { + Token{ kind, content: cont.into(), lineno, col_begin } + } + + #[inline] + pub fn from_str(kind: TokenKind, cont: &str) -> Self { + Token{ kind, content: Str::rc(cont), lineno: 0, col_begin: 0 } + } + + #[inline] + pub fn symbol(cont: &str) -> Self { Self::from_str(TokenKind::Symbol, cont) } + + pub const fn static_symbol(s: &'static str) -> Self { + Token{ kind: TokenKind::Symbol, content: Str::ever(s), lineno: 0, col_begin: 0 } + } + + pub const fn category(&self) -> TokenCategory { self.kind.category() } + + pub fn category_is(&self, category: TokenCategory) -> bool { self.kind.category() == category } + + pub fn is(&self, kind: TokenKind) -> bool { self.kind == kind } + + pub const fn is_block_op(&self) -> bool { self.category().is_block_op() } + + pub const fn inspect(&self) -> &Str { &self.content } + + pub fn is_procedural(&self) -> bool { self.inspect().ends_with("!") } +} + +#[derive(Debug, Clone)] +pub struct TokenStream(Vec); + +impl_displayable_stream_for_wrapper!(TokenStream, Token); diff --git a/src/compiler/table.rs b/src/compiler/table.rs new file mode 100644 index 00000000..ab81a2ea --- /dev/null +++ b/src/compiler/table.rs @@ -0,0 +1,2555 @@ +//! defines `SymbolTable`. +//! +//! SymbolTable(記号表)を定義する +use std::fmt; +use std::mem; +use std::option::Option; // conflicting to Type::Option +use std::cmp::Ordering; + +use common::Str; +use common::ty::Constraint; +use common::ty::RefinementType; +use common::ty::fresh_varname; +use common::{fn_name, get_hash, log, assume_unreachable, set, try_map, fmt_slice}; +use common::dict::Dict; +use common::set::Set; +use common::error::{Location, ErrorCore}; +use common::value::ValueObj; +use common::levenshtein::levenshtein; +use common::traits::{HasType, Locational, Stream}; +use common::ty::{ + Type, TyParam, TyParamOrdering, TyBound, ConstObj, + IntervalOp, FreeKind, HasLevel, SubrKind, SubrType, ParamTy, Predicate, +}; +use TyParamOrdering::*; +use Type::*; +use Predicate as Pred; +use ValueObj::{Inf, NegInf}; + +use parser::ast; +use ast::{VarName, DefId, TypeSpec, ParamTySpec, PreDeclTypeSpec, SimpleTypeSpec, TypeBoundSpec, TypeBoundSpecs, ParamSig}; +use parser::token::{Token, TokenKind}; + +use crate::hir; +use crate::eval::{Evaluator}; +use crate::error::{TyCheckError, TyCheckErrors, TyCheckResult, binop_to_dname, unaryop_to_dname}; +use crate::varinfo::{VarInfo, Mutability, Visibility, VarKind, ParamId}; +use Mutability::*; +use Visibility::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum DefaultInfo { + NonDefault, + WithDefault, +} + +impl DefaultInfo { + pub const fn has_default(&self) -> bool { matches!(self, DefaultInfo::WithDefault) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParamSpec { + pub(crate) name: Option<&'static str>, + pub(crate) t: Type, + pub default_info: DefaultInfo, +} + +impl ParamSpec { + pub const fn new(name: Option<&'static str>, t: Type, default: DefaultInfo) -> Self { + Self { name, t, default_info: default } + } + + pub const fn named(name: &'static str, t: Type, default: DefaultInfo) -> Self { + Self::new(Some(name), t, default) + } + + pub const fn named_nd(name: &'static str, t: Type) -> Self { + Self::new(Some(name), t, DefaultInfo::NonDefault) + } + + pub const fn t(name: &'static str, default: DefaultInfo) -> Self { Self::new(Some(name), Type, default) } + + pub const fn t_nd(name: &'static str) -> Self { Self::new(Some(name), Type, DefaultInfo::NonDefault) } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum TableKind { + Func, + Proc, + Tuple, + Record, + Class, + Trait, + StructuralTrait, + Patch, + StructuralPatch, + Module, + Instant, + Dummy, +} + +/// 記号表に登録されているモードを表す +/// Preregister: サブルーチンまたは定数式、前方参照できる +/// Normal: 前方参照できない +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RegistrationMode { + PreRegister, + Normal, +} + +use RegistrationMode::*; + +/// Symbol table for instantiating a quantified type +/// 量化型をインスタンス化するための記号表 +#[derive(Debug, Clone)] +pub struct TyVarTable { + level: usize, + // instances (stores free vars) + pub(crate) tyvar_instances: Dict, + pub(crate) typaram_instances: Dict, +} + +impl TyVarTable { + pub fn new(level: usize, bounds: Set) -> Self { + let mut self_ = Self{ level, tyvar_instances: Dict::new(), typaram_instances: Dict::new() }; + for bound in bounds.into_iter() { + self_.instantiate_bound(bound); + } + self_ + } + + fn instantiate_bound(&mut self, bound: TyBound) { + match bound { + TyBound::Subtype{ sub, sup } => { + let sup = match sup { + Type::Poly{ name, params } => { + let sup = Type::poly(name, params.into_iter().map(|p| self.instantiate_tp(p)).collect()); + sup + }, + Type::MonoProj{ lhs, rhs } => { + Type::mono_proj(self.instantiate_t(*lhs), rhs) + } + sup => sup, + }; + let constraint = Constraint::SubtypeOf(sup); + self.push_tyvar(Str::rc(sub.name()), Type::free_var(self.level, constraint)); + }, + TyBound::Instance{ name, t } => { + let t = match t { + Type::Poly{ name, params } => { + let t = Type::poly(name, params.into_iter().map(|p| self.instantiate_tp(p)).collect()); + t + }, + t => t, + }; + // TODO: type-like types + if &t == &Type { + let constraint = Constraint::TypeOf(t); + self.push_tyvar(name.clone(), Type::named_free_var(name, self.level, constraint)); + } else { + self.push_typaram(name.clone(), TyParam::named_free_var(name, self.level, t)); + } + }, + } + } + + fn _instantiate_pred(&self, _pred: Predicate) -> Predicate { + todo!() + } + + pub(crate) fn instantiate_t(&self, quantified: Type) -> Type { + match quantified { + Type::MonoQVar(n) => { + if let Some(t) = self.get_tyvar(&n) { + return t.clone() + } else if let Some(t) = self.get_typaram(&n) { + if let TyParam::Type(t) = t { return *t.clone() } + else { todo!() } + } else { todo!() } + }, + other => todo!("{other}"), + } + } + + fn instantiate_tp(&self, quantified: TyParam) -> TyParam { + match quantified { + TyParam::MonoQVar(n) => { + if let Some(t) = self.get_typaram(&n) { + return t.clone() + } else if let Some(t) = self.get_tyvar(&n) { + return TyParam::t(t.clone()) + } else { todo!() } + }, + TyParam::UnaryOp{ op, val } => { + let res = self.instantiate_tp(*val); + TyParam::unary(op, res) + }, + TyParam::BinOp{ op, lhs, rhs } => { + let lhs = self.instantiate_tp(*lhs); + let rhs = self.instantiate_tp(*rhs); + TyParam::bin(op, lhs, rhs) + }, + p @ TyParam::ConstObj(_) => p, + other => todo!("{other}"), + } + } + + pub(crate) fn push_tyvar(&mut self, name: Str, t: Type) { + self.tyvar_instances.insert(name, t); + } + + pub(crate) fn push_typaram(&mut self, name: Str, t: TyParam) { + self.typaram_instances.insert(name, t); + } + + pub(crate) fn get_tyvar(&self, name: &str) -> Option<&Type> { + self.tyvar_instances.get(name) + } + + pub(crate) fn get_typaram(&self, name: &str) -> Option<&TyParam> { + self.typaram_instances.get(name) + } +} + +#[derive(Debug)] +pub struct SymbolTable { + pub(crate) name: Str, + pub(crate) kind: TableKind, + // Type bounds & Predicates (if the table type is Subroutine) + // ユーザー定義APIでのみ使う + pub(crate) bounds: Vec, + pub(crate) preds: Vec, + // for looking up the parent scope + pub(crate) outer: Option>, + // patchによってsuper class/traitになったものはここに含まれない + pub(crate) super_classes: Vec, // if self is a patch, means patch classes + pub(crate) super_traits: Vec, // if self is not a trait, means implemented traits + // K: メソッド名, V: それを実装するパッチたち + // 提供メソッドはスコープごとに実装を切り替えることができる + pub(crate) _method_impl_patches: Dict>, + // .0: 関係付けるパッチ(glue patch), .1: サブタイプになる型, .2: スーパータイプになるトレイト + // 一つの型ペアを接着パッチは同時に一つまでしか存在しないが、付け替えは可能 + pub(crate) glue_patch_and_types: Vec<(VarName, Type, Type)>, + // stores declared names (not initialized) + pub(crate) decls: Dict, + // stores defined names + // 型の一致はHashMapでは判定できないため、keyはVarName + pub(crate) impls: Dict, + pub(crate) consts: Dict, + pub(crate) unnamed_params: Vec, + // FIXME: Compilerが持つ + pub(crate) eval: Evaluator, + // stores user-defined type tables + pub(crate) types: Dict, + pub(crate) patches: Dict, + pub(crate) _nlocals: usize, // necessary for CodeObj.nlocals + pub(crate) level: usize, +} + +impl Default for SymbolTable { + #[inline] + fn default() -> Self { + Self::new("".into(), TableKind::Dummy, vec![], None, vec![], vec![], Self::TOP_LEVEL) + } +} + +impl fmt::Display for SymbolTable { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SymbolTable") + .field("name", &self.name) + .field("bounds", &self.bounds) + .field("preds", &self.preds) + .field("decls", &self.decls) + .field("impls", &self.impls) + .field("consts", &self.consts) + .field("eval", &self.eval) + .field("types", &self.types) + .field("patches", &self.patches) + .finish() + } +} + +impl SymbolTable { + #[inline] + pub fn new( + name: Str, + kind: TableKind, + params: Vec, + outer: Option, + super_classes: Vec, + super_traits: Vec, + level: usize + ) -> Self { + Self::with_capacity(name, kind, params, outer, super_classes, super_traits, 0, level) + } + + pub fn with_capacity( + name: Str, + kind: TableKind, + params: Vec, + outer: Option, + super_classes: Vec, + super_traits: Vec, + capacity: usize, + level: usize, + ) -> Self { + let mut impls = Dict::with_capacity(capacity); + let mut unnamed_params = Vec::new(); + for (pos, param) in params.into_iter().enumerate() { + let id = DefId(get_hash(&(&name, ¶m))); + if let Some(name) = param.name { + let param_id = if param.default_info.has_default() { + ParamId::var_default(name.into(), pos) + } else { ParamId::var_non_default(name.into(), pos) }; + let kind = VarKind::parameter(id, param_id); + // TODO: is_const { Const } else { Immutable } + let vi = VarInfo::new(param.t, Immutable, Private, kind); + impls.insert(VarName::new(Token::static_symbol(name)), vi); + } else { + let param_id = if param.default_info.has_default() { + ParamId::PatWithDefault(pos) + } else { ParamId::PatNonDefault(pos) }; + let kind = VarKind::parameter(id, param_id); + let vi = VarInfo::new(param.t, Immutable, Private, kind); + unnamed_params.push(vi); + } + } + Self { + name, + kind, + bounds: vec![], + preds: vec![], + outer: outer.map(Box::new), + super_classes, + super_traits, + _method_impl_patches: Dict::default(), + glue_patch_and_types: Vec::default(), + decls: Dict::default(), + impls, + consts: Dict::default(), + unnamed_params, + eval: Evaluator::default(), + types: Dict::default(), + patches: Dict::default(), + _nlocals: 0, + level, + } + } + + #[inline] + pub fn mono( + name: Str, + kind: TableKind, + outer: Option, + super_classes: Vec, + super_traits: Vec, + level: usize + ) -> Self { + Self::with_capacity(name, kind, vec![], outer, super_classes, super_traits, 0, level) + } + + #[inline] + pub fn poly( + name: Str, + kind: TableKind, + params: Vec, + outer: Option, + super_classes: Vec, + super_traits: Vec, + level: usize + ) -> Self { + Self::with_capacity(name, kind, params, outer, super_classes, super_traits, 0, level) + } + + pub fn poly_trait>(name: S, params: Vec, supers: Vec, level: usize) -> Self { + let name = name.into(); + Self::poly(name, TableKind::Trait, params, None, vec![], supers, level) + } + + pub fn poly_class>(name: S, params: Vec, super_classes: Vec, impl_traits: Vec, level: usize) -> Self { + let name = name.into(); + Self::poly(name, TableKind::Class, params, None, super_classes, impl_traits, level) + } + + #[inline] + pub fn mono_trait>(name: S, supers: Vec, level: usize) -> Self { + Self::poly_trait(name, vec![], supers, level) + } + + #[inline] + pub fn mono_class>(name: S, super_classes: Vec, super_traits: Vec, level: usize) -> Self { + Self::poly_class(name, vec![], super_classes, super_traits, level) + } + + #[inline] + pub fn poly_patch>(name: S, params: Vec, patch_classes: Vec, impl_traits: Vec, level: usize) -> Self { + Self::poly(name.into(), TableKind::Trait, params, None, patch_classes, impl_traits, level) + } + + #[inline] + pub fn module(name: Str, capacity: usize) -> Self { + Self::with_capacity(name, TableKind::Module, vec![], None, vec![], vec![], capacity, Self::TOP_LEVEL) + } + + #[inline] + pub fn caused_by(&self) -> Str { self.name.clone() } + + fn registered(&self, name: &Q, recursive: bool) -> bool + where VarName: std::borrow::Borrow { + if self.impls.contains_key(name) { return true } + if recursive { + if let Some(outer) = &self.outer { + outer.registered(name, recursive) + } else { false } + } else { false } + } +} + +// setters +impl SymbolTable { + pub(crate) fn declare_var(&mut self, sig: &ast::VarSignature, opt_t: Option, id: Option) -> TyCheckResult<()> { + self.declare_var_pat(sig, opt_t, id) + } + + fn declare_var_pat(&mut self, sig: &ast::VarSignature, opt_t: Option, id: Option) -> TyCheckResult<()> { + let vis = Private; // TODO: + let muty = Mutability::from(&sig.inspect().unwrap()[..]); + match &sig.pat { + ast::VarPattern::VarName(v) => { + if sig.t_spec.is_none() && opt_t.is_none() { + Err(TyCheckError::no_type_spec_error(sig.loc(), self.caused_by(), v.inspect())) + } else { + if self.registered(v, v.inspect().is_uppercase()) { + return Err(TyCheckError::duplicate_decl_error(sig.loc(), self.caused_by(), v.inspect())) + } + let kind = id.map(|id| VarKind::Defined(id)).unwrap_or(VarKind::Declared); + let sig_t = self.instantiate_var_sig_t(sig, opt_t, PreRegister)?; + self.decls.insert(v.clone(), VarInfo::new(sig_t, muty, vis, kind)); + Ok(()) + } + } + ast::VarPattern::Array(a) => { + if let Some(opt_ts) = opt_t.and_then(|t| t.non_default_params().cloned()) { + for (elem, p) in a.iter().zip(opt_ts.into_iter()) { + self.declare_var_pat(elem, Some(p.ty), None)?; + } + } else { + for elem in a.iter() { + self.declare_var_pat(elem, None, None)?; + } + } + Ok(()) + } + _ => todo!(), + } + } + + pub(crate) fn declare_sub(&mut self, sig: &ast::SubrSignature, opt_ret_t: Option, id: Option) -> TyCheckResult<()> { + let name = sig.name.inspect(); + let muty = Mutability::from(&name[..]); + let kind = id.map(|id| VarKind::Defined(id)).unwrap_or(VarKind::Declared); + if self.registered(name, name.is_uppercase()) { + return Err(TyCheckError::duplicate_decl_error(sig.loc(), self.caused_by(), name)) + } + let t = self.instantiate_sub_sig_t(sig, opt_ret_t, PreRegister)?; + let vi = VarInfo::new(t, muty, Private, kind); + if let Some(_decl) = self.decls.remove(name) { + return Err(TyCheckError::duplicate_decl_error(sig.loc(), self.caused_by(), name)) + } else { + self.decls.insert(sig.name.clone(), vi); + } + Ok(()) + } + + pub(crate) fn assign_var(&mut self, sig: &ast::VarSignature, id: DefId, body_t: &Type) -> TyCheckResult<()> { + self.assign_var_sig(sig, body_t, id) + } + + fn assign_var_sig(&mut self, sig: &ast::VarSignature, body_t: &Type, id: DefId) -> TyCheckResult<()> { + self.validate_var_sig_t(sig, body_t, Normal)?; + let vis = Private; // TODO: + let muty = Mutability::from(&sig.inspect().unwrap()[..]); + let (generalized, bounds) = self.generalize_t(body_t.clone()); + let generalized = if !bounds.is_empty() { + if self.deep_supertype_of(&Type::CallableCommon, &generalized) { + Type::quantified(generalized, bounds) + } else { panic!() } + } else { generalized }; + match &sig.pat { + ast::VarPattern::Discard(_token) => Ok(()), + ast::VarPattern::VarName(v) => { + if self.registered(v, v.inspect().is_uppercase()) { + Err(TyCheckError::reassign_error(v.loc(), self.caused_by(), v.inspect())) + } else { + if let Some(_) = self.decls.remove(v.inspect()) { + // something to do? + } + let vi = VarInfo::new(generalized, muty, vis, VarKind::Defined(id)); + self.impls.insert(v.clone(), vi); + Ok(()) + } + } + ast::VarPattern::SelfDot(_) => todo!(), + ast::VarPattern::Array(arr) => { + for (elem, inf) in arr.iter().zip(generalized.inner_ts().iter()) { + let id = DefId(get_hash(&(&self.name, elem))); + self.assign_var_sig(elem, inf, id)?; + } + Ok(()) + } + ast::VarPattern::Tuple(_) => todo!(), + ast::VarPattern::Record{ .. } => todo!(), + } + } + + /// 宣言が既にある場合、opt_decl_tに宣言の型を渡す + fn assign_non_default_param(&mut self, sig: &ast::NonDefaultParamSignature, opt_param_pos: Option, opt_decl_t: Option<&ParamTy>) -> TyCheckResult<()> { + match &sig.pat { + ast::ParamPattern::Discard(_token) => Ok(()), + ast::ParamPattern::VarName(v) => { + if self.registered(v, v.inspect().is_uppercase()) { + Err(TyCheckError::reassign_error(v.loc(), self.caused_by(), v.inspect())) + } else { // ok, impl not found + let spec_t = self.instantiate_param_sig_t(sig, opt_decl_t, Normal)?; + let param_id = if let Some(param_pos) = opt_param_pos { + ParamId::var_non_default(v.inspect().into(), param_pos) + } else { ParamId::Embedded(v.inspect().into()) }; + let kind = VarKind::parameter(DefId(get_hash(&(&self.name, v))), param_id); + self.impls.insert(v.clone(), VarInfo::new(spec_t, Immutable, Private, kind)); + Ok(()) + } + } + ast::ParamPattern::Array(arr) => { + if let Some(decl_t) = opt_decl_t { + for (elem, p) in arr.elems.non_defaults.iter().zip(decl_t.ty.non_default_params().unwrap()) { + self.assign_non_default_param(elem, None, Some(p))?; + } + for (elem, p) in arr.elems.defaults.iter().zip(decl_t.ty.default_params().unwrap()) { + self.assign_default_param(elem, None, Some(p))?; + } + } else { + for elem in arr.elems.non_defaults.iter() { + self.assign_non_default_param(elem, None, None)?; + } + for elem in arr.elems.defaults.iter() { + self.assign_default_param(elem, None, None)?; + } + } + Ok(()) + } + ast::ParamPattern::Lit(_) => Ok(()), + _ => todo!(), + } + } + + fn assign_default_param(&mut self, sig: &ast::DefaultParamSignature, opt_param_pos: Option, opt_decl_t: Option<&ParamTy>) -> TyCheckResult<()> { + match &sig.pat { + ast::ParamPattern::Discard(_token) => Ok(()), + ast::ParamPattern::VarName(v) => { + if self.registered(v, v.inspect().is_uppercase()) { + Err(TyCheckError::reassign_error(v.loc(), self.caused_by(), v.inspect())) + } else { // ok, impl not found + let spec_t = self.instantiate_param_sig_t(sig, opt_decl_t, Normal)?; + let param_id = if let Some(param_pos) = opt_param_pos { + ParamId::var_default(v.inspect().into(), param_pos) + } else { ParamId::Embedded(v.inspect().into()) }; + let kind = VarKind::parameter(DefId(get_hash(&(&self.name, v))), param_id); + self.impls.insert(v.clone(), VarInfo::new(spec_t, Immutable, Private, kind)); + Ok(()) + } + } + ast::ParamPattern::Array(arr) => { + if let Some(decl_t) = opt_decl_t { + for (elem, p) in arr.elems.non_defaults.iter().zip(decl_t.ty.non_default_params().unwrap()) { + self.assign_non_default_param(elem, None, Some(p))?; + } + for (elem, p) in arr.elems.defaults.iter().zip(decl_t.ty.default_params().unwrap()) { + self.assign_default_param(elem, None, Some(p))?; + } + } else { + for elem in arr.elems.non_defaults.iter() { + self.assign_non_default_param(elem, None, None)?; + } + for elem in arr.elems.defaults.iter() { + self.assign_default_param(elem, None, None)?; + } + } + Ok(()) + } + ast::ParamPattern::Lit(_) => Ok(()), + _ => todo!(), + } + } + + pub(crate) fn assign_params(&mut self, params: &ast::Params, opt_decl_subr_t: Option) -> TyCheckResult<()> { + if let Some(decl_subr_t) = opt_decl_subr_t { + for (pos, (sig, pt)) in params.non_defaults.iter().zip(decl_subr_t.non_default_params.iter()).enumerate() { + self.assign_non_default_param(sig, Some(pos), Some(pt))?; + } + for (pos, (sig, pt)) in params.defaults.iter().zip(decl_subr_t.default_params.iter()).enumerate() { + self.assign_default_param(sig, Some(pos), Some(pt))?; + } + } else { + for (pos, sig) in params.non_defaults.iter().enumerate() { + self.assign_non_default_param(sig, Some(pos), None)?; + } + for (pos, sig) in params.defaults.iter().enumerate() { + self.assign_default_param(sig, Some(pos), None)?; + } + } + Ok(()) + } + + /// ## Errors + /// * TypeError: if `return_t` != typeof `body` + /// * AssignError: if `name` has already been registered + pub(crate) fn assign_subr(&mut self, sig: &ast::SubrSignature, id: DefId, body_t: &Type) -> TyCheckResult<()> { + let muty = if sig.name.is_const() { Mutability::Const } else { Mutability::Immutable }; + let name = &sig.name; + // FIXME: constでない関数 + let t = self.get_current_scope_local_var(&name.inspect()) + .map(|v| &v.t) + .unwrap(); + let non_default_params = t.non_default_params().unwrap(); + let default_params = t.default_params().unwrap(); + if let Some(spec_ret_t) = t.return_t() { + self.unify(spec_ret_t, body_t, Some(sig.loc()), None).map_err(|e| { + TyCheckError::return_type_error(e.core.loc, e.caused_by, name.inspect(), spec_ret_t, body_t) + })?; + } + if self.registered(name, name.inspect().is_uppercase()) { + Err(TyCheckError::reassign_error(name.loc(), self.caused_by(), name.inspect())) + } else { + let sub_t = if sig.name.is_procedural() { + Type::proc(non_default_params.clone(), default_params.clone(), body_t.clone()) + } else { + Type::func(non_default_params.clone(), default_params.clone(), body_t.clone()) + }; + sub_t.lift(); + let (generalized, bounds) = self.generalize_t(sub_t); + let found_t = if !bounds.is_empty() { + if self.deep_supertype_of(&Type::CallableCommon, &generalized) { + Type::quantified(generalized, bounds) + } else { panic!() } + } else { generalized }; + if let Some(mut vi) = self.decls.remove(name) { + if vi.t.has_unbound_var() { + vi.t.lift(); + let (generalized, bounds) = self.generalize_t(vi.t.clone()); + let generalized = if !bounds.is_empty() { + if self.deep_supertype_of(&Type::CallableCommon, &generalized) { + Type::quantified(generalized, bounds) + } else { panic!() } + } else { generalized }; + vi.t = generalized; + } + self.decls.insert(name.clone(), vi); + } + if let Some(vi) = self.decls.remove(name) { + if !self.deep_supertype_of(&vi.t, &found_t) { + return Err(TyCheckError::violate_decl_error( + sig.loc(), + self.caused_by(), + name.inspect(), + &vi.t, + &found_t, + )) + } + } + // TODO: visibility + let vi = VarInfo::new(found_t, muty, Private, VarKind::Defined(id)); + log!("Registered {}::{name}: {}", self.name, &vi.t); + self.impls.insert(name.clone(), vi); + Ok(()) + } + } + + pub fn push_subtype_bound(&mut self, sub: Type, sup: Type) { + self.bounds.push(TyBound::subtype(sub, sup)); + } + + pub fn push_instance_bound(&mut self, name: Str, t: Type) { + self.bounds.push(TyBound::instance(name, t)); + } +} + +// type variable related operations +impl SymbolTable { + pub const TOP_LEVEL: usize = 1; + // HACK: see doc/compiler/inference.md for details + pub const GENERIC_LEVEL: usize = usize::MAX; + + /// 型を非依存化する + fn _independentise<'a>(_t: Type, _ts: &[Type]) -> Type { + todo!() + } + + fn _generalize_tp(&self, free: TyParam) -> (TyParam, Set) { + match free { + // unwrapは後回し + TyParam::FreeVar(v) if v.is_linked() => { + let bounds: Set; + if let FreeKind::Linked(tp) = &mut *v.borrow_mut() { + (*tp, bounds) = self._generalize_tp(tp.clone()); + } else { assume_unreachable!() } + (TyParam::FreeVar(v), bounds) + }, + // TODO: 多相汎化 + TyParam::FreeVar(fv) if fv.level() > Some(self.level) => { + match &*fv.borrow() { + FreeKind::Unbound{ id, constraint, .. } => { + let name = id.to_string(); + let bound = match constraint { + Constraint::SubtypeOf(sup) => TyBound::subtype(Type::mono(name.clone()), sup.clone()), + Constraint::TypeOf(t) => TyBound::instance(Str::rc(&name[..]), t.clone()), + }; + (TyParam::mono_q(&name), set!{bound}) + }, + FreeKind::NamedUnbound{ name, constraint, .. } => { + let bound = match constraint { + Constraint::SubtypeOf(sup) => TyBound::subtype(Type::mono(name.clone()), sup.clone()), + Constraint::TypeOf(t) => TyBound::instance(Str::rc(&name[..]), t.clone()), + }; + (TyParam::mono_q(name), set!{bound}) + } + _ => assume_unreachable!(), + } + }, + other if other.has_no_unbound_var() => (other, set!{}), + other => todo!("{other}"), + } + } + + /// see doc/LANG/compiler/inference.md#一般化 for details + /// ``` + /// generalize_t(?T) == 'T: Type + /// generalize_t(?T(<: Nat) -> ?T) == |'T <: Nat| 'T -> 'T + /// generalize_t(?T(<: Nat) -> Int) == Nat -> Int // 戻り値に現れないなら量化しない + /// ``` + fn generalize_t(&self, free: Type) -> (Type, Set) { + match free { + // unwrapは後回し + FreeVar(v) if v.is_linked() => { + let bounds: Set; + if let FreeKind::Linked(t) = &mut *v.borrow_mut() { + (*t, bounds) = self.generalize_t(t.clone()); + } else { assume_unreachable!() } + (Type::FreeVar(v), bounds) + }, + // TODO: 多相汎化 + FreeVar(fv) if fv.level() > Some(self.level) => { + match &*fv.borrow() { + FreeKind::Unbound{ id, constraint, .. } => { + let name = id.to_string(); + let bound = match constraint { + Constraint::SubtypeOf(sup) => TyBound::subtype(Type::mono(name.clone()), sup.clone()), + Constraint::TypeOf(t) => TyBound::instance(Str::rc(&name[..]), t.clone()), + }; + (Type::mono_q(&name), set!{bound}) + }, + FreeKind::NamedUnbound{ name, constraint, .. } => { + let bound = match constraint { + Constraint::SubtypeOf(sup) => TyBound::subtype(Type::mono(name.clone()), sup.clone()), + Constraint::TypeOf(t) => TyBound::instance(Str::rc(&name[..]), t.clone()), + }; + (Type::mono_q(name), set!{bound}) + } + _ => assume_unreachable!(), + } + }, + Subr(mut subr) => { + let mut bounds = set!{}; + let kind = match subr.kind { + SubrKind::FuncMethod(self_t) => { + let (t, bs) = self.generalize_t(*self_t); + bounds.merge(bs); + SubrKind::fn_met(t) + }, + SubrKind::ProcMethod { before, after } => { + let (before, bs) = self.generalize_t(*before); + bounds.merge(bs); + if let Some(after) = after { + let (after, bs) = self.generalize_t(*after); + bounds.merge(bs); + SubrKind::pr_met(before, Some(after)) + } else { + SubrKind::pr_met(before, None) + } + }, + other => other, + }; + subr.non_default_params.iter_mut() + .for_each(|p| { + let (t, bs) = self.generalize_t(mem::take(&mut p.ty)); + p.ty = t; + bounds.merge(bs); + }); + subr.default_params.iter_mut() + .for_each(|p| { + let (t, bs) = self.generalize_t(mem::take(&mut p.ty)); + p.ty = t; + bounds.merge(bs); + }); + let (return_t, bs) = self.generalize_t(*subr.return_t); + bounds.merge(bs); + (Type::subr(kind, subr.non_default_params, subr.default_params, return_t), bounds) + }, + // REVIEW: その他何でもそのまま通していいのか? + other => (other, set!{}), + } + } + + pub(crate) fn bounds(&self) -> Set { + self.impls.iter() + .filter(|(_, vi)| vi.kind.is_parameter()) + .map(|(name, vi)| TyBound::instance(name.inspect().clone(), vi.t.clone())) + .collect() + } + + fn instantiate_tp(quantified: TyParam, tvtab: TyVarTable) -> (TyParam, TyVarTable) { + match quantified { + TyParam::MonoQVar(n) => { + if let Some(t) = tvtab.get_typaram(&n) { + (t.clone(), tvtab) + } else if let Some(_t) = tvtab.get_tyvar(&n) { + todo!() + } else { + panic!("type parameter {n} is not defined") + } + }, + TyParam::UnaryOp{ op, val } => { + let (res, tvtab) = Self::instantiate_tp(*val, tvtab); + (TyParam::unary(op, res), tvtab) + }, + TyParam::BinOp{ op, lhs, rhs } => { + let (lhs, tvtab) = Self::instantiate_tp(*lhs, tvtab); + let (rhs, tvtab) = Self::instantiate_tp(*rhs, tvtab); + (TyParam::bin(op, lhs, rhs), tvtab) + }, + TyParam::Type(t) => { + let (t, tvtab) = Self::instantiate_t(*t, tvtab); + (TyParam::t(t), tvtab) + }, + p @ (TyParam::ConstObj(_) | TyParam::Mono(_)) => (p, tvtab), + other => todo!("{other}"), + } + } + + /// 'T -> ?T (quantified to free) + pub(crate) fn instantiate_t(quantified: Type, mut tvtab: TyVarTable) -> (Type, TyVarTable) { + match quantified { + MonoQVar(n) => { + if let Some(t) = tvtab.get_tyvar(&n) { + (t.clone(), tvtab) + } else if let Some(_t) = tvtab.get_typaram(&n) { + todo!() + } else { + panic!("the type variable {n} is not defined") + } + }, + PolyQVar{ name, mut params } => { + for param in params.iter_mut() { + (*param, tvtab) = Self::instantiate_tp(mem::take(param), tvtab); + } + (Type::poly_q(name, params), tvtab) + }, + Refinement(mut refine) => { + refine.preds = refine.preds.into_iter().map(|mut pred| { + for tp in pred.typarams_mut() { + (*tp, tvtab) = Self::instantiate_tp(mem::take(tp), tvtab.clone()); + } + pred + }).collect(); + (Type::Refinement(refine), tvtab) + }, + Subr(mut subr) => { + let kind = match subr.kind { + SubrKind::FuncMethod(self_t) => { + let (res, _tvtab) = Self::instantiate_t(*self_t, tvtab); + tvtab = _tvtab; + SubrKind::FuncMethod(Box::new(res)) + } + SubrKind::ProcMethod{ before, after } => { + let (before, _tvtab) = Self::instantiate_t(*before, tvtab); + let (after, _tvtab) = if let Some(after) = after { + let (after, _tvtab) = Self::instantiate_t(*after, _tvtab); + (Some(after), _tvtab) + } else { + (None, _tvtab) + }; + tvtab = _tvtab; + SubrKind::pr_met(before, after) + } + other => other, + }; + for p in subr.non_default_params.iter_mut() { + (p.ty, tvtab) = Self::instantiate_t(mem::take(&mut p.ty), tvtab); + } + for p in subr.default_params.iter_mut() { + (p.ty, tvtab) = Self::instantiate_t(mem::take(&mut p.ty), tvtab); + } + let (return_t, tvtab) = Self::instantiate_t(*subr.return_t, tvtab); + (Type::subr(kind, subr.non_default_params, subr.default_params, return_t), tvtab) + }, + Type::Array{ t, len } => { + let (t, tvtab) = Self::instantiate_t(*t, tvtab); + let (len, tvtab) = Self::instantiate_tp(len, tvtab); + (Type::array(t, len), tvtab) + }, + Type::Dict{ k, v } => { + let (k, tvtab) = Self::instantiate_t(*k, tvtab); + let (v, tvtab) = Self::instantiate_t(*v, tvtab); + (Type::dict(k, v), tvtab) + }, + Tuple(mut ts) => { + for t in ts.iter_mut() { + (*t, tvtab) = Self::instantiate_t(mem::take(t), tvtab); + } + (Type::Tuple(ts), tvtab) + }, + Record(mut dict) => { + for v in dict.values_mut() { + (*v, tvtab) = Self::instantiate_t(mem::take(v), tvtab); + } + (Type::Record(dict), tvtab) + }, + Range(t) => { + let (t, tvtab) = Self::instantiate_t(*t, tvtab); + (Type::range(t), tvtab) + }, + Iter(t) => { + let (t, tvtab) = Self::instantiate_t(*t, tvtab); + (Type::iter(t), tvtab) + }, + Option(t) => { + let (t, tvtab) = Self::instantiate_t(*t, tvtab); + (Type::option(t), tvtab) + }, + OptionMut(t) => { + let (t, tvtab) = Self::instantiate_t(*t, tvtab); + (Type::option_mut(t), tvtab) + }, + Ref(t) => { + let (t, tvtab) = Self::instantiate_t(*t, tvtab); + (Type::refer(t), tvtab) + }, + RefMut(t) => { + let (t, tvtab) = Self::instantiate_t(*t, tvtab); + (Type::ref_mut(t), tvtab) + }, + VarArgs(t) => { + let (t, tvtab) = Self::instantiate_t(*t, tvtab); + (Type::var_args(t), tvtab) + } + MonoProj{ lhs, rhs } => { + let (lhs, tvtab) = Self::instantiate_t(*lhs, tvtab); + (Type::mono_proj(lhs, rhs), tvtab) + } + Poly{ name, mut params } => { + for param in params.iter_mut() { + (*param, tvtab) = Self::instantiate_tp(mem::take(param), tvtab); + } + (Type::poly(name, params), tvtab) + } + other if other.is_monomorphic() => (other, tvtab), + other => todo!("{other}"), + } + } + + fn instantiate(&self, quantified: Type, callee: &hir::Expr) -> TyCheckResult { + match quantified { + Quantified(quant) => { + let tvtab = TyVarTable::new(self.level, quant.bounds); + let (t, _) = Self::instantiate_t(*quant.unbound_callable, tvtab); + match &t { + Type::Subr(subr) => { + match (subr.kind.self_t(), callee.receiver_t()) { + (Some(l), Some(r)) => { + self.unify(l, r, None, Some(callee.loc()))?; + }, + (None, None) => {}, + _ => todo!(), + } + } + _ => unreachable!(), + } + Ok(t) + }, + // rank-1制限により、通常の型(rank-0型)の内側に量化型は存在しない + other => Ok(other), + } + } + + /// e.g. + /// ``` + /// substitute_call(instance: ((?T, ?U) -> ?T), [Int, Str], []) => instance: (Int, Str) -> Int + /// substitute_call(instance: ((?T, Int) -> ?T), [Int, Nat], []) => instance: (Int, Int) -> Str + /// substitute_call(instance: ((?M(: Nat)..?N(: Nat)) -> ?M+?N), [1..2], []) => instance: (1..2) -> {3} + /// substitute_call(instance: ((?L(: Add(?R, ?O)), ?R) -> ?O), [1, 2], []) => instance: (Nat, Nat) -> Nat + /// ``` + fn substitute_call(&self, callee: &hir::Expr, instance: &Type, pos_args: &[hir::PosArg], kw_args: &[hir::KwArg]) -> TyCheckResult<()> { + match instance { + Type::Subr(subr) => { + let params_len = + subr.non_default_params.len() + + subr.default_params.len(); + if params_len < pos_args.len() + kw_args.len() { + return Err(TyCheckError::too_many_args_error( + callee.loc(), + &callee.to_string(), + self.caused_by(), + params_len, + pos_args.len(), + kw_args.len(), + )) + } + let mut passed_params = set!{}; + let params = subr.non_default_params.iter().chain(subr.default_params.iter()); + for (param_ty, pos_arg) in params.clone().zip(pos_args) { + self.unify(¶m_ty.ty, pos_arg.expr.ref_t(), None, Some(pos_arg.loc())).map_err(|e| { + TyCheckError::type_mismatch_error( + e.core.loc, + e.caused_by, + param_ty.name.as_ref().map(|s| &s[..]).unwrap_or(""), + ¶m_ty.ty, + pos_arg.expr.ref_t() + ) + })?; + if let Some(name) = ¶m_ty.name { + if passed_params.contains(name) { + return Err(TyCheckError::multiple_args_error( + callee.loc(), + &callee.to_string(), + self.caused_by(), + name, + )) + } else { + passed_params.insert(name); + } + } + } + let param_ts = { + let mut param_ts = Dict::new(); + for param_ty in params { + if let Some(name) = ¶m_ty.name { + param_ts.insert(name, ¶m_ty.ty); + } + } + param_ts + }; + for kw_arg in kw_args.iter() { + if let Some(ty) = param_ts.get(kw_arg.keyword.inspect()) { + self.unify(ty, kw_arg.expr.ref_t(), None, Some(kw_arg.loc()))?; + } else { + return Err(TyCheckError::unexpected_kw_arg_error( + kw_arg.keyword.loc(), + &callee.to_string(), + self.caused_by(), + kw_arg.keyword.inspect() + )) + } + } + Ok(()) + }, + other => todo!("{other}"), + } + } + + // FIXME: + fn eliminate_linked_tp(tp: TyParam) -> TyCheckResult { + match tp { + TyParam::FreeVar(fv) if fv.is_linked() => Ok(fv.unwrap()), + TyParam::Type(t) => Ok(TyParam::t(Self::eliminate_linked_vars(*t)?)), + TyParam::App{ name, mut args } => { + for param in args.iter_mut() { + *param = Self::eliminate_linked_tp(mem::take(param))?; + } + Ok(TyParam::App{ name, args }) + } + t => Ok(t), + } + } + + // FIXME: + fn eliminate_linked_vars(t: Type) -> TyCheckResult { + match t { + Type::FreeVar(fv) if fv.is_linked() => Ok(fv.unwrap()), + // 未連携型変数のチェックはモジュール全体の型検査が終わった後にやる + // Type::FreeVar(_) => + // Err(TyCheckError::checker_bug(0, Location::Unknown, fn_name!(), line!())), + Type::Poly{ name, mut params } => { + for param in params.iter_mut() { + *param = Self::eliminate_linked_tp(mem::take(param))?; + } + Ok(Type::poly(name, params)) + }, + Type::Array{ mut t, mut len } => { + let t = Self::eliminate_linked_vars(mem::take(&mut t))?; + let len = Self::eliminate_linked_tp(mem::take(&mut len))?; + Ok(Type::array(t, len)) + }, + Type::Subr(mut subr) => { + match &mut subr.kind { + SubrKind::FuncMethod(t) => { + *t = Box::new(Self::eliminate_linked_vars(mem::take(t))?); + }, + SubrKind::ProcMethod{ before, after } => { + *before = Box::new(Self::eliminate_linked_vars(mem::take(before))?); + if let Some(after) = after { + *after = Box::new(Self::eliminate_linked_vars(mem::take(after))?); + } + }, + _ => {}, + } + let params = subr.non_default_params.iter_mut().chain(subr.default_params.iter_mut()); + for param in params { + param.ty = Self::eliminate_linked_vars(mem::take(&mut param.ty))?; + } + subr.return_t = Box::new(Self::eliminate_linked_vars(mem::take(&mut subr.return_t))?); + Ok(Type::Subr(subr)) + } + t => Ok(t), + } + } + + /// 可変依存型の変更を伝搬させる + fn propagate(&self, t: &Type, callee: &hir::Expr) -> TyCheckResult<()> { + match t { + Type::Subr(subr) => { + match &subr.kind { + SubrKind::ProcMethod{ before: _, after: Some(after) } => { + let receiver_t = callee.receiver_t().unwrap(); + self.reunify(receiver_t, after, Some(callee.loc()), None)?; + }, + _ => {}, + } + }, + _ => {}, + } + Ok(()) + } + + fn _occur(&self, _t: Type) -> TyCheckResult { todo!() } + + /// allow_divergence = trueにすると、Num型変数と±Infの単一化を許す + pub(crate) fn unify_tp(&self, l: &TyParam, r: &TyParam, bounds: Option<&Set>, allow_divergence: bool) -> TyCheckResult<()> { + if l.has_no_unbound_var() && r.has_no_unbound_var() && l.deep_eq(r) { return Ok(()) } + match (l, r) { + (TyParam::Type(l), TyParam::Type(r)) => + self.unify(&l, &r, None, None), + ( + ltp @ TyParam::FreeVar(lfv), + rtp @ TyParam::FreeVar(rfv), + ) if lfv.is_unbound() && rfv.is_unbound() => { + if lfv.level().unwrap() > rfv.level().unwrap() { lfv.link(rtp); } + else { rfv.link(ltp); } + Ok(()) + }, + (TyParam::FreeVar(fv), tp) + | (tp, TyParam::FreeVar(fv)) => { + match &*fv.borrow() { + FreeKind::Linked(l) => { return self.unify_tp(l, tp, bounds, allow_divergence) }, + FreeKind::Unbound{ .. } | FreeKind::NamedUnbound{ .. } => {}, + } // &fv is dropped + let fv_t = fv.borrow() + .constraint() + .unwrap().typ() + .unwrap().clone(); // fvを参照しないよいにcloneする(あとでborrow_mutするため) + let tp_t = self.eval.get_tp_t(tp, bounds, &self)?; + if self.deep_supertype_of(&fv_t, &tp_t) { + // 外部未連携型変数の場合、linkしないで制約を弱めるだけにする(see compiler/inference.md) + if fv.level() < Some(self.level) { + let new_constraint = Constraint::SubtypeOf(tp_t.clone()); + if self.is_sub_constraint_of(fv.borrow().constraint().unwrap(), &new_constraint) + || fv.borrow().constraint().unwrap().typ() == Some(&Type) { + fv.update_constraint(new_constraint); + } + } else { + fv.link(tp); + } + Ok(()) + } else { + if allow_divergence + && ( + self.eq_tp(&tp, &TyParam::value(Inf), None) + || self.eq_tp(&tp, &TyParam::value(NegInf), None) + ) && self.deep_subtype_of(&fv_t, &Type::mono("Num")) { + fv.link(tp); + Ok(()) + } else { + Err(TyCheckError::unreachable(fn_name!(), line!())) + } + } + }, + ( TyParam::UnaryOp{ op: lop, val: lval }, + TyParam::UnaryOp{ op: rop, val: rval } + ) if lop == rop => { + self.unify_tp(lval, rval, bounds, allow_divergence) + }, + ( + TyParam::BinOp{ op: lop, lhs, rhs }, + TyParam::BinOp{ op: rop, lhs: lhs2, rhs: rhs2 } + ) if lop == rop => { + self.unify_tp(lhs, lhs2, bounds, allow_divergence)?; + self.unify_tp(rhs, rhs2, bounds, allow_divergence) + }, + (l, r) => panic!("type-parameter unification failed:\nl:{l}\nr: {r}"), + } + } + + fn reunify_tp(&self, before: &TyParam, after: &TyParam, bounds: Option<&Set>) -> TyCheckResult<()> { + match (before, after) { + (TyParam::ConstObj(ConstObj::MutValue(l)),TyParam::ConstObj(ConstObj::Value(r))) => { + *l.borrow_mut() = r.clone(); + Ok(()) + }, + (TyParam::ConstObj(ConstObj::MutValue(l)),TyParam::ConstObj(ConstObj::MutValue(r))) => { + *l.borrow_mut() = r.borrow().clone(); + Ok(()) + }, + (TyParam::Type(l), TyParam::Type(r)) => + self.reunify(&l, &r, None, None), + ( TyParam::UnaryOp{ op: lop, val: lval }, + TyParam::UnaryOp{ op: rop, val: rval } + ) if lop == rop => { + self.reunify_tp(lval, rval, bounds) + }, + ( + TyParam::BinOp{ op: lop, lhs, rhs }, + TyParam::BinOp{ op: rop, lhs: lhs2, rhs: rhs2 } + ) if lop == rop => { + self.reunify_tp(lhs, lhs2, bounds)?; + self.reunify_tp(rhs, rhs2, bounds) + }, + (l, r) if self.eq_tp(l, r, None) => Ok(()), + (l, r) => panic!("type-parameter re-unification failed:\nl: {l}\nr: {r}"), + } + } + + /// predは正規化されているとする + fn unify_pred(&self, l_pred: &Predicate, r_pred: &Predicate) -> TyCheckResult<()> { + match (l_pred, r_pred) { + (Pred::Value(_), Pred::Value(_)) + | (Pred::Const(_), Pred::Const(_)) => Ok(()), + (Pred::Equal{ rhs, .. }, Pred::Equal{ rhs: rhs2, .. }) + | (Pred::GreaterEqual{ rhs, .. }, Pred::GreaterEqual{ rhs: rhs2, .. }) + | (Pred::LessEqual{ rhs, .. }, Pred::LessEqual{ rhs: rhs2, .. }) + | (Pred::NotEqual{ rhs, .. }, Pred::NotEqual{ rhs: rhs2, .. }) => + self.unify_tp(rhs, rhs2, None, false), + (Pred::And(l1, r1), Pred::And(l2, r2)) + | (Pred::Or(l1, r1), Pred::Or(l2, r2)) + | (Pred::Not(l1, r1), Pred::Not(l2, r2)) => + match (self.unify_pred(l1, l2), self.unify_pred(r1, r2)) { + (Ok(()), Ok(())) => Ok(()), + (Ok(()), Err(e)) | (Err(e), Ok(())) + | (Err(e), Err(_)) => Err(e), + } + // unify({I >= 0}, {I >= ?M and I <= ?N}): ?M => 0, ?N => Inf + (Pred::GreaterEqual{ rhs, .. }, Pred::And(l , r)) + | (Predicate::And(l, r), Pred::GreaterEqual{ rhs, .. }) => match (l.as_ref(), r.as_ref()) { + (Pred::GreaterEqual{ rhs: ge_rhs, .. }, Pred::LessEqual{ rhs: le_rhs, .. }) + | (Pred::LessEqual{ rhs: le_rhs, .. }, Pred::GreaterEqual{ rhs: ge_rhs, .. }) => { + self.unify_tp(rhs, ge_rhs, None, false)?; + self.unify_tp(le_rhs, &TyParam::value(Inf), None, true) + }, + _ => Err(TyCheckError::pred_unification_error(l_pred, r_pred, self.caused_by())), + }, + (Pred::LessEqual{ rhs, .. }, Pred::And(l , r)) + | (Pred::And(l, r), Pred::LessEqual{ rhs, .. }) => match (l.as_ref(), r.as_ref()) { + (Pred::GreaterEqual{ rhs: ge_rhs, .. }, Pred::LessEqual{ rhs: le_rhs, .. }) + | (Pred::LessEqual{ rhs: le_rhs, .. }, Pred::GreaterEqual{ rhs: ge_rhs, .. }) => { + self.unify_tp(rhs, le_rhs, None, false)?; + self.unify_tp(ge_rhs, &TyParam::value(NegInf), None, true) + }, + _ => Err(TyCheckError::pred_unification_error(l_pred, r_pred, self.caused_by())), + }, + (Pred::Equal{ rhs, .. }, Pred::And(l , r)) + | (Pred::And(l, r), Pred::Equal{ rhs, .. }) => match (l.as_ref(), r.as_ref()) { + (Pred::GreaterEqual{ rhs: ge_rhs, .. }, Pred::LessEqual{ rhs: le_rhs, .. }) + | (Pred::LessEqual{ rhs: le_rhs, .. }, Pred::GreaterEqual{ rhs: ge_rhs, .. }) => { + self.unify_tp(rhs, le_rhs, None, false)?; + self.unify_tp(rhs, ge_rhs, None, false) + }, + _ => Err(TyCheckError::pred_unification_error(l_pred, r_pred, self.caused_by())), + }, + _ => Err(TyCheckError::pred_unification_error(l_pred, r_pred, self.caused_by())), + } + } + + /// By default, all type variables are instances of Class ('T: Nominal) + /// So `unify(?T, Int); unify(?T, Bool)` will causes an error + /// To bypass the constraint, you need to specify `'T: Structural` in the type bounds + pub(crate) fn unify(&self, lhs_t: &Type, rhs_t: &Type, lhs_loc: Option, rhs_loc: Option) -> TyCheckResult<()> { + if lhs_t.has_no_unbound_var() && rhs_t.has_no_unbound_var() && self.deep_supertype_of(lhs_t, rhs_t) { return Ok(()) } + match (lhs_t, rhs_t) { + // unify(?T[2], ?U[3]): ?U[3] => ?T[2] + // bind the higher level var to lower one + (lt @ Type::FreeVar(lfv), rt @ Type::FreeVar(rfv)) + if lfv.is_unbound() && rfv.is_unbound() => { + if lfv.level().unwrap() > rfv.level().unwrap() { lfv.link(rt); } + else { rfv.link(lt); } + Ok(()) + }, + // unify(?L(<: Add(?R, ?O)), Nat): (?R => Nat, ?O => Nat, ?L => Nat) + // unify(?A(<: Mutate), [?T; 0]): (?A => [?T; 0]) + (Type::FreeVar(fv), t) + | (t, Type::FreeVar(fv)) => { + match &mut *fv.borrow_mut() { + FreeKind::Linked(l) => { return self.unify(l, t, lhs_loc, rhs_loc) }, + FreeKind::Unbound{ lev, constraint, .. } + | FreeKind::NamedUnbound{ lev, constraint, .. } => { + t.update_level(*lev); + // TODO: constraint.type_of() + if let Some(sup) = constraint.super_type_mut() { + // 下のような場合は制約を弱化する + // unify(?T(<: Nat), Int): (?T(<: Int)) + if self.deep_subtype_of(sup, t) { + *sup = t.clone(); + } else { + self.sub_unify(t, sup, rhs_loc, lhs_loc)?; + } + } + }, + } // &fv is dropped + let new_constraint = Constraint::SubtypeOf(t.clone()); + // 外部未連携型変数の場合、linkしないで制約を弱めるだけにする(see compiler/inference.md) + // fv == ?T(: Type)の場合は?T(<: U)にする + if fv.level() < Some(self.level) { + if self.is_sub_constraint_of(fv.borrow().constraint().unwrap(), &new_constraint) + || fv.borrow().constraint().unwrap().typ() == Some(&Type) { + fv.update_constraint(new_constraint); + } + } else { + fv.link(t); + } + Ok(()) + }, + (Type::Refinement(l), Type::Refinement(r)) => { + if !self.supertype_of(&l.t, &r.t, None) && !self.supertype_of(&r.t, &l.t, None) { + return Err(TyCheckError::unification_error(lhs_t, rhs_t, lhs_loc, rhs_loc, self.caused_by())) + } + // FIXME: 正規化する + for l_pred in l.preds.iter() { + for r_pred in r.preds.iter() { + self.unify_pred(l_pred, r_pred)?; + } + } + Ok(()) + }, + (Type::Refinement(_), r) => { + let rhs_t = self.into_refinement(r.clone()); + self.unify(lhs_t, &Type::Refinement(rhs_t), lhs_loc, rhs_loc) + }, + (l, Type::Refinement(_)) => { + let lhs_t = self.into_refinement(l.clone()); + self.unify(&Type::Refinement(lhs_t), rhs_t, lhs_loc, rhs_loc) + }, + (Type::Subr(ls), Type::Subr(rs)) if ls.kind.same_kind_as(&rs.kind) => { + if let (Some(l), Some(r)) = (ls.kind.self_t(), rs.kind.self_t()) { + self.unify(l, r, lhs_loc, rhs_loc)?; + } + for (l, r) in ls.non_default_params.iter().zip(rs.non_default_params.iter()) { + self.unify(&l.ty, &r.ty, lhs_loc, rhs_loc)?; + } + self.unify(&ls.return_t, &rs.return_t, lhs_loc, rhs_loc) + }, + (Range(l), Range(r)) + | (Iter(l), Iter(r)) + | (Type::Ref(l), Type::Ref(r)) + | (Type::RefMut(l), Type::RefMut(r)) + | (Type::Option(l), Type::Option(r)) + | (OptionMut(l), OptionMut(r)) + | (VarArgs(l), VarArgs(r)) => self.unify(l, r, lhs_loc, rhs_loc), + // REVIEW: + (Type::Ref(l), r) + | (Type::RefMut(l), r) => self.unify(l, r, lhs_loc, rhs_loc), + (l, Type::Ref(r)) + | (l, Type::RefMut(r)) => self.unify(l, r, lhs_loc, rhs_loc), + (Type::Poly{ name: ln, params: lps }, Type::Poly{ name: rn, params: rps }) => { + if ln != rn { return Err(TyCheckError::unification_error(lhs_t, rhs_t, lhs_loc, rhs_loc, self.caused_by())) } + for (l, r) in lps.iter().zip(rps.iter()) { + self.unify_tp(l, r, None, false)?; + } + Ok(()) + }, + (Type::Poly{ name: _, params: _ }, _r) => { + todo!() + }, + (l, r) => + Err(TyCheckError::unification_error(l, r, lhs_loc, rhs_loc, self.caused_by())), + } + } + + /// T: Array(Int, !0), U: Array(Int, !1) + /// reunify(T, U): + /// T: Array(Int, !1), U: Array(Int, !1) + pub(crate) fn reunify(&self, before_t: &Type, after_t: &Type, bef_loc: Option, aft_loc: Option) -> TyCheckResult<()> { + match (before_t, after_t) { + (Type::FreeVar(fv), r) if fv.is_linked() => + self.reunify(&fv.crack(), r, bef_loc, aft_loc), + (l, Type::FreeVar(fv)) if fv.is_linked() => + self.reunify(l, &fv.crack(), bef_loc, aft_loc), + (Type::Range(l), Type::Range(r)) + | (Type::Iter(l), Type::Iter(r)) + | (Type::Ref(l), Type::Ref(r)) + | (Type::RefMut(l), Type::RefMut(r)) + | (Type::Option(l), Type::Option(r)) + | (Type::OptionMut(l), Type::OptionMut(r)) + | (Type::VarArgs(l), Type::VarArgs(r)) => self.reunify(l, r, bef_loc, aft_loc), + // REVIEW: + (Type::Ref(l), r) + | (Type::RefMut(l), r) => self.reunify(l, r, bef_loc, aft_loc), + (l, Type::Ref(r)) + | (l, Type::RefMut(r)) => self.reunify(l, r, bef_loc, aft_loc), + (Type::Poly{ name: ln, params: lps }, Type::Poly{ name: rn, params: rps }) => { + if ln != rn { + let before_t = Type::poly(ln.clone(), lps.clone()); + return Err(TyCheckError::re_unification_error(&before_t, after_t, bef_loc, aft_loc, self.caused_by())) + } + for (l, r) in lps.iter().zip(rps.iter()) { + self.reunify_tp(l, r, None)?; + } + Ok(()) + }, + (l, r) if self.same_type_of(l, r, None) => Ok(()), + (l, r) => + Err(TyCheckError::re_unification_error(l, r, bef_loc, aft_loc, self.caused_by())), + } + } + + /// Assuming that `sub` is a subtype of `sup`, fill in the type variable to satisfy the assumption + /// ``` + /// sub_unify(Nat, Add(?R, ?O)): (?R => Nat, ?O => Nat) + /// sub_unify([?T; 0], Mutate): () + /// ``` + fn sub_unify(&self, sub: &Type, sup: &Type, sub_loc: Option, sup_loc: Option) -> TyCheckResult<()> { + if sub.has_no_unbound_var() && sup.has_no_unbound_var() { return Ok(()) } + match (sub, sup) { + (l @ Refinement(_), r @ Refinement(_)) => { + return self.unify(l ,r, sub_loc, sup_loc) + }, + _ => {} + } + let mut opt_smallest = None; + for table in self.get_sorted_supertype_tables(sub) { + let instances = table.super_classes.iter() + .chain(table.super_traits.iter()) + .filter(|t| self.supertype_of(sup, t, None)); + // instanceが複数ある場合、経験的に最も小さい型を選ぶのが良い + // これでうまくいかない場合は型指定してもらう(REVIEW: もっと良い方法があるか?) + if let Some(t) = self.smallest_ref_t(instances) { + opt_smallest = if let Some(small) = opt_smallest { self.min(small, t) } else { Some(t) }; + } + } + let glue_patch_and_types = self.deep_get_glue_patch_and_types(); + let patch_instances = glue_patch_and_types.iter() + .filter_map(|(patch_name, l, r)| { + let patch = self.deep_get_patch(patch_name).unwrap(); + let bounds = patch.bounds(); + if self.supertype_of(l, sub, Some(&bounds)) + && self.supertype_of(r, sup, Some(&bounds)) { + let tvtab = TyVarTable::new(self.level, bounds); + let (l, _) = Self::instantiate_t(l.clone(), tvtab.clone()); + let (r, _) = Self::instantiate_t(r.clone(), tvtab); + Some((l, r)) + } else { None } + }); + let opt_smallest_pair = self.smallest_pair(patch_instances); + match (opt_smallest, opt_smallest_pair) { + (Some(smallest), Some((l, r))) => { + if self.min(smallest, &r) == Some(&r) { + self.unify(sub, &l, sub_loc, None)?; + self.unify(sup, &r, sup_loc, None) + } else { + self.unify(sup, smallest, sup_loc, None) + } + }, + (Some(smallest), None) => { + self.unify(sup, smallest, sup_loc, None) + }, + (None, Some((l, r))) => { + self.unify(sub, &l, sub_loc, None)?; + self.unify(sup, &r, sup_loc, None)?; + Ok(()) + }, + (None, None) => { + log!("{sub}, {sup}"); + todo!() + } + } + } +} + +// (type) getters & validators +impl SymbolTable { + fn validate_var_sig_t(&self, sig: &ast::VarSignature, body_t: &Type, mode: RegistrationMode) -> TyCheckResult<()> { + let spec_t = self.instantiate_var_sig_t(sig, None, mode)?; + match &sig.pat { + ast::VarPattern::Discard(token) => { + if self.unify(&spec_t, body_t, None, Some(sig.loc())).is_err() { + return Err(TyCheckError::type_mismatch_error( + token.loc(), self.caused_by(), "_", &spec_t, body_t, + )) + } + }, + ast::VarPattern::VarName(n) => { + if self.unify(&spec_t, body_t, None, Some(sig.loc())).is_err() { + return Err(TyCheckError::type_mismatch_error( + n.loc(), self.caused_by(), n.inspect(), &spec_t, body_t, + )) + } + } + ast::VarPattern::Array(a) => { + for (elem, inf_elem_t) in a.iter().zip(body_t.inner_ts().iter()) { + self.validate_var_sig_t(elem, inf_elem_t, mode)?; + } + }, + _ => todo!(), + } + Ok(()) + } + + pub(crate) fn instantiate_var_sig_t(&self, sig: &ast::VarSignature, opt_t: Option, mode: RegistrationMode) -> TyCheckResult { + let ty = if let Some(s) = sig.t_spec.as_ref() { + self.instantiate_typespec(s, mode)? + } else { Type::free_var(self.level, Constraint::TypeOf(Type)) }; + if let Some(t) = opt_t { + self.unify(&ty, &t, sig.t_spec.as_ref().map(|s| s.loc()), None)?; + } + Ok(ty) + } + + pub(crate) fn instantiate_sub_sig_t(&self, sig: &ast::SubrSignature, opt_ret_t: Option, mode: RegistrationMode) -> TyCheckResult { + let non_defaults = sig.params.non_defaults.iter() + .map(|p| ParamTy::new( + p.inspect().cloned(), + self.instantiate_param_sig_t(p, None, mode).unwrap() + )).collect::>(); + let defaults = sig.params.defaults.iter() + .map(|p| ParamTy::new( + p.inspect().cloned(), self.instantiate_param_sig_t(p, None, mode).unwrap() + )).collect::>(); + let return_t = if let Some(s) = sig.return_t_spec.as_ref() { + self.instantiate_typespec(s, mode)? + } else { + // preregisterならouter scopeで型宣言(see inference.md) + let level = if mode == PreRegister { self.level } else { self.level+1 }; + Type::free_var(level, Constraint::TypeOf(Type)) + }; + if let Some(ret_t) = opt_ret_t { + self.unify(&return_t, &ret_t, sig.return_t_spec.as_ref().map(|s| s.loc()), None)?; + } + Ok( + if sig.name.is_procedural() { Type::proc(non_defaults, defaults, return_t) } + else { Type::func(non_defaults, defaults, return_t) } + ) + } + + /// spec_t == Noneかつリテラル推論が不可能なら型変数を発行する + pub(crate) fn instantiate_param_sig_t(&self, sig: impl ParamSig, opt_decl_t: Option<&ParamTy>, mode: RegistrationMode) -> TyCheckResult { + let t = if let Some(spec) = sig.t_spec() { + self.instantiate_typespec(spec, mode)? + } else { + match sig.pat() { + ast::ParamPattern::Lit(lit) => Type::enum_t(set![self.eval.eval_const_lit(lit)]), + // TODO: Array + _ => { + let level = if mode == PreRegister { self.level } else { self.level+1 }; + Type::free_var(level, Constraint::TypeOf(Type)) + }, + } + }; + if let Some(decl_t) = opt_decl_t { + self.unify(&t, &decl_t.ty, sig.t_spec().map(|s| s.loc()), None)?; + } + Ok(t) + } + + pub(crate) fn instantiate_predecl_t(&self, _predecl: &PreDeclTypeSpec) -> TyCheckResult { + match _predecl { + ast::PreDeclTypeSpec::Simple(simple) => self.instantiate_simple_t(simple), + _ => todo!(), + } + } + + pub(crate) fn instantiate_simple_t(&self, simple: &SimpleTypeSpec) -> TyCheckResult { + match &simple.name.inspect()[..] { + "Nat" => Ok(Type::Nat), + "Nat!" => Ok(Type::NatMut), + "Int" => Ok(Type::Int), + "Int!" => Ok(Type::IntMut), + "Ratio" => Ok(Type::Ratio), + "Ratio!" => Ok(Type::RatioMut), + "Float" => Ok(Type::Float), + "Float!" => Ok(Type::FloatMut), + "Str" => Ok(Type::Str), + "Str!" => Ok(Type::StrMut), + "Bool" => Ok(Type::Bool), + "Bool!" => Ok(Type::BoolMut), + "None" => Ok(Type::NoneType), + "Ellipsis" => Ok(Type::Ellipsis), + "NotImplemented" => Ok(Type::NotImplemented), + "Inf" => Ok(Type::Inf), + "Obj" => Ok(Type::Obj), + "Obj!" => Ok(Type::ObjMut), + "Array" => { + // TODO: kw + let mut args = simple.args.pos_args(); + if let Some(first) = args.next() { + let t = self.instantiate_const_expr_as_type(&first.expr)?; + let len = args.next().unwrap(); + let len = self.instantiate_const_expr(&len.expr); + Ok(Type::array(t, len)) + } else { + Ok(Type::ArrayCommon) + } + } + other if simple.args.is_empty() => Ok(Type::mono(Str::rc(other))), + other => { + // FIXME: kw args + let params = simple.args.pos_args().map(|arg| { + match &arg.expr { + ast::ConstExpr::Lit(lit) => TyParam::ConstObj(ConstObj::Value(ValueObj::from(lit))), + _ => { todo!() } + } + }); + Ok(Type::poly(Str::rc(other), params.collect())) + }, + } + } + + pub(crate) fn instantiate_const_expr(&self, expr: &ast::ConstExpr) -> TyParam { + match expr { + ast::ConstExpr::Lit(lit) => TyParam::ConstObj(ConstObj::Value(ValueObj::from(&lit.token))), + ast::ConstExpr::Accessor(ast::ConstAccessor::Local(name)) => TyParam::Mono(name.inspect().clone()), + _ => todo!(), + } + } + + pub(crate) fn instantiate_const_expr_as_type(&self, expr: &ast::ConstExpr) -> TyCheckResult { + match expr { + ast::ConstExpr::Accessor(ast::ConstAccessor::Local(name)) => Ok(Type::mono(name.inspect())), + _ => todo!(), + } + } + + fn instantiate_func_param_spec(&self, p: &ParamTySpec, mode: RegistrationMode) -> TyCheckResult { + let t = self.instantiate_typespec(&p.ty, mode)?; + Ok(ParamTy::new(p.name.as_ref().map(|t| t.inspect().to_owned()), t)) + } + + pub(crate) fn instantiate_typespec(&self, spec: &TypeSpec, mode: RegistrationMode) -> TyCheckResult { + match spec { + TypeSpec::PreDeclTy(predecl) => self.instantiate_predecl_t(predecl), + // TODO: Flatten + TypeSpec::And(lhs, rhs) => + Ok(Type::And(vec![self.instantiate_typespec(lhs, mode)?, self.instantiate_typespec(rhs, mode)?])), + TypeSpec::Not(lhs, rhs) => + Ok(Type::Not(vec![self.instantiate_typespec(lhs, mode)?, self.instantiate_typespec(rhs, mode)?])), + TypeSpec::Or(lhs, rhs) => + Ok(Type::Or(vec![self.instantiate_typespec(lhs, mode)?, self.instantiate_typespec(rhs, mode)?])), + TypeSpec::Array { .. } => todo!(), + // FIXME: unwrap + TypeSpec::Tuple(tys) => + Ok(Type::Tuple(tys.iter().map(|spec| self.instantiate_typespec(spec, mode).unwrap()).collect())), + // TODO: エラー処理(リテラルでない、ダブりがある)はパーサーにやらせる + TypeSpec::Enum(set) => Ok(Type::enum_t( + set.pos_args().map(|arg| if let ast::ConstExpr::Lit(lit) = &arg.expr { + ValueObj::from(lit) + } else { todo!() }).collect::>() + )), + TypeSpec::Interval{ op, lhs, rhs } => { + let op = match op.kind { + TokenKind::Closed => IntervalOp::Closed, + TokenKind::LeftOpen => IntervalOp::LeftOpen, + TokenKind::RightOpen => IntervalOp::RightOpen, + TokenKind::Open => IntervalOp::Open, + _ => assume_unreachable!(), + }; + let l = self.instantiate_const_expr(lhs); + let l = self.eval.eval_tp(&l, self)?; + let r = self.instantiate_const_expr(rhs); + let r = self.eval.eval_tp(&r, self)?; + if let Some(Greater) = self.try_cmp(&l, &r, None) { + panic!("{l}..{r} is not a valid interval type (should be lhs <= rhs)") + } + Ok(Type::int_interval(op, l, r)) + }, + TypeSpec::Subr(subr) => { + let non_defaults = try_map(subr.non_defaults.iter(), |p| self.instantiate_func_param_spec(p, mode))?; + let defaults = try_map(subr.defaults.iter(), |p| self.instantiate_func_param_spec(p, mode))?; + let return_t = self.instantiate_typespec(&subr.return_t, mode)?; + Ok(Type::subr(subr.kind.clone(), non_defaults, defaults, return_t)) + }, + } + } + + pub(crate) fn instantiate_ty_bound(&self, bound: &TypeBoundSpec, mode: RegistrationMode) -> TyCheckResult { + // REVIEW: 型境界の左辺に来れるのは型変数だけか? + // TODO: 高階型変数 + match bound { + TypeBoundSpec::Subtype{ sub, sup } => + Ok(TyBound::subtype(Type::mono_q(sub.inspect().clone()), self.instantiate_typespec(sup, mode)?)), + TypeBoundSpec::Instance{ name, ty } => + Ok(TyBound::instance(name.inspect().clone(), self.instantiate_typespec(ty, mode)?)), + } + } + + pub(crate) fn instantiate_ty_bounds(&self, bounds: &TypeBoundSpecs, mode: RegistrationMode) -> TyCheckResult> { + let mut new_bounds = set!{}; + for bound in bounds.iter() { + new_bounds.insert(self.instantiate_ty_bound(bound, mode)?); + } + Ok(new_bounds) + } + + pub(crate) fn get_current_scope_local_var(&self, name: &str) -> Option<&VarInfo> { + self.impls.get(name).or_else(|| self.decls.get(name)) + } + + fn get_match_call_t(&self, pos_args: &[hir::PosArg], kw_args: &[hir::KwArg]) -> TyCheckResult { + if !kw_args.is_empty() { todo!() } + for pos_arg in pos_args.iter().skip(1) { + let t = pos_arg.expr.ref_t(); + if !matches!(&pos_arg.expr, hir::Expr::Lambda(_)) { + return Err(TyCheckError::type_mismatch_error( + pos_arg.loc(), + self.caused_by(), + "match", + &Type::mono("LambdaFunc"), + &t, + )) + } + } + let expr_t = pos_args[0].expr.ref_t(); + // Never or T => T + let mut union_pat_t = Type::Never; + for (i, a) in pos_args.iter().skip(1).enumerate() { + let lambda = common::enum_unwrap!(&a.expr, hir::Expr::Lambda); + if !lambda.params.defaults.is_empty() { todo!() } + if lambda.params.len() != 1 { + return Err(TyCheckError::argument_error( + pos_args[i+1].loc(), + self.caused_by(), + 1, + pos_args[i+1].expr.ref_t().typaram_len(), + )) + } + let rhs = self.instantiate_param_sig_t(&lambda.params.non_defaults[0], None, Normal)?; + union_pat_t = self.union(&union_pat_t, &rhs); + } + // NG: expr_t: Nat, union_pat_t: {1, 2} + // OK: expr_t: Int, union_pat_t: {1} | 'T + if expr_t.has_no_unbound_var() + && self.supertype_of(&expr_t, &union_pat_t, None) + && !self.supertype_of(&union_pat_t, &expr_t, None) { + return Err(TyCheckError::match_error( + pos_args[0].loc(), + self.caused_by(), + &expr_t, + )) + } + let branch_ts = pos_args.iter().skip(1) + .map(|a| ParamTy::anonymous(a.expr.ref_t().clone())).collect::>(); + let mut return_t = branch_ts[0].ty.return_t().unwrap().clone(); + for arg_t in branch_ts.iter().skip(1) { + return_t = self.union(&return_t, arg_t.ty.return_t().unwrap()); + } + let expr_t = if expr_t.has_unbound_var() { union_pat_t } else { expr_t.clone() }; + let param_ts = [ + vec![ParamTy::anonymous(expr_t)], + branch_ts.iter().map(|pt| pt.clone()).collect() + ].concat(); + let t = Type::func(param_ts, vec![], return_t); + Ok(t) + } + + pub(crate) fn get_local_t(&self, name: &Token, namespace: &Str) -> TyCheckResult { + if let Some(vi) = self.impls.get(name.inspect()) + .or_else(|| self.decls.get(name.inspect())) { + Ok(vi.t()) + } else { + if let Some(parent) = self.outer.as_ref() { + return parent.get_local_t(name, namespace) + } + Err(TyCheckError::no_var_error( + name.loc(), + namespace.clone(), + name.inspect(), + self.get_similar_name(name.inspect()), + )) + } + } + + pub(crate) fn get_attr_t(&self, obj: &hir::Expr, name: &Token, namespace: &Str) -> TyCheckResult { + let self_t = obj.t(); + if self_t == ASTOmitted { panic!() } + for table in self.get_sorted_supertype_tables(&self_t) { + if let Ok(t) = table.get_local_t(name, namespace) { + return Ok(t) + } + } + // TODO: dependent type widening + if let Some(parent) = self.outer.as_ref() { + parent.get_attr_t(obj, name, namespace) + } else { + Err(TyCheckError::no_attr_error( + name.loc(), + namespace.clone(), + &self_t, + name.inspect(), + self.get_similar_attr(&self_t, name.inspect()), + )) + } + } + + /// 戻り値ではなく、call全体の型を返す + /// objは現時点ではAccessorのみ対応 + /// 受け入れるobj(Accessor)はcheckしてないハリボテ + fn search_call_t(&self, callee: &hir::Expr, namespace: &Str) -> TyCheckResult { + match callee { + hir::Expr::Accessor(hir::Accessor::Local(local)) => { + self.get_local_t(&local.name, namespace) + }, + hir::Expr::Accessor(hir::Accessor::Attr(attr)) => { + self.get_attr_t(&attr.obj, &attr.name, namespace) + } + _ => todo!(), + } + } + + pub(crate) fn get_binop_t(&self, op: &Token, args: &[hir::PosArg], namespace: &Str) -> TyCheckResult { + common::debug_power_assert!(args.len() == 2); + let symbol = Token::symbol(binop_to_dname(op.inspect())); + let mut op = hir::Expr::Accessor(hir::Accessor::local(symbol, Type::ASTOmitted)); + self.get_call_t(&mut op, args, &[], namespace).map_err(|e| { + // HACK: dname.loc()はダミーLocationしか返さないので、エラーならop.loc()で上書きする + let core = ErrorCore::new(e.core.errno, e.core.kind, op.loc(), e.core.desc, e.core.hint); + TyCheckError::new(core, e.caused_by) + }) + } + + pub(crate) fn get_unaryop_t(&self, op: &Token, args: &[hir::PosArg], namespace: &Str) -> TyCheckResult { + common::debug_power_assert!(args.len() == 1); + let symbol = Token::symbol(unaryop_to_dname(op.inspect())); + let mut op = hir::Expr::Accessor(hir::Accessor::local(symbol, Type::ASTOmitted)); + self.get_call_t(&mut op, args, &[], namespace).map_err(|e| { + let core = ErrorCore::new(e.core.errno, e.core.kind, op.loc(), e.core.desc, e.core.hint); + TyCheckError::new(core, e.caused_by) + }) + } + + pub(crate) fn get_call_t( + &self, + callee: &hir::Expr, + pos_args: &[hir::PosArg], + kw_args: &[hir::KwArg], + namespace: &Str + ) -> TyCheckResult { + match callee { + hir::Expr::Accessor(hir::Accessor::Local(local)) if &local.inspect()[..] == "match" => { + return self.get_match_call_t(pos_args, kw_args) + } + _ => {} + } + let found = self.search_call_t(callee, namespace)?; + log!("Found:\ncallee: {callee}\nfound: {found}"); + let instance = self.instantiate(found, callee)?; + log!("Instantiated:\ninstance: {instance}\npos_args: ({})\nkw_args: ({})", fmt_slice(pos_args), fmt_slice(kw_args)); + self.substitute_call(callee, &instance, pos_args, kw_args)?; + log!("Substituted:\ninstance: {instance}"); + let res = self.eval.eval_t(instance, &self, self.level)?; + log!("Evaluated:\nres: {res}\n"); + let res = Self::eliminate_linked_vars(res)?; + log!("Eliminated:\nres: {res}\n"); + self.propagate(&res, callee)?; + log!("Propagated:\nres: {res}\n"); + Ok(res) + } + + pub(crate) fn deep_supertype_of(&self, lhs: &Type, rhs: &Type) -> bool { + if self.supertype_of(lhs, rhs, None) { return true } + for sup_rhs in self.get_sorted_supertype_tables(rhs) { + let bounds = sup_rhs.bounds(); + if sup_rhs.super_classes.iter().any(|sup| self.supertype_of(lhs, sup, Some(&bounds))) + || sup_rhs.super_traits.iter().any(|sup| self.supertype_of(lhs, sup, Some(&bounds))) { return true } + } + for (patch_name, sub, sup) in self.glue_patch_and_types.iter() { + let patch = self.deep_get_patch(patch_name).unwrap(); + let bounds = patch.bounds(); + if self.supertype_of(sub, rhs, Some(&bounds)) + && self.supertype_of(sup, lhs, Some(&bounds)) { return true } + } + if let Some(outer) = &self.outer { + if outer.deep_supertype_of(lhs, rhs) { return true } + } + false + } + + pub(crate) fn deep_subtype_of(&self, lhs: &Type, rhs: &Type) -> bool { + self.deep_supertype_of(rhs, lhs) + } + + pub(crate) fn _deep_same_type_of(&self, lhs: &Type, rhs: &Type) -> bool { + self.deep_supertype_of(lhs, rhs) && self.deep_subtype_of(lhs, rhs) + } + + fn eq_tp(&self, lhs: &TyParam, rhs: &TyParam, bounds: Option<&Set>) -> bool { + match (lhs, rhs) { + (TyParam::Type(lhs), TyParam::Type(rhs)) => { return self.same_type_of(lhs, rhs, bounds) }, + (TyParam::Mono(l), TyParam::Mono(r)) => { + if let (Some((l, _)), Some((r, _))) = ( + self.types.iter().find(|(t, _)| t.name() == &l[..]), + self.types.iter().find(|(t, _)| t.name() == &r[..]), + ) { return self.supertype_of(l, r, None) || self.subtype_of(l, r, None) } + }, + (TyParam::MonoQVar(name), other) + | (other, TyParam::MonoQVar(name)) => { + if let Some(bs) = bounds { + if let Some(bound) = bs.iter().find(|b| b.mentions_as_instance(name)) { + let other_t = self.type_of(other, bounds); + return self.supertype_of(bound.t(), &other_t, bounds) + } else { todo!() } // subtyping + } + }, + ( + TyParam::App{ name: ln, args: largs }, + TyParam::App{ name: rn, args: rargs }, + ) => { + return ln == rn + && largs.len() == rargs.len() + && largs.iter().zip(rargs.iter()).all(|(l, r)| self.eq_tp(l, r, bounds)) + }, + (TyParam::FreeVar(fv), other) + | (other, TyParam::FreeVar(fv)) => { + match &*fv.borrow() { + FreeKind::Linked(tp) => { return self.eq_tp(tp, other, bounds) }, + FreeKind::Unbound{ constraint, .. } + | FreeKind::NamedUnbound{ constraint, .. }=> { + let t = constraint.typ().unwrap(); + let other_t = self.type_of(other, bounds); + return self.supertype_of(&t, &other_t, bounds) + } + } + }, + (l, r) if l == r => { return true }, + _ => {}, + } + self.eval.shallow_eq_tp(lhs, rhs, &self) + } + + /// lhs :> rhs? + /// ``` + /// assert supertype_of(Int, Nat) # i: Int = 1 as Nat + /// assert supertype_of(Bool, Bool) + /// ``` + /// TODO: Inputs/Outputs trait + /// 単一化、評価等はここでは行わない、スーパータイプになる可能性があるかだけ判定する + /// ので、lhsが(未連携)型変数の場合は単一化せずにtrueを返す + pub(crate) fn supertype_of(&self, lhs: &Type, rhs: &Type, bounds: Option<&Set>) -> bool { + if lhs.deep_eq(rhs) { return true } + match (lhs, rhs) { + // FIXME: Obj/Neverはクラス、Top/Bottomは構造型 + (Obj, _) | (_, Never) => true, + (_, Obj) | (Never, _) => false, + (Float | Ratio | Int | Nat | Bool, Bool) + | (Float | Ratio | Int | Nat, Nat) + | (Float | Ratio | Int, Int) + | (Float | Ratio, Ratio) + | (Float, Float) => true, + (FuncCommon, Subr(SubrType{ kind: SubrKind::Func, .. })) + | (ProcCommon, Subr(SubrType{ kind: SubrKind::Proc, .. })) + | (FuncMethodCommon, Subr(SubrType{ kind: SubrKind::FuncMethod(_), .. })) + | (ProcMethodCommon, Subr(SubrType{ kind: SubrKind::ProcMethod{ .. }, .. })) + | (ArrayCommon, Type::Array{ .. }) + | (DictCommon, Type::Dict{ .. }) => true, + (CallableCommon, Subr(_) | FuncCommon | ProcCommon | FuncMethodCommon | ProcMethodCommon) => true, + (Subr(ls), Subr(rs)) + if ls.kind.same_kind_as(&rs.kind) + && (ls.kind == SubrKind::Func || ls.kind == SubrKind::Proc) => { + // () -> Never <: () -> Int <: () -> Object + // (Object) -> Int <: (Int) -> Int <: (Never) -> Int + ls.non_default_params.len() == rs.non_default_params.len() + && ls.default_params.len() == rs.default_params.len() + && self.supertype_of(&ls.return_t, &rs.return_t, bounds) // covariant + && ls.non_default_params.iter() + .zip(rs.non_default_params.iter()) + .all(|(l, r)| self.subtype_of(&l.ty, &r.ty, bounds)) + && ls.default_params.iter() + .zip(rs.default_params.iter()) + .all(|(l, r)| self.subtype_of(&l.ty, &r.ty, bounds)) // contravariant + } + (Type::Array{ t: lhs, len: llen }, Type::Array{ t: rhs, len: rlen }) => { + self.eq_tp(llen, rlen, bounds) + && self.supertype_of(lhs, rhs, bounds) + } + (Tuple(lhs), Tuple(rhs)) => { + lhs.len() == rhs.len() + && lhs.iter() + .zip(rhs.iter()) + .all(|(l, r)| self.supertype_of(l, r, bounds)) + } + // RefMut, OptionMutは非変 + (Range(lhs), Range(rhs)) + | (Iter(lhs), Iter(rhs)) + | (Ref(lhs), Ref(rhs)) + | (Option(lhs), Option(rhs)) + | (VarArgs(lhs), VarArgs(rhs)) => self.supertype_of(lhs, rhs, bounds), + // 型変数の場合は、上位型になり得るならtrue、(型制約上)なり得ないならfalse + // 可能性に応じてその後の型判断を下すので、ここで型制約は課さない + (FreeVar(v), rhs) => { + match &*v.borrow() { + FreeKind::Linked(t) => self.supertype_of(t, rhs, bounds), + FreeKind::Unbound { constraint, .. } + | FreeKind::NamedUnbound{ constraint, .. } => match constraint { + // (?T <: Int) :> Nat == true, (?T <: Nat) :> Int == false + Constraint::SubtypeOf(sup) => self.supertype_of(sup, rhs, bounds), + // (?v: Type, rhs)ならOK + // (?v: Nat, rhs)なら何かがおかしい + // Class <: TypeだがNat + if self.supertype_of(&Type, t, bounds) { true } else { panic!() }, + }, + } + } + (lhs, FreeVar(v)) => { + match &*v.borrow() { + FreeKind::Linked(t) => self.supertype_of(lhs, t, bounds), + FreeKind::Unbound { constraint, .. } + | FreeKind::NamedUnbound{ constraint, .. } => match constraint { + // Nat :> (?T <: Int) == true, Int :> (?T <: Nat) == true + Constraint::SubtypeOf(_) => true, + Constraint::TypeOf(t) => + if self.supertype_of(&Type, t, bounds) { true } else { panic!() }, + }, + } + } + // (MonoQuantVar(_), _) | (_, MonoQuantVar(_)) => true, + // REVIEW: maybe this is incomplete + // ({I: Int | I >= 0} :> {N: Int | N >= 0}) == true, + // ({I: Int | I >= 0} :> {I: Int | I >= 1}) == true, + // ({I: Int | I >= 0} :> {N: Nat | N >= 1}) == true, + // ({I: Int | I > 1 or I < -1} :> {I: Int | I >= 0}) == false, + (Refinement(l), Refinement(r)) => { + if !self.supertype_of(&l.t, &r.t, bounds) { return false } + let mut r_preds_clone = r.preds.clone(); + for l_pred in l.preds.iter() { + for r_pred in r.preds.iter() { + if l_pred.subject().unwrap_or("") == &l.var[..] + && r_pred.subject().unwrap_or("") == &r.var[..] + && self.is_super_pred_of(l_pred, r_pred, bounds) { + r_preds_clone.remove(r_pred); + } + } + } + r_preds_clone.is_empty() + }, + (Nat, re @ Refinement(_)) => { + let nat = Type::Refinement(self.into_refinement(Nat)); + self.supertype_of(&nat, re, bounds) + } + (re @ Refinement(_), Nat) => { + let nat = Type::Refinement(self.into_refinement(Nat)); + self.supertype_of(re, &nat, bounds) + } + // Int :> {I: Int | ...} == true, Real :> {I: Int | ...} == false, Int :> {I: Str| ...} == false + (l, Refinement(r)) => { + self.supertype_of(l, &r.t, bounds) + }, + // ({I: Int | True} :> Int) == true, ({N: Nat | ...} :> Int) == false, ({I: Int | I >= 0} :> Int) == false + (Refinement(l), r) => { + if l.preds.iter().any(|p| p.mentions(&l.var) && p.can_be_false()) { + return false + } + self.supertype_of(&l.t, r, bounds) + }, + (Quantified(l), Quantified(r)) => { + // REVIEW: maybe this should be `unreachable` + if bounds.is_some() { panic!("Nested quantification") } + else { + // TODO: bounds同士の評価 + self.supertype_of(l.unbound_callable.as_ref(), r.unbound_callable.as_ref(), Some(&l.bounds)) + } + }, + (Quantified(q), r) => { + // REVIEW: maybe this should be `unreachable` + if bounds.is_some() { panic!("Nested quantification") } + else { self.supertype_of(q.unbound_callable.as_ref(), r, Some(&q.bounds)) } + }, + (lhs, Or(tys)) => tys.iter().all(|t| self.supertype_of(lhs, t, bounds)), + (And(tys), rhs) => tys.iter().all(|t| self.supertype_of(t, rhs, bounds)), + (VarArgs(lhs), rhs) => self.supertype_of(lhs, rhs, bounds), + // TはすべてのRef(T)のメソッドを持つので、Ref(T)のサブタイプ + (Ref(lhs), rhs) + | (RefMut(lhs), rhs) => self.supertype_of(lhs, rhs, bounds), + // TODO: Consider variance + (Poly{ name: ln, params: lp }, Poly{ name: rn, params: rp }) => { + ln == rn + && lp.len() == rp.len() + && lp.iter() + .zip(rp.iter()) + .all(|(l, r)| self.eq_tp(l, r, bounds)) + }, + (MonoQVar(name), r) => { + if let Some(bs) = bounds { + if let Some(bound) = bs.iter().find(|b| b.mentions_as_subtype(name)) { + self.supertype_of(bound.t(), r, bounds) + } else if let Some(bound) = bs.iter().find(|b| b.mentions_as_instance(name)) { + if self.same_type_of(bound.t(), &Type::Type, bounds) { true } else { todo!()} + } else { panic!("Unbound type variable: {name}") } + } else { panic!("No quantification") } + }, + (_l, MonoQVar(_name)) => todo!(), + (PolyQVar{ .. }, _r) => todo!(), + (_l, PolyQVar{ .. }) => todo!(), + (_l, _r) => false, + } + } + + /// lhs <: rhs? + pub(crate) fn subtype_of(&self, lhs: &Type, rhs: &Type, bounds: Option<&Set>) -> bool { + self.supertype_of(rhs, lhs, bounds) + } + + pub(crate) fn same_type_of(&self, lhs: &Type, rhs: &Type, bounds: Option<&Set>) -> bool { + self.supertype_of(lhs, rhs, bounds) && self.subtype_of(lhs, rhs, bounds) + } + + fn try_cmp(&self, l: &TyParam, r: &TyParam, bounds: Option<&Set>) -> Option { + match (l, r) { + (TyParam::ConstObj(l), TyParam::ConstObj(r)) => + l.try_cmp(r).map(Into::into), + // TODO: 型を見て判断する + (TyParam::BinOp{ op, lhs, rhs }, r) => { + if let Ok(l) = self.eval.eval_bin_tp(*op, lhs, rhs) { + self.try_cmp(&l, r, bounds) + } else { Some(Any) } + }, + (TyParam::FreeVar(fv), p) if fv.is_linked() => { + self.try_cmp(&*fv.crack(), p, bounds) + } + (p, TyParam::FreeVar(fv)) if fv.is_linked() => { + self.try_cmp(p, &*fv.crack(), bounds) + } + ( + l @ (TyParam::FreeVar(_) | TyParam::Erased(_) | TyParam::MonoQVar(_)), + r @ (TyParam::FreeVar(_) | TyParam::Erased(_) | TyParam::MonoQVar(_)), + ) /* if v.is_unbound() */ => { + let l_t = self.eval.get_tp_t(l, bounds, self).unwrap(); + let r_t = self.eval.get_tp_t(r, bounds, self).unwrap(); + if self.deep_supertype_of(&l_t, &r_t) || self.deep_subtype_of(&l_t, &r_t) { + Some(Any) + } else { Some(NotEqual) } + }, + // Intervalとしてのl..rはl<=rであることが前提となっている + // try_cmp((n: 1..10), 1) -> Some(GreaterEqual) + // try_cmp((n: 0..2), 1) -> Some(Any) + // try_cmp((n: 2.._), 1) -> Some(Greater) + // try_cmp((n: -1.._), 1) -> Some(Any) + (l @ (TyParam::Erased(_) | TyParam::FreeVar(_) | TyParam::MonoQVar(_)), p) => { + let t = self.eval.get_tp_t(l, bounds, &self).unwrap(); + let inf = self.inf(&t); + let sup = self.sup(&t); + if let (Some(inf), Some(sup)) = (inf, sup) { + // (n: Int, 1) -> (-inf..inf, 1) -> (cmp(-inf, 1), cmp(inf, 1)) -> (Less, Greater) -> Any + // (n: 5..10, 2) -> (cmp(5..10, 2), cmp(5..10, 2)) -> (Greater, Greater) -> Greater + match ( + self.try_cmp(&inf, p, bounds).unwrap(), + self.try_cmp(&sup, p, bounds).unwrap() + ) { + (Less, Less) => Some(Less), + (Less, Equal) => Some(LessEqual), + (Less, LessEqual) => Some(LessEqual), + (Less, NotEqual) => Some(NotEqual), + (Less, Greater | GreaterEqual | Any) => Some(Any), + (Equal, Less) => assume_unreachable!(), + (Equal, Equal) => Some(Equal), + (Equal, Greater) => Some(GreaterEqual), + (Equal, LessEqual) => Some(Equal), + (Equal, NotEqual) => Some(GreaterEqual), + (Equal, GreaterEqual | Any) => Some(GreaterEqual), + (Greater, Less) => assume_unreachable!(), + (Greater, Equal) => assume_unreachable!(), + (Greater, Greater | NotEqual | GreaterEqual | Any) => Some(Greater), + (Greater, LessEqual) => assume_unreachable!(), + (LessEqual, Less) => assume_unreachable!(), + (LessEqual, Equal | LessEqual) => Some(LessEqual), + (LessEqual, Greater | NotEqual | GreaterEqual | Any) => Some(Any), + (NotEqual, Less) => Some(Less), + (NotEqual, Equal | LessEqual) => Some(LessEqual), + (NotEqual, Greater | GreaterEqual | Any) => Some(Any), + (NotEqual, NotEqual) => Some(NotEqual), + (GreaterEqual, Less) => assume_unreachable!(), + (GreaterEqual, Equal | LessEqual) => Some(Equal), + (GreaterEqual, Greater | NotEqual | GreaterEqual | Any) => Some(GreaterEqual), + (Any, Less) => Some(Less), + (Any, Equal | LessEqual) => Some(LessEqual), + (Any, Greater | NotEqual | GreaterEqual | Any) => Some(Any), + } + } else { None } + } + (l, r @ (TyParam::Erased(_) | TyParam::MonoQVar(_) | TyParam::FreeVar(_))) => + self.try_cmp(r, l, bounds).map(|ord| ord.reverse()), + (_l, _r) => { + common::fmt_dbg!(_l, _r,); + None + }, + } + } + + fn into_refinement(&self, t: Type) -> RefinementType { + match t { + Nat => { + let var = Str::from(fresh_varname()); + RefinementType::new(var.clone(), Int, set!{Predicate::ge(var, TyParam::value(0))}) + } + Refinement(r) => r, + t => { + let var = Str::from(fresh_varname()); + RefinementType::new(var.clone(), t, set!{}) + } + } + } + + /// 和集合(A or B)を返す + fn union(&self, lhs: &Type, rhs: &Type) -> Type { + match (self.deep_supertype_of(lhs, rhs), self.deep_subtype_of(lhs, rhs)) { + (true, true) => return lhs.clone(), // lhs = rhs + (true, false) => return lhs.clone(), // lhs :> rhs + (false, true) => return rhs.clone(), + (false, false) => {}, + } + match (lhs, rhs) { + (Refinement(l), Refinement(r)) => + Type::Refinement(self.union_refinement(l, r)), + (Or(ts), t) + | (t, Or(ts)) => Or([vec![t.clone()], ts.clone()].concat()), + (t, Type::Never) | (Type::Never, t) => t.clone(), + (t, Refinement(r)) + | (Refinement(r), t) => { + let t = self.into_refinement(t.clone()); + Type::Refinement(self.union_refinement(&t, r)) + }, + (l, r) => Type::Or(vec![l.clone(), r.clone()]), + } + } + + fn union_refinement(&self, lhs: &RefinementType, rhs: &RefinementType) -> RefinementType { + if !self.supertype_of(&lhs.t, &rhs.t, None) && !self.subtype_of(&lhs.t, &rhs.t, None) { + log!("{lhs}\n{rhs}"); + todo!() + } else { + let name = lhs.var.clone(); + let rhs_preds = rhs.preds.iter().map(|p| p.clone().change_subject_name(name.clone())).collect(); + // FIXME: predの包含関係も考慮する + RefinementType::new( + lhs.var.clone(), + *lhs.t.clone(), + lhs.preds.clone().concat(rhs_preds), + ) + } + } + + /// see doc/LANG/compiler/refinement_subtyping.md + /// ``` + /// assert is_super_pred({I >= 0}, {I == 0}) + /// assert is_super_pred({T >= 0}, {I == 0}) + /// assert !is_super_pred({I < 0}, {I == 0}) + /// ``` + fn is_super_pred_of(&self, lhs: &Predicate, rhs: &Predicate, bounds: Option<&Set>) -> bool { + match (lhs, rhs) { + (Pred::LessEqual{ rhs, .. }, _) if !rhs.has_upper_bound() => true, + (Pred::GreaterEqual{ rhs, .. }, _) if !rhs.has_lower_bound() => true, + ( + Pred::Equal{ .. }, + Pred::GreaterEqual{ .. } | Pred::LessEqual{ .. } | Pred::NotEqual{ .. } + ) + | (Pred::LessEqual{ .. }, Pred::GreaterEqual{ .. }) + | (Pred::GreaterEqual{ .. }, Pred::LessEqual{ .. }) + | (Pred::NotEqual{ .. }, Pred::Equal{ .. }) => false, + (Pred::Equal{ rhs, .. }, Pred::Equal{ rhs: rhs2, .. }) + | (Pred::NotEqual{ rhs, .. }, Pred::NotEqual{ rhs: rhs2, .. }) => + self.try_cmp(rhs, rhs2, bounds).unwrap().is_eq(), + // {T >= 0} :> {T >= 1}, {T >= 0} :> {T == 1} + ( + Pred::GreaterEqual{ rhs, .. }, + Pred::GreaterEqual{ rhs: rhs2, .. } | Pred::Equal{ rhs: rhs2, .. }, + ) => self.try_cmp(rhs, rhs2, bounds).unwrap().is_le(), + ( + Pred::LessEqual{ rhs, .. }, + Pred::LessEqual{ rhs: rhs2, .. } | Pred::Equal{ rhs: rhs2, .. }, + ) => self.try_cmp(rhs, rhs2, bounds).unwrap().is_ge(), + (lhs @ (Pred::GreaterEqual{ .. } | Pred::LessEqual { .. }), Pred::And(l, r)) => + self.is_super_pred_of(lhs, l, bounds) || self.is_super_pred_of(lhs, r, bounds), + (lhs, Pred::Or(l, r)) => + self.is_super_pred_of(lhs, l, bounds) && self.is_super_pred_of(lhs, r, bounds), + (Pred::Or(l, r), rhs @ (Pred::GreaterEqual{ .. } | Pred::LessEqual{ .. })) => + self.is_super_pred_of(l, rhs, bounds) || self.is_super_pred_of(r, rhs, bounds), + (Pred::And(l, r), rhs) => + self.is_super_pred_of(l, rhs, bounds) && self.is_super_pred_of(r, rhs, bounds), + (lhs, rhs) => todo!("{lhs}/{rhs}"), + } + } + + fn is_sub_constraint_of(&self, l: &Constraint, r: &Constraint) -> bool { + match (l, r) { + // |T <: Nat| <: |T <: Int| + // |I: Nat| <: |I: Int| + (Constraint::SubtypeOf(lhs), Constraint::SubtypeOf(rhs)) + | (Constraint::TypeOf(lhs), Constraint::TypeOf(rhs)) => + self.deep_subtype_of(lhs, rhs), + (Constraint::SubtypeOf(_), Constraint::TypeOf(Type)) => true, + _ => false, + } + } + + #[inline] + fn type_of(&self, p: &TyParam, bounds: Option<&Set>) -> Type { + self.eval.get_tp_t(p, bounds, &self).unwrap() + } + + // sup/inf({±∞}) = ±∞ではあるが、Inf/NegInfにはOrdを実装しない + fn sup(&self, t: &Type) -> Option { + match t { + Int | Nat | Float => Some(TyParam::value(Inf)), + Refinement(refine) => { + let mut maybe_max = None; + for pred in refine.preds.iter() { + match pred { + Pred::LessEqual{ lhs, rhs } | + Pred::Equal{ lhs, rhs } if lhs == &refine.var => { + if let Some(max) = &maybe_max { + if self.try_cmp(rhs, &max, None).unwrap() == Greater { + maybe_max = Some(rhs.clone()); + } + } else { + maybe_max = Some(rhs.clone()); + } + }, + _ => {} + } + } + maybe_max + }, + _other => None, + } + } + + fn inf(&self, t: &Type) -> Option { + match t { + Int | Float => Some(TyParam::value(-Inf)), + Nat => Some(TyParam::value(0usize)), + Refinement(refine) => { + let mut maybe_min = None; + for pred in refine.preds.iter() { + match pred { + Predicate::GreaterEqual{ lhs, rhs } | + Predicate::Equal{ lhs, rhs } if lhs == &refine.var => { + if let Some(min) = &maybe_min { + if self.try_cmp(rhs, &min, None).unwrap() == Less { + maybe_min = Some(rhs.clone()); + } + } else { + maybe_min = Some(rhs.clone()); + } + }, + _ => {} + } + } + maybe_min + }, + _other => None, + } + } + + /// lhsとrhsが包含関係にあるとき小さいほうを返す + /// 関係なければNoneを返す + fn min<'t>(&self, lhs: &'t Type, rhs: &'t Type) -> Option<&'t Type> { + if self.deep_supertype_of(lhs, rhs) { Some(rhs) } + else if self.deep_subtype_of(lhs, rhs) { Some(rhs) } + else { None } + } + + fn cmp_t<'t>(&self, lhs: &'t Type, rhs: &'t Type) -> Ordering { + match self.min(lhs, rhs) { + Some(l) if l == lhs => Ordering::Less, + Some(_) => Ordering::Greater, + None => todo!(), + } + } + + // TODO: + fn smallest_pair>(&self, ts: I) -> Option<(Type, Type)> { + ts.min_by(|(_, lhs), (_, rhs)| self.cmp_t(lhs, rhs)) + } + + fn smallest_ref_t<'t, I: Iterator>(&self, ts: I) -> Option<&'t Type> { + ts.min_by(|lhs, rhs| self.cmp_t(lhs, rhs)) + } + + pub(crate) fn get_local(&self, name: &Token, namespace: &Str) -> TyCheckResult { + if let Some(obj) = self.consts.get(name.inspect()) { + Ok(obj.clone()) + } else { + if let Some(parent) = self.outer.as_ref() { + return parent.get_local(name, namespace) + } + Err(TyCheckError::no_var_error( + name.loc(), + namespace.clone(), + name.inspect(), + self.get_similar_name(name.inspect()), + )) + } + } + + pub(crate) fn _get_attr(&self, obj: &hir::Expr, name: &Token, namespace: &Str) -> TyCheckResult { + let self_t = obj.t(); + for table in self.get_sorted_supertype_tables(&self_t) { + if let Ok(t) = table.get_local(name, namespace) { + return Ok(t) + } + } + // TODO: dependent type widening + if let Some(parent) = self.outer.as_ref() { + parent._get_attr(obj, name, namespace) + } else { + Err(TyCheckError::no_attr_error( + name.loc(), + namespace.clone(), + &self_t, + name.inspect(), + self.get_similar_attr(&self_t, name.inspect()), + )) + } + } + + pub(crate) fn get_similar_name(&self, name: &str) -> Option<&Str> { + if name.len() <= 1 { return None } + let most_similar_name = self.impls.keys().min_by_key(|v| levenshtein(v.inspect(), name))?.inspect(); + let len = most_similar_name.len(); + if levenshtein(most_similar_name, name) >= len/2 { + let outer = self.outer.as_ref()?; + outer.get_similar_name(name) + } + else { Some(most_similar_name) } + } + + pub(crate) fn get_similar_attr<'a>(&'a self, self_t: &'a Type, name: &str) -> Option<&'a Str> { + for table in self.get_sorted_supertype_tables(self_t) { + if let Some(name) = table.get_similar_name(name) { return Some(name) } + } + None + } +} + +impl SymbolTable { + pub(crate) fn grow(&mut self, name: &str, kind: TableKind, vis: Visibility) -> TyCheckResult<()> { + let name = if vis.is_public() { + format!("{parent}.{name}", parent = self.name) + } else { format!("{parent}::{name}", parent = self.name) }; + log!("{}: current namespace: {name}", fn_name!()); + self.outer = Some(Box::new(mem::take(self))); + self.name = name.into(); + self.kind = kind; + Ok(()) + } + + pub(crate) fn pop(&mut self) -> Result<(), TyCheckErrors> { + let mut uninited_errs = TyCheckErrors::empty(); + for (name, vi) in self.decls.iter() { + uninited_errs.push(TyCheckError::uninitialized_error( + name.loc(), + self.caused_by(), + name.inspect(), + &vi.t, + )); + } + if let Some(parent) = &mut self.outer { + *self = mem::take(parent); + log!("{}: current namespace: {}", fn_name!(), self.name); + if !uninited_errs.is_empty() { Err(uninited_errs) } + else { Ok(()) } + } else { + Err(TyCheckErrors::from(TyCheckError::checker_bug(0, Location::Unknown, fn_name!(), line!()))) + } + } + + pub(crate) fn get_sorted_supertype_tables<'a>(&'a self, t: &'a Type) -> impl Iterator { + let mut tables = self._deep_get_supertype_tables(t).collect::>(); + tables.sort_by(|(lhs, _), (rhs, _)| self.cmp_t(lhs, rhs)); + tables.into_iter().map(|(_, table)| table) + } + + /// this method is for `get_sorted_supertype_tables` only + fn _deep_get_supertype_tables<'a>(&'a self, t: &'a Type) -> impl Iterator { + let i = self._get_supertype_tables(t); + if i.size_hint().1 == Some(0) { + if let Some(outer) = &self.outer { + return outer._deep_get_supertype_tables(t) + } + } + i + } + + /// this method is for `deep_get_supertype_tables` only + fn _get_supertype_tables<'a>(&'a self, t: &'a Type) -> impl Iterator { + self.types.iter() + .filter_map(|(maybe_sup, table)| { + let bounds = table.bounds(); + if self.supertype_of(maybe_sup, t, Some(&bounds)) { Some((maybe_sup, table)) } else { None } + }) + } + + fn deep_get_glue_patch_and_types(&self) -> Vec<(VarName, Type, Type)> { + if let Some(outer) = &self.outer { + [&self.glue_patch_and_types[..], &outer.deep_get_glue_patch_and_types()].concat() + } else { + self.glue_patch_and_types.clone() + } + } + + /// this method is for `get_sorted_supertype_tables` only + fn deep_get_patch(&self, name: &VarName) -> Option<&SymbolTable> { + if let Some(patch) = self.patches.get(name) { + return Some(patch) + } else { + if let Some(outer) = &self.outer { + return outer.deep_get_patch(name) + } + } + None + } + + // 再帰サブルーチン/型の推論を可能にするため、予め登録しておく + pub(crate) fn preregister(&mut self, block: &Vec) -> TyCheckResult<()> { + for expr in block.iter() { + match expr { + ast::Expr::Def(def) => { + let id = Some(def.body.id); + let eval_body_t = || { + self.eval.eval_const_block(&def.body.block, &self) + .map(|c| Type::enum_t(set![c])) + }; + match &def.sig { + ast::Signature::Subr(sig) => { + let opt_ret_t = if let Some(spec) = sig.return_t_spec.as_ref() { + Some(self.instantiate_typespec(spec, PreRegister)?) + } else { eval_body_t() }; + self.declare_sub(&sig, opt_ret_t, id)?; + } + ast::Signature::Var(sig) if sig.is_const() => { + let t = if let Some(spec) = sig.t_spec.as_ref() { + Some(self.instantiate_typespec(spec, PreRegister)?) + } else { eval_body_t() }; + self.declare_var(&sig, t, id)?; + } + _ => {} + } + }, + _ => {}, + } + } + Ok(()) + } +} diff --git a/src/compiler/tests/dependent.er b/src/compiler/tests/dependent.er new file mode 100644 index 00000000..f34baabf --- /dev/null +++ b/src/compiler/tests/dependent.er @@ -0,0 +1,4 @@ +concat|T: Type, M, N: Nat|(l: [T; M], r: [T; N]): [T; M + N] = l + r + +l: [Nat; 6] = concat [1, 2, 3], [4, 5, 6] +assert l == [1, 2, 3, 4, 5, 6] diff --git a/src/compiler/tests/fib.er b/src/compiler/tests/fib.er new file mode 100644 index 00000000..474fd6b6 --- /dev/null +++ b/src/compiler/tests/fib.er @@ -0,0 +1,6 @@ +fib 0 = 0 +fib 1 = 1 +fib(n: 2.. log "hello" +if! True, () => print! "hello" +# if True, () => print! "hello" # this should cause a type error +if True, () -> + _x = "aaa" + input!() # this should cause an effect error + print! "hello" # this should cause an effect error + +f x: Int = log x +g x: Int = print! x # this should cause an effect error diff --git a/src/compiler/varinfo.rs b/src/compiler/varinfo.rs new file mode 100644 index 00000000..bc1c7570 --- /dev/null +++ b/src/compiler/varinfo.rs @@ -0,0 +1,155 @@ +use std::fmt; + +use common::Str; +use common::ty::{Type}; +use common::traits::HasType; + +use parser::ast::DefId; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum Mutability { + Immutable, + Const, +} + +impl From<&str> for Mutability { + fn from(item: &str) -> Self { + if item.chars().next().unwrap().is_uppercase() { + Self::Const + } else { Self::Immutable } + } +} + +impl Mutability { + pub const fn is_const(&self) -> bool { matches!(self, Self::Const) } +} + +use Mutability::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum Visibility { + Private, + Public, +} + +impl Visibility { + pub const fn is_public(&self) -> bool { matches!(self, Self::Public) } + pub const fn is_private(&self) -> bool { matches!(self, Self::Private) } +} + +use Visibility::*; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ParamId { + /// 変数でないパターン + /// e.g. `[x, y]` of `f [x, y], z = ...` + PatNonDefault(usize), + /// e.g. `[x, y]` of `f [x, y] |= [0, 1] = ...` + PatWithDefault(usize), + /// 変数パターン + /// e.g. `z` of `f [x, y], z = ...` + VarNonDefault{ keyword: Str, pos: usize }, + /// e.g. `z` of `f [x, y], z |= 0 = ...` + VarWithDefault{ keyword: Str, pos: usize }, + /// パターンに埋め込まれた変数パターン + /// この場合デフォルト値はない + /// e.g. `x` or `y` of `f [x, y], z = ...` + Embedded(Str), +} + +impl ParamId { + pub const fn var_default(keyword: Str, pos: usize) -> Self { Self::VarWithDefault{ keyword, pos } } + pub const fn var_non_default(keyword: Str, pos: usize) -> Self { Self::VarNonDefault{ keyword, pos } } + + pub const fn pos(&self) -> Option { + match self { + Self::PatNonDefault(pos) + | Self::PatWithDefault(pos) + | Self::VarNonDefault{ pos, .. } + | Self::VarWithDefault{ pos, .. } => Some(*pos), + _ => None, + } + } + + pub const fn has_default(&self) -> bool { + matches!(self, Self::PatWithDefault(_) | Self::VarWithDefault{ .. }) + } + + pub const fn is_embedded(&self) -> bool { matches!(self, Self::Embedded(_)) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum VarKind { + Defined(DefId), + Declared, + Parameter{ def_id: DefId, param_id: ParamId }, + Generated, + DoesNotExist, + Builtin, +} + +impl VarKind { + pub const fn parameter(def_id: DefId, param_id: ParamId) -> Self { + Self::Parameter{ def_id, param_id } + } + + pub const fn pos_as_param(&self) -> Option { + match self { + Self::Parameter{ param_id, .. } => param_id.pos(), + _ => None, + } + } + + pub const fn has_default(&self) -> bool { + match self { + Self::Parameter{ param_id, .. } => param_id.has_default(), + _ => false, + } + } + + pub const fn is_parameter(&self) -> bool { + matches!(self, Self::Parameter{ .. }) + } + + pub const fn is_embedded_param(&self) -> bool { + matches!(self, Self::Parameter{ param_id, .. } if param_id.is_embedded()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct VarInfo { + pub t: Type, + pub muty: Mutability, + pub vis: Visibility, + pub kind: VarKind, +} + +impl fmt::Display for VarInfo { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VarInfo{{t: {}, muty: {:?}, vis: {:?} kind: {:?}}}", self.t, self.muty, self.vis, self.kind) + } +} + +impl HasType for VarInfo { + #[inline] + fn ref_t(&self) -> &Type { &self.t } + #[inline] + fn signature_t(&self) -> Option<&Type> { None } +} + +impl VarInfo { + pub const ILLEGAL: &'static Self = &VarInfo::new(Type::Failure, Immutable, Private, VarKind::DoesNotExist); + + pub const fn new(t: Type, muty: Mutability, vis: Visibility, kind: VarKind) -> Self { + Self { t, muty, vis, kind } + } + + pub fn same_id_as(&self, id: DefId) -> bool { + match self.kind { + VarKind::Defined(i) | VarKind::Parameter{ def_id: i, .. } => id == i, + _ => false, + } + } +} diff --git a/src/dummy.rs b/src/dummy.rs new file mode 100644 index 00000000..4e144603 --- /dev/null +++ b/src/dummy.rs @@ -0,0 +1,40 @@ +use common::config::{ErgConfig, Input, SEMVER, BUILD_INFO}; +use common::python_util::eval_pyc; +use common::str::Str; +use common::traits::Runnable; + +use compiler::Compiler; +use compiler::error::{CompileError, CompileErrors}; + +#[derive(Debug)] +pub struct DummyVM { + cfg: ErgConfig, + compiler: Compiler, +} + +impl Runnable for DummyVM { + type Err = CompileError; + type Errs = CompileErrors; + + fn new(cfg: ErgConfig) -> Self { + Self { + compiler: Compiler::new(cfg.copy()), + cfg, + } + } + + #[inline] + fn input(&self) -> &Input { &self.cfg.input } + + #[inline] + fn start_message(&self) -> String { format!("Erg interpreter {} {}\n", SEMVER, &*BUILD_INFO) } + + fn clear(&mut self) { + self.compiler.clear(); + } + + fn eval(&mut self, src: Str) -> Result { + self.compiler.compile_and_dump_as_pyc(src, "o.pyc")?; + Ok(eval_pyc("o.pyc")) + } +} diff --git a/src/e2wasm/Cargo.toml b/src/e2wasm/Cargo.toml new file mode 100644 index 00000000..82ce29ed --- /dev/null +++ b/src/e2wasm/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "e2wasm" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/e2wasm/README.md b/src/e2wasm/README.md new file mode 100644 index 00000000..07ceb6e0 --- /dev/null +++ b/src/e2wasm/README.md @@ -0,0 +1,3 @@ +# The Erg-to-WebAssembly Converter (e2wasm) + +`e2wasm` converts the extended Python bytecode output of `cm` into the WebAssembly binary format. diff --git a/src/e2wasm/main.rs b/src/e2wasm/main.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/e2wasm/opcode.rs b/src/e2wasm/opcode.rs new file mode 100644 index 00000000..186d7c91 --- /dev/null +++ b/src/e2wasm/opcode.rs @@ -0,0 +1,183 @@ +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq)] +#[repr(u8)] +pub enum Opcode { + Unreachable = 0x0, + Nop = 0x1, + Block = 0x2, + Loop = 0x3, + If = 0x4, + Else = 0x5, + Try = 0x6, + Catch = 0x7, + Throw = 0x8, + Rethrow = 0x9, + BrOnExn = 0xA, + End = 0xB, + Br = 0xC, + BrIf = 0xD, + BrTable = 0xE, + Return = 0xF, + Call = 0x10, + CallIndirect = 0x11, + ReturnCall = 0x12, + ReturnCallIndirect = 0x13, + Drop = 0x1A, + Select = 0x1B, + LocalGet = 0x20, + LocalSet = 0x21, + LocalTee = 0x22, + GlobalGet = 0x23, + GlobalSet = 0x24, + I32Load = 0x28, + I64Load = 0x29, + F32Load = 0x2A, + F64Load = 0x2B, + I32Load8S = 0x2C, + I32Load8U = 0x2D, + I32Load16S = 0x2E, + I32Load16U = 0x2F, + I64Load8S = 0x30, + I64Load8U = 0x31, + I64Load16S = 0x32, + I64Load16U = 0x33, + I64Load32S = 0x34, + I64Load32U = 0x35, + I32Store = 0x36, + I64Store = 0x37, + F32Store = 0x38, + F64Store = 0x39, + I32Store8 = 0x3A, + I32Store16 = 0x3B, + I64Store8 = 0x3C, + I64Store16 = 0x3D, + I64Store32 = 0x3E, + MemorySize = 0x3F, + MemoryGrow = 0x40, + I32Const = 0x41, + I64Const = 0x42, + F32Const = 0x43, + F64Const = 0x44, + I32Eqz = 0x45, + I32Eq = 0x46, + I32Ne = 0x47, + I32LtS = 0x48, + I32LtU = 0x49, + I32GtS = 0x4A, + I32GtU = 0x4B, + I32LeS = 0x4C, + I32LeU = 0x4D, + I32GeS = 0x4E, + I32GeU = 0x4F, + I64Eqz = 0x50, + I64Eq = 0x51, + I64Ne = 0x52, + I64LtS = 0x53, + I64LtU = 0x54, + I64GtS = 0x55, + I64GtU = 0x56, + I64LeS = 0x57, + I64LeU = 0x58, + I64GeS = 0x59, + I64GeU = 0x5A, + F32Eq = 0x5B, + F32Ne = 0x5C, + F32Lt = 0x5D, + F32Gt = 0x5E, + F32Le = 0x5F, + F32Ge = 0x60, + F64Eq = 0x61, + F64Ne = 0x62, + F64Lt = 0x63, + F64Gt = 0x64, + F64Le = 0x65, + F64Ge = 0x66, + I32Clz = 0x67, + I32Ctz = 0x68, + I32Popcnt = 0x69, + I32Add = 0x6A, + I32Sub = 0x6B, + I32Mul = 0x6C, + I32DivS = 0x6D, + I32DivU = 0x6E, + I32RemS = 0x6F, + I32RemU= 0x70, + I32And = 0x71, + I32Or = 0x72, + I32Xor = 0x73, + I32Shl = 0x74, + I32ShrS = 0x75, + I32ShrU = 0x76, + I32Rotl = 0x77, + I32Rotr = 0x78, + I64Clz = 0x79, + I64Ctz = 0x7A, + I64Popcnt = 0x7B, + I64Add = 0x7C, + I64Sub = 0x7D, + I64Mul = 0x7E, + I64DivS = 0x7F, + I64DivU = 0x80, + I64RemS = 0x81, + I64RemU = 0x82, + I64And = 0x83, + I64Or = 0x84, + I64Xor = 0x85, + I64Shl = 0x86, + I64ShrS = 0x87, + I64ShrU = 0x88, + I64Rotl = 0x89, + I64Rotr = 0x8A, + F32Abs = 0x8B, + F32Neg = 0x8C, + F32Ceil = 0x8D, + F32Floor = 0x8E, + F32Trunc = 0x8F, + F32Nearest = 0x90, + F32Sqrt = 0x91, + F32Add = 0x92, + F32Sub = 0x93, + F32Mul = 0x94, + F32Div = 0x95, + F32Min = 0x96, + F32Max = 0x97, + F32Copysign = 0x98, + F64Abs = 0x99, + F64Neg = 0x9A, + F64Ceil = 0x9B, + F64Floor = 0x9C, + F64Trunc = 0x9D, + F64Nearest = 0x9E, + F64Sqrt = 0x9F, + F64Add = 0xA0, + F64Sub = 0xA1, + F64Mul = 0xA2, + F64Div = 0xA3, + F64Min = 0xA4, + F64Max = 0xA5, + F64Copysign = 0xA6, + I32WrapI64 = 0xA7, + I32TruncF32S = 0xA8, + I32TruncF32U = 0xA9, + I32TruncF64S = 0xAA, + I32TruncF64U = 0xAB, + I64ExtendI32S = 0xAC, + I64ExtendI32U = 0xAD, + I64TruncF32S = 0xAE, + I64TruncF32U = 0xAF, + I64TruncF64S = 0xB0, + I64TruncF64U = 0xB1, + F32ConvertI32S = 0xB2, + F32ConvertI32U = 0xB3, + F32ConvertI64S = 0xB4, + F32ConvertI64U = 0xB5, + F32DemoteF64 = 0xB6, + F64ConvertI32S = 0xB7, + F64ConvertI32U = 0xB8, + F64ConvertI64S = 0xB9, + F64ConvertI64U = 0xBA, + F64PromoteF32 = 0xBB, + I32ReinterpretF32 = 0xBC, + I64ReinterpretF64 = 0xBD, + F32ReinterpretI32 = 0xBE, + F64ReinterpretI64 = 0xBF, +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..85d4f28e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +extern crate common; +extern crate compiler; +pub mod dummy; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..da93b65c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,31 @@ +extern crate compiler; +extern crate parser; +extern crate erg; + +use std::process; + +use common::config::{ErgConfig}; +use common::deserialize::Deserializer; +use common::traits::Runnable; + +use parser::lex::LexerRunner; +use parser::ParserRunner; + +use compiler::Compiler; + +use erg::dummy::DummyVM; + +fn main() { + let cfg = ErgConfig::parse(); + match cfg.mode { + "lex" => { LexerRunner::run(cfg); } + "parse" => { ParserRunner::run(cfg); } + "compile" => { Compiler::run(cfg); } + "exec" => { DummyVM::run(cfg); } + "read" => { Deserializer::run(cfg); } + other => { + eprintln!("invalid mode: {other}"); + process::exit(1); + } + } +} diff --git a/src/packagemanager/README.md b/src/packagemanager/README.md new file mode 100644 index 00000000..beb17514 --- /dev/null +++ b/src/packagemanager/README.md @@ -0,0 +1 @@ +# The Erg package manager (codename: Mol) diff --git a/src/std/prelude.er b/src/std/prelude.er new file mode 100644 index 00000000..8ea6c05a --- /dev/null +++ b/src/std/prelude.er @@ -0,0 +1,57 @@ +@Attach NeImpl +Eq(R |= Self()) = Trait { + .`==` = Self(R).(R) -> Bool +} + +NeImpl R = Patch Eq R +NeImpl(R).`!=`(self, other: R): Bool = not(self == other) + +# Should this return `Ordering`? +@Attach EqImpl, GeImpl, GtImpl, LtImpl +Ord(R |= Self()) = Trait { + .`<=` = Self(R).(R) -> Bool +} + +EqImpl R = Patch Ord(R), Impl: Eq() +EqImpl(R).`==`(self, other: R): Bool = self <= other and other <= self + +GeImpl = Patch Ord() +GeImpl.`>=`(self, other: Self): Bool = other <= self +GtImpl = Patch Ord() +GtImpl.`>`(self, other: Self): Bool = other < self +LtImpl = Patch Ord() +LtImpl.`<`(self, other: Self): Bool = self <= other and self != other + +Add(R |= Self(), O |= Self()) = Trait { + .`_+_` = Self(R, O).(R) -> O +} +Sub(R |= Self(), O |= Self()) = Trait { + .`_-_` = Self(R, O).(R) -> O +} +Mul(R |= Self(), O |= Self()) = Trait { + .`*` = Self(R, O).(R) -> O +} +Div(R |= Self(), O |= Self()) = Trait { + .`/` = Self(R, O).(R) -> O or Panic +} +Num: (R |= Type, O |= Type) -> Type +Num = Add and Sub and Mul + +Seq T = Trait { + .__len__ = Ref(Self(T)).() -> Nat + .get = Ref(Self(T)).(Nat) -> T +} + +`_+_`: |R, O: Type, A <: Add(R, O)| (A, R) -> O +`_-_`: |R, O: Type, S <: Add(R, O)| (S, R) -> O +`*`: |R, O: Type, M <: Add(R, O)| (M, R) -> O +`/`: |R, O: Type, D <: Add(R, O)| (D, R) -> O + +AddForInt = Patch Int, Impl: Add() +AddForInt.`_+_`: (self: Self, other: Int) -> Int + +# TODO: Mul and Div +NumForInterval M, N, O, P: Int = + Patch M..N, Impl: Add(R: O..P, O: M+O..N+P) and Sub(R: O..P, O: M-P..N-O) +NumForInterval(M, N, O, P).`_+_`: (self: Self, other: O..P) -> M+O..N+P +NumForInterval(M, N, O, P).`_-_`: (self: Self, other: O..P) -> M-P..N-O diff --git a/tests/add.er b/tests/add.er new file mode 100644 index 00000000..d939d349 --- /dev/null +++ b/tests/add.er @@ -0,0 +1,5 @@ +add x, y = + x + y + +print! 1 + 1 +print! 1.0 + 1 \ No newline at end of file diff --git a/tests/fizzbuzz.er b/tests/fizzbuzz.er new file mode 100644 index 00000000..2368ddba --- /dev/null +++ b/tests/fizzbuzz.er @@ -0,0 +1,6 @@ +for! 1..100, i => + match (i % 3, i % 5): + 0, 0 => print! "FizzBuzz" + 0, _ => print! "Fizz" + _, 0 => print! "Buzz" + _ => print! i diff --git a/tests/mut_array.er b/tests/mut_array.er new file mode 100644 index 00000000..4e637175 --- /dev/null +++ b/tests/mut_array.er @@ -0,0 +1,5 @@ +v = ![] +for! 0..10, i => + v.push! i + +log v.sum() diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 00000000..cd986cc4 --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,11 @@ +extern crate erg; + +mod tests { + /* + use common::config::{ErgConfig, Input}; + use common::error::MultiErrorFmt; + use common::traits::Runnable; + + const FILE3: &str = "tests/test3_object_system.er"; + */ +}

+ Previous | Next +