koatl 0.1.22__tar.gz → 0.1.24__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 (118) hide show
  1. {koatl-0.1.22 → koatl-0.1.24}/Cargo.lock +1 -1
  2. {koatl-0.1.22 → koatl-0.1.24}/PKG-INFO +1 -1
  3. {koatl-0.1.22 → koatl-0.1.24}/koatl/Cargo.toml +1 -1
  4. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/prelude/functional/__init__.tl +16 -3
  5. koatl-0.1.24/koatl/python/koatl/prelude/functional/list.tl +26 -0
  6. {koatl-0.1.22 → koatl-0.1.24/koatl}/python/koatl/prelude/functional/memo.tl +5 -4
  7. {koatl-0.1.22 → koatl-0.1.24/koatl}/python/koatl/prelude/functional/monad.tl +12 -4
  8. koatl-0.1.24/koatl/python/koatl/prelude/functional/result.tl +131 -0
  9. koatl-0.1.24/koatl/python/koatl/prelude/iterable.tl +83 -0
  10. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/runtime/helpers.py +5 -0
  11. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/runtime/virtual.py +2 -2
  12. {koatl-0.1.22 → koatl-0.1.24}/koatl/src/emit_py.rs +20 -3
  13. {koatl-0.1.22 → koatl-0.1.24}/koatl/src/lib.rs +23 -8
  14. koatl-0.1.24/koatl/tests/e2e/prelude/list.tl +18 -0
  15. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/parser/src/parser.rs +11 -1
  16. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/resolve_scopes.rs +25 -13
  17. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/transform.rs +46 -36
  18. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/prelude/functional/__init__.tl +16 -3
  19. koatl-0.1.24/python/koatl/prelude/functional/list.tl +26 -0
  20. {koatl-0.1.22/koatl → koatl-0.1.24}/python/koatl/prelude/functional/memo.tl +5 -4
  21. {koatl-0.1.22/koatl → koatl-0.1.24}/python/koatl/prelude/functional/monad.tl +12 -4
  22. koatl-0.1.24/python/koatl/prelude/functional/result.tl +131 -0
  23. koatl-0.1.24/python/koatl/prelude/iterable.tl +83 -0
  24. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/runtime/helpers.py +5 -0
  25. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/runtime/virtual.py +2 -2
  26. koatl-0.1.22/koatl/python/koatl/prelude/functional/result.tl +0 -93
  27. koatl-0.1.22/koatl/python/koatl/prelude/iterable.tl +0 -56
  28. koatl-0.1.22/python/koatl/prelude/functional/result.tl +0 -93
  29. koatl-0.1.22/python/koatl/prelude/iterable.tl +0 -56
  30. {koatl-0.1.22 → koatl-0.1.24}/Cargo.toml +0 -0
  31. {koatl-0.1.22 → koatl-0.1.24}/README.md +0 -0
  32. {koatl-0.1.22 → koatl-0.1.24}/koatl/.github/workflows/CI.yml +0 -0
  33. {koatl-0.1.22 → koatl-0.1.24}/koatl/.gitignore +0 -0
  34. {koatl-0.1.22 → koatl-0.1.24}/koatl/LICENSE +0 -0
  35. {koatl-0.1.22 → koatl-0.1.24}/koatl/README.md +0 -0
  36. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/__init__.py +0 -0
  37. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/__main__.py +0 -0
  38. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/cli.py +0 -0
  39. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/notebook/__init__.py +0 -0
  40. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/notebook/magic.py +0 -0
  41. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/prelude/__init__.tl +0 -0
  42. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/prelude/functional/async.tl +0 -0
  43. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
  44. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/prelude/functional/reader.tl +0 -0
  45. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/runtime/__init__.py +0 -0
  46. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/runtime/meta_finder.py +0 -0
  47. {koatl-0.1.22 → koatl-0.1.24}/koatl/python/koatl/runtime/record.py +0 -0
  48. {koatl-0.1.22 → koatl-0.1.24}/koatl/requirements.txt +0 -0
  49. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/coal.tl +0 -0
  50. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/containers.tl +0 -0
  51. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/decorators.tl +0 -0
  52. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
  53. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/destructure.tl +0 -0
  54. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/escape_ident.tl +0 -0
  55. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/fstr.tl +0 -0
  56. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/functions.tl +0 -0
  57. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/generator.tl +0 -0
  58. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/if_expr.tl +0 -0
  59. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/imports.tl +0 -0
  60. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/loops.tl +0 -0
  61. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/match.tl +0 -0
  62. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/nary-list.tl +0 -0
  63. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/placeholder.tl +0 -0
  64. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/precedence.tl +0 -0
  65. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/scopes.tl +0 -0
  66. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
  67. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/slice.tl +0 -0
  68. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/base/try.tl +0 -0
  69. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/destructure.tl +0 -0
  70. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/prelude/async.tl +0 -0
  71. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/prelude/iterables.tl +0 -0
  72. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/prelude/memo.tl +0 -0
  73. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/prelude/reader.tl +0 -0
  74. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/prelude/result.tl +0 -0
  75. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/prelude/virtual.tl +0 -0
  76. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/util/__init__.py +0 -0
  77. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/util/module0.tl +0 -0
  78. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/util/module1.tl +0 -0
  79. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/e2e/util/module2.tl +0 -0
  80. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/parse/arith.tl +0 -0
  81. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/parse/assign.tl +0 -0
  82. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/parse/block-comments.tl +0 -0
  83. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/parse/deco.tl +0 -0
  84. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/parse/func.tl +0 -0
  85. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/parse/matches.tl +0 -0
  86. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/test_e2e.py +0 -0
  87. {koatl-0.1.22 → koatl-0.1.24}/koatl/tests/test_parse.py +0 -0
  88. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/Cargo.toml +0 -0
  89. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/parser/Cargo.toml +0 -0
  90. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/parser/src/ast.rs +0 -0
  91. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/parser/src/lexer.rs +0 -0
  92. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/parser/src/lib.rs +0 -0
  93. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/parser/src/util.rs +0 -0
  94. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/parser/tests/lexer.rs +0 -0
  95. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/inference.rs +0 -0
  96. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/lib.rs +0 -0
  97. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/main.rs +0 -0
  98. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/parse_timer.rs +0 -0
  99. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/parser.rs +0 -0
  100. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/py/ast.rs +0 -0
  101. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/py/emit.rs +0 -0
  102. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/py/mod.rs +0 -0
  103. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/py/util.rs +0 -0
  104. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/types.rs +0 -0
  105. {koatl-0.1.22 → koatl-0.1.24}/koatl-core/src/util.rs +0 -0
  106. {koatl-0.1.22 → koatl-0.1.24}/pyproject.toml +0 -0
  107. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/__init__.py +0 -0
  108. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/__main__.py +0 -0
  109. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/cli.py +0 -0
  110. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/notebook/__init__.py +0 -0
  111. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/notebook/magic.py +0 -0
  112. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/prelude/__init__.tl +0 -0
  113. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/prelude/functional/async.tl +0 -0
  114. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/prelude/functional/async_util.py +0 -0
  115. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/prelude/functional/reader.tl +0 -0
  116. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/runtime/__init__.py +0 -0
  117. {koatl-0.1.22 → koatl-0.1.24}/python/koatl/runtime/meta_finder.py +0 -0
  118. {koatl-0.1.22 → koatl-0.1.24}/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.22"
