koatl 0.1.38__tar.gz → 0.1.40__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 (139) hide show
  1. {koatl-0.1.38 → koatl-0.1.40}/Cargo.lock +1 -1
  2. {koatl-0.1.38 → koatl-0.1.40}/PKG-INFO +1 -1
  3. {koatl-0.1.38 → koatl-0.1.40}/koatl/Cargo.toml +1 -1
  4. koatl-0.1.40/koatl/python/koatl/prelude/__init__.tl +40 -0
  5. koatl-0.1.40/koatl/python/koatl/runtime/__init__.py +47 -0
  6. koatl-0.1.40/koatl/python/koatl/runtime/record.py +249 -0
  7. koatl-0.1.38/koatl/python/koatl/runtime/virtual.py → koatl-0.1.40/koatl/python/koatl/runtime/vattr.py +1 -37
  8. koatl-0.1.40/koatl/python/koatl/std/alg/do.tl +34 -0
  9. {koatl-0.1.38 → koatl-0.1.40/koatl}/python/koatl/std/alg/result.tl +17 -1
  10. koatl-0.1.40/koatl/python/koatl/std/data/__init__.tl +2 -0
  11. koatl-0.1.40/koatl/python/koatl/std/data/record.tl +5 -0
  12. koatl-0.1.40/koatl/python/koatl/std/ext.tl +24 -0
  13. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/std/lazy_module.tl +23 -5
  14. {koatl-0.1.38 → koatl-0.1.40/koatl}/python/koatl/std/trait.py +2 -2
  15. {koatl-0.1.38 → koatl-0.1.40}/koatl/src/lib.rs +20 -10
  16. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/containers.tl +4 -1
  17. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/parser/src/lexer.rs +4 -28
  18. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/parser/src/parser.rs +0 -1
  19. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/py/emit.rs +3 -1
  20. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/resolve_scopes.rs +5 -3
  21. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/transform.rs +9 -2
  22. koatl-0.1.40/python/koatl/prelude/__init__.tl +40 -0
  23. koatl-0.1.40/python/koatl/runtime/__init__.py +47 -0
  24. koatl-0.1.40/python/koatl/runtime/record.py +249 -0
  25. koatl-0.1.38/python/koatl/runtime/virtual.py → koatl-0.1.40/python/koatl/runtime/vattr.py +1 -37
  26. koatl-0.1.40/python/koatl/std/alg/do.tl +34 -0
  27. {koatl-0.1.38/koatl → koatl-0.1.40}/python/koatl/std/alg/result.tl +17 -1
  28. koatl-0.1.40/python/koatl/std/data/__init__.tl +2 -0
  29. koatl-0.1.40/python/koatl/std/data/record.tl +5 -0
  30. koatl-0.1.40/python/koatl/std/ext.tl +24 -0
  31. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/std/lazy_module.tl +23 -5
  32. {koatl-0.1.38/koatl → koatl-0.1.40}/python/koatl/std/trait.py +2 -2
  33. koatl-0.1.38/koatl/python/koatl/prelude/__init__.tl +0 -61
  34. koatl-0.1.38/koatl/python/koatl/runtime/__init__.py +0 -62
  35. koatl-0.1.38/koatl/python/koatl/runtime/helpers.py +0 -71
  36. koatl-0.1.38/koatl/python/koatl/runtime/record.py +0 -116
  37. koatl-0.1.38/koatl/python/koatl/std/data/__init__.tl +0 -1
  38. koatl-0.1.38/python/koatl/prelude/__init__.tl +0 -61
  39. koatl-0.1.38/python/koatl/runtime/__init__.py +0 -62
  40. koatl-0.1.38/python/koatl/runtime/helpers.py +0 -71
  41. koatl-0.1.38/python/koatl/runtime/record.py +0 -116
  42. koatl-0.1.38/python/koatl/std/data/__init__.tl +0 -1
  43. {koatl-0.1.38 → koatl-0.1.40}/Cargo.toml +0 -0
  44. {koatl-0.1.38 → koatl-0.1.40}/README.md +0 -0
  45. {koatl-0.1.38 → koatl-0.1.40}/koatl/.github/workflows/CI.yml +0 -0
  46. {koatl-0.1.38 → koatl-0.1.40}/koatl/.gitignore +0 -0
  47. {koatl-0.1.38 → koatl-0.1.40}/koatl/LICENSE +0 -0
  48. {koatl-0.1.38 → koatl-0.1.40}/koatl/README.md +0 -0
  49. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/__init__.py +0 -0
  50. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/__main__.py +0 -0
  51. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/cli.py +0 -0
  52. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/notebook/__init__.py +0 -0
  53. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/notebook/magic.py +0 -0
  54. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  55. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/std/alg/__init__.tl +0 -0
  56. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/std/alg/async.tl +0 -0
  57. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/std/alg/base.tl +0 -0
  58. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/std/alg/env.tl +0 -0
  59. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/std/alg/memo.tl +0 -0
  60. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/std/data/list.tl +0 -0
  61. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/std/io.tl +0 -0
  62. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/std/iter.tl +0 -0
  63. {koatl-0.1.38 → koatl-0.1.40}/koatl/python/koatl/std/re.tl +0 -0
  64. {koatl-0.1.38 → koatl-0.1.40}/koatl/requirements.txt +0 -0
  65. {koatl-0.1.38 → koatl-0.1.40}/koatl/src/emit_py.rs +0 -0
  66. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/data.txt +0 -0
  67. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/decorators.tl +0 -0
  68. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  69. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/destructure.tl +0 -0
  70. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  71. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/fstr.tl +0 -0
  72. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/functions.tl +0 -0
  73. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/generator.tl +0 -0
  74. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/if_expr.tl +0 -0
  75. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/loops.tl +0 -0
  76. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/match.tl +0 -0
  77. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/nary-list.tl +0 -0
  78. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/placeholder.tl +0 -0
  79. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/precedence.tl +0 -0
  80. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/scopes.tl +0 -0
  81. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  82. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  83. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/base/with.tl +0 -0
  84. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/async.tl +0 -0
  85. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/aug_assign.tl +0 -0
  86. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/coal.tl +0 -0
  87. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/env.tl +0 -0
  88. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/imports.tl +0 -0
  89. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  90. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/list.tl +0 -0
  91. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/memo.tl +0 -0
  92. {koatl-0.1.38/koatl/tests/e2e/base → koatl-0.1.40/koatl/tests/e2e/prelude}/record.tl +0 -0
  93. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/result.tl +0 -0
  94. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/slice.tl +0 -0
  95. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/try.tl +0 -0
  96. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  97. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/util/__init__.py +0 -0
  98. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/util/module0.tl +0 -0
  99. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/util/module1.tl +0 -0
  100. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/e2e/util/module2.tl +0 -0
  101. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/parse/arith.tl +0 -0
  102. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/parse/assign.tl +0 -0
  103. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/parse/block-comments.tl +0 -0
  104. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/parse/deco.tl +0 -0
  105. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/parse/func.tl +0 -0
  106. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/parse/matches.tl +0 -0
  107. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/test_e2e.py +0 -0
  108. {koatl-0.1.38 → koatl-0.1.40}/koatl/tests/test_parse.py +0 -0
  109. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/Cargo.toml +0 -0
  110. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/parser/Cargo.toml +0 -0
  111. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/parser/src/ast.rs +0 -0
  112. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/parser/src/lib.rs +0 -0
  113. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/parser/src/util.rs +0 -0
  114. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/inference.rs +0 -0
  115. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/lib.rs +0 -0
  116. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/main.rs +0 -0
  117. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/parse_timer.rs +0 -0
  118. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/parser.rs +0 -0
  119. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/py/ast.rs +0 -0
  120. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/py/mod.rs +0 -0
  121. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/py/util.rs +0 -0
  122. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/types.rs +0 -0
  123. {koatl-0.1.38 → koatl-0.1.40}/koatl-core/src/util.rs +0 -0
  124. {koatl-0.1.38 → koatl-0.1.40}/pyproject.toml +0 -0
  125. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/__init__.py +0 -0
  126. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/__main__.py +0 -0
  127. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/cli.py +0 -0
  128. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/notebook/__init__.py +0 -0
  129. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/notebook/magic.py +0 -0
  130. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/runtime/meta_finder.py +0 -0
  131. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/std/alg/__init__.tl +0 -0
  132. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/std/alg/async.tl +0 -0
  133. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/std/alg/base.tl +0 -0
  134. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/std/alg/env.tl +0 -0
  135. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/std/alg/memo.tl +0 -0
  136. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/std/data/list.tl +0 -0
  137. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/std/io.tl +0 -0
  138. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/std/iter.tl +0 -0
  139. {koatl-0.1.38 → koatl-0.1.40}/python/koatl/std/re.tl +0 -0
