koatl 0.1.27__tar.gz → 0.1.28__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 (122) hide show
  1. {koatl-0.1.27 → koatl-0.1.28}/Cargo.lock +1 -1
  2. {koatl-0.1.27 → koatl-0.1.28}/PKG-INFO +1 -1
  3. {koatl-0.1.27 → koatl-0.1.28}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.27 → koatl-0.1.28/koatl}/python/koatl/prelude/functional/algebra.tl +5 -10
  5. {koatl-0.1.27 → koatl-0.1.28/koatl}/python/koatl/prelude/functional/list.tl +2 -3
  6. {koatl-0.1.27 → koatl-0.1.28/koatl}/python/koatl/prelude/functional/result.tl +5 -6
  7. {koatl-0.1.27 → koatl-0.1.28/koatl}/python/koatl/prelude/iterable.tl +22 -26
  8. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/runtime/__init__.py +4 -1
  9. koatl-0.1.28/koatl/python/koatl/runtime/classes.py +92 -0
  10. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/runtime/helpers.py +0 -10
  11. koatl-0.1.28/koatl/python/koatl/runtime/virtual.py +103 -0
  12. {koatl-0.1.27 → koatl-0.1.28}/koatl/src/lib.rs +9 -5
  13. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/coal.tl +9 -0
  14. koatl-0.1.28/koatl/tests/e2e/prelude/aug_assign.tl +23 -0
  15. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/transform.rs +38 -60
  16. {koatl-0.1.27/koatl → koatl-0.1.28}/python/koatl/prelude/functional/algebra.tl +5 -10
  17. {koatl-0.1.27/koatl → koatl-0.1.28}/python/koatl/prelude/functional/list.tl +2 -3
  18. {koatl-0.1.27/koatl → koatl-0.1.28}/python/koatl/prelude/functional/result.tl +5 -6
  19. {koatl-0.1.27/koatl → koatl-0.1.28}/python/koatl/prelude/iterable.tl +22 -26
  20. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/runtime/__init__.py +4 -1
  21. koatl-0.1.28/python/koatl/runtime/classes.py +92 -0
  22. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/runtime/helpers.py +0 -10
  23. koatl-0.1.28/python/koatl/runtime/virtual.py +103 -0
  24. koatl-0.1.27/koatl/python/koatl/runtime/virtual.py +0 -139
  25. koatl-0.1.27/koatl/tests/e2e/prelude/aug_assign.tl +0 -13
  26. koatl-0.1.27/python/koatl/runtime/virtual.py +0 -139
  27. {koatl-0.1.27 → koatl-0.1.28}/Cargo.toml +0 -0
  28. {koatl-0.1.27 → koatl-0.1.28}/README.md +0 -0
  29. {koatl-0.1.27 → koatl-0.1.28}/koatl/.github/workflows/CI.yml +0 -0
  30. {koatl-0.1.27 → koatl-0.1.28}/koatl/.gitignore +0 -0
  31. {koatl-0.1.27 → koatl-0.1.28}/koatl/LICENSE +0 -0
  32. {koatl-0.1.27 → koatl-0.1.28}/koatl/README.md +0 -0
  33. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/__init__.py +0 -0
  34. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/__main__.py +0 -0
  35. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/cli.py +0 -0
  36. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/notebook/__init__.py +0 -0
  37. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/notebook/magic.py +0 -0
  38. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/prelude/__init__.tl +0 -0
  39. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/prelude/functional/__init__.tl +0 -0
  40. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/prelude/functional/async.tl +0 -0
  41. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
  42. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/prelude/functional/memo.tl +0 -0
  43. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/prelude/functional/reader.tl +0 -0
  44. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  45. {koatl-0.1.27 → koatl-0.1.28}/koatl/python/koatl/runtime/record.py +0 -0
  46. {koatl-0.1.27 → koatl-0.1.28}/koatl/requirements.txt +0 -0
  47. {koatl-0.1.27 → koatl-0.1.28}/koatl/src/emit_py.rs +0 -0
  48. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/containers.tl +0 -0
  49. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/decorators.tl +0 -0
  50. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  51. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/destructure.tl +0 -0
  52. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  53. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/fstr.tl +0 -0
  54. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/functions.tl +0 -0
  55. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/generator.tl +0 -0
  56. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/if_expr.tl +0 -0
  57. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/imports.tl +0 -0
  58. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/loops.tl +0 -0
  59. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/match.tl +0 -0
  60. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/nary-list.tl +0 -0
  61. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/placeholder.tl +0 -0
  62. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/precedence.tl +0 -0
  63. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/record.tl +0 -0
  64. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/scopes.tl +0 -0
  65. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  66. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/base/short_circuit.tl +0 -0
  67. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/destructure.tl +0 -0
  68. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/prelude/async.tl +0 -0
  69. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  70. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/prelude/list.tl +0 -0
  71. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/prelude/memo.tl +0 -0
  72. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/prelude/reader.tl +0 -0
  73. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/prelude/result.tl +0 -0
  74. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/prelude/slice.tl +0 -0
  75. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/prelude/try.tl +0 -0
  76. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  77. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/util/__init__.py +0 -0
  78. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/util/module0.tl +0 -0
  79. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/util/module1.tl +0 -0
  80. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/e2e/util/module2.tl +0 -0
  81. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/parse/arith.tl +0 -0
  82. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/parse/assign.tl +0 -0
  83. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/parse/block-comments.tl +0 -0
  84. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/parse/deco.tl +0 -0
  85. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/parse/func.tl +0 -0
  86. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/parse/matches.tl +0 -0
  87. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/test_e2e.py +0 -0
  88. {koatl-0.1.27 → koatl-0.1.28}/koatl/tests/test_parse.py +0 -0
  89. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/Cargo.toml +0 -0
  90. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/parser/Cargo.toml +0 -0
  91. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/parser/src/ast.rs +0 -0
  92. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/parser/src/lexer.rs +0 -0
  93. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/parser/src/lib.rs +0 -0
  94. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/parser/src/parser.rs +0 -0
  95. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/parser/src/util.rs +0 -0
  96. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/parser/tests/lexer.rs +0 -0
  97. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/inference.rs +0 -0
  98. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/lib.rs +0 -0
  99. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/main.rs +0 -0
  100. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/parse_timer.rs +0 -0
  101. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/parser.rs +0 -0
  102. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/py/ast.rs +0 -0
  103. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/py/emit.rs +0 -0
  104. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/py/mod.rs +0 -0
  105. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/py/util.rs +0 -0
  106. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/resolve_scopes.rs +0 -0
  107. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/types.rs +0 -0
  108. {koatl-0.1.27 → koatl-0.1.28}/koatl-core/src/util.rs +0 -0
  109. {koatl-0.1.27 → koatl-0.1.28}/pyproject.toml +0 -0
  110. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/__init__.py +0 -0
  111. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/__main__.py +0 -0
  112. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/cli.py +0 -0
  113. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/notebook/__init__.py +0 -0
  114. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/notebook/magic.py +0 -0
  115. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/prelude/__init__.tl +0 -0
  116. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/prelude/functional/__init__.tl +0 -0
  117. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/prelude/functional/async.tl +0 -0
  118. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/prelude/functional/async_util.py +0 -0
  119. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/prelude/functional/memo.tl +0 -0
  120. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/prelude/functional/reader.tl +0 -0
  121. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/runtime/meta_finder.py +0 -0
  122. {koatl-0.1.27 → koatl-0.1.28}/python/koatl/runtime/record.py +0 -0