102
+ version = "0.1.24"
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.22
3
+ Version: 0.1.24
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.22"
3
+ version = "0.1.24"
4
4
  edition = "2021"
5
5
 
6
6
  [lib]
@@ -3,9 +3,17 @@ export import .result.*
3
3
  export import .async.*
4
4
  export import .reader.*
5
5
  export import .memo.*
6
+ export import .list.*
6
7
 
7
- export Fn = class:
8
- compose = staticmethod& (*args) =>
8
+ named = name => fn =>
9
+ fn.__name__ = name
10
+ fn.__qualname__ = name
11
+ fn
12
+
13
+ methods = {
14
+ id: named("id")& x => x
15
+
16
+ compose: named("compose")& (*args) =>
9
17
  args match:
10
18
  [] => raise ValueError("At least one function is required for composition")
11
19
  [f] => f
@@ -21,4 +29,9 @@ export Fn = class:
21
29
  composed.signature = fs[-1].signature
22
30
 
23
31
  composed
24
- default raise ValueError("Invalid arguments for Fn.compose()")
32
+ default raise ValueError("Invalid arguments for Fn.compose()")
33
+ }
34
+
35
+ globals().update(methods)
36
+
37
+ __all__ = methods.keys() | tuple
@@ -0,0 +1,26 @@
1
+ import collections.UserList
2
+ import itertools
3
+
4
+ import .monad.Monad
5
+ import ..iterable.Iterable
6
+
7
+ export List = class(UserList, Monad):
8
+ bind_once = (self, f) =>
9
+ raise NotImplementedError(
10
+ "Binding List in a do-block doesn't work in Koatl " +
11
+ "due to generator limitations. " +
12
+ "Wrap in Ok to use the regular Result monad."
13
+ )
14
+
15
+ bind = (self, f) => List(self.flat_map(f))
16
+
17
+ pure = staticmethod& x => [x]
18
+
19
+ traverse = Iterable.traverse
20
+
21
+
22
+ for name, method in List.__dict__:
23
+ if name.startswith("_"):
24
+ continue
25
+
26
+ register_global_attr(list, name, method)
@@ -1,6 +1,6 @@
1
1
  import collections.defaultdict
