koatl 0.3.15__tar.gz → 0.3.17__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {koatl-0.3.15 → koatl-0.3.17}/Cargo.lock +1 -1
- {koatl-0.3.15 → koatl-0.3.17}/PKG-INFO +1 -1
- {koatl-0.3.15 → koatl-0.3.17}/koatl/Cargo.toml +1 -1
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/prelude/__init__.tl +5 -4
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/re.tl +1 -1
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/coal.tl +2 -2
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/try.tl +2 -2
- koatl-0.3.17/koatl/tests/test_re.tl +204 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/util/__init__.py +6 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/ast.rs +1 -4
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/ast_builder.rs +0 -4
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/inference.rs +2 -3
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/lift_cst.rs +11 -5
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/resolve_scopes.rs +4 -11
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/transform.rs +18 -16
- {koatl-0.3.15 → koatl-0.3.17}/koatl-parser/src/cst.rs +2 -3
- {koatl-0.3.15 → koatl-0.3.17}/koatl-parser/src/parser.rs +6 -6
- {koatl-0.3.15 → koatl-0.3.17}/koatl-parser/src/simple_fmt.rs +1 -1
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/prelude/__init__.tl +5 -4
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/re.tl +1 -1
- {koatl-0.3.15 → koatl-0.3.17}/Cargo.toml +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/README.md +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/.gitignore +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/LICENSE +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/README.md +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/cli.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/runtime/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/runtime/record.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/runtime/vattr.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/control/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/control/async.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/control/base.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/control/do.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/control/env.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/control/memo.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/control/result.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/data/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/data/list.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/data/record.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/ext.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/io.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/iter.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/json.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/lazy_module.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/pickle.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/python/koatl/std/trait.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/requirements.txt +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/src/emit_py.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/src/lib.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/conftest.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/arguments.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/data.txt +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/imports2.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/loops.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/match.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/scopes.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/short_circuit.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/base/with.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/env.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/iterables.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/list.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/memo.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/record.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/result.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/slice.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/test_modules/module0.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/test_modules/module1.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/e2e/test_modules/module2.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/fail-parse/arguments_mixed_defaults.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/fail-parse/arguments_multiple_kwargs.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/fail-parse/arguments_multiple_kwonly_markers.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/fail-parse/arguments_multiple_posonly_markers.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/fail-parse/arguments_multiple_varargs.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/fail-parse/arguments_posonly_after_kwonly.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/fail-parse/arguments_vararg_and_kwonly_marker.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/fail-parse/unmatched_closing_delimiter.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/parse/block-comments.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/parse/deco.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/parse/mutual-recursion.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/parse/numbers.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/parse/operators.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/test_e2e.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/test_iterable.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/test_parse.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl/tests/test_parse_fail.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/Cargo.toml +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/lib.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/main.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/py/ast.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/py/ast_builder.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/py/emit.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/types.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-core/src/util.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-parser/Cargo.toml +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-parser/src/lexer.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/koatl-parser/src/lib.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/pyproject.toml +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/__main__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/cli.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/runtime/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/runtime/record.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/runtime/vattr.py +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/control/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/control/async.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/control/base.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/control/do.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/control/env.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/control/memo.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/control/result.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/data/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/data/list.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/data/record.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/ext.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/io.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/iter.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/json.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/lazy_module.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/pickle.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.17}/python/koatl/std/trait.py +0 -0
|
@@ -14,15 +14,14 @@ import koatl.std.(
|
|
|
14
14
|
export std = LazyModule("koatl.std")
|
|
15
15
|
export mod = RootModulesProxy()
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Extension.property(list, "len")! List.len.fget
|
|
17
|
+
# Extensions
|
|
20
18
|
|
|
21
|
-
Extension.
|
|
19
|
+
Extension.trait(Iterable)
|
|
22
20
|
|
|
23
21
|
Extension.method(str, "match")! (regex, str) => Pattern(regex).match(str)
|
|
24
22
|
Extension.method(str, "matches")! (str, regex) => Pattern(regex).match(str)
|
|
25
23
|
|
|
24
|
+
Extension.property(list, "len")! List.len.fget
|
|
26
25
|
Extension.method(list, "map")! (self, f) => self.iter.map(f).list()
|
|
27
26
|
Extension.method(list, "filter")! (self, f) => self.iter.filter(f).list()
|
|
28
27
|
|
|
@@ -33,6 +32,8 @@ Extension.method(dict, "filter")! (self, f) => self.iter.filter(f).dict()
|
|
|
33
32
|
Extension.method(dict, "filter_keys")! (self, f) => self.iter.filter([k, v] => f(k)).dict()
|
|
34
33
|
Extension.method(dict, "filter_values")! (self, f) => self.iter.filter([k, v] => f(v)).dict()
|
|
35
34
|
|
|
35
|
+
# Implementations for language features
|
|
36
|
+
|
|
36
37
|
__tl__.do = koatl.std.control.do.do
|
|
37
38
|
|
|
38
39
|
__tl__.op_map = koatl.std.control.result.op_map
|
|
@@ -20,7 +20,7 @@ export Pattern = class:
|
|
|
20
20
|
if not isinstance(string, str):
|
|
21
21
|
raise TypeError(f"Expected a string, but got {type(string).__name__}")
|
|
22
22
|
|
|
23
|
-
self.pattern.match(string, flags).
|
|
23
|
+
self.pattern.match(string, flags).(Result).map(Match)
|
|
24
24
|
|
|
25
25
|
export Match = collections.abc.Sequence.register! class:
|
|
26
26
|
__slots__ = ("_match",)
|
|
@@ -4,7 +4,7 @@ none = None
|
|
|
4
4
|
assert_eq(none?(1)?(2), None)
|
|
5
5
|
assert_eq(none?[1]?.a, None)
|
|
6
6
|
assert_eq(none?.a, None)
|
|
7
|
-
assert_eq(none?.
|
|
7
|
+
assert_eq(none?.(a), None)
|
|
8
8
|
|
|
9
9
|
obj = (class:
|
|
10
10
|
a = 1
|
|
@@ -13,7 +13,7 @@ obj = (class:
|
|
|
13
13
|
assert_eq((x => x)?(1), 1)
|
|
14
14
|
assert_eq([1]?[0], 1)
|
|
15
15
|
assert_eq(obj?.a, 1)
|
|
16
|
-
assert_eq(1?.
|
|
16
|
+
assert_eq(1?.($ + 1), 2)
|
|
17
17
|
|
|
18
18
|
assert_eq(None ?? 1, 1)
|
|
19
19
|
assert_eq(int(5) ?? 1, 5)
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import util.(assert_eq, fail)
|
|
2
|
+
import koatl.std.re.(Pattern, Match)
|
|
3
|
+
import koatl.std.control.(Ok, Err)
|
|
4
|
+
|
|
5
|
+
# Basic pattern compilation and matching
|
|
6
|
+
test_pattern_from_string = () =>
|
|
7
|
+
let pat = Pattern("hello")
|
|
8
|
+
assert_eq(pat.pattern.pattern, "hello")
|
|
9
|
+
|
|
10
|
+
test_pattern_from_pattern = () =>
|
|
11
|
+
import re
|
|
12
|
+
let py_pat = re.compile("test")
|
|
13
|
+
let pat = Pattern(py_pat)
|
|
14
|
+
assert_eq(pat.pattern.pattern, "test")
|
|
15
|
+
|
|
16
|
+
test_pattern_from_pattern_copy = () =>
|
|
17
|
+
let pat1 = Pattern("copy")
|
|
18
|
+
let pat2 = Pattern(pat1)
|
|
19
|
+
assert_eq(pat2.pattern.pattern, "copy")
|
|
20
|
+
|
|
21
|
+
# Pattern.match() tests
|
|
22
|
+
test_pattern_match_success = () =>
|
|
23
|
+
let pat = Pattern("\\d+")
|
|
24
|
+
let result = pat.match("123abc")
|
|
25
|
+
assert_eq(result.ok, True)
|
|
26
|
+
|
|
27
|
+
test_pattern_match_failure = () =>
|
|
28
|
+
let pat = Pattern("\\d+")
|
|
29
|
+
let result = pat.match("abc")
|
|
30
|
+
assert_eq(result.ok, False)
|
|
31
|
+
|
|
32
|
+
test_pattern_match_groups = () =>
|
|
33
|
+
let pat = Pattern("(\\d+)\\.(\\d+)")
|
|
34
|
+
if pat.match("123.456") matches Ok(m):
|
|
35
|
+
assert_eq(list(m), ["123", "456"])
|
|
36
|
+
assert_eq(m.match, "123.456")
|
|
37
|
+
|
|
38
|
+
test_pattern_match_no_groups = () =>
|
|
39
|
+
let pat = Pattern("hello")
|
|
40
|
+
if pat.match("hello world") matches Ok(m):
|
|
41
|
+
assert_eq(list(m), [])
|
|
42
|
+
assert_eq(m.match, "hello")
|
|
43
|
+
|
|
44
|
+
# String extension method tests
|
|
45
|
+
test_str_match_success = () =>
|
|
46
|
+
let result = "\\w+".match("hello")
|
|
47
|
+
assert_eq(result.ok, True)
|
|
48
|
+
if result matches Ok(m):
|
|
49
|
+
assert_eq(m.match, "hello")
|
|
50
|
+
|
|
51
|
+
test_str_match_failure = () =>
|
|
52
|
+
let result = "[a-z]+".match("123")
|
|
53
|
+
assert_eq(result.ok, False)
|
|
54
|
+
|
|
55
|
+
test_str_match_with_groups = () =>
|
|
56
|
+
let result = "(\\d{3})-(\\d{4})".match("123-4567")
|
|
57
|
+
if result matches Ok(m):
|
|
58
|
+
assert_eq(m.match, "123-4567")
|
|
59
|
+
assert_eq(m[1], "123")
|
|
60
|
+
assert_eq(m[2], "4567")
|
|
61
|
+
assert_eq(list(m), ["123", "4567"])
|
|
62
|
+
|
|
63
|
+
test_str_matches_alias = () =>
|
|
64
|
+
# matches is an alias for match
|
|
65
|
+
let result1 = "\\w+".match("test")
|
|
66
|
+
let result2 = "test".matches("\\w+")
|
|
67
|
+
assert_eq(result1.ok, True)
|
|
68
|
+
assert_eq(result2.ok, True)
|
|
69
|
+
|
|
70
|
+
# Match object tests
|
|
71
|
+
test_match_sequence_behavior = () =>
|
|
72
|
+
let pat = Pattern("(\\w+)@(\\w+)\\.(\\w+)")
|
|
73
|
+
if pat.match("user@example.com") matches Ok(m):
|
|
74
|
+
assert_eq(len(m), 3)
|
|
75
|
+
assert_eq(m[1], "user")
|
|
76
|
+
assert_eq(m[2], "example")
|
|
77
|
+
assert_eq(m[3], "com")
|
|
78
|
+
|
|
79
|
+
test_match_iteration = () =>
|
|
80
|
+
let pat = Pattern("(\\d+),(\\d+),(\\d+)")
|
|
81
|
+
if pat.match("10,20,30") matches Ok(m):
|
|
82
|
+
let groups = []
|
|
83
|
+
for group in m:
|
|
84
|
+
groups.append(group)
|
|
85
|
+
assert_eq(groups, ["10", "20", "30"])
|
|
86
|
+
|
|
87
|
+
test_match_full_match_property = () =>
|
|
88
|
+
let pat = Pattern("hello (\\w+)")
|
|
89
|
+
if pat.match("hello world!") matches Ok(m):
|
|
90
|
+
assert_eq(m.match, "hello world")
|
|
91
|
+
assert_eq(m[1], "world")
|
|
92
|
+
|
|
93
|
+
# Complex regex patterns
|
|
94
|
+
test_email_pattern = () =>
|
|
95
|
+
let email_pat = Pattern("([\\w.-]+)@([\\w.-]+)\\.([a-z]{2,})")
|
|
96
|
+
if email_pat.match("user.name@example.co.uk") matches Ok(m):
|
|
97
|
+
assert_eq(m[1], "user.name")
|
|
98
|
+
assert_eq(m[2], "example.co")
|
|
99
|
+
assert_eq(m[3], "uk")
|
|
100
|
+
|
|
101
|
+
test_url_pattern = () =>
|
|
102
|
+
let url_pat = Pattern("https?://([\\w.-]+)(?:/([\\w/.-]*))?")
|
|
103
|
+
if url_pat.match("https://example.com/path/to/page") matches Ok(m):
|
|
104
|
+
assert_eq(m[1], "example.com")
|
|
105
|
+
assert_eq(m[2], "path/to/page")
|
|
106
|
+
|
|
107
|
+
test_phone_pattern = () =>
|
|
108
|
+
let phone_pat = Pattern("\\(?(\\d{3})\\)?[-.]?(\\d{3})[-.]?(\\d{4})")
|
|
109
|
+
if phone_pat.match("(555) 123-4567") matches Ok(m):
|
|
110
|
+
assert_eq(list(m), ["555", "123", "4567"])
|
|
111
|
+
|
|
112
|
+
# Edge cases
|
|
113
|
+
test_empty_pattern = () =>
|
|
114
|
+
let pat = Pattern("")
|
|
115
|
+
let result = pat.match("anything")
|
|
116
|
+
assert_eq(result.ok, True)
|
|
117
|
+
if result matches Ok(m):
|
|
118
|
+
assert_eq(m.match, "")
|
|
119
|
+
assert_eq(list(m), [])
|
|
120
|
+
|
|
121
|
+
test_match_at_start_only = () =>
|
|
122
|
+
let pat = Pattern("\\d+")
|
|
123
|
+
# match() only matches at the beginning of the string
|
|
124
|
+
assert_eq(pat.match("123abc").ok, True)
|
|
125
|
+
assert_eq(pat.match("abc123").ok, False)
|
|
126
|
+
|
|
127
|
+
test_special_characters = () =>
|
|
128
|
+
let pat = Pattern("\\$\\d+\\.\\d{2}")
|
|
129
|
+
if pat.match("$19.99 total") matches Ok(m):
|
|
130
|
+
assert_eq(m.match, "$19.99")
|
|
131
|
+
|
|
132
|
+
test_unicode_pattern = () =>
|
|
133
|
+
let pat = Pattern("[α-ω]+")
|
|
134
|
+
if pat.match("αβγδε") matches Ok(m):
|
|
135
|
+
assert_eq(m.match, "αβγδε")
|
|
136
|
+
|
|
137
|
+
# Named groups aren't directly exposed in Match, but the full match object is accessible
|
|
138
|
+
test_multiple_groups = () =>
|
|
139
|
+
let pat = Pattern("(\\d{4})-(\\d{2})-(\\d{2})")
|
|
140
|
+
if pat.match("2025-12-13") matches Ok(m):
|
|
141
|
+
assert_eq(m[1], "2025")
|
|
142
|
+
assert_eq(m[2], "12")
|
|
143
|
+
assert_eq(m[3], "13")
|
|
144
|
+
assert_eq(len(m), 3)
|
|
145
|
+
|
|
146
|
+
# Result monad integration
|
|
147
|
+
test_result_map = () =>
|
|
148
|
+
let result = "(\\d+)".match("123").map(m => m[1])
|
|
149
|
+
if result matches Ok(value):
|
|
150
|
+
assert_eq(value, "123")
|
|
151
|
+
|
|
152
|
+
test_result_chaining = () =>
|
|
153
|
+
let result = "(\\d+)\\.(\\d+)".match("3.14")
|
|
154
|
+
.map(m => [m[1], m[2]])
|
|
155
|
+
.map([a, b] => [int(a), int(b)])
|
|
156
|
+
|
|
157
|
+
if result matches Ok([major, minor]):
|
|
158
|
+
assert_eq(major, 3)
|
|
159
|
+
assert_eq(minor, 14)
|
|
160
|
+
|
|
161
|
+
test_result_or_else = () =>
|
|
162
|
+
let result = "\\d+".match("abc")
|
|
163
|
+
if result matches Err(_):
|
|
164
|
+
assert_eq(True, True)
|
|
165
|
+
else:
|
|
166
|
+
fail()
|
|
167
|
+
|
|
168
|
+
# Pattern matching with matches expression
|
|
169
|
+
test_matches_expression_success = () =>
|
|
170
|
+
if "(\\d+)".match("123") matches Ok(m):
|
|
171
|
+
assert_eq(m[1], "123")
|
|
172
|
+
else:
|
|
173
|
+
fail()
|
|
174
|
+
|
|
175
|
+
test_matches_expression_failure = () =>
|
|
176
|
+
if "(\\d+)".match("abc") matches Ok(_):
|
|
177
|
+
fail()
|
|
178
|
+
else:
|
|
179
|
+
assert_eq(True, True)
|
|
180
|
+
|
|
181
|
+
# Practical examples
|
|
182
|
+
test_extract_version_numbers = () =>
|
|
183
|
+
let version_pat = Pattern("v?(\\d+)\\.(\\d+)\\.(\\d+)")
|
|
184
|
+
if version_pat.match("v1.2.3") matches Ok(m):
|
|
185
|
+
let [major, minor, patch] = list(m)
|
|
186
|
+
assert_eq(major, "1")
|
|
187
|
+
assert_eq(minor, "2")
|
|
188
|
+
assert_eq(patch, "3")
|
|
189
|
+
|
|
190
|
+
test_parse_csv_line = () =>
|
|
191
|
+
let csv_pat = Pattern("([^,]+),([^,]+),([^,]+)")
|
|
192
|
+
if csv_pat.match("John,Doe,30") matches Ok(m):
|
|
193
|
+
assert_eq(list(m), ["John", "Doe", "30"])
|
|
194
|
+
|
|
195
|
+
test_validate_hex_color = () =>
|
|
196
|
+
let hex_pat = Pattern("#([0-9a-fA-F]{6})")
|
|
197
|
+
assert_eq(hex_pat.match("#FF5733").ok, True)
|
|
198
|
+
assert_eq(hex_pat.match("#GG5733").ok, False)
|
|
199
|
+
assert_eq(hex_pat.match("FF5733").ok, False)
|
|
200
|
+
|
|
201
|
+
test_extract_quoted_string = () =>
|
|
202
|
+
let quote_pat = Pattern('"([^"]*)"')
|
|
203
|
+
if quote_pat.match('"hello world"') matches Ok(m):
|
|
204
|
+
assert_eq(m[1], "hello world")
|
|
@@ -3,3 +3,9 @@ def assert_eq(received, expected):
|
|
|
3
3
|
|
|
4
4
|
def assert_contains(haystack, needle):
|
|
5
5
|
assert needle in haystack, f"Expected to find {needle} in {haystack}"
|
|
6
|
+
|
|
7
|
+
def assert_true(value, message=None):
|
|
8
|
+
assert value is True, message or f"Expected True, but got {value}"
|
|
9
|
+
|
|
10
|
+
def fail(message):
|
|
11
|
+
raise AssertionError(message)
|
|
@@ -186,16 +186,15 @@ pub enum Expr<'a, TTree: Tree> {
|
|
|
186
186
|
Call(TTree::Expr, Vec<CallItem<'a, TTree>>),
|
|
187
187
|
Subscript(TTree::Expr, Vec<ListItem<TTree>>),
|
|
188
188
|
RawAttribute(TTree::Expr, SIdent<'a>),
|
|
189
|
-
ScopedAttribute(TTree::Expr, TTree::Expr),
|
|
190
189
|
Attribute(TTree::Expr, SIdent<'a>),
|
|
191
190
|
MaybeAttribute(TTree::Expr, SIdent<'a>),
|
|
192
191
|
|
|
193
192
|
MappedCall(TTree::Expr, Vec<CallItem<'a, TTree>>),
|
|
194
193
|
MappedSubscript(TTree::Expr, Vec<ListItem<TTree>>),
|
|
195
194
|
MappedRawAttribute(TTree::Expr, SIdent<'a>),
|
|
196
|
-
MappedScopedAttribute(TTree::Expr, TTree::Expr),
|
|
197
195
|
MappedAttribute(TTree::Expr, SIdent<'a>),
|
|
198
196
|
MappedMaybeAttribute(TTree::Expr, SIdent<'a>),
|
|
197
|
+
CallNullable(TTree::Expr, TTree::Expr),
|
|
199
198
|
|
|
200
199
|
Try(TTree::Expr, Vec<MatchCase<TTree>>, Option<TTree::Expr>),
|
|
201
200
|
Checked(TTree::Expr, Option<TTree::Pattern>),
|
|
@@ -205,8 +204,6 @@ pub enum Expr<'a, TTree: Tree> {
|
|
|
205
204
|
Fn(Vec<ArgDefItem<'a, TTree>>, TTree::Expr),
|
|
206
205
|
Fstr(Spanned<String>, Vec<(FmtExpr<TTree>, Spanned<String>)>),
|
|
207
206
|
|
|
208
|
-
// these are removed during desugaring
|
|
209
|
-
Decorated(TTree::Expr, TTree::Expr),
|
|
210
207
|
Placeholder,
|
|
211
208
|
}
|
|
212
209
|
|
|
@@ -162,10 +162,6 @@ impl AstBuilder {
|
|
|
162
162
|
Expr::RawAttribute(value.indirect(), attr.into().spanned(self.span)).spanned(self.span)
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
pub fn then<'src>(&self, left: SExpr<'src>, right: SExpr<'src>) -> SExpr<'src> {
|
|
166
|
-
Expr::ScopedAttribute(left.indirect(), right.indirect()).spanned(self.span)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
165
|
pub fn function<'src>(&self, args: Vec<SArgDefItem<'src>>, body: SExpr<'src>) -> SExpr<'src> {
|
|
170
166
|
Expr::Fn(args, body.indirect()).spanned(self.span)
|
|
171
167
|
}
|
|
@@ -235,9 +235,9 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for Indirect<SExpr<'src>> {
|
|
|
235
235
|
expr.traverse(ctx);
|
|
236
236
|
Type::Any
|
|
237
237
|
}
|
|
238
|
-
Expr::
|
|
238
|
+
Expr::CallNullable(expr, func) => {
|
|
239
239
|
expr.traverse(ctx);
|
|
240
|
-
|
|
240
|
+
func.traverse(ctx);
|
|
241
241
|
Type::Any
|
|
242
242
|
}
|
|
243
243
|
Expr::Checked(expr, _pattern) => {
|
|
@@ -265,7 +265,6 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for Indirect<SExpr<'src>> {
|
|
|
265
265
|
Type::Any
|
|
266
266
|
}
|
|
267
267
|
Expr::Literal(..) | Expr::Ident(..) => Type::Any,
|
|
268
|
-
Expr::Decorated(_, _) => panic!("Decorated expressions should not be traversed"),
|
|
269
268
|
Expr::Placeholder => panic!("Placeholder expression should not be traversed"),
|
|
270
269
|
};
|
|
271
270
|
|
|
@@ -274,9 +274,6 @@ impl<'src, 'tok> Lift<Indirect<ast::SExpr<'src>>> for cst::SExpr<'src, 'tok> {
|
|
|
274
274
|
ast::Expr::Fn(args_list, body.lift())
|
|
275
275
|
}
|
|
276
276
|
cst::Expr::Fstr { head, parts, .. } => lift_fstr(head, parts),
|
|
277
|
-
cst::Expr::Decorated {
|
|
278
|
-
expr, decorator, ..
|
|
279
|
-
} => ast::Expr::Decorated(expr.lift(), decorator.lift()),
|
|
280
277
|
cst::Expr::Memo { async_kw, body, .. } => {
|
|
281
278
|
ast::Expr::Memo(body.lift(), async_kw.is_some())
|
|
282
279
|
}
|
|
@@ -382,11 +379,20 @@ impl<'src, 'tok> Lift<Indirect<ast::SExpr<'src>>> for cst::SExpr<'src, 'tok> {
|
|
|
382
379
|
..
|
|
383
380
|
} => {
|
|
384
381
|
if question.is_some() {
|
|
385
|
-
ast::Expr::
|
|
382
|
+
ast::Expr::CallNullable(expr.lift(), rhs.lift())
|
|
386
383
|
} else {
|
|
387
|
-
|
|
384
|
+
let func = rhs.lift();
|
|
385
|
+
let arg = ast::CallItem::Arg(expr.lift());
|
|
386
|
+
ast::Expr::Call(func, vec![arg])
|
|
388
387
|
}
|
|
389
388
|
}
|
|
389
|
+
cst::Expr::Decorated {
|
|
390
|
+
expr, decorator, ..
|
|
391
|
+
} => {
|
|
392
|
+
let func = decorator.lift();
|
|
393
|
+
let arg = ast::CallItem::Arg(expr.lift());
|
|
394
|
+
ast::Expr::Call(func, vec![arg])
|
|
395
|
+
}
|
|
390
396
|
cst::Expr::Checked { expr, pattern, .. } => {
|
|
391
397
|
ast::Expr::Checked(expr.lift(), pattern.as_ref().map(|p| p.lift()))
|
|
392
398
|
}
|
|
@@ -1055,10 +1055,6 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1055
1055
|
|
|
1056
1056
|
Expr::Checked(expr.traverse_guarded(state), pattern)
|
|
1057
1057
|
}
|
|
1058
|
-
Expr::Decorated(deco, expr) => Expr::Call(
|
|
1059
|
-
deco.traverse(state),
|
|
1060
|
-
vec![CallItem::Arg(expr.traverse(state))],
|
|
1061
|
-
),
|
|
1062
1058
|
Expr::Matches(x, pattern) => {
|
|
1063
1059
|
let (pattern, meta) = pattern.traverse(state, false);
|
|
1064
1060
|
if !meta.captures.is_empty() {
|
|
@@ -1446,14 +1442,14 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1446
1442
|
|
|
1447
1443
|
return traversed;
|
|
1448
1444
|
}
|
|
1449
|
-
Expr::
|
|
1450
|
-
Expr::
|
|
1445
|
+
Expr::Call(a, items) => {
|
|
1446
|
+
Expr::Call(a.traverse_guarded(state), traverse_call_items(state, items))
|
|
1451
1447
|
}
|
|
1452
|
-
Expr::
|
|
1448
|
+
Expr::CallNullable(expr, value) => {
|
|
1453
1449
|
let (rhs, fn_ctx) =
|
|
1454
1450
|
with_phantom_fninfo(state, span, |state| value.traverse_guarded(state));
|
|
1455
1451
|
|
|
1456
|
-
let traversed = Expr::
|
|
1452
|
+
let traversed = Expr::CallNullable(expr.traverse(state), rhs)
|
|
1457
1453
|
.spanned(span)
|
|
1458
1454
|
.indirect();
|
|
1459
1455
|
|
|
@@ -1463,9 +1459,6 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
|
|
|
1463
1459
|
|
|
1464
1460
|
return traversed;
|
|
1465
1461
|
}
|
|
1466
|
-
Expr::Call(a, items) => {
|
|
1467
|
-
Expr::Call(a.traverse(state), traverse_call_items(state, items))
|
|
1468
|
-
}
|
|
1469
1462
|
Expr::MappedCall(a, call_items) => {
|
|
1470
1463
|
let (rhs, fn_ctx) = with_phantom_fninfo(state, span, |state| {
|
|
1471
1464
|
traverse_call_items(state, call_items)
|
|
@@ -319,10 +319,6 @@ fn transform_assignment<'src, 'ast>(
|
|
|
319
319
|
cur_node = left;
|
|
320
320
|
decorators.push(right);
|
|
321
321
|
}
|
|
322
|
-
Expr::Decorated(deco, right) => {
|
|
323
|
-
cur_node = right;
|
|
324
|
-
decorators.push(deco);
|
|
325
|
-
}
|
|
326
322
|
Expr::Call(left, right) => {
|
|
327
323
|
if right.len() != 1 {
|
|
328
324
|
break;
|
|
@@ -1429,13 +1425,11 @@ fn transform_postfix_expr<'src, 'ast>(
|
|
|
1429
1425
|
Expr::RawAttribute(obj, _) => (false, obj),
|
|
1430
1426
|
Expr::Subscript(obj, _) => (false, obj),
|
|
1431
1427
|
Expr::Call(obj, _) => (false, obj),
|
|
1432
|
-
Expr::ScopedAttribute(obj, _) => (false, obj),
|
|
1433
1428
|
Expr::Attribute(obj, _) => (false, obj),
|
|
1434
1429
|
Expr::MaybeAttribute(obj, _) => (false, obj),
|
|
1435
1430
|
Expr::MappedRawAttribute(obj, _) => (true, obj),
|
|
1436
1431
|
Expr::MappedSubscript(obj, _) => (true, obj),
|
|
1437
1432
|
Expr::MappedCall(obj, _) => (true, obj),
|
|
1438
|
-
Expr::MappedScopedAttribute(obj, _) => (true, obj),
|
|
1439
1433
|
Expr::MappedAttribute(obj, _) => (true, obj),
|
|
1440
1434
|
Expr::MappedMaybeAttribute(obj, _) => (true, obj),
|
|
1441
1435
|
_ => {
|
|
@@ -1466,10 +1460,6 @@ fn transform_postfix_expr<'src, 'ast>(
|
|
|
1466
1460
|
let t = inner_pre.bind(transform_subscript_items(ctx, &list, &expr.span)?);
|
|
1467
1461
|
a.subscript(lhs, t, access_ctx)
|
|
1468
1462
|
}
|
|
1469
|
-
Expr::ScopedAttribute(_, rhs) | Expr::MappedScopedAttribute(_, rhs) => {
|
|
1470
|
-
let t = inner_pre.bind(rhs.transform(ctx)?);
|
|
1471
|
-
a.call(t, vec![PyCallItem::Arg(lhs)])
|
|
1472
|
-
}
|
|
1473
1463
|
Expr::RawAttribute(_, attr) | Expr::MappedRawAttribute(_, attr) => {
|
|
1474
1464
|
a.attribute(lhs, attr.value.escape(), access_ctx)
|
|
1475
1465
|
}
|
|
@@ -2024,10 +2014,6 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for SExpr<'src> {
|
|
|
2024
2014
|
|
|
2025
2015
|
a.load_ident(name)
|
|
2026
2016
|
}
|
|
2027
|
-
Expr::Decorated(deco, expr) => a.call(
|
|
2028
|
-
pre.bind(deco.transform(ctx)?),
|
|
2029
|
-
vec![PyCallItem::Arg(pre.bind(expr.transform(ctx)?))],
|
|
2030
|
-
),
|
|
2031
2017
|
Expr::Literal(lit) => a.literal(lit.value.transform(ctx)?),
|
|
2032
2018
|
Expr::Ident(_) => {
|
|
2033
2019
|
let ident = ctx.py_ident(self);
|
|
@@ -2039,14 +2025,30 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for SExpr<'src> {
|
|
|
2039
2025
|
| Expr::MappedCall(..)
|
|
2040
2026
|
| Expr::Subscript(..)
|
|
2041
2027
|
| Expr::MappedSubscript(..)
|
|
2042
|
-
| Expr::ScopedAttribute(..)
|
|
2043
|
-
| Expr::MappedScopedAttribute(..)
|
|
2044
2028
|
| Expr::Attribute(..)
|
|
2045
2029
|
| Expr::MappedAttribute(..)
|
|
2046
2030
|
| Expr::MaybeAttribute(..)
|
|
2047
2031
|
| Expr::MappedMaybeAttribute(..) => {
|
|
2048
2032
|
pre.bind(transform_postfix_expr(ctx, self, access_ctx)?)
|
|
2049
2033
|
}
|
|
2034
|
+
Expr::CallNullable(obj, func) => {
|
|
2035
|
+
let a = PyAstBuilder::new(span);
|
|
2036
|
+
let obj_transformed = pre.bind(obj.transform(ctx)?);
|
|
2037
|
+
let func_transformed = pre.bind(func.transform(ctx)?);
|
|
2038
|
+
|
|
2039
|
+
let maparg = ctx.create_aux_var("maparg", span.start);
|
|
2040
|
+
|
|
2041
|
+
a.call(
|
|
2042
|
+
a.tl_builtin("op_map"),
|
|
2043
|
+
vec![
|
|
2044
|
+
a.call_arg(obj_transformed),
|
|
2045
|
+
a.call_arg(a.lambda(
|
|
2046
|
+
PyArgList::simple_args(vec![(maparg.clone().into(), None)]),
|
|
2047
|
+
a.call(func_transformed, vec![a.call_arg(a.load_ident(maparg))]),
|
|
2048
|
+
)),
|
|
2049
|
+
],
|
|
2050
|
+
)
|
|
2051
|
+
}
|
|
2050
2052
|
Expr::With(pattern, value, body) => {
|
|
2051
2053
|
let temp_var = ctx.create_aux_var("with", span.start);
|
|
2052
2054
|
let value = pre.bind(value.transform(ctx)?);
|
|
@@ -509,11 +509,10 @@ pub enum Expr<TTree: Tree> {
|
|
|
509
509
|
end: TTree::Token,
|
|
510
510
|
},
|
|
511
511
|
|
|
512
|
-
// these are removed during desugaring
|
|
513
512
|
Decorated {
|
|
514
|
-
expr: TTree::Expr,
|
|
515
|
-
op: TTree::Token,
|
|
516
513
|
decorator: TTree::Expr,
|
|
514
|
+
op: TTree::Token,
|
|
515
|
+
expr: TTree::Expr,
|
|
517
516
|
},
|
|
518
517
|
Placeholder {
|
|
519
518
|
token: TTree::Token,
|
|
@@ -1289,7 +1289,7 @@ impl<'src: 'tok, 'tok> ParseCtx<'src, 'tok> {
|
|
|
1289
1289
|
},
|
|
1290
1290
|
Decorator {
|
|
1291
1291
|
op: &'tok SToken<'src>,
|
|
1292
|
-
|
|
1292
|
+
decorated: SExpr<'src, 'tok>,
|
|
1293
1293
|
},
|
|
1294
1294
|
}
|
|
1295
1295
|
|
|
@@ -1332,8 +1332,8 @@ impl<'src: 'tok, 'tok> ParseCtx<'src, 'tok> {
|
|
|
1332
1332
|
|
|
1333
1333
|
let decorator = |ctx: &mut Self| {
|
|
1334
1334
|
let op = ctx.symbol("!")?;
|
|
1335
|
-
let
|
|
1336
|
-
Ok(Postfix::Decorator { op,
|
|
1335
|
+
let decorated = ctx.expr()?;
|
|
1336
|
+
Ok(Postfix::Decorator { op, decorated })
|
|
1337
1337
|
};
|
|
1338
1338
|
|
|
1339
1339
|
let dot_attribute = |ctx: &mut Self| {
|
|
@@ -1438,7 +1438,7 @@ impl<'src: 'tok, 'tok> ParseCtx<'src, 'tok> {
|
|
|
1438
1438
|
question2,
|
|
1439
1439
|
attr,
|
|
1440
1440
|
},
|
|
1441
|
-
Postfix::Decorator { op,
|
|
1441
|
+
Postfix::Decorator { op, decorated } => {
|
|
1442
1442
|
if question.is_some() {
|
|
1443
1443
|
return Err(self.set_error(
|
|
1444
1444
|
start,
|
|
@@ -1446,9 +1446,9 @@ impl<'src: 'tok, 'tok> ParseCtx<'src, 'tok> {
|
|
|
1446
1446
|
));
|
|
1447
1447
|
}
|
|
1448
1448
|
Expr::Decorated {
|
|
1449
|
-
expr:
|
|
1449
|
+
expr: decorated.boxed(),
|
|
1450
1450
|
op,
|
|
1451
|
-
decorator:
|
|
1451
|
+
decorator: expr.boxed(),
|
|
1452
1452
|
}
|
|
1453
1453
|
}
|
|
1454
1454
|
}
|
|
@@ -350,7 +350,7 @@ impl<'src, 'tok> SimpleFmt for SExprInner<'src, 'tok> {
|
|
|
350
350
|
Expr::Decorated {
|
|
351
351
|
expr, decorator, ..
|
|
352
352
|
} => {
|
|
353
|
-
format!("{}
|
|
353
|
+
format!("{}! {}", expr.simple_fmt(), decorator.simple_fmt())
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
356
|
}
|
|
@@ -14,15 +14,14 @@ import koatl.std.(
|
|
|
14
14
|
export std = LazyModule("koatl.std")
|
|
15
15
|
export mod = RootModulesProxy()
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Extension.property(list, "len")! List.len.fget
|
|
17
|
+
# Extensions
|
|
20
18
|
|
|
21
|
-
Extension.
|
|
19
|
+
Extension.trait(Iterable)
|
|
22
20
|
|
|
23
21
|
Extension.method(str, "match")! (regex, str) => Pattern(regex).match(str)
|
|
24
22
|
Extension.method(str, "matches")! (str, regex) => Pattern(regex).match(str)
|
|
25
23
|
|
|
24
|
+
Extension.property(list, "len")! List.len.fget
|
|
26
25
|
Extension.method(list, "map")! (self, f) => self.iter.map(f).list()
|
|
27
26
|
Extension.method(list, "filter")! (self, f) => self.iter.filter(f).list()
|
|
28
27
|
|
|
@@ -33,6 +32,8 @@ Extension.method(dict, "filter")! (self, f) => self.iter.filter(f).dict()
|
|
|
33
32
|
Extension.method(dict, "filter_keys")! (self, f) => self.iter.filter([k, v] => f(k)).dict()
|
|
34
33
|
Extension.method(dict, "filter_values")! (self, f) => self.iter.filter([k, v] => f(v)).dict()
|
|
35
34
|
|
|
35
|
+
# Implementations for language features
|
|
36
|
+
|
|
36
37
|
__tl__.do = koatl.std.control.do.do
|
|
37
38
|
|
|
38
39
|
__tl__.op_map = koatl.std.control.result.op_map
|
|
@@ -20,7 +20,7 @@ export Pattern = class:
|
|
|
20
20
|
if not isinstance(string, str):
|
|
21
21
|
raise TypeError(f"Expected a string, but got {type(string).__name__}")
|
|
22
22
|
|
|
23
|
-
self.pattern.match(string, flags).
|
|
23
|
+
self.pattern.match(string, flags).(Result).map(Match)
|
|
24
24
|
|
|
25
25
|
export Match = collections.abc.Sequence.register! class:
|
|
26
26
|
__slots__ = ("_match",)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|