koatl 0.1.22__tar.gz → 0.1.23__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {koatl-0.1.22 → koatl-0.1.23}/Cargo.lock +1 -1
- {koatl-0.1.22 → koatl-0.1.23}/PKG-INFO +1 -1
- {koatl-0.1.22 → koatl-0.1.23}/koatl/Cargo.toml +1 -1
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/__init__.tl +16 -3
- koatl-0.1.23/koatl/python/koatl/prelude/functional/list.tl +26 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/monad.tl +10 -4
- {koatl-0.1.22 → koatl-0.1.23/koatl}/python/koatl/prelude/functional/result.tl +38 -41
- koatl-0.1.23/koatl/python/koatl/prelude/iterable.tl +83 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/runtime/virtual.py +2 -2
- {koatl-0.1.22 → koatl-0.1.23}/koatl/src/lib.rs +23 -8
- koatl-0.1.23/koatl/tests/e2e/prelude/list.tl +18 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/parser/src/parser.rs +11 -1
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/prelude/functional/__init__.tl +16 -3
- koatl-0.1.23/python/koatl/prelude/functional/list.tl +26 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/prelude/functional/monad.tl +10 -4
- {koatl-0.1.22/koatl → koatl-0.1.23}/python/koatl/prelude/functional/result.tl +38 -41
- koatl-0.1.23/python/koatl/prelude/iterable.tl +83 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/runtime/virtual.py +2 -2
- koatl-0.1.22/koatl/python/koatl/prelude/iterable.tl +0 -56
- koatl-0.1.22/python/koatl/prelude/iterable.tl +0 -56
- {koatl-0.1.22 → koatl-0.1.23}/Cargo.toml +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/README.md +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/.github/workflows/CI.yml +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/.gitignore +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/LICENSE +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/README.md +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/__init__.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/__main__.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/cli.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/async.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/memo.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/prelude/functional/reader.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/runtime/__init__.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/runtime/helpers.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/python/koatl/runtime/record.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/requirements.txt +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/src/emit_py.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/coal.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/containers.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/decorators.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/destructure-for-and-fn.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/destructure.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/escape_ident.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/fstr.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/functions.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/generator.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/if_expr.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/imports.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/loops.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/match.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/nary-list.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/placeholder.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/precedence.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/scopes.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/semantic_whitespace.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/slice.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/base/try.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/destructure.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/prelude/async.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/prelude/iterables.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/prelude/memo.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/prelude/reader.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/prelude/result.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/prelude/virtual.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/util/__init__.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/util/module0.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/util/module1.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/e2e/util/module2.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/parse/arith.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/parse/assign.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/parse/block-comments.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/parse/deco.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/parse/func.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/parse/matches.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/test_e2e.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl/tests/test_parse.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/Cargo.toml +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/parser/Cargo.toml +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/parser/src/ast.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/parser/src/lexer.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/parser/src/lib.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/parser/src/util.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/parser/tests/lexer.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/inference.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/lib.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/main.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/parse_timer.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/parser.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/py/ast.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/py/emit.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/py/mod.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/py/util.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/resolve_scopes.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/transform.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/types.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/koatl-core/src/util.rs +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/pyproject.toml +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/__init__.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/__main__.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/cli.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/notebook/__init__.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/notebook/magic.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/prelude/__init__.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/prelude/functional/async.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/prelude/functional/async_util.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/prelude/functional/memo.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/prelude/functional/reader.tl +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/runtime/__init__.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/runtime/helpers.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/runtime/meta_finder.py +0 -0
- {koatl-0.1.22 → koatl-0.1.23}/python/koatl/runtime/record.py +0 -0
|
@@ -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
|
-
|
|
8
|
-
|
|
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,12 +1,18 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
|
|
3
3
|
export Monad = class(abc.ABC):
|
|
4
|
+
# The default implementation required for `@` syntax that should be overridden by subclasses.
|
|
5
|
+
bind_once = abc.abstractmethod& (self, f) => None
|
|
6
|
+
|
|
7
|
+
pure = staticmethod& abc.abstractmethod& value => None
|
|
8
|
+
|
|
9
|
+
# Automatically given by bind_once.
|
|
4
10
|
bind = (self, f) => self.bind_once(f)
|
|
5
11
|
|
|
6
12
|
# An optional, optimized implementation of `bind` that skips deep recursion.
|
|
13
|
+
# TODO: can this be automatically generated?
|
|
7
14
|
bind_gen = (self, gen) => raise NotImplementedError()
|
|
8
15
|
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
pure = staticmethod& abc.abstractmethod& value => None
|
|
16
|
+
# Automatically generated implementations.
|
|
17
|
+
map = (self, f) => self.bind(x => self.pure(f(x)))
|
|
18
|
+
apply = (self, f) => self.bind(x => f.map(fn => fn(x)))
|
|
@@ -8,6 +8,8 @@ export Result = class(Monad):
|
|
|
8
8
|
Ok() => f(self)
|
|
9
9
|
default => self
|
|
10
10
|
|
|
11
|
+
bind = bind_once
|
|
12
|
+
|
|
11
13
|
bind_gen = (self, gen) =>
|
|
12
14
|
try:
|
|
13
15
|
while True:
|
|
@@ -23,6 +25,28 @@ export Result = class(Monad):
|
|
|
23
25
|
return Ok(None)
|
|
24
26
|
return value
|
|
25
27
|
|
|
28
|
+
pure = staticmethod& x => Ok(x)
|
|
29
|
+
|
|
30
|
+
apply = (self, f) =>
|
|
31
|
+
if not __tl__.ok(self):
|
|
32
|
+
return self
|
|
33
|
+
if not __tl__.ok(f):
|
|
34
|
+
return f
|
|
35
|
+
|
|
36
|
+
# unwrap the Oks - this is a bit weird
|
|
37
|
+
# but it allows bare types to be Results
|
|
38
|
+
if self matches Ok(value):
|
|
39
|
+
self = self.value
|
|
40
|
+
if f matches Ok(value):
|
|
41
|
+
f = f.value
|
|
42
|
+
|
|
43
|
+
f(self)
|
|
44
|
+
|
|
45
|
+
# Map must not be defined since it will override any special map
|
|
46
|
+
# implementation for traits, specifically for Iterable -
|
|
47
|
+
# is there a better way around this (in the vtable implementation)?
|
|
48
|
+
# map = ...
|
|
49
|
+
|
|
26
50
|
map_err = (self, f) =>
|
|
27
51
|
if self matches Err():
|
|
28
52
|
return f(self)
|
|
@@ -44,50 +68,23 @@ export Ok = class(metaclass=OkMeta):
|
|
|
44
68
|
__init__ = (self, value) =>
|
|
45
69
|
self.value = value
|
|
46
70
|
|
|
47
|
-
# Since we are using a custom metaclass, we can't derive from Result
|
|
48
|
-
# so need to copy the methods over manually from Result
|
|
49
|
-
|
|
50
|
-
# We could just use the object fallback methods, but this avoids
|
|
51
|
-
# the manual __tl__.vget so is slightly faster
|
|
52
|
-
bind_once = Result.bind_once
|
|
53
|
-
bind_gen = Result.bind_gen
|
|
54
|
-
map_err = Result.map_err
|
|
55
|
-
map_none = Result.map_none
|
|
56
|
-
|
|
57
71
|
assert = staticmethod& value =>
|
|
58
72
|
value match:
|
|
59
73
|
BaseException() as e => raise e
|
|
60
74
|
None => raise ValueError("Expected a value, got None")
|
|
61
75
|
default value
|
|
62
76
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
register_global_attr(
|
|
78
|
-
object,
|
|
79
|
-
"bind",
|
|
80
|
-
Result.bind_once
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
register_global_attr(
|
|
84
|
-
object,
|
|
85
|
-
"bind_once",
|
|
86
|
-
Result.bind_once
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
register_global_attr(
|
|
90
|
-
object,
|
|
91
|
-
"bind_gen",
|
|
92
|
-
Result.bind_gen
|
|
93
|
-
)
|
|
77
|
+
|
|
78
|
+
# Since we are using a custom metaclass, we can't derive from Result
|
|
79
|
+
# so need to copy the methods over manually from Result
|
|
80
|
+
|
|
81
|
+
# We could just use the object fallback methods, but this avoids
|
|
82
|
+
# the vget so is slightly faster
|
|
83
|
+
for name, method in Result.__dict__:
|
|
84
|
+
if name.startswith("_"):
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
setattr(Ok, name, method)
|
|
88
|
+
register_global_attr(object, name, method)
|
|
89
|
+
|
|
90
|
+
export Err = BaseException
|
|
@@ -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)
|
|
@@ -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)
|
|
@@ -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
|
-
|
|
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
|
-
|
|
94
|
-
let mro = obj.get_type().mro();
|
|
96
|
+
let vtbl = VTBL.lock().unwrap();
|
|
95
97
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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("."))
|
|
@@ -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
|
-
|
|
8
|
-
|
|
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,12 +1,18 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
|
|
3
3
|
export Monad = class(abc.ABC):
|
|
4
|
+
# The default implementation required for `@` syntax that should be overridden by subclasses.
|
|
5
|
+
bind_once = abc.abstractmethod& (self, f) => None
|
|
6
|
+
|
|
7
|
+
pure = staticmethod& abc.abstractmethod& value => None
|
|
8
|
+
|
|
9
|
+
# Automatically given by bind_once.
|
|
4
10
|
bind = (self, f) => self.bind_once(f)
|
|
5
11
|
|
|
6
12
|
# An optional, optimized implementation of `bind` that skips deep recursion.
|
|
13
|
+
# TODO: can this be automatically generated?
|
|
7
14
|
bind_gen = (self, gen) => raise NotImplementedError()
|
|
8
15
|
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
pure = staticmethod& abc.abstractmethod& value => None
|
|
16
|
+
# Automatically generated implementations.
|
|
17
|
+
map = (self, f) => self.bind(x => self.pure(f(x)))
|
|
18
|
+
apply = (self, f) => self.bind(x => f.map(fn => fn(x)))
|
|
@@ -8,6 +8,8 @@ export Result = class(Monad):
|
|
|
8
8
|
Ok() => f(self)
|
|
9
9
|
default => self
|
|
10
10
|
|
|
11
|
+
bind = bind_once
|
|
12
|
+
|
|
11
13
|
bind_gen = (self, gen) =>
|
|
12
14
|
try:
|
|
13
15
|
while True:
|
|
@@ -23,6 +25,28 @@ export Result = class(Monad):
|
|
|
23
25
|
return Ok(None)
|
|
24
26
|
return value
|
|
25
27
|
|
|
28
|
+
pure = staticmethod& x => Ok(x)
|
|
29
|
+
|
|
30
|
+
apply = (self, f) =>
|
|
31
|
+
if not __tl__.ok(self):
|
|
32
|
+
return self
|
|
33
|
+
if not __tl__.ok(f):
|
|
34
|
+
return f
|
|
35
|
+
|
|
36
|
+
# unwrap the Oks - this is a bit weird
|
|
37
|
+
# but it allows bare types to be Results
|
|
38
|
+
if self matches Ok(value):
|
|
39
|
+
self = self.value
|
|
40
|
+
if f matches Ok(value):
|
|
41
|
+
f = f.value
|
|
42
|
+
|
|
43
|
+
f(self)
|
|
44
|
+
|
|
45
|
+
# Map must not be defined since it will override any special map
|
|
46
|
+
# implementation for traits, specifically for Iterable -
|
|
47
|
+
# is there a better way around this (in the vtable implementation)?
|
|
48
|
+
# map = ...
|
|
49
|
+
|
|
26
50
|
map_err = (self, f) =>
|
|
27
51
|
if self matches Err():
|
|
28
52
|
return f(self)
|
|
@@ -44,50 +68,23 @@ export Ok = class(metaclass=OkMeta):
|
|
|
44
68
|
__init__ = (self, value) =>
|
|
45
69
|
self.value = value
|
|
46
70
|
|
|
47
|
-
# Since we are using a custom metaclass, we can't derive from Result
|
|
48
|
-
# so need to copy the methods over manually from Result
|
|
49
|
-
|
|
50
|
-
# We could just use the object fallback methods, but this avoids
|
|
51
|
-
# the manual __tl__.vget so is slightly faster
|
|
52
|
-
bind_once = Result.bind_once
|
|
53
|
-
bind_gen = Result.bind_gen
|
|
54
|
-
map_err = Result.map_err
|
|
55
|
-
map_none = Result.map_none
|
|
56
|
-
|
|
57
71
|
assert = staticmethod& value =>
|
|
58
72
|
value match:
|
|
59
73
|
BaseException() as e => raise e
|
|
60
74
|
None => raise ValueError("Expected a value, got None")
|
|
61
75
|
default value
|
|
62
76
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
register_global_attr(
|
|
78
|
-
object,
|
|
79
|
-
"bind",
|
|
80
|
-
Result.bind_once
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
register_global_attr(
|
|
84
|
-
object,
|
|
85
|
-
"bind_once",
|
|
86
|
-
Result.bind_once
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
register_global_attr(
|
|
90
|
-
object,
|
|
91
|
-
"bind_gen",
|
|
92
|
-
Result.bind_gen
|
|
93
|
-
)
|
|
77
|
+
|
|
78
|
+
# Since we are using a custom metaclass, we can't derive from Result
|
|
79
|
+
# so need to copy the methods over manually from Result
|
|
80
|
+
|
|
81
|
+
# We could just use the object fallback methods, but this avoids
|
|
82
|
+
# the vget so is slightly faster
|
|
83
|
+
for name, method in Result.__dict__:
|
|
84
|
+
if name.startswith("_"):
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
setattr(Ok, name, method)
|
|
88
|
+
register_global_attr(object, name, method)
|
|
89
|
+
|
|
90
|
+
export Err = BaseException
|
|
@@ -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)
|
|
@@ -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)
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
methods = {
|
|
2
|
-
map: (x, f) =>
|
|
3
|
-
for i in x:
|
|
4
|
-
yield f(i)
|
|
5
|
-
|
|
6
|
-
filter: (x, f) =>
|
|
7
|
-
for i in x:
|
|
8
|
-
if f(i):
|
|
9
|
-
yield i
|
|
10
|
-
|
|
11
|
-
flat_map: (x, f) =>
|
|
12
|
-
for i in x:
|
|
13
|
-
yield from f(i)
|
|
14
|
-
|
|
15
|
-
fold: (x, init, f) =>
|
|
16
|
-
let acc = init
|
|
17
|
-
for i in x:
|
|
18
|
-
acc = f(acc, i)
|
|
19
|
-
acc
|
|
20
|
-
|
|
21
|
-
first: (x, f) =>
|
|
22
|
-
for i in x:
|
|
23
|
-
if f(i):
|
|
24
|
-
return i
|
|
25
|
-
return None
|
|
26
|
-
|
|
27
|
-
last: (x, f) =>
|
|
28
|
-
let result = None
|
|
29
|
-
for i in x:
|
|
30
|
-
if f(i):
|
|
31
|
-
result = i
|
|
32
|
-
return result
|
|
33
|
-
|
|
34
|
-
at: (x, index) =>
|
|
35
|
-
for i in x:
|
|
36
|
-
if i == index:
|
|
37
|
-
return i
|
|
38
|
-
raise IndexError("Index out of range")
|
|
39
|
-
|
|
40
|
-
sum: x =>
|
|
41
|
-
let acc = 0
|
|
42
|
-
for i in x:
|
|
43
|
-
acc = acc + i
|
|
44
|
-
acc
|
|
45
|
-
|
|
46
|
-
list: x =>
|
|
47
|
-
list(x.iter)
|
|
48
|
-
|
|
49
|
-
record: x =>
|
|
50
|
-
Record(x.iter)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
# TODO merge this with typing.Iterable?
|
|
54
|
-
|
|
55
|
-
export Iterable = Trait(__name__, "Iterable", methods, requires=["iter"])
|
|
56
|
-
register_global_trait(Iterable)
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
methods = {
|
|
2
|
-
map: (x, f) =>
|
|
3
|
-
for i in x:
|
|
4
|
-
yield f(i)
|
|
5
|
-
|
|
6
|
-
filter: (x, f) =>
|
|
7
|
-
for i in x:
|
|
8
|
-
if f(i):
|
|
9
|
-
yield i
|
|
10
|
-
|
|
11
|
-
flat_map: (x, f) =>
|
|
12
|
-
for i in x:
|
|
13
|
-
yield from f(i)
|
|
14
|
-
|
|
15
|
-
fold: (x, init, f) =>
|
|
16
|
-
let acc = init
|
|
17
|
-
for i in x:
|
|
18
|
-
acc = f(acc, i)
|
|
19
|
-
acc
|
|
20
|
-
|
|
21
|
-
first: (x, f) =>
|
|
22
|
-
for i in x:
|
|
23
|
-
if f(i):
|
|
24
|
-
return i
|
|
25
|
-
return None
|
|
26
|
-
|
|
27
|
-
last: (x, f) =>
|
|
28
|
-
let result = None
|
|
29
|
-
for i in x:
|
|
30
|
-
if f(i):
|
|
31
|
-
result = i
|
|
32
|
-
return result
|
|
33
|
-
|
|
34
|
-
at: (x, index) =>
|
|
35
|
-
for i in x:
|
|
36
|
-
if i == index:
|
|
37
|
-
return i
|
|
38
|
-
raise IndexError("Index out of range")
|
|
39
|
-
|
|
40
|
-
sum: x =>
|
|
41
|
-
let acc = 0
|
|
42
|
-
for i in x:
|
|
43
|
-
acc = acc + i
|
|
44
|
-
acc
|
|
45
|
-
|
|
46
|
-
list: x =>
|
|
47
|
-
list(x.iter)
|
|
48
|
-
|
|
49
|
-
record: x =>
|
|
50
|
-
Record(x.iter)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
# TODO merge this with typing.Iterable?
|
|
54
|
-
|
|
55
|
-
export Iterable = Trait(__name__, "Iterable", methods, requires=["iter"])
|
|
56
|
-
register_global_trait(Iterable)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|