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.
Files changed (153) hide show
  1. {koatl-0.3.15 → koatl-0.3.16}/Cargo.lock +1 -1
  2. {koatl-0.3.15 → koatl-0.3.16}/PKG-INFO +1 -1
  3. {koatl-0.3.15 → koatl-0.3.16}/koatl/Cargo.toml +1 -1
  4. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/prelude/__init__.tl +5 -4
  5. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/re.tl +1 -1
  6. koatl-0.3.16/koatl/tests/test_re.tl +204 -0
  7. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/util/__init__.py +6 -0
  8. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/prelude/__init__.tl +5 -4
  9. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/re.tl +1 -1
  10. {koatl-0.3.15 → koatl-0.3.16}/Cargo.toml +0 -0
  11. {koatl-0.3.15 → koatl-0.3.16}/README.md +0 -0
  12. {koatl-0.3.15 → koatl-0.3.16}/koatl/.github/workflows/CI.yml +0 -0
  13. {koatl-0.3.15 → koatl-0.3.16}/koatl/.gitignore +0 -0
  14. {koatl-0.3.15 → koatl-0.3.16}/koatl/LICENSE +0 -0
  15. {koatl-0.3.15 → koatl-0.3.16}/koatl/README.md +0 -0
  16. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/__init__.py +0 -0
  17. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/__main__.py +0 -0
  18. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/cli.py +0 -0
  19. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/notebook/__init__.py +0 -0
  20. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/notebook/magic.py +0 -0
  21. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/runtime/__init__.py +0 -0
  22. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  23. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/runtime/record.py +0 -0
  24. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/runtime/vattr.py +0 -0
  25. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/__init__.tl +0 -0
  26. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/__init__.tl +0 -0
  27. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/async.tl +0 -0
  28. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/base.tl +0 -0
  29. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/do.tl +0 -0
  30. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/env.tl +0 -0
  31. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/memo.tl +0 -0
  32. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/control/result.tl +0 -0
  33. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/data/__init__.tl +0 -0
  34. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/data/list.tl +0 -0
  35. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/data/record.tl +0 -0
  36. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/ext.tl +0 -0
  37. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/io.tl +0 -0
  38. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/iter.tl +0 -0
  39. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/json.tl +0 -0
  40. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/lazy_module.tl +0 -0
  41. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/pickle.tl +0 -0
  42. {koatl-0.3.15 → koatl-0.3.16}/koatl/python/koatl/std/trait.py +0 -0
  43. {koatl-0.3.15 → koatl-0.3.16}/koatl/requirements.txt +0 -0
  44. {koatl-0.3.15 → koatl-0.3.16}/koatl/src/emit_py.rs +0 -0
  45. {koatl-0.3.15 → koatl-0.3.16}/koatl/src/lib.rs +0 -0
  46. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/conftest.py +0 -0
  47. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/arguments.tl +0 -0
  48. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/containers.tl +0 -0
  49. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/data.txt +0 -0
  50. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/decorators.tl +0 -0
  51. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  52. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/destructure.tl +0 -0
  53. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  54. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/fstr.tl +0 -0
  55. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/functions.tl +0 -0
  56. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/generator.tl +0 -0
  57. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/if_expr.tl +0 -0
  58. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/imports.tl +0 -0
  59. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/imports2.tl +0 -0
  60. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/loops.tl +0 -0
  61. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/match.tl +0 -0
  62. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/nary-list.tl +0 -0
  63. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/placeholder.tl +0 -0
  64. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/precedence.tl +0 -0
  65. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/scopes.tl +0 -0
  66. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  67. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  68. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/base/with.tl +0 -0
  69. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/async.tl +0 -0
  70. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
  71. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/coal.tl +0 -0
  72. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/env.tl +0 -0
  73. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  74. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/list.tl +0 -0
  75. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/memo.tl +0 -0
  76. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/record.tl +0 -0
  77. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/result.tl +0 -0
  78. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/slice.tl +0 -0
  79. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/try.tl +0 -0
  80. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  81. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/test_modules/module0.tl +0 -0
  82. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/test_modules/module1.tl +0 -0
  83. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/e2e/test_modules/module2.tl +0 -0
  84. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_mixed_defaults.tl +0 -0
  85. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_multiple_kwargs.tl +0 -0
  86. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_multiple_kwonly_markers.tl +0 -0
  87. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_multiple_posonly_markers.tl +0 -0
  88. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_multiple_varargs.tl +0 -0
  89. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_posonly_after_kwonly.tl +0 -0
  90. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/arguments_vararg_and_kwonly_marker.tl +0 -0
  91. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/fail-parse/unmatched_closing_delimiter.tl +0 -0
  92. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/arith.tl +0 -0
  93. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/assign.tl +0 -0
  94. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/block-comments.tl +0 -0
  95. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/deco.tl +0 -0
  96. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/func.tl +0 -0
  97. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/matches.tl +0 -0
  98. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/mutual-recursion.tl +0 -0
  99. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/numbers.tl +0 -0
  100. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/parse/operators.tl +0 -0
  101. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/test_e2e.py +0 -0
  102. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/test_iterable.tl +0 -0
  103. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/test_parse.py +0 -0
  104. {koatl-0.3.15 → koatl-0.3.16}/koatl/tests/test_parse_fail.py +0 -0
  105. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/Cargo.toml +0 -0
  106. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/ast.rs +0 -0
  107. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/ast_builder.rs +0 -0
  108. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/inference.rs +0 -0
  109. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/lib.rs +0 -0
  110. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/lift_cst.rs +0 -0
  111. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/main.rs +0 -0
  112. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/py/ast.rs +0 -0
  113. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/py/ast_builder.rs +0 -0
  114. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/py/emit.rs +0 -0
  115. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/py/mod.rs +0 -0
  116. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/resolve_scopes.rs +0 -0
  117. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/transform.rs +0 -0
  118. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/types.rs +0 -0
  119. {koatl-0.3.15 → koatl-0.3.16}/koatl-core/src/util.rs +0 -0
  120. {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/Cargo.toml +0 -0
  121. {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/src/cst.rs +0 -0
  122. {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/src/lexer.rs +0 -0
  123. {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/src/lib.rs +0 -0
  124. {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/src/parser.rs +0 -0
  125. {koatl-0.3.15 → koatl-0.3.16}/koatl-parser/src/simple_fmt.rs +0 -0
  126. {koatl-0.3.15 → koatl-0.3.16}/pyproject.toml +0 -0
  127. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/__init__.py +0 -0
  128. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/__main__.py +0 -0
  129. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/cli.py +0 -0
  130. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/notebook/__init__.py +0 -0
  131. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/notebook/magic.py +0 -0
  132. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/runtime/__init__.py +0 -0
  133. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/runtime/meta_finder.py +0 -0
  134. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/runtime/record.py +0 -0
  135. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/runtime/vattr.py +0 -0
  136. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/__init__.tl +0 -0
  137. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/__init__.tl +0 -0
  138. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/async.tl +0 -0
  139. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/base.tl +0 -0
  140. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/do.tl +0 -0
  141. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/env.tl +0 -0
  142. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/memo.tl +0 -0
  143. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/control/result.tl +0 -0
  144. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/data/__init__.tl +0 -0
  145. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/data/list.tl +0 -0
  146. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/data/record.tl +0 -0
  147. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/ext.tl +0 -0
  148. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/io.tl +0 -0
  149. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/iter.tl +0 -0
  150. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/json.tl +0 -0
  151. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/lazy_module.tl +0 -0
  152. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/pickle.tl +0 -0
  153. {koatl-0.3.15 → koatl-0.3.16}/python/koatl/std/trait.py +0 -0
@@ -190,7 +190,7 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
190
190
 
191
191
  [[package]]
192
192
  name = "koatl"
193
- version = "0.3.15"
193
+ version = "0.3.16"
194
194
  dependencies = [
195
195
  "ariadne",
196
196
  "koatl-core",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: koatl
3
- Version: 0.3.15
3
+ Version: 0.3.16
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "koatl"
3
- version = "0.3.15"
3
+ version = "0.3.16"
4
4
  edition = "2021"
5
5
  homepage = "https://koatl.org"
6
6
 
@@ -14,15 +14,14 @@ import koatl.std.(
14
14
  export std = LazyModule("koatl.std")
15
15
  export mod = RootModulesProxy()
16
16
 
17
- Extension.trait(Iterable)
18
-
19
- Extension.property(list, "len")! List.len.fget
17
+ # Extensions
20
18
 
21
- Extension.property(object, "result")! self => Result(self)
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).result.map(Match)
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
- Extension.trait(Iterable)
18
-
19
- Extension.property(list, "len")! List.len.fget
17
+ # Extensions
20
18
 
21
- Extension.property(object, "result")! self => Result(self)
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).result.map(Match)
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