2
2
  import functools.wraps
3
- import .result.Ok
3
+ import .result.(Result, Ok)
4
4
  import .Monad
5
5
 
6
6
  export Memo = class(Monad):
@@ -11,7 +11,8 @@ export Memo = class(Monad):
11
11
  __repr__ = self => f"Memo.Cache({self.cache})"
12
12
 
13
13
  try_get = (self, name, deps) =>
14
- try self.cache[name][deps] except KeyError()
14
+ # TODO should try operator return a Result directly?
15
+ Result(try self.cache[name][deps] except KeyError())
15
16
 
16
17
  update = (self, name, deps, value) =>
17
18
  self.cache[name][deps] = value
@@ -23,7 +24,7 @@ export Memo = class(Monad):
23
24
 
24
25
  value = staticmethod& (id, deps, f) =>
25
26
  Memo(ctx =>
26
- if ctx.try_get(id, tuple(deps)) matches Ok() as value:
27
+ if ctx.try_get(id, tuple(deps)) matches Ok(value):
27
28
  return value
28
29
 
29
30
  ctx.update(id, tuple(deps), f())
@@ -34,7 +35,7 @@ export Memo = class(Monad):
34
35
  let deps = (tuple(args), tuple(kwargs.items()))
35
36
 
36
37
  Memo(ctx =>
37
- if ctx.try_get(id, deps) matches Ok() as value:
38
+ if ctx.try_get(id, deps) matches Ok(value):
38
39
  return value
39
40
 
40
41
  let v = f(*args, **kwargs)
@@ -1,12 +1,20 @@
1
1
  import abc
2
2
 
3
3
  export Monad = class(abc.ABC):
4
+ __slots__ = ()
5
+
6
+ # The default implementation required for `@` syntax that should be overridden by subclasses.
7
+ bind_once = abc.abstractmethod& (self, f) => None
8
+
9
+ pure = staticmethod& abc.abstractmethod& value => None
10
+
11
+ # Automatically given by bind_once.
4
12
  bind = (self, f) => self.bind_once(f)
5
13
 
6
14
  # An optional, optimized implementation of `bind` that skips deep recursion.
15
+ # TODO: can this be automatically generated?
7
16
  bind_gen = (self, gen) => raise NotImplementedError()
8
17
 
9
- # The default implementation required for `@` syntax that should be overridden by subclasses.
10
- bind_once = abc.abstractmethod& (self, f) => None
11
-
12
- pure = staticmethod& abc.abstractmethod& value => None
18
+ # Automatically generated implementations.
19
+ map = (self, f) => self.bind(x => self.pure(f(x)))
20
+ apply = (self, f) => self.bind(x => f.map(fn => fn(x)))
@@ -0,0 +1,131 @@
1
+ import functools.wraps
2
+ import .monad.Monad
3
+ import koatl.runtime.virtual.register_global_attr
4
+
5
+ export Result = class:
6
+ __slots__ = ()
7
+ __match_args__ = ("value",)
8
+
9
+ __new__ = (cls, value) =>
10
+ if isinstance(value, Result):
11
+ return value
12
+ value.ok then Ok(value) else Err(value)
13
+
14
+ __init__ = (self, *args, **kwargs) =>
15
+ raise ValueError("Result should not be instantiated directly, use Ok or Err")
16
+
17
+ raw = self =>
18
+ """
19
+ Unwraps the value from a Result, making it look like
20
+ a non-monadic Python value.
21
+ """
22
+ if self matches not Result():
23
+ return self
24
+
25
+ if self.ok:
26
+ if self.value.ok:
27
+ return self.value
28
+ else:
29
+ return self
30
+ else:
31
+ if self.value.ok:
32
+ return self
33
+ else:
34
+ return self.value
35
+
36
+ bind_once = (self, f) =>
37
+ self = Result(self)
38
+ if not self.ok:
39
+ return self
40
+
41
+ let v = Result(f(self.value))
42
+
43
+ bind = bind_once
44
+
45
+ bind_gen = (self, gen) =>
46
+ self = Result(self)
47
+ try:
48
+ while True:
49
+ if not self.ok:
50
+ return self
51
+
52
+ self = Result(gen.send(self.value))
53
+ except StopIteration(value=value):
54
+ return Result.pure(value)
55
+
56
+ pure = staticmethod& x => Ok(x)
57
+
58
+ apply = (self, f) =>
59
+ self = Result(self)
60
+ f = Result(f)
61
+
62
+ if not self.ok:
63
+ return self
64
+ if not f.ok:
65
+ return f
66
+
67
+ Ok(f(self))
68
+
69
+ map = (self, f) =>
70
+ self = Result(self)
71
+ if self.ok:
72
+ return Ok(f(self.value))
73
+ return self
74
+
75
+ map_err = (self, f) =>
76
+ self = Result(self)
77
+ if not self.ok:
78
+ return Err(f(self.value))
79
+ return self
80
+
81
+ map_none = (self, f) =>
82
+ self = Result(self)
83
+ if not self.ok and self.value === None:
84
+ return Err(f())
85
+ return self
86
+
87
+ export Ok = class(Result):
88
+ __slots__ = ("value",)
89
+ ok = True
90
+ __new__ = (cls, value) => object.__new__(cls)
91
+ __init__ = (self, value) =>
92
+ if hasattr(self, "value"):
93
+ # This is necessary to prevent overwriting the value
94
+ # since Python's Result.__new__ will call Ok.__init__ again
95
+ # if the object is already created.
96
+ return None
97
+ self.value = value
98
+
99
+ __repr__ = self => f"Ok({repr(self.value)})"
100
+ unwrap = self => self.value
101
+
102
+ export Err = class(Result):
103
+ __slots__ = ("value",)
104
+ ok = False
105
+ __new__ = (cls, value) => object.__new__(cls)
106
+ __init__ = (self, value) =>
107
+ if hasattr(self, "value"):
108
+ return None
109
+ self.value = value
110
+
111
+ __repr__ = self => f"Err({repr(self.value)})"
112
+ unwrap = self =>
113
+ if self.value matches BaseException():
114
+ raise self.value
115
+ raise ValueError(f"Expected Ok, got {repr(self.value)}")
116
+
117
+
118
+ # Provide a default implementation for *most* Result methods
119
+ # ...we shouldn't provide a default for `map` since that
120
+ # would prevent virtual trait lookup. TODO: fix this?
121
+ for name, method in Result.__dict__:
122
+ if name.startswith("_"):
123
+ continue
124
+ if name == "map":
125
+ continue
126
+
127
+ let make_closure = method => (self, *args, **kwargs) => Result.raw(method(self, *args, **kwargs))
128
+
129
+ register_global_attr(object, name, make_closure(method))
130
+
131
+ register_global_attr(object, "ok", ExtensionProperty(__tl__.ok))
@@ -0,0 +1,83 @@
1
+ import itertools
2
+ import builtins
3
+
4
+ methods = {
5
+ map: (x, f) => builtins.map(f, x.iter)
6
+
7
+ filter: (x, f) => builtins.filter(f, x.iter)
8
+
9
+ flat_map: (x, f) => itertools.chain.from_iterable(x.map(f))
10
+
11
+ traverse: (x, f) =>
12
+ let it = x.iter
13
+ let v
14
+ try:
15
+ v = next(it)
16
+ except StopIteration():
17
+ return []
18
+
19
+ let m = f(v)
20
+
21
+ if not hasattr(m, "apply"):
22
+ # special case for bare types - slightly more efficient
23
+ # ...also required since bare types don't have .map
24
+ if not __tl__.ok(m):
25
+ return m
26
+
27
+ let acc = [m]
28
+ for v in it:
29
+ let fv = f(v)
30
+ if not __tl__.ok(fv):
31
+ return fv
32
+ acc.append(fv)
33
+ return acc
34
+
35
+ let acc = m.map([$])
36
+ for v in it:
37
+ m = f(v)
38
+ # acc = liftA2((list, value) => [*list, value])(acc, f(v))
39
+ acc = acc.apply(m.map(x => acc => [*acc, x]))
40
+ acc
41
+
42
+ fold: (x, init, f) =>
43
+ let acc = init
44
+ for i in x:
45
+ acc = f(acc, i)
46
+ acc
47
+
48
+ first: (x, f) =>
49
+ for i in x:
50
+ if f(i):
51
+ return i
52
+ return None
53
+
54
+ last: (x, f) =>
55
+ let result = None
56
+ for i in x:
57
+ if f(i):
58
+ result = i
59
+ return result
60
+
61
+ at: (x, index) =>
62
+ for i in x:
63
+ if i == index:
64
+ return i
65
+ raise IndexError("Index out of range")
66
+
67
+ sum: x =>
68
+ let acc = 0
69
+ for i in x:
70
+ acc = acc + i
71
+ acc
72
+
73
+ list: x =>
74
+ list(x.iter)
75
+
76
+ record: x =>
77
+ Record(x.iter)
78
+ }
79
+
80
+ # TODO merge this with typing.Iterable?
81
+
82
+ export Iterable = Trait(__name__, "Iterable", methods, requires=["iter"])
83
+ register_global_trait(Iterable)
@@ -46,6 +46,11 @@ def unpack_record(obj):
46
46
 
47
47
 
48
48
  def ok(obj):
49
+ try:
50
+ return obj.ok
51
+ except AttributeError:
52
+ pass
53
+
49
54
  if obj is None:
50
55
  return False
51
56
  if isinstance(obj, BaseException):
@@ -9,7 +9,7 @@ def ExtensionProperty(f):
9
9
  return f
10
10
 
11
11
 
12
- def vget(obj, name):
12
+ def vget(obj, name, ignore_traits=False):
13
13
  try:
14
14
  return getattr(obj, name)
15
15
  except:
@@ -34,7 +34,7 @@ def vget(obj, name):
34
34
  except TypeError:
35
35
  pass
36
36
 
37
- v = fast_vget(obj, name)
37
+ v = fast_vget(obj, name, ignore_traits)
38
38
  if v is not None:
39
39
  if hasattr(v, "ext_prop"):
40
40
  return v(obj)
@@ -502,7 +502,7 @@ impl<'src> PyExprExt<'src> for SPyExpr<'src> {
502
502
  ctx.ast_node("Name", (ident, c.emit_py(ctx)?), &self.tl_span)?
503
503
  }
504
504
  PyExpr::Binary(op, left, right) => {
505
- let py_op_str = match op {
505
+ let py_bin_op = match op {
506
506
  PyBinaryOp::Add => Some("Add"),
507
507
  PyBinaryOp::Sub => Some("Sub"),
508
508
  PyBinaryOp::Mult => Some("Mult"),
@@ -512,18 +512,35 @@ impl<'src> PyExprExt<'src> for SPyExpr<'src> {
512
512
  _ => None,
513
513
  };
514
514
 
515
- if let Some(py_op_str) = py_op_str {
515
+ if let Some(py_bin_op) = py_bin_op {
516
516
  return ctx.ast_node(
517
517
  "BinOp",
518
518
  (
519
519
  left.emit_py(ctx)?,
520
- ctx.ast_cls(py_op_str, ())?,
520
+ ctx.ast_cls(py_bin_op, ())?,
521
521
  right.emit_py(ctx)?,
522
522
  ),
523
523
  &self.tl_span,
524
524
  );
525
525
  }
526
526
 
527
+ let py_bool_op = match op {
528
+ PyBinaryOp::And => Some("And"),
529
+ PyBinaryOp::Or => Some("Or"),
530
+ _ => None,
531
+ };
532
+
533
+ if let Some(py_bool_op) = py_bool_op {
534
+ return ctx.ast_node(
535
+ "BoolOp",
536
+ (
537
+ ctx.ast_cls(py_bool_op, ())?,
538
+ [left.emit_py(ctx)?, right.emit_py(ctx)?],
539
+ ),
540
+ &self.tl_span,
541
+ );
542
+ }
543
+
527
544
  let py_cmp_op = match op {
528
545
  PyBinaryOp::Lt => Some("Lt"),
529
546
  PyBinaryOp::Gt => Some("Gt"),
@@ -79,27 +79,42 @@ struct TraitAttr {
79
79
  static VTBL2: Lazy<Mutex<HashMap<String, Vec<TraitAttr>>>> =
80
80
  Lazy::new(|| Mutex::new(HashMap::new()));
81
81
 
82
- #[pyfunction(signature=(obj, name))]
82
+ #[pyfunction(signature=(obj, name, ignore_traits))]
83
83
  fn fast_vget<'py, 'ptr>(
84
84
  obj: &'ptr Bound<'py, PyAny>,
85
85
  name: &'ptr Bound<'py, PyString>,
86
+ ignore_traits: bool,
86
87
  ) -> PyResult<PyObject> {
87
88
  let name = name.to_string();
88
89
 
89
90
  let py = obj.py();
90
91
 
91
- let vtbl = VTBL.lock().unwrap();
92
+ {
93
+ // This block is necessary to ensure the lock is released
94
+ // before we possibly re-enter fast_vget through __tl__.vget below.
92
95
 
93
- if let Some(types) = vtbl.get(&name) {
94
- let mro = obj.get_type().mro();
96
+ let vtbl = VTBL.lock().unwrap();
95
97
 
96
- for typ in mro {
97
- if let Some(attr) = types.get(&(typ.as_ptr() as usize)) {
98
- return Ok((*attr).clone_ref(py));
98
+ if let Some(types) = vtbl.get(&name) {
99
+ let mro = obj.get_type().mro();
100
+
101
+ for typ in mro {
102
+ if let Some(attr) = types.get(&(typ.as_ptr() as usize)) {
103
+ return Ok((*attr).clone_ref(py));
104
+ }
99
105
  }
100
106
  }
101
107
  }
102
108
 
109
+ if ignore_traits {
110
+ // This prevents a deadlock from fast_vget being called
111
+ // *again* from inside __tl_.vget while vtbl2 is locked.
112
+
113
+ // Also, prevents a trait from being dependent on other traits,
114
+ // which might cause an infinite loop sometimes.
115
+ return Ok(py.None().into_any());
116
+ }
117
+
103
118
  let vtbl2 = VTBL2.lock().unwrap();
104
119
 
105
120
  if let Some(traits) = vtbl2.get(&name) {
@@ -109,7 +124,7 @@ fn fast_vget<'py, 'ptr>(
109
124
  for t in traits {
110
125
  let mut ok = true;
111
126
  for r in t.requirements.iter() {
112
- if vget.call1((obj, r)).is_err() {
127
+ if vget.call1((obj, r, true)).is_err() {
113
128
  ok = false;
114
129
  break;
115
130
  }
@@ -0,0 +1,18 @@
1
+ import util.assert_eq
2
+
3
+ # Traverse makes sure that apply and map implementations are correct.
4
+
5
+ assert_eq(
6
+ [1, 2, 3].traverse(x => @Async.pure(x)).run() # run in a new event loop
7
+ [1, 2, 3]
8
+ )
9
+
10
+ assert_eq(
11
+ [1, 2, 3].traverse(x => x + 1)
12
+ [2, 3, 4]
13
+ )
14
+
15
+ assert_eq(
16
+ [1, 2, 3].traverse(x => None)
17
+ None
18
+ )
@@ -640,7 +640,17 @@ where
640
640
  .or_not()
641
641
  .then_ignore(just(Token::Kw("import")))
642
642
  .then(group((
643
- symbol(".").repeated().count(),
643
+ // TODO this looks extremely cursed
644
+ symbol("..")
645
+ .to(2)
646
+ .repeated()
647
+ .foldr(
648
+ symbol(".")
649
+ .or_not()
650
+ .map(|x| if x.is_some() { 1 } else { 0 }),
651
+ |acc, sum| acc + sum,
652
+ )
653
+ .boxed(),
644
654
  ident
645
655
  .clone()
646
656
  .then_ignore(symbol("."))
@@ -2,6 +2,7 @@ use std::collections::HashMap;
2
2
  use std::collections::HashSet;
3
3
  use std::fmt::Display;
4
4
 
5
+ use slotmap::Key;
5
6
  use slotmap::SlotMap;
6
7
  use slotmap::new_key_type;
7
8
 
@@ -70,10 +71,10 @@ fn top_scope_and_key<'src, 'state>(
70
71
  #[allow(dead_code)]
71
72
  impl<'src> ResolveState<'src> {
72
73
  fn new(source: &'src str) -> Self {
73
- let mut root_scope = Scope::new();
74
+ let mut root_scope = Scope::new(ScopeKey::null());
74
75
  root_scope.is_global = true;
75
76
 
76
- let err_scope = Scope::new();
77
+ let err_scope = Scope::new(ScopeKey::null());
77
78
 
78
79
  let mut scopes = SlotMap::with_key();
79
80
  let root_scope_key = scopes.insert(root_scope);
@@ -107,6 +108,10 @@ impl<'src> ResolveState<'src> {
107
108
  top_scope(&self.scope_stack, &mut self.scopes)
108
109
  }
109
110
 
111
+ fn top_scope_key(&mut self) -> ScopeKey {
112
+ *self.scope_stack.last_mut().unwrap()
113
+ }
114
+
110
115
  fn top_scope_and_key(&mut self) -> (&mut Scope, ScopeKey) {
111
116
  top_scope_and_key(&self.scope_stack, &mut self.scopes)
112
117
  }
@@ -185,10 +190,10 @@ impl<'src> ResolveState<'src> {
185
190
  where
186
191
  F: FnOnce(&mut ResolveState<'src>) -> Indirect<SExpr<'src>>,
187
192
  {
188
- let mut temp_scope = Scope::new();
189
- temp_scope.is_fn = true;
193
+ let mut dummy_scope = Scope::new(ScopeKey::null());
194
+ dummy_scope.is_fn = true;
190
195
 
191
- let temp_scope_key = self.scopes.insert(temp_scope);
196
+ let temp_scope_key = self.scopes.insert(dummy_scope);
192
197
 
193
198
  let ph_decl = self.declarations.insert_declaration(
194
199
  Ident("x".into()).spanned(span),
@@ -460,6 +465,9 @@ impl Display for Declaration<'_> {
460
465
 
461
466
  #[derive(Debug)]
462
467
  pub struct Scope {
468
+ // TODO make this option
469
+ pub parent: ScopeKey,
470
+
463
471
  pub locals: Vec<DeclarationKey>,
464
472
 
465
473
  pub is_class: bool,
@@ -488,8 +496,9 @@ impl Display for Scope {
488
496
  }
489
497
 
490
498
  impl Scope {
491
- fn new() -> Scope {
499
+ fn new(parent: ScopeKey) -> Scope {
492
500
  Self {
501
+ parent,
493
502
  locals: Vec::new(),
494
503
  lifted_decls: Vec::new(),
495
504
 
@@ -797,7 +806,7 @@ fn pattern_scoped<'src>(
797
806
  state: &mut ResolveState<'src>,
798
807
  pattern: Indirect<SPattern<'src>>,
799
808
  ) -> (Indirect<SPattern<'src>>, ScopeKey, PatternMeta<'src>) {
800
- let scope = Scope::new();
809
+ let scope = Scope::new(state.top_scope_key());
801
810
  let scope_key = state.scopes.insert(scope);
802
811
 
803
812
  let (pattern, meta) = pattern.traverse(state);
@@ -946,7 +955,8 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
946
955
  }
947
956
  new_stmts
948
957
  } else {
949
- let scope = state.scopes.insert(Scope::new());
958
+ let parent = state.top_scope_key();
959
+ let scope = state.scopes.insert(Scope::new(parent));
950
960
  state
951
961
  .scoped(scope, |state| {
952
962
  let mut new_stmts = Vec::new();
@@ -1040,7 +1050,8 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
1040
1050
  let (pattern, scope, _meta) = pattern_scoped(state, pattern);
1041
1051
  (Some(pattern), scope)
1042
1052
  } else {
1043
- (None, state.scopes.insert(Scope::new()))
1053
+ let parent = state.top_scope_key();
1054
+ (None, state.scopes.insert(Scope::new(parent)))
1044
1055
  };
1045
1056
 
1046
1057
  SMatchCase {
@@ -1058,7 +1069,7 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
1058
1069
  Expr::Class(bases, body) => {
1059
1070
  let bases = traverse_call_items(state, bases);
1060
1071
 
1061
- let mut scope = Scope::new();
1072
+ let mut scope = Scope::new(state.top_scope_key());
1062
1073
  scope.is_class = true;
1063
1074
  let scope = state.scopes.insert(scope);
1064
1075
 
@@ -1071,7 +1082,7 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
1071
1082
  Expr::Fn(arg_def_items, body) => {
1072
1083
  let mut decls = vec![];
1073
1084
 
1074
- let mut scope = Scope::new();
1085
+ let mut scope = Scope::new(state.top_scope_key());
1075
1086
  scope.is_fn = true;
1076
1087
  let scope = state.scopes.insert(scope);
1077
1088
 
@@ -1297,7 +1308,7 @@ impl<'src> SExprExt<'src> for Indirect<SExpr<'src>> {
1297
1308
  let mut fn_ctx = FnInfo::new();
1298
1309
  fn_ctx.is_memo = true;
1299
1310
 
1300
- let mut scope = Scope::new();
1311
+ let mut scope = Scope::new(state.top_scope_key());
1301
1312
  scope.is_fn = true;
1302
1313
 
1303
1314
  // TODO it's confusing that we need to
@@ -1467,7 +1478,8 @@ impl<'src> SStmtExt<'src> for Indirect<SStmt<'src>> {
1467
1478
  let (pattern, scope, _meta) = pattern_scoped(state, pattern);
1468
1479
  (Some(pattern), scope)
1469
1480
  } else {
1470
- (None, state.scopes.insert(Scope::new()))
1481
+ let parent = state.top_scope_key();
1482
+ (None, state.scopes.insert(Scope::new(parent)))
1471
1483
  };
1472
1484
 
1473
1485
  let body = state