@@ -99,7 +99,7 @@ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
99
99
 
100
100
  [[package]]
101
101
  name = "koatl"
102
- version = "0.1.27"
102
+ version = "0.1.28"
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.27
3
+ Version: 0.1.28
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.27"
3
+ version = "0.1.28"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -1,6 +1,4 @@
1
- import abc
2
-
3
- export Monad = class(abc.ABC):
1
+ export Monad = class(Trait):
4
2
  """
5
3
  Abstract base class for monads.
6
4
 
@@ -9,17 +7,16 @@ export Monad = class(abc.ABC):
9
7
  """
10
8
  __slots__ = ()
11
9
 
12
- bind = abc.abstractmethod& (self, f) => None
10
+ bind = Abstract& (self, f) => None
13
11
 
14
- pure = staticmethod& abc.abstractmethod& value => None
12
+ pure = staticmethod& Abstract& value => None
15
13
 
16
14
  # Automatically generated implementations.
17
15
  map = (self, f) => self.bind(x => self.pure(f(x)))
18
16
  apply = (self, f) => self.bind(x => f.map(fn => fn(x)))
19
17
 
20
18
 
21
- # TODO: should this inherit from Monad?
22
- export MonadOnce = class(abc.ABC):
19
+ export MonadOnce = class(Monad, Trait):
23
20
  """
24
21
  Abstract base class for single-use monads.
25
22
 
@@ -32,9 +29,7 @@ export MonadOnce = class(abc.ABC):
32
29
  __slots__ = ()
33
30
 
34
31
  # The default implementation required for `@` syntax that should be overridden by subclasses.
35
- bind_once = abc.abstractmethod& (self, f) => None
36
-
37
- pure = staticmethod& abc.abstractmethod& value => None
32
+ bind_once = Abstract& (self, f) => None
38
33
 
39
34
  # An optional, optimized implementation of `bind` that skips deep recursion.
40
35
  # TODO: can this be automatically generated from bind_once?
@@ -1,10 +1,9 @@
1
- import collections.UserList
2
1
  import itertools
3
2
 
4
3
  import .Monad
5
4
  import ..iterable.Iterable
6
5
 
7
- export List = class(UserList, Monad):
6
+ export List = class(Monad):
8
7
  bind_once = (self, f) =>
9
8
  raise NotImplementedError(
10
9
  "Binding List in a do-block doesn't work in Koatl " +
@@ -23,4 +22,4 @@ for name, method in List.__dict__:
23
22
  if name.startswith("_"):
24
23
  continue
25
24
 
26
- register_global_attr(list, name, method)
25
+ ExtensionMethod(list, name)& method
@@ -1,5 +1,4 @@
1
1
  import functools.wraps
2
- import koatl.runtime.virtual.register_global_attr
3
2
  import .algebra.MonadOnce
4
3
 
5
4
  export Result = class(MonadOnce):
@@ -118,14 +117,14 @@ export Err = class(Result):
118
117
  coalesce = (self, f) => f()
119
118
 
120
119
 
121
- register_global_attr(type(None), "ok", ExtensionProperty(_ => False))
122
- register_global_attr(BaseException, "ok", ExtensionProperty(_ => False))
123
- register_global_attr(object, "ok", ExtensionProperty(_ => True))
120
+ ExtensionProperty(type(None), "ok")& _ => False
121
+ ExtensionProperty(BaseException, "ok")& _ => False
122
+ ExtensionProperty(object, "ok")& _ => True
124
123
 
125
- register_global_attr(object, "result", ExtensionProperty(Result))
124
+ ExtensionProperty(object, "result")& Result
126
125
 
127
126
  # Enables @ operator for bare objects.
128
- register_global_attr(object, "bind_gen", Result.bind_gen)
127
+ ExtensionMethod(object, "bind_gen")& Result.bind_gen
129
128
 
130
129
  __tl__.Ok = Ok
131
130
  __tl__.Err = Err
@@ -1,15 +1,17 @@
1
1
  import itertools
2
2
  import builtins
3
3
 
4
- methods = {
5
- map: (x, f) => builtins.map(f, x.iter)
4
+ export Iterable = ExtensionTrait& class(Trait):
5
+ iter = Abstract
6
6
 
7
- filter: (x, f) => builtins.filter(f, x.iter)
7
+ map = (self, f) => builtins.map(f, self.iter)
8
8
 
9
- flat_map: (x, f) => itertools.chain.from_iterable(x.map(f))
9
+ filter = (self, f) => builtins.filter(f, self.iter)
10
10
 
11
- traverse: (x, f) =>
12
- let it = x.iter
11
+ flat_map = (self, f) => itertools.chain.from_iterable(self.map(f))
12
+
13
+ traverse = (self, f) =>
14
+ let it = self.iter
13
15
  let v
14
16
  try:
15
17
  v = next(it)
@@ -39,45 +41,39 @@ methods = {
39
41
  acc = acc.apply(m.map(x => acc => [*acc, x]))
40
42
  acc
41
43
 
42
- fold: (x, init, f) =>
44
+ fold = (self, init, f) =>
43
45
  let acc = init
44
- for i in x:
46
+ for i in self:
45
47
  acc = f(acc, i)
46
48
  acc
47
49
 
48
- first: (x, f) =>
49
- for i in x:
50
+ first = (self, f) =>
51
+ for i in self:
50
52
  if f(i):
51
53
  return i
52
54
  return None
53
55
 
54
- last: (x, f) =>
56
+ last = (self, f) =>
55
57
  let result = None
56
- for i in x:
58
+ for i in self:
57
59
  if f(i):
58
60
  result = i
59
61
  return result
60
62
 
61
- at: (x, index) =>
62
- for i in x:
63
+ at = (self, index) =>
64
+ for i in self:
63
65
  if i == index:
64
66
  return i
65
67
  raise IndexError("Index out of range")
66
68
 
67
- sum: x =>
69
+ sum = self =>
68
70
  let acc = 0
69
- for i in x:
71
+ for i in self:
70
72
  acc = acc + i
71
73
  acc
72
74
 
73
- list: x =>
74
- list(x.iter)
75
-
76
- record: x =>
77
- Record(x.iter)
78
- }
79
-
80
- # TODO merge this with typing.Iterable?
75
+ list = self =>
76
+ list(self.iter)
81
77
 
82
- export Iterable = Trait(__name__, "Iterable", methods, requires=["iter"])
83
- register_global_trait(Iterable)
78
+ record = self =>
79
+ Record(self.iter)
@@ -19,6 +19,7 @@ meta_finder.install_hook()
19
19
  from .virtual import *
20
20
  from .record import *
21
21
  from .helpers import *
22
+ from .classes import *
22
23
 
23
24
 
24
25
  def dummy(name):
@@ -33,9 +34,9 @@ def dummy(name):
33
34
  __tl__ = SimpleNamespace(
34
35
  Exception=Exception,
35
36
  slice=slice,
37
+ type=type,
36
38
  vget=virtual.vget,
37
39
  vhas=virtual.vhas,
38
- unpack_record=helpers.unpack_record,
39
40
  set_exports=helpers.set_exports,
40
41
  do=helpers.do,
41
42
  partial=functools.partial,
@@ -50,6 +51,7 @@ __tl__ = SimpleNamespace(
50
51
  **{name: helpers.__dict__[name] for name in helpers.__all__},
51
52
  **{name: record.__dict__[name] for name in record.__all__},
52
53
  **{name: virtual.__dict__[name] for name in virtual.__all__},
54
+ **{name: classes.__dict__[name] for name in classes.__all__},
53
55
  )
54
56
 
55
57
 
@@ -58,4 +60,5 @@ __all__ = [
58
60
  *helpers.__all__,
59
61
  *record.__all__,
60
62
  *virtual.__all__,
63
+ *classes.__all__,
61
64
  ]
@@ -0,0 +1,92 @@
1
+ import abc
2
+ import collections
3
+ import inspect
4
+
5
+ from koatl.runtime.virtual import vhas
6
+
7
+
8
+ @collections.abc.Mapping.register
9
+ class MappingMeta(type):
10
+ def __getitem__(self, key):
11
+ return getattr(self, key)
12
+
13
+ def __iter__(self):
14
+ return self.keys().__iter__()
15
+
16
+ def __len__(self):
17
+ return len(self.items())
18
+
19
+ def __contains__(self, key):
20
+ return hasattr(self, key)
21
+
22
+ def get(self, key, default=None):
23
+ if hasattr(self, key):
24
+ return getattr(self, key)
25
+ else:
26
+ return default
27
+
28
+ def keys(self):
29
+ return [k for k, _ in self.items()]
30
+
31
+ def values(self):
32
+ return [v for _, v in self.items()]
33
+
34
+ def items(self):
35
+ return [(k, v) for k, v in inspect.getmembers(self) if not k.startswith("_")]
36
+
37
+
38
+ class Class(metaclass=MappingMeta):
39
+ pass
40
+
41
+
42
+ # temporary value, since TraitMeta needs to reference Trait.
43
+ Trait = None
44
+
45
+
46
+ class TraitMeta(MappingMeta):
47
+ def __init__(cls, name, bases, namespace):
48
+ super().__init__(name, bases, namespace)
49
+
50
+ if Trait in bases:
51
+ cls._trait_reqs = []
52
+ cls._own_methods = []
53
+
54
+ for key, value in inspect.getmembers(cls):
55
+ if (
56
+ hasattr(value, "__isabstractmethod__")
57
+ and value.__isabstractmethod__
58
+ ):
59
+ cls._trait_reqs.append(key)
60
+
61
+ for key, value in namespace.items():
62
+ if (
63
+ hasattr(value, "__isabstractmethod__")
64
+ and value.__isabstractmethod__
65
+ ):
66
+ # Abstract methods are not considered "own methods".
67
+ continue
68
+
69
+ if callable(value) and not key.startswith("_"):
70
+ cls._own_methods.append(key)
71
+
72
+ def __instancecheck__(cls, instance):
73
+ # Check if the exact class has _trait_reqs, in which case it's a trait.
74
+ if "_trait_reqs" in cls.__dict__:
75
+ for req in cls._trait_reqs:
76
+ if not vhas(instance, req):
77
+ return False
78
+
79
+ return True
80
+ else:
81
+ return type.__instancecheck__(cls, instance)
82
+
83
+
84
+ class Trait(metaclass=TraitMeta):
85
+ pass
86
+
87
+
88
+ Abstract = lambda value: abc.abstractmethod(value)
89
+ Abstract.__isabstractmethod__ = True
90
+
91
+
92
+ __all__ = ["Class", "Trait", "Abstract"]
@@ -35,16 +35,6 @@ def set_exports(package_name, globals_dict, exports, module_star_exports):
35
35
  globals_dict["__all__"] = tuple(set(globals_dict["__all__"]) | exports)
36
36
 
37
37
 
38
- def unpack_record(obj):
39
- """
40
- used in record unpacking
41
- """
42
- if hasattr(obj, "items"):
43
- return Record(obj.items())
44
- else:
45
- return Record(obj.__dict__)
46
-
47
-
48
38
  def ok(obj):
49
39
  try:
50
40
  return obj.ok
@@ -0,0 +1,103 @@
1
+ from functools import partial
2
+ from itertools import count
3
+ from .._rs import fast_vget, fast_vset, fast_vset_trait
4
+
5
+
6
+ def vget(obj, name, ignore_traits=False):
7
+ try:
8
+ return getattr(obj, name)
9
+ except:
10
+ # special case for iter - this could be implemented using types and trait vtbls
11
+ # but this is simpler and probably faster
12
+ if name == "iter":
13
+ if isinstance(obj, slice):
14
+ start = obj.start if obj.start is not None else 0
15
+ step = obj.step if obj.step is not None else 1
16
+ if obj.stop is None:
17
+ return count(start, step)
18
+ else:
19
+ return range(start, obj.stop, step)
20
+
21
+ try:
22
+ return obj.items()
23
+ except AttributeError:
24
+ pass
25
+
26
+ try:
27
+ return iter(obj)
28
+ except TypeError:
29
+ pass
30
+
31
+ v = fast_vget(obj, name, ignore_traits)
32
+ if v is not None:
33
+ if hasattr(v, "_ext_prop"):
34
+ return v(obj)
35
+
36
+ return partial(v, obj)
37
+
38
+ raise AttributeError(
39
+ f"'{type(obj).__name__}' object has no v-attribute '{name}'"
40
+ ) from None
41
+
42
+
43
+ def vhas(obj, name, ignore_traits=False):
44
+ if hasattr(obj, name):
45
+ return True
46
+
47
+ if name == "iter":
48
+ if isinstance(obj, slice):
49
+ return True
50
+
51
+ try:
52
+ obj.items()
53
+ return True
54
+ except AttributeError:
55
+ pass
56
+
57
+ try:
58
+ iter(obj)
59
+ return True
60
+ except TypeError:
61
+ pass
62
+
63
+ v = fast_vget(obj, name, ignore_traits)
64
+ if v is not None:
65
+ return True
66
+
67
+ return False
68
+
69
+
70
+ def ExtensionProperty(type, name):
71
+ def impl(value):
72
+ value._ext_prop = True
73
+ fast_vset(type, name, value)
74
+ return value
75
+
76
+ return impl
77
+
78
+
79
+ def ExtensionMethod(type, name):
80
+ def impl(value):
81
+ fast_vset(type, name, value)
82
+ return value
83
+
84
+ return impl
85
+
86
+
87
+ def ExtensionTrait(type):
88
+ for name in type._own_methods:
89
+ fast_vset_trait(type.__name__, type._trait_reqs, name, type.__dict__[name])
90
+ return type
91
+
92
+
93
+ def TraitProperty(value):
94
+ value._ext_prop = True
95
+ return value
96
+
97
+
98
+ __all__ = [
99
+ "ExtensionProperty",
100
+ "ExtensionMethod",
101
+ "ExtensionTrait",
102
+ "TraitProperty",
103
+ ]
@@ -178,11 +178,15 @@ fn fast_vset_trait<'py, 'ptr>(
178
178
  vtbl.insert(name.clone(), Vec::new());
179
179
  }
180
180
 
181
- vtbl.get_mut(&name).unwrap().push(TraitAttr {
182
- name: trait_name.to_string(),
183
- requirements: reqs,
184
- value: value.clone().unbind(),
185
- });
181
+ // 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
+ );
186
190
 
187
191
  Ok(())
188
192
  }
@@ -17,3 +17,12 @@ assert_eq(1?.($ + 1)(), 2)
17
17
 
18
18
  assert_eq(None ?? 1, 1)
19
19
  assert_eq(int(5) ?? 1, 5)
20
+
21
+ counts = 0
22
+ f = () =>
23
+ counts = counts + 1
24
+
25
+ f() ?? 1
26
+
27
+ # should only call once
28
+ assert_eq(counts, 1)
@@ -0,0 +1,23 @@
1
+ import util.assert_eq
2
+
3
+ x = 1
4
+ x += 2
5
+ assert_eq(x, 3)
6
+
7
+ x = None
8
+ x ??= 2
9
+ assert_eq(x, 2)
10
+
11
+ x = 4
12
+ x |= $ + 2
13
+ assert_eq(x, 6)
14
+
15
+
16
+ counts = 0
17
+ f = () =>
18
+ counts += 1
19
+ {x: None}
20
+ f().x ??= 2
21
+
22
+ # should only call once
23
+ assert_eq(counts, 1)
@@ -230,6 +230,29 @@ fn simple_err(msg: impl Into<String>, span: Span) -> TlErrs {
230
230
  TlErrBuilder::new().message(msg.into()).span(span).build()
231
231
  }
232
232
 
233
+ fn deduplicate<'src, 'ast>(
234
+ ctx: &mut TlCtx<'src, 'ast>,
235
+ expr: SPyExpr<'src>,
236
+ span: Span,
237
+ ) -> TlResult<SPyExprWithPre<'src>> {
238
+ let var_name = ctx.create_aux_var("lhs", span.start);
239
+ let a = PyAstBuilder::new(span);
240
+ let mut pre = PyBlock::new();
241
+
242
+ let expr = match expr.value {
243
+ PyExpr::Ident(id, _) => a.load_ident(id),
244
+ x => {
245
+ pre.push(a.assign(
246
+ a.ident(var_name.clone(), PyAccessCtx::Store),
247
+ (x, expr.tl_span).into(),
248
+ ));
249
+ a.load_ident(var_name.clone())
250
+ }
251
+ };
252
+
253
+ Ok(SPyExprWithPre { value: expr, pre })
254
+ }
255
+
233
256
  impl<'src> BlockExt<'src> for [Indirect<SStmt<'src>>] {
234
257
  fn transform<'ast>(
235
258
  &'ast self,
@@ -715,10 +738,16 @@ fn create_throwing_matcher<'src, 'ast>(
715
738
  let fail = PyBlock(vec![a.raise(Some(a.call(
716
739
  a.load_ident("MatchError"),
717
740
  vec![a.call_arg(a.fstr(vec![
718
- a.fstr_str("failed to match value "),
719
- a.fstr_expr(a.load_ident(cursor.clone()), None),
741
+ a.fstr_str("failed to match value of type "),
742
+ a.fstr_expr(
743
+ a.call(
744
+ a.tl_builtin("type"),
745
+ vec![a.call_arg(a.load_ident(cursor.clone()))],
746
+ ),
747
+ None,
748
+ ),
720
749
  a.fstr_str(format!(
721
- " to pattern {}",
750
+ " to pattern \"{}\"",
722
751
  &ctx.source[pattern.span.start..pattern.span.end]
723
752
  )),
724
753
  ]))],
@@ -1528,29 +1557,6 @@ impl<'src> SStmtExt<'src> for SStmt<'src> {
1528
1557
  if let Some(BinaryOp::Coalesce | BinaryOp::Pipe) = op {
1529
1558
  let lhs = pre.bind(lhs.transform_store(ctx)?);
1530
1559
 
1531
- fn deduplicate<'src, 'ast>(
1532
- ctx: &mut TlCtx<'src, 'ast>,
1533
- expr: SPyExpr<'src>,
1534
- span: Span,
1535
- ) -> TlResult<SPyExprWithPre<'src>> {
1536
- let var_name = ctx.create_aux_var("lhs", span.start);
1537
- let a = PyAstBuilder::new(span);
1538
- let mut pre = PyBlock::new();
1539
-
1540
- let expr = match expr.value {
1541
- PyExpr::Ident(id, _) => a.load_ident(id),
1542
- x => {
1543
- pre.push(a.assign(
1544
- a.ident(var_name.clone(), PyAccessCtx::Store),
1545
- (x, expr.tl_span).into(),
1546
- ));
1547
- a.load_ident(var_name.clone())
1548
- }
1549
- };
1550
-
1551
- Ok(SPyExprWithPre { value: expr, pre })
1552
- }
1553
-
1554
1560
  // TODO this is a bit hacky. We need to get separate the root node
1555
1561
  // from the rest of the expression to avoid double evaluation
1556
1562
  let (safe_lhs_store, safe_lhs_load) = match lhs.value {
@@ -1568,11 +1574,11 @@ impl<'src> SStmtExt<'src> for SStmt<'src> {
1568
1574
  }
1569
1575
  PyExpr::Subscript(left, right, _) => {
1570
1576
  let left_span = left.tl_span;
1571
- // TODO, critical: this leads to double evaluation of subscript indices
1572
1577
  let dedup = pre.bind(deduplicate(ctx, *left, left_span)?);
1578
+ let dedup_right = pre.bind(deduplicate(ctx, *right, left_span)?);
1573
1579
  (
1574
- a.subscript(dedup.clone(), *right.clone(), PyAccessCtx::Store),
1575
- a.subscript(dedup, *right, PyAccessCtx::Load),
1580
+ a.subscript(dedup.clone(), dedup_right.clone(), PyAccessCtx::Store),
1581
+ a.subscript(dedup, dedup_right, PyAccessCtx::Load),
1576
1582
  )
1577
1583
  }
1578
1584
  _ => panic!(),
@@ -1717,17 +1723,6 @@ impl<'src> SStmtExt<'src> for SStmt<'src> {
1717
1723
  trait SExprExt<'src, 'ast> {
1718
1724
  fn transform(&'ast self, ctx: &mut TlCtx<'src, 'ast>) -> TlResult<SPyExprWithPre<'src>>;
1719
1725
 
1720
- /**
1721
- * Transforms
1722
- * expr
1723
- * to
1724
- * x = expr
1725
- * x
1726
- *
1727
- * to avoid evaluating expr multiple times
1728
- */
1729
- fn transform_lifted(&'ast self, ctx: &mut TlCtx<'src, 'ast>) -> TlResult<SPyExprWithPre<'src>>;
1730
-
1731
1726
  fn transform_store(&'ast self, ctx: &mut TlCtx<'src, 'ast>) -> TlResult<SPyExprWithPre<'src>>;
1732
1727
 
1733
1728
  fn transform_full(
@@ -1742,25 +1737,6 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for SExpr<'src> {
1742
1737
  self.transform_full(ctx, PyAccessCtx::Load)
1743
1738
  }
1744
1739
 
1745
- fn transform_lifted(&'ast self, ctx: &mut TlCtx<'src, 'ast>) -> TlResult<SPyExprWithPre<'src>> {
1746
- let mut pre = PyBlock::new();
1747
- let value = pre.bind(self.transform(ctx)?);
1748
- let a = PyAstBuilder::new(self.span);
1749
-
1750
- let expr = match self.value {
1751
- Expr::Ident(..) | Expr::Literal(..) => value,
1752
- _ => {
1753
- let temp_var = ctx.create_aux_var("tmp", self.span.start);
1754
-
1755
- pre.push(a.assign(a.ident(temp_var.clone(), PyAccessCtx::Store), value));
1756
-
1757
- a.ident(temp_var, PyAccessCtx::Load)
1758
- }
1759
- };
1760
-
1761
- Ok(SPyExprWithPre { value: expr, pre })
1762
- }
1763
-
1764
1740
  fn transform_store(&'ast self, ctx: &mut TlCtx<'src, 'ast>) -> TlResult<SPyExprWithPre<'src>> {
1765
1741
  self.transform_full(ctx, PyAccessCtx::Store)
1766
1742
  }
@@ -1939,10 +1915,12 @@ impl<'src, 'ast> SExprExt<'src, 'ast> for SExpr<'src> {
1939
1915
  }
1940
1916
  Expr::Binary(op, lhs, rhs) => 'block: {
1941
1917
  if let BinaryOp::Coalesce = op {
1942
- let py_lhs = pre.bind(lhs.transform_lifted(ctx)?);
1918
+ let py_lhs = pre.bind(lhs.transform(ctx)?);
1943
1919
  let call = pre.bind(create_coalesce(ctx, py_lhs, rhs, span)?);
1944
1920
  break 'block call;
1945
1921
  } else if let BinaryOp::And | BinaryOp::Or = op {
1922
+ // handle short-circuiting manually
1923
+
1946
1924
  let is_and = *op == BinaryOp::And;
1947
1925
 
1948
1926
  let py_lhs = pre.bind(lhs.transform(ctx)?);