@@ -99,7 +99,7 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
99
99
 
100
100
  [[package]]
101
101
  name = "koatl"
102
- version = "0.1.38"
102
+ version = "0.1.40"
103
103
  dependencies = [
104
104
  "ariadne",
105
105
  "koatl-core",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: koatl
3
- Version: 0.1.38
3
+ Version: 0.1.40
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.1.38"
3
+ version = "0.1.40"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -0,0 +1,40 @@
1
+ export import koatl.std.(
2
+ trait.*
3
+ ext.*
4
+ data.*
5
+ iter.*
6
+ alg.(Result, Ok, Err, Env, Memo, Async, AsyncMemo)
7
+ alg.do
8
+ )
9
+
10
+ import koatl.std.(
11
+ re.Pattern
12
+ lazy_module.(LazyModule, RootModulesProxy)
13
+ )
14
+
15
+ export std = LazyModule("koatl.std")
16
+ export mod = RootModulesProxy()
17
+
18
+ Extension.trait(Iterable)
19
+
20
+ Extension.property(list, "len")& List.len.fget
21
+
22
+ Extension.property(object, "result")& self => Result(self)
23
+
24
+ Extension.method(str, "match")& (regex, str) => Pattern(regex).match(str)
25
+
26
+ # Note: the below methods have arguments in reverse order to Python's re module.
27
+ Extension.method(str, "matches")& (str, regex) => Pattern(regex).match(str)
28
+ Extension.method(str, "search")& (str, regex) => Pattern(regex).search(str)
29
+
30
+ __tl__.do = koatl.std.alg.do.do
31
+
32
+ __tl__.op_map = koatl.std.alg.result.op_map
33
+ __tl__.op_coal = koatl.std.alg.result.op_coal
34
+
35
+ __tl__.Ok = koatl.std.alg.result.Ok
36
+ __tl__.Err = koatl.std.alg.result.Err
37
+ __tl__.Result = koatl.std.alg.result.Result
38
+
39
+ __tl__.memo_value = koatl.std.alg.memo.Memo.value
40
+ __tl__.async_memo_value = koatl.std.alg.memo.AsyncMemo.value
@@ -0,0 +1,47 @@
1
+ import functools
2
+ import importlib
3
+ from types import SimpleNamespace
4
+
5
+ from koatl.runtime.record import Record
6
+ from . import meta_finder, vattr
7
+
8
+ meta_finder.install_hook()
9
+
10
+ __all__ = ["__tl__"]
11
+
12
+
13
+ def set_exports(package_name, globals_dict, exports, module_star_exports):
14
+ exports = set(exports)
15
+
16
+ for module in module_star_exports:
17
+ mod = importlib.import_module(module, package_name)
18
+
19
+ if hasattr(mod, "__all__"):
20
+ for name in mod.__all__:
21
+ exports.add(name)
22
+ else:
23
+ for name in dir(mod):
24
+ if name.startswith("_"):
25
+ continue
26
+
27
+ exports.add(name)
28
+
29
+ if "__all__" not in globals_dict:
30
+ globals_dict["__all__"] = ()
31
+
32
+ globals_dict["__all__"] = tuple(set(globals_dict["__all__"]) | exports)
33
+
34
+
35
+ __tl__ = SimpleNamespace(
36
+ Exception=Exception,
37
+ slice=slice,
38
+ type=type,
39
+ partial=functools.partial,
40
+ #
41
+ record_literal=Record.from_dict_ref,
42
+ #
43
+ set_exports=set_exports,
44
+ #
45
+ vget=vattr.vget,
46
+ vhas=vattr.vhas,
47
+ )
@@ -0,0 +1,249 @@
1
+ import collections
2
+ from collections.abc import MutableMapping
3
+ import functools
4
+ import inspect
5
+ import re
6
+
7
+ _property = property
8
+
9
+ __all__ = ["Record"]
10
+
11
+
12
+ @collections.abc.MutableMapping.register
13
+ class Record:
14
+ def __init__(self, data=None, /, **kwargs):
15
+ if data is None:
16
+ self.__dict__ = kwargs
17
+ else:
18
+ self.__dict__ = dict(data, **kwargs)
19
+
20
+ @classmethod
21
+ def from_dict_ref(cls, dict_obj):
22
+ self = cls.__new__(cls)
23
+ self.__dict__ = dict_obj
24
+ return self
25
+
26
+ @staticmethod
27
+ def method(fn):
28
+ fn._method = True
29
+ return fn
30
+
31
+ @staticmethod
32
+ def property(fn):
33
+ fn._property = True
34
+ return fn
35
+
36
+ def __getattribute__(self, name):
37
+ dict = object.__getattribute__(self, "__dict__")
38
+
39
+ try:
40
+ attr = dict[name]
41
+
42
+ if hasattr(attr, "_property"):
43
+ return attr(self)
44
+ elif hasattr(attr, "_method"):
45
+ return functools.partial(attr, self)
46
+ except KeyError:
47
+ pass
48
+
49
+ return object.__getattribute__(self, name)
50
+
51
+ @_property
52
+ def iter(self):
53
+ return self.items()
54
+
55
+ # MutableMapping
56
+ def __len__(self):
57
+ return len(self.__dict__)
58
+
59
+ def __getitem__(self, key):
60
+ try:
61
+ return self.__dict__[key]
62
+ except KeyError as e:
63
+ raise e
64
+
65
+ def __setitem__(self, key, item):
66
+ self.__dict__[key] = item
67
+
68
+ def __delitem__(self, key):
69
+ del self.__dict__[key]
70
+
71
+ def __iter__(self):
72
+ return iter(self.__dict__)
73
+
74
+ def __contains__(self, key):
75
+ return key in self.__dict__
76
+
77
+ def get(self, key, default=None):
78
+ return self.__dict__.get(key, default)
79
+
80
+ def items(self):
81
+ return self.__dict__.items()
82
+
83
+ def keys(self):
84
+ return self.__dict__.keys()
85
+
86
+ def values(self):
87
+ return self.__dict__.values()
88
+
89
+ # dict
90
+ def __repr__(self):
91
+ return repr(self.__dict__)
92
+
93
+ def __or__(self, other):
94
+ if isinstance(other, Record):
95
+ return self.__class__(self.__dict__ | other.__dict__)
96
+ if isinstance(other, dict):
97
+ return self.__class__(self.__dict__ | other)
98
+ return NotImplemented
99
+
100
+ def __ror__(self, other):
101
+ if isinstance(other, Record):
102
+ return self.__class__(other.__dict__ | self.__dict__)
103
+ if isinstance(other, dict):
104
+ return self.__class__(other | self.__dict__)
105
+ return NotImplemented
106
+
107
+ def __ior__(self, other):
108
+ if isinstance(other, Record):
109
+ self.__dict__ |= other.__dict__
110
+ else:
111
+ self.__dict__ |= other
112
+ return self
113
+
114
+ def __copy__(self):
115
+ inst = self.__class__.__new__(self.__class__)
116
+ inst.__dict__ = self.__dict__.copy()
117
+ return inst
118
+
119
+ def copy(self):
120
+ if self.__class__ is Record:
121
+ return Record(self.__dict__.copy())
122
+ import copy
123
+
124
+ data = self.__dict__
125
+ try:
126
+ self.__dict__ = {}
127
+ c = copy.copy(self)
128
+ finally:
129
+ self.__dict__ = data
130
+ c.update(self)
131
+ return c
132
+
133
+ @_property
134
+ def len(self):
135
+ return len(self)
136
+
137
+ # Other
138
+
139
+ def __repr__(self):
140
+ return self._repr_with_visited(set())
141
+
142
+ def __hash__(self):
143
+ return hash(tuple(sorted(self.items())))
144
+
145
+ def __eq__(self, other):
146
+ if isinstance(other, Record):
147
+ return self.__dict__ == other.__dict__
148
+
149
+ if isinstance(other, dict):
150
+ return self.__dict__ == other
151
+
152
+ return NotImplemented
153
+
154
+ def _repr_with_visited(self, visited):
155
+ # Handle cycles by checking if this object is already being processed
156
+ obj_id = id(self)
157
+ if obj_id in visited:
158
+ return "{...}"
159
+
160
+ visited.add(obj_id)
161
+ try:
162
+ if not self:
163
+ return "{}"
164
+
165
+ items = []
166
+ for key, value in self.items():
167
+ key_str = self._format_key(key)
168
+
169
+ # Handle value representation with cycle detection
170
+ if isinstance(value, Record):
171
+ value_str = value._repr_with_visited(visited.copy())
172
+ elif hasattr(value, "__dict__") and hasattr(value, "__class__"):
173
+ # For other objects that might contain cycles, use a simple repr
174
+ value_str = repr(value)
175
+ else:
176
+ value_str = repr(value)
177
+
178
+ items.append(f"{key_str}: {value_str}")
179
+
180
+ return "{" + ", ".join(items) + "}"
181
+ finally:
182
+ visited.remove(obj_id)
183
+
184
+ def _format_key(self, key):
185
+ if isinstance(key, str):
186
+ if self._is_identifier(key):
187
+ # If key is an identifier, drop the quotes
188
+ return key
189
+ return f'"{key}"'
190
+
191
+ elif isinstance(key, (int, float, bool, type(None))):
192
+ # If key is a literal like 0, 1, True, False, None, use repr
193
+ return repr(key)
194
+
195
+ else:
196
+ # Otherwise, use f"({repr(key)})"
197
+ return f"({repr(key)})"
198
+
199
+ def _is_identifier(self, s):
200
+ return (
201
+ isinstance(s, str)
202
+ and re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", s)
203
+ and s not in koatl_keywords
204
+ )
205
+
206
+
207
+ for key, value in MutableMapping.__dict__.items():
208
+ # copy over mixin methods from MutableMapping
209
+ if key.startswith("__"):
210
+ continue
211
+
212
+ if not inspect.isfunction(value):
213
+ continue
214
+
215
+ if hasattr(value, "__isabstractmethod__") and value.__isabstractmethod__:
216
+ continue
217
+
218
+ if key not in Record.__dict__:
219
+ setattr(Record, key, value)
220
+
221
+ koatl_keywords = {
222
+ "if",
223
+ "then",
224
+ "else",
225
+ "import",
226
+ "export",
227
+ "as",
228
+ "class",
229
+ "while",
230
+ "for",
231
+ "in",
232
+ "break",
233
+ "continue",
234
+ "with",
235
+ "yield",
236
+ "global",
237
+ "return",
238
+ "raise",
239
+ "try",
240
+ "except",
241
+ "finally",
242
+ "and",
243
+ "or",
244
+ "not",
245
+ "await",
246
+ "let",
247
+ "const",
248
+ "with",
249
+ }
@@ -1,7 +1,6 @@
1
1
  from functools import partial
2
2
  from itertools import count
3
- from types import SimpleNamespace
4
- from koatl._rs import fast_vget, fast_vset, fast_vset_trait
3
+ from koatl._rs import fast_vget
5
4
 
6
5
 
7
6
  def vget(obj, name, ignore_traits=False):
@@ -66,38 +65,3 @@ def vhas(obj, name, ignore_traits=False):
66
65
  return True
67
66
 
68
67
  return False
69
-
70
-
71
- def ext_prop(type, name):
72
- def impl(value):
73
- value._property = True
74
- fast_vset(type, name, value)
75
- return value
76
-
77
- return impl
78
-
79
-
80
- def ext_method(type, name):
81
- def impl(value):
82
- fast_vset(type, name, value)
83
- return value
84
-
85
- return impl
86
-
87
-
88
- def ext_trait(type):
89
- for name, value in type._own_methods.items():
90
- fast_vset_trait(type.__name__, type._trait_reqs, name, value)
91
- return type
92
-
93
-
94
- Extension = SimpleNamespace(
95
- property=ext_prop,
96
- method=ext_method,
97
- trait=ext_trait,
98
- )
99
-
100
-
101
- __all__ = [
102
- "Extension",
103
- ]
@@ -0,0 +1,34 @@
1
+ import functools.wraps
2
+
3
+
4
+ export do = f => wraps(f)& (*args, **kwargs) =>
5
+ let gen = f(*args, **kwargs)
6
+
7
+ let m
8
+ try:
9
+ m = gen.send(None)
10
+ except StopIteration() as e:
11
+ raise ValueError(
12
+ "Returning before `@` is not allowed. "
13
+ "Use `return @MonadType.pure(value)` instead."
14
+ )
15
+
16
+ try:
17
+ # TODO: this is a workaround to avoid recursion.
18
+ # is it possible to derive bind_gen directly from bind_once?
19
+
20
+ return m.bind_gen(gen)
21
+ except AttributeError():
22
+ None
23
+
24
+ let recurse = v =>
25
+ try:
26
+ m = gen.send(v)
27
+ return m.bind_once(recurse)
28
+ except StopIteration() as e:
29
+ return m.pure(e.value)
30
+
31
+ try:
32
+ return m.bind_once(recurse)
33
+ except AttributeError():
34
+ raise ValueError("@ can only be used with an object that has `bind_once`.")
@@ -7,6 +7,22 @@ infer_ok = x =>
7
7
  BaseException() => False
8
8
  default True
9
9
 
10
+ op_coal = (x, f) =>
11
+ if x matches Result():
12
+ return x.coalesce(f)
13
+
14
+ if not infer_ok(x):
15
+ return f()
16
+ return x
17
+
18
+ op_map = (x, f) =>
19
+ if x matches Result():
20
+ return x.map(f)
21
+
22
+ if infer_ok(x):
23
+ return f(x)
24
+ return x
25
+
10
26
  export Result = class(MonadOnce):
11
27
  __slots__ = ()
12
28
  __match_args__ = ("value",)
@@ -158,4 +174,4 @@ export Err = class(Result):
158
174
  if self.value matches BaseException():
159
175
  raise self.value
160
176
  raise ValueError(f"Expected Ok, got {repr(self)}")
161
- coalesce = (self, f) => f()
177
+ coalesce = (self, f) => f()
@@ -0,0 +1,2 @@
1
+ export import .list.List
2
+ export import .record.Record
@@ -0,0 +1,5 @@
1
+ import koatl.runtime
2
+
3
+ # TODO: should we just move this to this module?
4
+
5
+ export Record = runtime.record.Record
@@ -0,0 +1,24 @@
1
+ import koatl._rs.(fast_vset, fast_vset_trait)
2
+
3
+ ext_prop = (type, name) => value =>
4
+ value::_property = True
5
+ fast_vset(type, name, value)
6
+ value
7
+
8
+
9
+ ext_method = (type, name) => value =>
10
+ fast_vset(type, name, value)
11
+ value
12
+
13
+
14
+ ext_trait = type =>
15
+ for name, value in type::_own_methods:
16
+ fast_vset_trait(type::__name__, type::_trait_reqs, name, value)
17
+ type
18
+
19
+
20
+ export Extension = {
21
+ property: ext_prop,
22
+ method: ext_method,
23
+ trait: ext_trait,
24
+ }
@@ -3,14 +3,14 @@ import collections
3
3
 
4
4
  export LazyModule = class:
5
5
  __init__ = (self, name) =>
6
- self._name = name
7
- self._module = None
8
- self._submodules = {}
6
+ self.__dict__["_name"] = name
7
+ self.__dict__["_module"] = None
8
+ self.__dict__["_submodules"] = {}
9
9
 
10
10
  __getattr__ = (self, attr) =>
11
11
  try:
12
12
  if self._module === None:
13
- self._module = importlib.import_module(self._name)
13
+ self.__dict__["_module"] = importlib.import_module(self._name)
14
14
 
15
15
  if hasattr(self._module, "__path__"):
16
16
  if attr not in self._submodules:
@@ -30,4 +30,22 @@ export LazyModule = class:
30
30
 
31
31
  raise AttributeError(f"Module '{self._name}' has no attribute '{attr}'")
32
32
 
33
- __repr__ = self => f"LazyModule({self._name})"
33
+ __setattr__ = (self, attr, value) =>
34
+ if self._module === None:
35
+ self.__dict__["_module"] = importlib.import_module(self._name)
36
+
37
+ setattr(self._module, attr, value)
38
+
39
+ __repr__ = self => f"LazyModule({self._name})"
40
+
41
+
42
+ export RootModulesProxy = class:
43
+ __init__ = (self) =>
44
+ self._modules = {}
45
+
46
+ __getattr__ = (self, attr) =>
47
+ if attr not in self._modules:
48
+ self._modules[attr] = LazyModule(attr)
49
+ return self._modules[attr]
50
+
51
+ __repr__ = self => f"RootModulesProxy({self._modules})"
@@ -2,7 +2,7 @@ import abc
2
2
  import collections
3
3
  import inspect
4
4
 
5
- from koatl.runtime.virtual import vhas
5
+ from koatl.prelude import __tl__
6
6
 
7
7
 
8
8
  @collections.abc.Mapping.register
@@ -90,7 +90,7 @@ class TraitMeta(MappingMeta):
90
90
  # Check if the exact class has _trait_reqs, in which case it's a trait.
91
91
  if "_trait_reqs" in cls.__dict__:
92
92
  for req in cls._trait_reqs:
93
- if not vhas(instance, req):
93
+ if not __tl__.vhas(instance, req):
94
94
  return False
95
95
 
96
96
  return True
@@ -118,8 +118,9 @@ fn fast_vget<'py, 'ptr>(
118
118
  let vtbl2 = VTBL2.lock().unwrap();
119
119
 
120
120
  if let Some(traits) = vtbl2.get(&name) {
121
- let tlmod = py.import("koatl.runtime.virtual")?;
122
- let vget = tlmod.getattr("vget")?;
121
+ let runtime = py.import("koatl.runtime")?;
122
+ let tl = runtime.getattr("__tl__")?;
123
+ let vget = tl.getattr("vget")?;
123
124
 
124
125
  for t in traits {
125
126
  let mut ok = true;
@@ -179,14 +180,23 @@ fn fast_vset_trait<'py, 'ptr>(
179
180
  }
180
181
 
181
182
  // TODO: what about conflicting traits?
182
- vtbl.get_mut(&name).unwrap().insert(
183
- 0,
184
- TraitAttr {
185
- name: trait_name.to_string(),
186
- requirements: reqs,
187
- value: value.clone().unbind(),
188
- },
189
- );
183
+ let vtbl_entry = vtbl.get_mut(&name).unwrap();
184
+ if let Some(existing) = vtbl_entry
185
+ .iter_mut()
186
+ .find(|e| e.name == trait_name.to_string())
187
+ {
188
+ existing.requirements = reqs;
189
+ existing.value = value.clone().unbind();
190
+ } else {
191
+ vtbl_entry.insert(
192
+ 0,
193
+ TraitAttr {
194
+ name: trait_name.to_string(),
195
+ requirements: reqs,
196
+ value: value.clone().unbind(),
197
+ },
198
+ );
199
+ }
190
200
 
191
201
  Ok(())
192
202
  }
@@ -2,7 +2,10 @@ import util.assert_eq
2
2
 
3
3
  assert_eq(type([1, 2, 3]), list)
4
4
  assert_eq(type((1, 2, 3)), tuple)
5
- assert_eq(type({1: 2}), Record)
5
+
6
+ # Record type is not directly made available by the runtime.
7
+ assert_eq(type({1: 2}).__name__, "Record")
8
+
6
9
  assert_eq(type(()), tuple)
7
10
  assert_eq(type((1,)), tuple)
8
11
  assert_eq(type(1), int)
@@ -132,7 +132,6 @@ pub fn py_escape_str(s: &str) -> String {
132
132
  }
133
133
 
134
134
  pub fn py_escape_fstr(s: &str) -> String {
135
- // TODO
136
135
  py_escape_str(s)
137
136
  .chars()
138
137
  .map(|c| match c {
@@ -143,30 +142,6 @@ pub fn py_escape_fstr(s: &str) -> String {
143
142
  .collect()
144
143
  }
145
144
 
146
- pub fn escape_str(s: &str) -> String {
147
- // TODO
148
- s.chars()
149
- .map(|c| match c {
150
- '\n' => "\\n".to_string(),
151
- '"' => "\\\"".to_string(),
152
- _ => c.to_string(),
153
- })
154
- .collect()
155
- }
156
-
157
- pub fn escape_fstr(s: &str) -> String {
158
- // TODO
159
- s.chars()
160
- .map(|c| match c {
161
- '{' => "{{".to_string(),
162
- '}' => "}}".to_string(),
163
- '\n' => "\\n".to_string(),
164
- '"' => "\\\"".to_string(),
165
- _ => c.to_string(),
166
- })
167
- .collect()
168
- }
169
-
170
145
  // TODO don't duplicate this with parser below
171
146
  pub fn is_valid_ident(s: &str) -> bool {
172
147
  if s.is_empty() {
@@ -661,9 +636,11 @@ where
661
636
  current_str = String::new();
662
637
 
663
638
  self.parse_nonsemantic()?;
664
- let _ = self.try_parse(|x| x.parse_newline());
639
+ while self.try_parse(TokenizeCtx::parse_empty_line).is_ok() {}
640
+
665
641
  let sexpr = self.try_parse(|x| x.parse_block(0, NewBlockType::BeginInput))?;
666
- let _ = self.try_parse(|x| x.parse_newline());
642
+
643
+ while self.try_parse(TokenizeCtx::parse_empty_line).is_ok() {}
667
644
 
668
645
  self.parse_indentation()?;
669
646
 
@@ -851,7 +828,6 @@ where
851
828
  ) -> TResult<'src, Spanned<TokenList<'src>>> {
852
829
  let mut tokens = vec![];
853
830
 
854
- // TODO should parse_empty_line be part of parse_indentation?
855
831
  while self.try_parse(TokenizeCtx::parse_empty_line).is_ok() {}
856
832
 
857
833
  let indent_level = self.try_parse(|x| x.parse_indentation()).map_err(|_| {