Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v3.1.1 #842

Merged
merged 52 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
7045486
Reenable develop
evhub Mar 2, 2024
777afca
Add test
evhub Mar 2, 2024
fcb2c2e
Fix color
evhub Mar 4, 2024
2566839
Fix min and max
evhub Mar 11, 2024
338a338
Fix pytest error
evhub Mar 11, 2024
4ec91fb
Add case def
evhub Mar 11, 2024
9a9b41e
Fix case def
evhub Mar 11, 2024
76c956c
Improve PEP 695 implementation
evhub Mar 21, 2024
c4893a3
Fix tests
evhub Mar 22, 2024
c3925ef
Fix test
evhub Mar 22, 2024
301eaf8
Improve case def
evhub Mar 23, 2024
c4f3afb
Fix case def docstrings
evhub Mar 23, 2024
c34d32b
Add support for op case defs
evhub Mar 23, 2024
90df442
Improve call operator
evhub Mar 23, 2024
6528598
Fix typing
evhub Mar 24, 2024
3399074
Add CoconutWarning
evhub Apr 14, 2024
15f3825
Add tests and documentation
evhub Apr 15, 2024
0884adf
Add more tests
evhub Apr 15, 2024
2e2f06c
Improve errors
evhub Apr 15, 2024
878aacf
Add deprecation warning
evhub Apr 18, 2024
33ff96a
Use new arg name ellision syntax
evhub Apr 20, 2024
634eb5a
Use case not match in case def
evhub Apr 26, 2024
dc68af5
Fix tests
evhub Apr 27, 2024
32296cf
Fix xonsh test
evhub Apr 28, 2024
c52c48c
Further fix prelude test
evhub Apr 28, 2024
7c60c77
Improve pypy tests
evhub Apr 28, 2024
42958a6
Start implementing pyright
evhub Apr 28, 2024
89825ed
Warn on implicit str concat
evhub May 4, 2024
1790eab
Remove unnecessary optimization
evhub May 5, 2024
add5a3e
Make small optimizations
evhub May 5, 2024
452034b
Clean up header
evhub May 9, 2024
34f1381
Fix f str = handling
evhub May 11, 2024
b2c4e5f
Fix f str handling
evhub May 12, 2024
0196d71
Improve color detection
evhub May 13, 2024
570c918
Improve watching
evhub May 24, 2024
2b4edd0
Further improve watching
evhub May 24, 2024
e2d8a20
Improve --profile
evhub May 26, 2024
e2ccf35
Optimize with new cPyparsing
evhub May 26, 2024
9a38ac8
Reduce cache usage
evhub May 26, 2024
dfbd5b6
Fix tests
evhub May 27, 2024
33e8671
Fix errors
evhub May 27, 2024
b3c887a
Add hybrid parsing support
evhub May 28, 2024
9959820
Fix py2
evhub May 29, 2024
bad4ec5
Fix str parsing
evhub May 29, 2024
ea17565
Fix 3.12 test
evhub May 31, 2024
03eade4
Improve watching
evhub Jun 6, 2024
01e6b34
Add initial pyright support
evhub Jun 7, 2024
d6e527d
Bump dependencies
evhub Jun 7, 2024
1d682b7
Increase robustness
evhub Jun 7, 2024
81286d9
Improve pyright support
evhub Jun 7, 2024
f8641c4
Prepare for v3.1.1
evhub Jun 8, 2024
7734cf8
Fix pyright support
evhub Jun 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ jobs:
matrix:
python-version:
- '2.7'
- '3.5'
- '3.6'
- '3.7'
- '3.8'
Expand All @@ -24,9 +23,9 @@ jobs:
fail-fast: false
name: Python ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup python
uses: MatteoH2O1999/setup-python@v2
uses: MatteoH2O1999/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: pip
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repos:
- --experimental
- --ignore=W503,E501,E722,E402,E721
- repo: https://github.com/pre-commit/pre-commit-hooks.git
rev: v4.5.0
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: fix-byte-order-marker
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ After you've tested your changes locally, you'll want to add more permanent test
1. Preparation:
1. Run `make check-reqs` and update dependencies as necessary
2. Run `sudo make format`
3. Make sure `make test`, `make test-py2`, and `make test-easter-eggs` are passing
3. Make sure `make test`, `make test-pyright`, and `make test-easter-eggs` are passing
4. Ensure that `coconut --watch` can successfully compile files when they're modified
5. Check changes in [`compiled-cocotest`](https://github.com/evhub/compiled-cocotest), [`pyprover`](https://github.com/evhub/pyprover), and [`coconut-prelude`](https://github.com/evhub/coconut-prelude)
6. Check [Codebeat](https://codebeat.co/a/evhub/projects) and [LGTM](https://lgtm.com/dashboard) for `coconut` and `compiled-cocotest`
Expand Down
145 changes: 106 additions & 39 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ The full list of optional dependencies is:
- `kernel`: lightweight subset of `jupyter` that only includes the dependencies that are strictly necessary for Coconut's [Jupyter kernel](#kernel).
- `watch`: enables use of the `--watch` flag.
- `mypy`: enables use of the `--mypy` flag.
- `pyright`: enables use of the `--pyright` flag.
- `xonsh`: enables use of Coconut's [`xonsh` support](#xonsh-support).
- `numpy`: installs everything necessary for making use of Coconut's [`numpy` integration](#numpy-integration).
- `jupyterlab`: installs everything necessary to use [JupyterLab](https://github.com/jupyterlab/jupyterlab) with Coconut.
Expand Down Expand Up @@ -121,11 +122,11 @@ depth: 1

```
coconut [-h] [--and source [dest ...]] [-v] [-t version] [-i] [-p] [-a] [-l]
[--no-line-numbers] [-k] [-w] [-r] [-n] [-d] [-q] [-s] [--no-tco]
[--no-wrap-types] [-c code] [--incremental] [-j processes] [-f] [--minify]
[--jupyter ...] [--mypy ...] [--argv ...] [--tutorial] [--docs] [--style name]
[--vi-mode] [--recursion-limit limit] [--stack-size kbs] [--site-install]
[--site-uninstall] [--verbose] [--trace] [--profile]
[--no-line-numbers] [-k] [-w] [-r] [-n] [-d] [-q] [-s] [--no-tco] [--no-wrap-types]
[-c code] [-j processes] [-f] [--minify] [--jupyter ...] [--mypy ...] [--pyright]
[--argv ...] [--tutorial] [--docs] [--style name] [--vi-mode]
[--recursion-limit limit] [--stack-size kbs] [--fail-fast] [--no-cache]
[--site-install] [--site-uninstall] [--verbose] [--trace] [--profile]
[source] [dest]
```

Expand Down Expand Up @@ -184,6 +185,7 @@ dest destination directory for compiled files (defaults to
Jupyter)
--mypy ... run MyPy on compiled Python (remaining args passed to MyPy) (implies
--package --line-numbers)
--pyright run Pyright on compiled Python (implies --package)
--argv ..., --args ...
set sys.argv to source plus remaining args for use in the Coconut script
being run
Expand Down Expand Up @@ -280,6 +282,8 @@ To make Coconut built-ins universal across Python versions, Coconut makes availa
- `py_xrange`
- `py_repr`
- `py_breakpoint`
- `py_min`
- `py_max`

_Note: Coconut's `repr` can be somewhat tricky, as it will attempt to remove the `u` before reprs of unicode strings on Python 2, but will not always be able to do so if the unicode string is nested._

Expand Down Expand Up @@ -334,6 +338,7 @@ If the `--strict` (`-s` for short) flag is enabled, Coconut will perform additio
The style issues which will cause `--strict` to throw an error are:

- mixing of tabs and spaces
- use of `"hello" "world"` implicit string concatenation (use explicit `+` instead)
- use of `from __future__` imports (Coconut does these automatically)
- inheriting from `object` in classes (Coconut does this automatically)
- semicolons at end of lines
Expand Down Expand Up @@ -449,6 +454,10 @@ You can also run `mypy`—or any other static type checker—directly on the com

To distribute your code with checkable type annotations, you'll need to include `coconut` as a dependency (though a `--no-deps` install should be fine), as installing it is necessary to make the requisite stub files available. You'll also probably want to include a [`py.typed`](https://peps.python.org/pep-0561/) file.

##### Pyright Integration

Though not as well-supported as MyPy, Coconut also has built-in [Pyright](https://github.com/microsoft/pyright) support. Simply pass `--pyright` to automatically run Pyright on all compiled code. To adjust Pyright options, rather than pass them at the command-line, add your settings to the file `~/.coconut_pyrightconfig.json` (automatically generated the first time `coconut --pyright` is run).

##### Syntax

To explicitly annotate your code with types to be checked, Coconut supports (on all Python versions):
Expand All @@ -464,7 +473,7 @@ Sometimes, MyPy will not know how to handle certain Coconut constructs, such as

##### Interpreter

Coconut even supports `--mypy` in the interpreter, which will intelligently scan each new line of code, in the context of previous lines, for newly-introduced MyPy errors. For example:
Coconut even supports `--mypy` (though not `--pyright`) in the interpreter, which will intelligently scan each new line of code, in the context of previous lines, for newly-introduced MyPy errors. For example:
```coconut_pycon
>>> a: str = count()[0]
<string>:14: error: Incompatible types in assignment (expression has type "int", variable has type "str")
Expand Down Expand Up @@ -541,9 +550,9 @@ a `b` c, left (captures lambda)
all custom operators
?? left (short-circuits)
..>, <.., ..*>, <*.., n/a (captures lambda)
..**>, <**..
..**>, <**.., etc.
|>, <|, |*>, <*|, left (captures lambda)
|**>, <**|
|**>, <**|, etc.
==, !=, <, >,
<=, >=,
in, not in,
Expand Down Expand Up @@ -1318,11 +1327,10 @@ data Empty() from Tree
data Leaf(n) from Tree
data Node(l, r) from Tree

def depth(Tree()) = 0

addpattern def depth(Tree(n)) = 1

addpattern def depth(Tree(l, r)) = 1 + max([depth(l), depth(r)])
case def depth:
case(Tree()) = 0
case(Tree(n)) = 1
case(Tree(l, r)) = 1 + max(depth(l), depth(r))

Empty() |> depth |> print
Leaf(5) |> depth |> print
Expand All @@ -1338,26 +1346,26 @@ def duplicate_first([x] + xs as l) =
```
_Showcases head-tail splitting, one of the most common uses of pattern-matching, where a `+ <var>` (or `:: <var>` for any iterable) at the end of a list or tuple literal can be used to match the rest of the sequence._

```
def sieve([head] :: tail) =
[head] :: sieve(n for n in tail if n % head)

addpattern def sieve((||)) = []
```coconut
case def sieve:
case([head] :: tail) =
[head] :: sieve(n for n in tail if n % head)
case((||)) = []
```
_Showcases how to match against iterators, namely that the empty iterator case (`(||)`) must come last, otherwise that case will exhaust the whole iterator before any other pattern has a chance to match against it._

```
```coconut
def odd_primes(p=3) =
(p,) :: filter(=> _ % p != 0, odd_primes(p + 2))

def primes() =
(2,) :: odd_primes()

def twin_primes(_ :: [p, (.-2) -> p] :: ps) =
[(p, p+2)] :: twin_primes([p + 2] :: ps)

addpattern def twin_primes() = # type: ignore
twin_primes(primes())
case def twin_primes:
case(_ :: [p, (.-2) -> p] :: ps) =
[(p, p+2)] :: twin_primes([p + 2] :: ps)
case() =
twin_primes(primes())

twin_primes()$[:5] |> list |> print
```
Expand Down Expand Up @@ -1386,7 +1394,7 @@ match <value>:
```
where `<pattern>` is any `match` pattern, `<value>` is the item to match against, `<cond>` is an optional additional check, and `<body>` is simply code that is executed if the header above it succeeds. Note the absence of an `in` in the `match` statements: that's because the `<value>` in `case <value>` is taking its place. If no `else` is present and no match succeeds, then the `case` statement is simply skipped over as with [`match` statements](#match) (though unlike [destructuring assignments](#destructuring-assignment)).

Additionally, `cases` can be used as the top-level keyword instead of `match`, and in such a `case` block `match` is allowed for each case rather than `case`. _Deprecated: Coconut also supports `case` instead of `cases` as the top-level keyword for backwards-compatibility purposes._
_Deprecated: Additionally, `cases` or `case` can be used as the top-level keyword instead of `match`, and in such a block `match` is used for each case rather than `case`._

##### Examples

Expand Down Expand Up @@ -1520,15 +1528,14 @@ data Empty()
data Leaf(n)
data Node(l, r)

def size(Empty()) = 0

addpattern def size(Leaf(n)) = 1

addpattern def size(Node(l, r)) = size(l) + size(r)
case def size:
case(Empty()) = 0
case(Leaf(n)) = 1
case(Node(l, r)) = size(l) + size(r)

size(Node(Empty(), Leaf(10))) == 1
```
_Showcases the algebraic nature of `data` types when combined with pattern-matching._
_Showcases the use of pattern-matching to deconstruct `data` types._

```coconut
data vector(*pts):
Expand Down Expand Up @@ -2219,7 +2226,7 @@ quad = 5 * x**2 + 3 * x + 1

When passing in long variable names as keyword arguments of the same name, Coconut supports the syntax
```
f(...=long_variable_name)
f(long_variable_name=)
```
as a shorthand for
```
Expand All @@ -2228,15 +2235,17 @@ f(long_variable_name=long_variable_name)

Such syntax is also supported in [partial application](#partial-application) and [anonymous `namedtuple`s](#anonymous-namedtuples).

_Deprecated: Coconut also supports `f(...=long_variable_name)` as an alternative shorthand syntax._

##### Example

**Coconut:**
```coconut
really_long_variable_name_1 = get_1()
really_long_variable_name_2 = get_2()
main_func(
...=really_long_variable_name_1,
...=really_long_variable_name_2,
really_long_variable_name_1=,
really_long_variable_name_2=,
)
```

Expand Down Expand Up @@ -2521,6 +2530,58 @@ range(5) |> last_two |> print
_Can't be done without a long series of checks at the top of the function. See the compiled code for the Python syntax._


### `case` Functions

For easily defining a pattern-matching function with many different cases, Coconut provides the `case def` syntax based on Coconut's [`case`](#case) syntax. The basic syntax is
```
case def <name>:
case(<arg>, <arg>, ... [if <cond>]):
<body>
case(<arg>, <arg>, ... [if <cond>]):
<body>
...
```
where the patterns in each `case` are checked in sequence until a match is found and the body under that match is executed, or a [`MatchError`](#matcherror) is raised. Each `case(...)` statement is effectively treated as a separate pattern-matching function signature that is checked independently, as if they had each been defined separately and then combined with [`addpattern`](#addpattern).

Any individual body can also be defined with [assignment function syntax](#assignment-functions) such that
```
case def <name>:
case(<arg>, <arg>, ... [if <cond>]) = <body>
```
is equivalent to
```
case def <name>:
case(<arg>, <arg>, ... [if <cond>]): return <body>
```

`case` function definition can also be combined with `async` functions, [`copyclosure` functions](#copyclosure-functions), and [`yield` functions](#explicit-generators). The various keywords in front of the `def` can be put in any order.

`case def` also allows for easily providing type annotations for pattern-matching functions. To add type annotations, inside the body of the `case def`, instead of just `case(...)` statements, include some `type(...)` statements as well, which will compile into [`typing.overload`](https://docs.python.org/3/library/typing.html#overload) declarations. The syntax is
```
case def <name>[<type vars>]:
type(<arg>: <type>, <arg>: <type>, ...) -> <type>
type(<arg>: <type>, <arg>: <type>, ...) -> <type>
...
```
which can be interspersed with the `case(...)` statements.

##### Example

**Coconut:**
```coconut
case def my_min[T]:
type(x: T, y: T) -> T
case(x, y if x <= y) = x
case(x, y) = y

type(xs: T[]) -> T
case([x]) = x
case([x] + xs) = my_min(x, my_min(xs))
```

**Python:**
_Can't be done without a long series of checks for each pattern-matching. See the compiled code for the Python syntax._

### `addpattern` Functions

Coconut provides the `addpattern def` syntax as a shortcut for the full
Expand All @@ -2531,10 +2592,12 @@ match def func(...):
```
syntax using the [`addpattern`](#addpattern) decorator.

Additionally, `addpattern def` will act just like a normal [`match def`](#pattern-matching-functions) if the function has not previously been defined, allowing for `addpattern def` to be used for each case rather than requiring `match def` for the first case and `addpattern def` for future cases.

If you want to put a decorator on an `addpattern def` function, make sure to put it on the _last_ pattern function.

For complex multi-pattern functions, it is generally recommended to use [`case def`](#case-functions) over `addpattern def` in most situations.

_Deprecated: `addpattern def` will act just like a normal [`match def`](#pattern-matching-functions) if the function has not previously been defined. This will show a [`CoconutWarning`](#coconutwarning) and is not recommended._

##### Example

**Coconut:**
Expand Down Expand Up @@ -2952,7 +3015,7 @@ depth: 1
Takes one argument that is a [pattern-matching function](#pattern-matching-functions), and returns a decorator that adds the patterns in the existing function to the new function being decorated, where the existing patterns are checked first, then the new. `addpattern` also supports a shortcut syntax where the new patterns can be passed in directly.

Roughly equivalent to:
```
```coconut_python
def _pattern_adder(base_func, add_func):
def add_pattern_func(*args, **kwargs):
try:
Expand Down Expand Up @@ -2990,7 +3053,7 @@ print_type() # appears to work
print_type(1) # TypeError: print_type() takes 0 positional arguments but 1 was given
```

This can be fixed by using either the `match` or `addpattern` keyword. For example:
This can be fixed by using either the `match` keyword. For example:
```coconut
match def print_type():
print("Received no arguments.")
Expand Down Expand Up @@ -3341,6 +3404,10 @@ Additionally, if you are using [view patterns](#match), you might need to raise

In some cases where there are multiple Coconut packages installed at the same time, there may be multiple `MatchError`s defined in different packages. Coconut can perform some magic under the hood to make sure that all these `MatchError`s will seamlessly interoperate, but only if all such packages are compiled in [`--package` mode rather than `--standalone` mode](#compilation-modes).

### `CoconutWarning`

`CoconutWarning` is the [`Warning`](https://docs.python.org/3/library/exceptions.html#Warning) subclass used for all runtime Coconut warnings; see [`warnings`](https://docs.python.org/3/library/warnings.html).


### Generic Built-In Functions

Expand Down Expand Up @@ -4594,7 +4661,7 @@ else:

#### `reveal_type` and `reveal_locals`

When using MyPy, `reveal_type(<expr>)` will cause MyPy to print the type of `<expr>` and `reveal_locals()` will cause MyPy to print the types of the current `locals()`. At runtime, `reveal_type(x)` is always the identity function and `reveal_locals()` always returns `None`. See [the MyPy documentation](https://mypy.readthedocs.io/en/stable/common_issues.html#reveal-type) for more information.
When using static type analysis tools integrated with Coconut such as [MyPy](#mypy-integration), `reveal_type(<expr>)` will cause MyPy to print the type of `<expr>` and `reveal_locals()` will cause MyPy to print the types of the current `locals()`. At runtime, `reveal_type(x)` is always the identity function and `reveal_locals()` always returns `None`. See [the MyPy documentation](https://mypy.readthedocs.io/en/stable/common_issues.html#reveal-type) for more information.

##### Example

Expand Down
2 changes: 1 addition & 1 deletion HELP.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ def factorial(n):
```

By making use of the [Coconut `addpattern` syntax](./DOCS.md#addpattern), we can take that from three indentation levels down to one. Take a look:
```
```coconut
def factorial(0) = 1

addpattern def factorial(int() as n if n > 0) =
Expand Down
20 changes: 18 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -161,15 +161,31 @@ test-mypy-tests: clean-no-tests
python ./coconut/tests/dest/runner.py
python ./coconut/tests/dest/extras.py

# same as test-mypy but uses pyright instead
.PHONY: test-pyright
test-pyright: export COCONUT_USE_COLOR=TRUE
test-pyright: clean
python ./coconut/tests --strict --keep-lines --force --target sys --no-cache --pyright
python ./coconut/tests/dest/runner.py
python ./coconut/tests/dest/extras.py

# same as test-univ but includes verbose output for better debugging
# regex for getting non-timing lines: ^(?!\s*(Time|Packrat|Loaded|Saving|Adaptive|Errorless|Grammar|Failed|Incremental|Pruned)\s)[^\n]*\n*
# regex for getting non-timing lines: ^(?!'|\s*(Time|Packrat|Loaded|Saving|Adaptive|Errorless|Grammar|Failed|Incremental|Pruned|Compiled)\s)[^\n]*\n*
.PHONY: test-verbose
test-verbose: export COCONUT_USE_COLOR=TRUE
test-verbose: clean
python ./coconut/tests --strict --keep-lines --force --verbose
python ./coconut/tests/dest/runner.py
python ./coconut/tests/dest/extras.py

# same as test-verbose but reuses the incremental cache
.PHONY: test-verbose-cache
test-verbose-cache: export COCONUT_USE_COLOR=TRUE
test-verbose-cache: clean-no-tests
python ./coconut/tests --strict --keep-lines --force --verbose
python ./coconut/tests/dest/runner.py
python ./coconut/tests/dest/extras.py

# same as test-verbose but doesn't use the incremental cache
.PHONY: test-verbose-no-cache
test-verbose-no-cache: export COCONUT_USE_COLOR=TRUE
Expand Down Expand Up @@ -359,7 +375,7 @@ check-reqs:
.PHONY: profile
profile: export COCONUT_USE_COLOR=TRUE
profile:
coconut ./coconut/tests/src/cocotest/agnostic/util.coco ./coconut/tests/dest/cocotest --force --jobs 0 --profile --verbose --stack-size 4096 --recursion-limit 4096 2>&1 | tee ./profile.log
coconut ./coconut/tests/src/cocotest/agnostic/util.coco ./coconut/tests/dest/cocotest --force --verbose --profile --stack-size 4096 --recursion-limit 4096 2>&1 | tee ./profile.log

.PHONY: open-speedscope
open-speedscope:
Expand Down
Loading
Loading