koatl 0.3.15__tar.gz → 0.3.16__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.16}/Cargo.lock +1 -1
- {koatl-0.3.15 → koatl-0.3.16}/PKG-INFO +1 -1
- {koatl-0.3.15 → koatl-0.3.16}/koatl/Cargo.toml +1 -1
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/prelude/__init__.tl +5 -4
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/re.tl +1 -1
- koatl-0.3.16/koatl/tests/test_re.tl +204 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/util/__init__.py +6 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/prelude/__init__.tl +5 -4
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/re.tl +1 -1
- {koatl-0.3.15 → koatl-0.3.16}/Cargo.toml +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/README.md +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/.gitignore +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/LICENSE +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/README.md +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/cli.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/runtime/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/runtime/record.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/runtime/vattr.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/async.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/base.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/do.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/env.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/memo.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/result.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/data/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/data/list.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/data/record.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/ext.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/io.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/iter.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/json.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/lazy_module.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/pickle.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/trait.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/requirements.txt +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/src/emit_py.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/src/lib.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/conftest.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/arguments.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/data.txt +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/imports2.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/loops.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/match.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/scopes.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/short_circuit.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/with.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/coal.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/env.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/iterables.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/list.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/memo.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/record.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/result.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/slice.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/try.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/test_modules/module0.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/test_modules/module1.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/test_modules/module2.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_mixed_defaults.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_multiple_kwargs.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_multiple_kwonly_markers.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_multiple_posonly_markers.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_multiple_varargs.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_posonly_after_kwonly.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_vararg_and_kwonly_marker.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/unmatched_closing_delimiter.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/block-comments.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/deco.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/mutual-recursion.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/numbers.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/operators.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/test_e2e.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/test_iterable.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/test_parse.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/test_parse_fail.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/Cargo.toml +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/ast.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/ast_builder.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/inference.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/lib.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/lift_cst.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/main.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/py/ast.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/py/ast_builder.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/py/emit.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/resolve_scopes.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/transform.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/types.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/util.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/Cargo.toml +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/src/cst.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/src/lexer.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/src/lib.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/src/parser.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/src/simple_fmt.rs +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/pyproject.toml +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/__main__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/cli.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/runtime/__init__.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/runtime/record.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/runtime/vattr.py +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/async.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/base.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/do.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/env.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/memo.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/result.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/data/__init__.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/data/list.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/data/record.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/ext.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/io.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/iter.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/json.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/lazy_module.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/pickle.tl +0 -0
- {koatl-0.3.15 → koatl-0.3.16}/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",)
|
|
@@ -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)
|
|
@@ -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
|
|
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
|