Compare commits

...

706 Commits

Author SHA1 Message Date
Sofia Rodrigues
b2791f1564 fix: body refactor 2026-03-20 23:51:33 -03:00
Sofia Rodrigues
c69f5d63dc Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-20 22:44:58 -03:00
Sofia Rodrigues
41470c1c0a Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-20 22:44:51 -03:00
Sofia Rodrigues
a5551e3291 refactor: to stream again 2026-03-20 18:27:50 -03:00
Sofia Rodrigues
96253d357f Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-19 16:28:53 -03:00
Sofia Rodrigues
db1d553245 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-19 16:28:35 -03:00
Sofia Rodrigues
286182df24 feat: getKnownSize and setKnownSize 2026-03-19 16:28:28 -03:00
Sofia Rodrigues
3eee136224 fix: server 2026-03-19 16:23:24 -03:00
Sofia Rodrigues
38f189dab2 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-19 15:56:00 -03:00
Sofia Rodrigues
55ce4dc2b0 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-19 15:52:50 -03:00
Sofia Rodrigues
bb90f72a40 fix: remove useless comments 2026-03-19 15:52:43 -03:00
Sofia Rodrigues
c485824d11 fix: tests 2026-03-19 15:50:08 -03:00
Sofia Rodrigues
afe1676e4a Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-19 15:48:10 -03:00
Sofia Rodrigues
64889857b2 Merge branch 'master' of https://github.com/leanprover/lean4 into sofia/async-http-uri 2026-03-19 15:42:35 -03:00
Markus Himmel
34cf4575f3 feat: verify String.startsWith and String.skipPrefix? (#12990)
This PR verifies the `String.startsWith` and `String.skipPrefix?`
functions for our various pattern types.
2026-03-19 18:11:37 +00:00
Markus Himmel
0f730662de refactor: reorganize functions for skipping/dropping prefixes/suffixes of strings (#12988)
This PR introduces the functions `String.Slice.skipPrefix?`,
`String.Slice.Pos.skip?`, `String.Slice.skipPrefixWhile`,
`String.Slice.Pos.skipWhile` and redefines `String.Slice.takeWhile` and
`String.Slice.dropWhile` to use these new functions.
2026-03-19 15:45:53 +00:00
Wojciech Różowski
5cc6585c9b chore: disable cbv usage warning (#12986)
This disables `cbv` usage warning and reflects that in the corresponding
unit tests.
2026-03-19 14:12:04 +00:00
Sebastian Ullrich
d9c3bbf1b4 fix: prevent induction/cases from swallowing diagnostics when using clause contains by (#12953)
This PR fixes an issue where the `induction` and `cases` tactics would
swallow diagnostics (such as unsolved goals errors) when the `using`
clause contains a nested tactic.

Closes #12815
2026-03-19 13:52:16 +00:00
Markus Himmel
9c5d2bf62e refactor: rename ForwardPattern.dropPrefix? to ForwardPattern.skipPrefix? (#12984)
This PR renames the function `ForwardPattern.dropPrefix?` to
`ForwardPattern.skipPrefix`?

This function `(s : String.Slice) -> Option s.Pos` is not to be confused
with `String.Slice.dropPrefix? : (s : String.Slice) -> Option
String.Slice`.
2026-03-19 13:05:55 +00:00
Wojciech Różowski
8f6ade06ea fix: interaction between cbv_opaque and inline (#12981)
This PR fixes the interaction between `cbv_opaque` and
`inline`/`always_inline` annotations, to make sure that inlined
definitions marked as `cbv_opaque` are not unfolded during the
preprocessing stage of `cbv` tactic.
2026-03-19 11:23:57 +00:00
Markus Himmel
e758c0e35c feat: String.toNat? lemmas (#12828)
This PR redefines the `String.isNat` function to use less state and
perform short-circuiting. It then verifies the `String.isNat` and
`String.toNat?` functions.

Recall that `isNat` and `toNat?` allow `_` as a digit separator. This is
why we get the complicated statement
```lean
public theorem isNat_iff {s : String} :
    s.isNat = true ↔
      s ≠ "" ∧
      (∀ c ∈ s.toList, c.isDigit ∨ c = '_') ∧
      ¬ ['_', '_'] <:+: s.toList ∧
      s.toList.head? ≠ some '_' ∧
      s.toList.getLast? ≠ some '_'
```

For `toNat?`, we prove the fully general
```lean
public theorem toNat?_eq_some_ofDigitChars {s : String} (h : s.isNat = true) :
    s.toNat? = some (Nat.ofDigitChars 10 (s.toList.filter (· != '_')) 0)
```
as well as the useful `(Nat.repr n).toNat? = some n` (and the corollary
that `Nat.repr` is injective).

For people implementing formatting routines that involve digit
separators, we have
```lean
public theorem isNat_of_isDigit {s : String} (hne : s ≠ "")
    (hdigit : ∀ c ∈ s.toList, c.isDigit) : s.isNat = true

public theorem isNat_append_underscore_append {s t : String}
    (hs : s.isNat = true) (ht : t.isNat = true) :
    (s ++ "_" ++ t).isNat = true

public theorem toNat?_append_underscore_append_eq_some {s t : String} {n m : Nat}
    (hs : s.toNat? = some n) (ht : t.toNat? = some m) :
   (s ++ "_" ++ t).toNat? =
      some (10 ^ (t.toList.filter (· != '_')).length * n + m)
```

The missing bit here is `(s.leftpad k '0').toNat? = s.toNat?`, which is
missing because we don't have `String.leftpad` (yet). For any reasonable
definition of `leftpad`, this will follow from
`toNat?_eq_some_ofDigitChars` since we prove the necessary ingredients
about `ofDigitChars`.

There are some rough edges around `ofDigitChars`, and in the future it
will be nice to connect this all to mathlib's `Nat.digits` and
`Nat.ofDigits`, which are similar but different.
2026-03-19 11:02:56 +00:00
Joachim Breitner
747262e498 fix: respect pp.privateNames in #print signature (#12979)
This PR makes `#print` show the full internal private name (including
module prefix) in the declaration signature when `pp.privateNames` is
set to true. Previously, `pp.privateNames` only affected names in the
body but the signature always stripped the private prefix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 09:16:48 +00:00
Markus Himmel
f8a3c13e0b feat: assorted lemmas (#12980)
This PR adds theorems about `Char`, `Nat` and `List`.
2026-03-19 09:14:54 +00:00
Markus Himmel
a045a7c094 perf: remove simp annotations (#12977)
This PR removes most of the `simp` annotations added in #12945, to
mitigate the performance impact. The lemmas remain.
2026-03-19 07:58:32 +00:00
Derrik Petrin
87180a09c4 fix: fix a collection of docstring errors (#12959)
This PR fixes a series of errors in docstrings.

This includes:
- incorrect gramar
- errant reference to "dependent" in the non-dependent `HashMap` files
- reference to expression metavariables as universe level metavariables
- outdated reference to `usizeSz` instead of `USize.size`
- syntax errors in code examples
- a broken link to a paper

---------

Co-authored-by: Derrik Petrin <derrik.petrin@pm.me>
2026-03-19 06:42:11 +00:00
Mac Malone
c1bbc6abaa feat: lake: parallel cache artifact transfers (#12974)
This PR changes `lake cache get` and `lake cache put` to transfer
artifacts in parallel (using `curl --parallel`) when uploading or
eagerly downloading artifacts. Transfers are still recorded one-by-one
in the output -- no progress meter yet.
2026-03-19 04:03:58 +00:00
Joachim Breitner
b7380758ae refactor: remove Lean.Environment.replay from core (#12972)
This PR removes the obsolete `Lean.Environment.replay` from
`src/Lean/Replay.lean` and replaces it with the improved version from
`src/LeanChecker/Replay.lean`, which includes fixes for duplicate
theorem handling and Quot/Eq dependency ordering. The primed names
(`Replay'`, `replay'`) are renamed back to `Replay` and `replay`.

A test for the original issue (nested inductives failing with `replay`)
is added as `tests/elab/issue12819.lean`.

Closes #12819

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 22:11:42 +00:00
Wojciech Nawrocki
24ee19e405 chore: add aarch64-darwin to flake (#12915) 2026-03-18 15:55:34 +00:00
Sebastian Ullrich
b13325f95d fix: shake: avoid panic on header-only files without trailing newline (#12963)
This PR fixes a panic in `lake shake` when applied to a header-only file
without trailing newline
2026-03-18 15:44:39 +00:00
Leonardo de Moura
58ef418dda feat: add sym => interactive mode (#12970)
This PR adds a `sym =>` tactic that enters an interactive symbolic
simulation
mode built on `grind`. Unlike `grind =>`, it does not eagerly introduce
hypotheses or apply by-contradiction, giving users explicit control over
`intro`, `apply`, and `internalize` steps.

New tactics available in `sym =>` mode:
- `intro` / `intros`: introduce binders and internalize into the E-graph
by
  default. Use `intro~` or `intro (internalize := false)` to skip
  internalization.
- `apply t`: apply backward rules with caching for `repeat`.
- `internalize` / `internalize_all`: internalize hypotheses into the
E-graph.
- `by_contra`: apply proof by contradiction, negating the target.

Satellite solvers (`lia`, `ring`, `linarith`) automatically introduce
remaining
binders and apply by-contradiction in `sym =>` mode, matching their
behavior in
default tactic mode. All existing `grind =>` tactics (`finish`,
`instantiate`,
`cases`, etc.) also work in `sym =>` mode. The sym-specific tactics are
guarded
and rejected in regular `grind =>` mode.

```lean
example (x : Nat) : myP x → myQ x := by
  sym [myP_myQ] =>
    intro h
    finish

example (x y z : Nat) : x > 1 → x + y + z > 0 := by
  sym =>
    lia
```
2026-03-18 14:29:18 +00:00
Joachim Breitner
b2aec782eb fix: re-privatize constant name prefix in realizeConst to avoid diamond import collisions (#12964)
This PR fixes an issue where `realizeConst` would generate auxiliary
declarations
(like `_sparseCasesOn`) using the original defining module's private
name prefix
rather than the realizing module's prefix. When two modules
independently realized
the same imported constant, they produced identically-named auxiliary
declarations,
causing "environment already contains" errors on diamond import.

The fix re-privatizes the constant name under the current module before
passing it
to `withDeclNameForAuxNaming`, ensuring each realizing module generates
distinctly
named auxiliary declarations.

Fixes #12825

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 13:54:50 +00:00
Lean stage0 autoupdater
ea49bc9bcf chore: update stage0 2026-03-18 14:07:05 +00:00
Kim Morrison
7ee8c4aaeb fix: use libtool instead of ar for static libs on macOS (#12957)
This PR fixes a build failure on macOS introduced by #12540. macOS BSD
`ar` does not support the `@file` response file syntax that #12540
enabled unconditionally. On macOS, when building core (i.e., `bootsrap
:= true`), `recBuildStatic` now uses `libtool -static -filelist`, which
handles long argument lists natively.

Includes a `stage0/src/stdlib_flags.h` trigger so CI will automatically
run `update-stage0` after merge.

🤖 Prepared with Claude Code

Implementation adjusted by @tydeu

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Mac Malone <mac@lean-fro.org>
Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2026-03-18 12:41:19 +00:00
Lean stage0 autoupdater
09da0d22a1 chore: update stage0 2026-03-18 12:37:41 +00:00
Markus Himmel
cb0455e379 feat: simprocs for n.digitChar = c (#12966)
This PR adds simp lemmas that simplify `n.digitChar = '0'` to `n = 0`
and a simproc that simplifies `n.digitChar = '!'` to `False`.
2026-03-18 12:00:24 +00:00
Garmelon
e14230c0f3 chore: import measure.py instead of calling it (#12962)
Thereby avoiding the overhead of one python interpreter per wrapped lean
call during the build benchmarks.
2026-03-18 10:23:32 +00:00
Joachim Breitner
0717cb73d5 chore: hints about ccache in CLAUDE.md (#12960)
This PR adds instructions for building Lean with ccache in sandboxes.
2026-03-18 10:01:26 +00:00
Leonardo de Moura
f0b367d7aa fix: mark List.length as @[implicit_reducible] (#12924)
This PR fixes a regression introduced in Lean 4.29.0-rc2 where `simp` no
longer simplifies inside type class instance arguments due to the
`backward.isDefEq.respectTransparency` change. This breaks proofs where
a term like `(a :: l).length` appears both in the main expression and
inside implicit instance arguments (e.g., determining a `BitVec` width).

**The problem:** After `simp only [List.length_cons]`, the main
expression has `l.length + 1` but instances still have `(a ::
l).length`. Since `simp` no longer simplifies inside instances, and
`isDefEq` won't unfold `List.length` at the default transparency,
subsequent lemma applications fail.

**Reproducer** (from Son Ho, reported by Sebastian Ullrich):
```lean
theorem BitVec.getElem!_eq_testBit_toNat {w : Nat} (x : BitVec w) (i : Nat) :
     x[i]! = x.toNat.testBit i := by sorry

example (l : List Nat) (a : Nat) (j : Nat) :
  (0#((a :: l).length))[j]! = (0#((a :: l).length)).toNat.testBit j := by
  simp only [List.length_cons]
  simp only [BitVec.getElem!_eq_testBit_toNat] -- works in 4.28.0-rc1, fails in 4.29.0-rc6
```

**The fix:** Mark `List.length` as `@[implicit_reducible]`, allowing
`isDefEq` to unfold it when checking implicit arguments. Several proofs
that previously needed a trailing `rfl` after `simp` now close directly,
since `simp` can see through `List.length` in more positions.

**Longer term:** The root cause is that `GetElem` carries complex proof
obligations in its type class instances, making implicit arguments
sensitive to definitional equality of collection sizes. We are
considering a redesign with a noncomputable `GetElemV` variant based on
`Nonempty` that avoids these casts entirely, but that is a larger change
planned for a future release.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 08:45:15 +00:00
Bhavik Mehta
0917260341 feat: add simp lemmas for kernel-friendly functions (#12950)
This PR adds simp lemmas equating kernel-friendly function names with
their operator notation equivalents: `Nat.land_eq`, `Nat.lor_eq`,
`Nat.xor_eq`, `Nat.shiftLeft_eq'`, `Nat.shiftRight_eq'`, and
`Bool.rec_eq`. These are useful when proofs involve reflection and need
to simplify kernel-reduced terms back to operator notation.

Closes #12716

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-18 07:22:06 +00:00
Mac Malone
61a3443a95 feat: lake: track platform dependency in cache maps (#12954)
This PR changes the Lake `CacheMap` data structure to track the
platform-dependence of outputs. Platform-independent packages will no
longer include platform-dependent mappings in the output files produced
by `lake build -o`.
2026-03-18 01:18:57 +00:00
Sofia Rodrigues
bf4f51e704 fix: windows build for signal handlers (#12955)
This PR fixes the windows build with signal handlers.
2026-03-17 23:02:01 +00:00
Sofia Rodrigues
0ac5d75bac fix: body 2026-03-17 19:36:36 -03:00
Sofia Rodrigues
e4f2f5717c refactor: architecture 2026-03-17 16:42:14 -03:00
Sofia Rodrigues
abbe36c0d2 refactor: architecture 2026-03-17 16:42:10 -03:00
Lean stage0 autoupdater
4ba85acc46 chore: update stage0 2026-03-17 17:55:05 +00:00
Henrik Böving
32643234b5 fix: actively ignore borrow annotations for export functions (#12952)
This PR ensures that when a function is marked `export` its borrow
annotations (if present) are always ignored.

This was the previous behavior in the C++ version of this file but
slightly modified when porting to the old IR and thus subsequently
ported to LCNF wrongly as well.
2026-03-17 17:22:56 +00:00
Wojciech Nawrocki
147ce5ab18 chore: use IO.CancelToken in server (#12948)
This PR moves `RequestCancellationToken` from `IO.Ref` to
`IO.CancelToken`.

They consist of the same data, but the constructor of `CancelToken` is
private. Hence there is no way to take the `Ref` in a
`RequestCancellationToken` and turn it into a `CancelToken`. This in
turn means that we can't set `Core.Context.cancelTk?` to be the one in
`RequestContext` when launching `CoreM` tasks in request handlers.
2026-03-17 16:48:53 +00:00
Garmelon
6b7f0ad5fc chore: check test output before exit code in piles (#12947)
This improves the feedback when tests fail. Getting a diff is more
useful than a vague exit code.
2026-03-17 16:34:21 +00:00
Sofia Rodrigues
7ef652911e revert: uri 2026-03-17 12:18:20 -03:00
Sofia Rodrigues
9ef386d7c3 revert: uri changes 2026-03-17 12:15:43 -03:00
Sofia Rodrigues
b9b2e08181 Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-03-17 12:13:57 -03:00
Sofia Rodrigues
33caa4e82f fix: test 2026-03-17 12:03:35 -03:00
Markus Himmel
5f5a450eb9 feat: forall lemmas (#12945)
This PR adds a few `forall` lemmas to the `simp` set.
2026-03-17 15:00:39 +00:00
Sofia Rodrigues
8c292c70ee Merge branch 'master' of https://github.com/leanprover/lean4 into sofia/async-http-uri 2026-03-17 10:42:30 -03:00
Sofia Rodrigues
4f4ee7c789 Merge branch 'master' of https://github.com/leanprover/lean4 into sofia/async-http-uri 2026-03-17 10:42:19 -03:00
Garmelon
7c011aa522 fix: use process signal numbers from correct architecture (#12900)
This PR fixes some process signals that were incorrectly numbered.

From what I can tell, the code used signals and signal numbers for
Alpha/SPARC, not x86/ARM. The test was also broken and always green,
hiding the mistake.
2026-03-17 13:33:13 +00:00
Sofia Rodrigues
d7ea3a5984 fix: redundant end and namespace 2026-03-17 10:23:17 -03:00
Sofia Rodrigues
33c36c7466 fix: absolute-form parse and add helper functions 2026-03-17 10:17:00 -03:00
Wojciech Różowski
6160d17e2d feat: allow @[cbv_eval] to override @[cbv_opaque] (#12944)
This PR changes the interaction between `@[cbv_opaque]` and
`@[cbv_eval]`
attributes in the `cbv` tactic. Previously, `@[cbv_opaque]` completely
blocked
all reduction including `@[cbv_eval]` rewrite rules. Now, `@[cbv_eval]`
rules
can fire on `@[cbv_opaque]` constants, allowing users to provide custom
rewrite
rules without exposing the full definition. Equation theorems, unfold
theorems,
and kernel reduction remain suppressed for opaque constants.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 13:08:21 +00:00
Sofia Rodrigues
a0048bf703 feat: introduce Headers data type for HTTP (#12127)
This PR introduces the `Headers` data type, that provides a good and
convenient abstraction for parsing, querying, and encoding HTTP/1.1
headers.

This contains the same code as #10478, divided into separate pieces to
facilitate easier review.

The pieces of this feature are:
- Core data structures: #12126
- Headers: #12127
- URI:  #12128
- Body: #12144
- H1: #12146
- Server: #12151
- Client:

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2026-03-17 12:25:01 +00:00
Lean stage0 autoupdater
72a97a747a chore: update stage0 2026-03-17 11:48:51 +00:00
Sebastian Ullrich
1127eefdca chore: consistent build flags between USE_LAKE ON and OFF (#12941)
Fixes the stage 2 build using USE_LAKE=OFF. We should not use
`lakefile.toml.in` for any semantically relevant flags.
2026-03-17 11:02:55 +00:00
Robin Arnez
aa18927d2e fix: segfault in idbgClientLoop (#12940)
This PR fixes a segfault when running `idbgClientLoop`. `@[extern]`
expects that the function doesn't include erased arguments in the
signature; however, `@[export]` exports the function with all arguments,
including erased ones. This causes a function signature mismatch between
`idbgClientLoopImpl` and `idbgClientLoop`, causing segfaults. However,
instead of solving the deeper problem that `@[extern]` - `@[export]`
pairs can cause such problems, this PR removes the erased arguments from
`idbgClientLoopImpl` and replaces occurrences of `α` with `NonScalar`.
2026-03-17 10:55:54 +00:00
Henrik Böving
606c149cd6 chore: fix update-stage0 with make build (#12943)
This PR fixes the stage0 build with -DUSE_LAKE=OFF
2026-03-17 10:28:51 +00:00
Sebastian Ullrich
3c32607020 fix: incorrect borrow annotation on demangleBtLinCStr leading to segfault on panic (#12939) 2026-03-17 09:24:57 +00:00
Eric Wieser
6714601ee4 fix: remove accidental type monomorphism in Id.run_seqLeft (#12936)
This PR fixes `Id.run_seqLeft` and `Id.run_seqRight` to apply when the
two monad results are different.
2026-03-17 06:43:51 +00:00
damiano
6b604625f2 fix: add missing pp-spaces in grind_pattern (#11686)
This PR adds a pretty-printed space in `grind_pattern`.

[#lean4 > Some pretty printing quirks @
💬](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Some.20pretty.20printing.20quirks/near/563848793)

Co-authored-by: Kim Morrison <kim@tqft.net>
2026-03-17 04:15:02 +00:00
Kim Morrison
e96b0ff39c fix: use response files on all platforms to avoid ARG_MAX (#12540)
This PR extends Lake's use of response files (`@file`) from Windows-only
to all platforms, avoiding `ARG_MAX` limits when invoking `clang`/`ar`
with many object files.

Lake already uses response files on Windows to avoid exceeding CLI
length limits. On macOS and Linux, linking Mathlib's ~15,000 object
files into a shared library can exceed macOS's `ARG_MAX` (262,144
bytes). Both `clang` and `gcc` support `@file` response files on all
platforms, so this is safe to enable unconditionally.

Reported as a macOS issue at
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/The.20clang.20command.20line.20with.20all.20~15.2C000.20Mathlib.20.2Ec.2Eo.2Eexport/near/574369912:
the Mathlib cache ships Linux `.so` shared libs but not macOS `.dylib`
files, so `precompileModules` on macOS triggers a full re-link that
exceeds `ARG_MAX`.

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 04:14:37 +00:00
Kim Morrison
50ee6dff0a chore: update leantar to v0.1.19 (#12938) 2026-03-17 03:55:21 +00:00
Mac Malone
9e0aa14b6f feat: lake: fixedToolchain package configuration (#12935)
This PR adds the `fixedToolchain` Lake package configuration option.
Setting this to `true` informs Lake that the package is only expected to
function on a single toolchain (like Mathlib). This causes Lake's
toolchain update procedure to prioritize its toolchain and avoids the
need to separate input-to-output mappings for the package by toolchain
version in the Lake cache.
2026-03-17 02:37:55 +00:00
Garmelon
5c685465bd chore: handle absence of meld in fix_expected.py (#12934) 2026-03-16 19:07:44 +00:00
Garmelon
ef87f6b9ac chore: delete temp files before, not after tests (#12932) 2026-03-16 19:02:28 +00:00
Garmelon
49715fe63c chore: improve how test suite interacts with stages (#12913)
The tests need to run with certain environment variables set that only
cmake really knows and that differ between stages. Cmake could just set
the variables directly when running the tests and benchmarks, but that
would leave no good way to manually run a single benchmark. So cmake
generates some stage-specific scripts instead that set the required
environment variables.

Previously, those scripts were sourced directly by the individual
`run_*` scripts, so the env scripts of different stages would overwrite
each other. This PR changes the setup so they can instead be generated
next to each other. This also simplifies the `run_*` scripts themselves
a bit, and makes `tests/bench/build` less of a hack.
2026-03-16 15:20:03 +00:00
Lean stage0 autoupdater
133fd016b4 chore: update stage0 2026-03-16 13:15:14 +00:00
Bhavik Mehta
76e593a52d fix: rename Int.sq_nonnneg to Int.sq_nonneg (#12909)
This PR fixes the typo in `Int.sq_nonnneg`.

Closes #12906.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-16 10:52:57 +00:00
Jesse Alama
fa9a32b5c8 fix: correct swapped operands in Std.Time subtraction instances (#12919)
This PR fixes the `HSub PlainTime Duration` instance, which had its
operands reversed: it computed `duration - time` instead of `time -
duration`. For example, subtracting 2 minutes from `time("13:02:01")`
would give `time("10:57:59")` rather than the expected
`time("13:00:01")`. We also noticed that `HSub PlainDateTime
Millisecond.Offset` is similarly affected.

Closes #12918
2026-03-16 10:52:06 +00:00
Henrik Böving
2d999d7622 refactor: ignore borrow annotations at export/extern tricks (#12930)
This PR places `set_option compiler.ignoreBorrowAnnotation true in` on
to all `export`/`extern`
pairs. This is necessary because `export` forces all arguments to be
passed as owned while `extern`
respects borrow annotations. The current approach to the
`export`/`extern` trick was always broken
but never surfaced. However, with upcoming changes many
`export`/`extern` pairs are going to be
affected by borrow annotations and would've broken without this.
2026-03-16 10:03:40 +00:00
Sebastian Ullrich
ddd5c213c6 chore: CLAUDE.md: stage 2 build instructions (#12929) 2026-03-16 09:47:14 +00:00
Kim Morrison
c9ceba1784 fix: use null-safe while-read loop for subverso manifest sync (#12928)
This PR replaces `find -print0 | xargs -0 -I{} sh -c '...'` with
`find -print0 | while IFS= read -r -d '' f; do ... done` for the
subverso sub-manifest sync in release_steps.py. The original xargs
invocation had fragile nested shell quoting; the while-read loop is
both null-delimiter safe and more readable.

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 08:17:32 +00:00
Mac Malone
57df23f27e feat: lake: cached compressed module artifacts (#12914)
This PR adds packing and unpacking of module artifacts into `.ltar`
archives using `leantar`.
2026-03-16 04:36:19 +00:00
Mac Malone
ea8fca2d9f refactor: lake: download arts by default in cache get (#12927)
This PR changes `lake cache get` to download artifacts by default.
Artifacts can be downloaded on demand with the new `--mappings-only`
option (`--download-arts` is now obsolete).

In the future, the plan is to have Lake download mappings when cloning
dependencies. Then, `lake cache get` will primarily be used to download
artifacts eagerly. Thus, it makes sense to have that as the default.
2026-03-16 02:29:44 +00:00
Paul Reichert
274997420a refactor: remove backward compatibility options from iterator/slice/range modules (#12925)
This PR removes `respectTransparency`, `reducibleClassField` and `simp
+instances` usages in the iterator/slice/range modules.
2026-03-15 14:03:51 +00:00
Wojciech Różowski
6631352136 fix: remove accidentally added code from Sym.Simp.Pattern (#12926)
This PR removes unused functions (`mkPatternCoreFromLambda`,
`mkPatternFromLambda`, `mkSimprocPatternFromExpr`) and the `import
Lean.Meta.AbstractMVars` that were added to `Lean.Meta.Sym.Pattern`
after merging #12597.
2026-03-15 10:30:26 +00:00
Leonardo de Moura
cfa8c5a036 fix: handle universe level commutativity in sym pattern matching (#12923)
This PR fixes a bug where `max u v` and `max v u` fail to match in
SymM's pattern matching. Both `processLevel` (Phase 1) and
`isLevelDefEqS` (Phase 2) treated `max` positionally, so `max u v ≠ max
v u` structurally even though they are semantically equal.

The fix has three parts:
- Eagerly normalize universe levels in patterns at creation time
(`preprocessDeclPattern`, `preprocessExprPattern`,
`mkSimprocPatternFromExpr`)
- Normalize the target level in `processLevel` before matching, using a
`where go` refactor
- Add `tryApproxMaxMax` to `processLevel` and `isLevelDefEqS`: when
positional `max/max` matching would fail, check if one argument from
each side matches structurally and match the remaining pair

Also moves `normalizeLevels` from `Grind.Util` to `Sym.Util` to avoid
code duplication, since both Sym and Grind need it.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 01:06:16 +00:00
Leonardo de Moura
7120d9aef5 fix: eta-reduce expressions in sym discrimination tree lookup (#12920)
This PR adds eta reduction to the sym discrimination tree lookup
functions (`getMatch`, `getMatchWithExtra`, `getMatchLoop`). Without
this, expressions like `StateM Nat` that unfold to eta-expanded forms
`(fun α => StateT Nat Id α)` fail to match discrimination tree entries
for the eta-reduced form `(StateT Nat Id)`.

Also optimizes `etaReduce` with an early exit for non-lambda expressions
and removes a redundant `n == 0` check.
Includes a test verifying that `P (StateM Nat)` matches a disc tree
entry for `P (StateT Nat Id)`.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 16:57:10 +00:00
Sofia Rodrigues
7fbecca6f0 fix: test 2026-03-13 23:54:45 -03:00
Sofia Rodrigues
ae5a3d2c8b Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-13 23:51:15 -03:00
Sofia Rodrigues
1a270555ae fix: uri 2026-03-13 23:51:00 -03:00
Sofia Rodrigues
72702c3538 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-13 23:47:38 -03:00
Sofia Rodrigues
e86dbf3992 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-13 23:46:40 -03:00
Sofia Rodrigues
d71f0bdae7 fix: uri test 2026-03-13 23:46:35 -03:00
Sofia Rodrigues
6ae49d7639 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-13 23:13:32 -03:00
Sofia Rodrigues
232d173af3 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-13 23:13:19 -03:00
Sofia Rodrigues
3a4a309aed feat: split uri types 2026-03-13 23:13:05 -03:00
Sofia Rodrigues
9c87a9f044 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-12 15:52:36 -03:00
Sofia Rodrigues
34c9cafc12 fix: type 2026-03-12 15:52:29 -03:00
Sofia Rodrigues
014dd1d263 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-12 15:43:28 -03:00
Sofia Rodrigues
2a7a407875 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-12 15:43:06 -03:00
Sofia Rodrigues
e359001026 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-12 15:39:08 -03:00
Sofia Rodrigues
72244398dc fix: test 2026-03-12 15:38:59 -03:00
Sofia Rodrigues
c0e60b797c Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-12 15:33:48 -03:00
Sofia Rodrigues
400908a2f4 fix: port and fragment 2026-03-12 15:09:49 -03:00
Sofia Rodrigues
394c999c2a fix: uri 2026-03-12 15:03:49 -03:00
Sofia Rodrigues
b7e88dadeb Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-03-12 15:01:03 -03:00
Sofia Rodrigues
a39a0575a0 Merge branch 'master' into sofia/async-http-headers 2026-03-12 14:35:25 -03:00
Sofia Rodrigues
5815f33342 Merge branch 'sofia/fix-native-decide' into sofia/async-http-headers 2026-03-12 14:17:13 -03:00
Sofia Rodrigues
4fdf94ed3d refactor: simplify error 2026-03-10 15:58:40 -03:00
Sofia Rodrigues
66743e80a6 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-10 15:58:20 -03:00
Sofia Rodrigues
2d0d63f5d3 refactor: move logic 2026-03-10 15:58:07 -03:00
Sofia Rodrigues
10951fdb57 refactor: use closewitherror 2026-03-10 15:37:06 -03:00
Sofia Rodrigues
71d3967338 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-10 15:36:54 -03:00
Sofia Rodrigues
34dbcb2ca5 refactor: add close with error 2026-03-10 15:35:34 -03:00
Sofia Rodrigues
abb60e47c8 refactor: make smaller 2026-03-10 15:19:27 -03:00
Sofia Rodrigues
7a852aedb6 fix: squeeze simp and paren 2026-03-10 10:08:22 -03:00
Sofia Rodrigues
1554f57525 fix: import 2026-03-09 21:18:57 -03:00
Sofia Rodrigues
1fa01cdadb style: just removed variable 2026-03-09 20:35:53 -03:00
Sofia Rodrigues
758e5afb07 refactor: simplify 2026-03-09 20:35:53 -03:00
Sofia Rodrigues
11516bbf09 fix: import 2026-03-09 20:35:53 -03:00
Sofia Rodrigues
f76dca5bba fix: proof 2026-03-09 20:35:53 -03:00
Sofia Rodrigues
fe6ac812af fix: panic 2026-03-09 20:35:53 -03:00
Sofia Rodrigues
51a00843ea fix: remove usage of 2026-03-09 20:35:53 -03:00
Sofia Rodrigues
c8c702af8d Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-04 16:40:39 -03:00
Sofia Rodrigues
5b5b0fad70 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-04 16:40:33 -03:00
Sofia Rodrigues
eab144bbb2 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-04 16:40:27 -03:00
Sofia Rodrigues
cfe282f024 fix: port parse 2026-03-04 16:40:03 -03:00
Sofia Rodrigues
e7f06c8fa2 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-04 16:34:41 -03:00
Sofia Rodrigues
beb85dd6b0 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-04 16:34:27 -03:00
Sofia Rodrigues
debafcf0ef Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-04 16:29:43 -03:00
Sofia Rodrigues
2668f07808 fix: alpha and isdigit 2026-03-04 16:29:31 -03:00
Sofia Rodrigues
e3928b7b1a Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-04 16:26:56 -03:00
Sofia Rodrigues
2f3a97ed8a Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-04 16:26:48 -03:00
Sofia Rodrigues
0315d56389 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-04 16:26:38 -03:00
Sofia Rodrigues
b9e489cc8f Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-03-04 16:22:58 -03:00
Sofia Rodrigues
135b049080 Merge branch 'master' into sofia/async-http-headers 2026-03-04 13:22:45 -03:00
Sofia Rodrigues
4005bd027b fix: size 2026-03-04 12:04:53 -03:00
Sofia Rodrigues
fbf03e31f9 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-04 10:21:46 -03:00
Sofia Rodrigues
39ab2b289c test: move test from encode to the uri tests 2026-03-04 10:20:25 -03:00
Sofia Rodrigues
6c6f9a5d83 refactor: change Char.isDigit and Char.isAlpha 2026-03-04 10:00:41 -03:00
Sofia Rodrigues
a7aea9a12d style: format 2026-03-04 09:50:30 -03:00
Sofia Rodrigues
9517b5bc2d fix: h1 informational 2026-03-04 09:27:56 -03:00
Sofia Rodrigues
71debba5a2 refactor: change agentName field 2026-03-03 14:24:46 -03:00
Sofia Rodrigues
a2c5f3c79e Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-03 14:22:47 -03:00
Sofia Rodrigues
fd9117fc12 fix: server name 2026-03-03 14:22:39 -03:00
Sofia Rodrigues
1b6357dc03 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-03 13:59:18 -03:00
Sofia Rodrigues
38cb50d629 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-03 13:59:13 -03:00
Sofia Rodrigues
74af777707 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-03 13:59:07 -03:00
Sofia Rodrigues
3dfb5e002a Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-03-03 13:59:01 -03:00
Sofia Rodrigues
3075e5091b Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-03 13:58:55 -03:00
Sofia Rodrigues
af12f7e9be revert: wrong comment 2026-03-03 13:58:08 -03:00
Sofia Rodrigues
a2f9f74740 refactor: remove h1.0 tests 2026-03-03 13:57:14 -03:00
Sofia Rodrigues
13fb8a5980 fix: h1 discovers the port to host match 2026-03-03 13:56:41 -03:00
Sofia Rodrigues
41d2984f25 refactor: head -> line 2026-03-03 13:56:09 -03:00
Sofia Rodrigues
f63639d42b Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-03 13:42:13 -03:00
Sofia Rodrigues
6df74943e0 fix: http and https host port match 2026-03-03 13:41:30 -03:00
Sofia Rodrigues
865b147a91 fix: http1.0 behavior 2026-03-03 13:30:28 -03:00
Sofia Rodrigues
c2f2b3cf32 fix: refactor changes 2026-03-03 12:50:31 -03:00
Sofia Rodrigues
4173713f94 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-03 12:45:10 -03:00
Sofia Rodrigues
53c9277209 test: rename head to line 2026-03-03 12:43:44 -03:00
Sofia Rodrigues
f14977f495 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-03 12:42:31 -03:00
Sofia Rodrigues
cfa5cf76fc Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-03-03 12:42:23 -03:00
Sofia Rodrigues
238925a681 style: change field names 2026-03-03 12:38:52 -03:00
Sofia Rodrigues
8cb236e9eb style: remove parenthesis 2026-03-03 12:35:29 -03:00
Sofia Rodrigues
3d039f8dba fix: bugs and code style 2026-03-03 12:34:12 -03:00
Sofia Rodrigues
203d5362d4 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-03-03 12:12:38 -03:00
Sofia Rodrigues
6189d4c130 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-03 12:10:31 -03:00
Sofia Rodrigues
58f14d34d7 chore: improve comments 2026-03-03 12:10:22 -03:00
Sofia Rodrigues
710eee2b49 refactor: head to line 2026-03-03 12:08:26 -03:00
Sofia Rodrigues
bd4af50d04 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-03 12:03:55 -03:00
Sofia Rodrigues
8cb30347b6 fix: rename head to line 2026-03-03 12:03:47 -03:00
Sofia Rodrigues
d8e6b09b90 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-03 11:58:30 -03:00
Sofia Rodrigues
df8abc2b3f fix: remove token let tchar 2026-03-03 11:58:21 -03:00
Sofia Rodrigues
5a852bdffd Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-03 11:57:32 -03:00
Sofia Rodrigues
11d3860c69 fix: remove char testBit 2026-03-03 11:57:20 -03:00
Sofia Rodrigues
5a253001b3 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-03 11:39:12 -03:00
Sofia Rodrigues
083fec29c8 test: fix 2026-03-03 11:38:54 -03:00
Sofia Rodrigues
d41753a5f9 fix: suggestions 2026-03-03 10:11:25 -03:00
Sofia Rodrigues
a086a817e0 feat: v10 2026-03-03 09:26:45 -03:00
Sofia Rodrigues
e434a4d44b feat: v10 2026-03-03 09:26:27 -03:00
Sofia Rodrigues
7295389284 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-03 08:34:31 -03:00
Sofia Rodrigues
f8e1bc685a Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-03 08:34:23 -03:00
Sofia Rodrigues
5e1204e70d fix: test 2026-03-03 08:34:18 -03:00
Sofia Rodrigues
a00ec10261 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-03 08:31:51 -03:00
Sofia Rodrigues
cb9b182824 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-03 08:31:44 -03:00
Sofia Rodrigues
61d7c151da fix: close status for empty 2026-03-03 08:31:33 -03:00
Sofia Rodrigues
f9f1bdc77b chore: comments 2026-03-03 08:29:55 -03:00
Sofia Rodrigues
f3452c09a9 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-03 08:26:37 -03:00
Sofia Rodrigues
2bed27681a chore: comments 2026-03-03 01:06:23 -03:00
Sofia Rodrigues
5bb3b08698 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-03 00:55:17 -03:00
Sofia Rodrigues
82645d0953 docs: improve comments on h1 machine and errors 2026-03-03 00:55:12 -03:00
Sofia Rodrigues
2ab52fb864 fix: test 2026-03-03 00:46:06 -03:00
Sofia Rodrigues
1bba3082f0 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-03 00:37:22 -03:00
Sofia Rodrigues
7ed7a1b69d fix: rfc 2026-03-03 00:37:13 -03:00
Sofia Rodrigues
bd10d0193e Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-03 00:36:46 -03:00
Sofia Rodrigues
67822f4c42 refactor: remove bv 2026-03-03 00:36:38 -03:00
Sofia Rodrigues
e7f6fbb473 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-03 00:35:13 -03:00
Sofia Rodrigues
1cb3d56618 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-03 00:34:37 -03:00
Sofia Rodrigues
d99485dd79 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-03 00:34:30 -03:00
Sofia Rodrigues
f85b9b8d09 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-03-03 00:34:21 -03:00
Sofia Rodrigues
5fb254b7ef Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-03 00:24:40 -03:00
Sofia Rodrigues
6e202e34a4 feat: all char predicates 2026-03-03 00:24:16 -03:00
Sofia Rodrigues
843c814778 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-03-03 00:18:49 -03:00
Sofia Rodrigues
c7d4d8d799 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-03 00:17:31 -03:00
Sofia Rodrigues
91c60f801c fix: rstore treeMap tests from master 2026-03-03 00:16:26 -03:00
Sofia Rodrigues
ae30f55728 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-02 20:52:24 -03:00
Sofia Rodrigues
63b0cc17c4 fix: char predicates 2026-03-02 20:51:55 -03:00
Sofia Rodrigues
c9a5111dcc feat: add client states 2026-03-02 20:02:00 -03:00
Sofia Rodrigues
8e12a4181c Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-02 19:48:01 -03:00
Sofia Rodrigues
33393a7c00 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-02 19:47:55 -03:00
Sofia Rodrigues
7434b97511 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-03-02 19:47:48 -03:00
Sofia Rodrigues
29c8f8cfa1 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-02 19:44:03 -03:00
Sofia Rodrigues
36b2d99e3d fix: encode 2026-03-02 19:43:57 -03:00
Sofia Rodrigues
4b8a48c817 fix: method parsing 2026-03-02 19:36:08 -03:00
Sofia Rodrigues
e0862a0220 fix: tests 2026-03-02 19:35:36 -03:00
Sofia Rodrigues
10fc7da3fa Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-03-02 12:26:17 -03:00
Sofia Rodrigues
a1f535d9d8 fix: remove treemap 2026-03-02 12:26:13 -03:00
Sofia Rodrigues
993c87dd80 fix: methods 2026-03-02 12:26:00 -03:00
Sofia Rodrigues
742e3080c9 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-03-02 12:02:25 -03:00
Sofia Rodrigues
3de1d21c86 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-03-02 12:01:25 -03:00
Sofia Rodrigues
83a0756b05 fix: remove treemap 2026-03-02 12:01:18 -03:00
Sofia Rodrigues
b8f2cd94aa Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-03-02 12:00:58 -03:00
Sofia Rodrigues
64ff045559 fix: remove treemap 2026-03-02 12:00:55 -03:00
Sofia Rodrigues
109ab8eb68 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-03-02 11:59:21 -03:00
Sofia Rodrigues
bf09ea8ff5 feat: remove tests temporarily 2026-03-02 11:56:45 -03:00
Sofia Rodrigues
7ce9fe9f97 feat: remove tests temporarily 2026-03-02 11:54:16 -03:00
Sofia Rodrigues
aff9e0c459 refactor: rust-types-rs like method enum with IANA specification 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
a74df33feb fix: method 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
dd63b614eb fix: comments 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
515e6e20c0 fix: test 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
cc45fc9cc2 fix: dots 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
bc9c18f0b0 fix: small changes 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
8ee21a7176 fix: comment 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
92aa9f2b8a fix: RFC checks and small improvements 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
c2243a0ea5 fix: tests 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
efbd23a6d9 fix: format 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
26440fcf6a fix: extension values 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
ac4c5451e4 fix: data char 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
c94c5cb7e4 fix: comments 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
78ca6edc99 feat: specialize quote 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
d92dc22df3 fix: test 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
48ab74f044 fix: status code 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
da68a63902 feat: reason phrase in custom status code 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
db99fd2d7d feat: ignore reasonphrase 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
a61712c962 feat: validation in reasonPhrase 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
ea36555588 fix: reasonPhrase 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
b02bc4d6d2 feat: reason phrase 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
c836fe8723 fix: typos and compareName 2026-03-02 11:53:56 -03:00
Sofia Rodrigues
8068ed317c fix: typos 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
0bd44ab745 fix: comment 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
172d12c75c refactor: move trailers 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
6b6b9fffff feat: add extension handling of quotes and ExtensionName 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
f3fa5c8242 fix: chunked 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
b0c5667f06 fix: import 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
2d262c9755 fix: interpolation 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
571898bf63 fix: extensions 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
0570277a2e feat: add extensions 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
557709d9bb fix: apply suggestions 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
0229508ca7 refactor: remove headers 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
ace10ee42b fix: default size 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
4e36dcc98f fix: apply suggestions
Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2026-03-02 11:53:55 -03:00
Sofia Rodrigues
a93ea184fe fix: status and chunkedbuffer 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
c309a3c07e feat: basic headers structure to more structured approach 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
30641c617f feat: data components 2026-03-02 11:53:55 -03:00
Sofia Rodrigues
37fcb2ce55 refactor: comments 2026-03-02 09:22:43 -03:00
Sofia Rodrigues
97cd66afde fix: comments 2026-03-01 16:45:51 -03:00
Sofia Rodrigues
6dbb6b8d0e Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-28 15:41:39 -03:00
Sofia Rodrigues
4306782b93 Merge branch 'sofia/async-http-uri' into sofia/async-http-server 2026-02-28 15:41:35 -03:00
Sofia Rodrigues
6935306439 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-28 15:41:25 -03:00
Sofia Rodrigues
1aa23cd92b Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-28 15:41:18 -03:00
Sofia Rodrigues
0bb4ba72d4 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-28 15:41:11 -03:00
Sofia Rodrigues
57a4d9ad4b Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-28 15:41:04 -03:00
Sofia Rodrigues
bfc6617c12 fix: method 2026-02-28 15:40:55 -03:00
Sofia Rodrigues
c1b5b64797 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-28 12:41:44 -03:00
Sofia Rodrigues
9b563220b2 fix: strict method 2026-02-28 12:41:39 -03:00
Sofia Rodrigues
0eb4a6e8c6 fix: timeout and config 2026-02-28 12:40:46 -03:00
Sofia Rodrigues
4614def4cd Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-27 21:14:21 -03:00
Sofia Rodrigues
c97dfe585a feat: headers rfc refs 2026-02-27 21:14:09 -03:00
Sofia Rodrigues
74ecbca430 feat: tests 2026-02-27 21:12:33 -03:00
Sofia Rodrigues
6fa6d2e3f7 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-27 14:58:31 -03:00
Sofia Rodrigues
05c4d9202a fix: comments 2026-02-27 14:58:24 -03:00
Sofia Rodrigues
3a4e9f6eca Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-27 14:52:23 -03:00
Sofia Rodrigues
aa09ab0cd9 fix: function name 2026-02-27 14:52:16 -03:00
Sofia Rodrigues
8affe05767 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-27 14:31:17 -03:00
Sofia Rodrigues
3aa02eede3 fix: comments 2026-02-27 14:31:09 -03:00
Sofia Rodrigues
c86f926d1b Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-27 14:22:38 -03:00
Sofia Rodrigues
ff4419357c Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-27 14:20:02 -03:00
Sofia Rodrigues
3c131da050 fix: comments 2026-02-27 14:19:32 -03:00
Sofia Rodrigues
5fd94a1e1d Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-27 13:24:18 -03:00
Sofia Rodrigues
fcc4185bb2 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-27 13:24:08 -03:00
Sofia Rodrigues
bae251d15a Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-27 13:23:58 -03:00
Sofia Rodrigues
6edc0c7427 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-27 13:22:51 -03:00
Sofia Rodrigues
563189fec9 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-27 13:04:51 -03:00
Sofia Rodrigues
25d7db2e62 Merge branch 'master' of https://github.com/leanprover/lean4 into sofia/async-http-data 2026-02-27 10:08:06 -03:00
Sofia Rodrigues
e569c9ef64 feat: remove unacurate test 2026-02-27 09:04:24 -03:00
Sofia Rodrigues
c467175336 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-26 18:57:21 -03:00
Sofia Rodrigues
7562c103dd Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-26 18:57:17 -03:00
Sofia Rodrigues
1be8c11cee fix: empty chunk 2026-02-26 18:57:05 -03:00
Sofia Rodrigues
ea6c1e65f6 fix: small changes 2026-02-26 18:56:48 -03:00
Sofia Rodrigues
67300c640c fix: tests 2026-02-26 18:56:16 -03:00
Sofia Rodrigues
625e1c9a32 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-26 17:33:41 -03:00
Sofia Rodrigues
b09946684b Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-26 17:09:33 -03:00
Sofia Rodrigues
beedfa1e4e fix: small comments fix and parameters 2026-02-26 16:49:15 -03:00
Sofia Rodrigues
f68c2420e7 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-26 16:17:48 -03:00
Sofia Rodrigues
cdfd24171a fix: test 2026-02-26 16:15:03 -03:00
Sofia Rodrigues
718e549de3 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-26 16:10:56 -03:00
Sofia Rodrigues
81f76a24d8 fix:lower case scheme 2026-02-26 16:10:46 -03:00
Sofia Rodrigues
292f297006 feat: small comments 2026-02-26 15:41:20 -03:00
Sofia Rodrigues
b7be57272a feat: forIn 2026-02-26 15:24:17 -03:00
Sofia Rodrigues
a0dc1dbbc0 fix: test 2026-02-26 15:18:32 -03:00
Sofia Rodrigues
2e604884dd Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-26 15:16:35 -03:00
Sofia Rodrigues
2049542833 feat: order 2026-02-26 15:16:23 -03:00
Sofia Rodrigues
caf19b8458 feat: order 2026-02-26 15:13:56 -03:00
Sofia Rodrigues
c5180b2dfc Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-26 01:23:46 -03:00
Sofia Rodrigues
91c5b717f0 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-26 01:23:24 -03:00
Sofia Rodrigues
cb6f540efb fix: test 2026-02-26 01:22:46 -03:00
Sofia Rodrigues
ec833b52ee Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-26 01:01:46 -03:00
Sofia Rodrigues
ba36c1dee2 fix: comments 2026-02-26 01:01:41 -03:00
Sofia Rodrigues
5cb510cdf7 fix: precedence 2026-02-26 01:01:24 -03:00
Sofia Rodrigues
a72de461cd Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-25 22:34:45 -03:00
Sofia Rodrigues
228f0d24a7 fix: remove unused headers 2026-02-25 22:34:41 -03:00
Sofia Rodrigues
73cf41d7e5 fix: comments 2026-02-25 22:30:43 -03:00
Sofia Rodrigues
819d4c6c1f fix: uri comments 2026-02-25 22:13:19 -03:00
Sofia Rodrigues
4de3e40349 fix: builder 2026-02-25 22:00:11 -03:00
Sofia Rodrigues
03f1d47462 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-25 21:59:08 -03:00
Sofia Rodrigues
a88908572c Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-25 21:54:21 -03:00
Sofia Rodrigues
55d357dbb4 fix: dots 2026-02-25 21:54:18 -03:00
Sofia Rodrigues
49d00ae056 fix: comments and small formatting errors 2026-02-25 21:52:12 -03:00
Sofia Rodrigues
e9eed5cbe4 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-25 20:57:02 -03:00
Sofia Rodrigues
2652ae0fb8 fix: small changes 2026-02-25 20:56:19 -03:00
Sofia Rodrigues
3f48ef4af9 fix: comment 2026-02-25 20:45:57 -03:00
Sofia Rodrigues
a9de308aea fix: RFC checks and small improvements 2026-02-25 20:45:22 -03:00
Sofia Rodrigues
405d03aac9 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-25 17:53:29 -03:00
Sofia Rodrigues
d5a819f30f fix: config names 2026-02-25 17:53:25 -03:00
Sofia Rodrigues
81c3e5034a fix: pull 2026-02-25 17:52:59 -03:00
Sofia Rodrigues
c971d3f490 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-25 15:06:39 -03:00
Sofia Rodrigues
26bcd2d065 feat: avoid forbidden trailer headers 2026-02-25 15:06:32 -03:00
Sofia Rodrigues
9c1054adca fix: slow attack 2026-02-25 15:06:19 -03:00
Sofia Rodrigues
cba7bfbbe7 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-25 14:15:51 -03:00
Sofia Rodrigues
2990b41d44 feat: improve incrementality 2026-02-25 13:43:47 -03:00
Sofia Rodrigues
f543206d4a fix: test 2026-02-25 08:53:15 -03:00
Sofia Rodrigues
1cd2cba130 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-25 00:53:38 -03:00
Sofia Rodrigues
a009ad2a68 fix: transfer 2026-02-25 00:53:04 -03:00
Sofia Rodrigues
6a19fc5a21 fix: host 2026-02-25 00:51:41 -03:00
Sofia Rodrigues
91275b3747 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-24 23:24:28 -03:00
Sofia Rodrigues
df80ac720a fix: semaphore 2026-02-24 23:24:16 -03:00
Sofia Rodrigues
6797ca9345 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-24 20:16:10 -03:00
Sofia Rodrigues
c266649454 fix: sleep 2026-02-24 20:16:05 -03:00
Sofia Rodrigues
7160b92bfb fix: semaphore 2026-02-24 20:15:56 -03:00
Sofia Rodrigues
6d1a0ecc8a fix: semaphore 2026-02-24 20:15:22 -03:00
Sofia Rodrigues
fd96be3870 feat: rfc compliance with some features 2026-02-24 19:09:33 -03:00
Sofia Rodrigues
3a3620e8aa fix: tests 2026-02-24 14:48:08 -03:00
Sofia Rodrigues
11fd4c8244 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-24 14:43:49 -03:00
Sofia Rodrigues
2731e1d942 fix: headers 2026-02-24 14:43:44 -03:00
Sofia Rodrigues
0ef3c83ed8 feat: ignore prior crlf 2026-02-24 14:39:02 -03:00
Sofia Rodrigues
edad8a090b Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-24 14:31:54 -03:00
Sofia Rodrigues
74dc55152f fix: test 2026-02-24 14:31:29 -03:00
Sofia Rodrigues
bf2471b8f1 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-24 14:28:32 -03:00
Sofia Rodrigues
21821ef062 fix: encoding duplication 2026-02-24 14:28:14 -03:00
Sofia Rodrigues
5ba3a6d4fc Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-24 10:47:43 -03:00
Sofia Rodrigues
8492e58a82 fix: comments 2026-02-24 10:38:11 -03:00
Sofia Rodrigues
e65e20e1cb feat: field content 2026-02-24 09:57:22 -03:00
Sofia Rodrigues
de7c029c9f feat: field content 2026-02-24 09:56:52 -03:00
Sofia Rodrigues
89c992a3c9 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-24 09:26:59 -03:00
Sofia Rodrigues
0b76c3de69 fix: tests 2026-02-24 09:20:52 -03:00
Sofia Rodrigues
ff99979855 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-24 09:09:39 -03:00
Sofia Rodrigues
9ddbb59fe1 fix: format 2026-02-24 09:09:35 -03:00
Sofia Rodrigues
36f87f98f8 fix: char 2026-02-24 09:09:14 -03:00
Sofia Rodrigues
5914fe3a4a Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-24 08:54:43 -03:00
Sofia Rodrigues
29f651a89c fix: extension values 2026-02-24 08:53:16 -03:00
Sofia Rodrigues
2e1bdd922e 2026-02-24 08:35:36 -03:00
Sofia Rodrigues
ab5d50cbc3 fix: data char 2026-02-23 22:18:15 -03:00
Sofia Rodrigues
7902db17c2 fix: comments 2026-02-21 01:25:05 -03:00
Sofia Rodrigues
5626ee369c feat: specialize quote 2026-02-21 00:53:15 -03:00
Sofia Rodrigues
682e2b99f3 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 22:30:31 -03:00
Sofia Rodrigues
6ed32edec0 feat: reorganize fix EOF 2026-02-20 22:30:24 -03:00
Sofia Rodrigues
662bed5a28 tests: add 2026-02-20 22:28:45 -03:00
Sofia Rodrigues
d0e884dc54 fix: config 2026-02-20 18:28:42 -03:00
Sofia Rodrigues
abf3305397 fix: move test from 200 to 400 2026-02-20 18:27:51 -03:00
Sofia Rodrigues
a6f42abe62 feat: remove lenience 2026-02-20 18:26:57 -03:00
Sofia Rodrigues
7a50344af4 feat: add header max config 2026-02-20 18:24:17 -03:00
Sofia Rodrigues
c7bcd4fbed Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 18:22:43 -03:00
Sofia Rodrigues
d367a9fe80 fix: enforce crlf and header bytes 2026-02-20 18:22:34 -03:00
Sofia Rodrigues
0e0578eacb Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 18:03:08 -03:00
Sofia Rodrigues
663eec9dc3 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-20 18:03:03 -03:00
Sofia Rodrigues
e62f8d608d Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-20 18:02:56 -03:00
Sofia Rodrigues
0fb57a405f fix: wrong comment 2026-02-20 18:02:46 -03:00
Sofia Rodrigues
ce009e2dca fix: remove double crlf 2026-02-20 18:02:28 -03:00
Sofia Rodrigues
c9cf60f173 fix: timeout on slow connections 2026-02-20 18:02:14 -03:00
Sofia Rodrigues
5263c32ea4 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 17:17:50 -03:00
Sofia Rodrigues
89191367b7 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-20 17:11:33 -03:00
Sofia Rodrigues
999ce40ca6 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-20 17:11:27 -03:00
Sofia Rodrigues
bfa18ef30c fix: uri builder 2026-02-20 17:11:05 -03:00
Sofia Rodrigues
a850879adf fix: reason phrase 2026-02-20 17:06:06 -03:00
Sofia Rodrigues
34c5c70ec6 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-20 17:00:21 -03:00
Sofia Rodrigues
81492aa5b2 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-20 17:00:13 -03:00
Sofia Rodrigues
e0efb8aec9 fix: scheme parser 2026-02-20 16:59:24 -03:00
Sofia Rodrigues
530f6865f9 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-20 16:40:06 -03:00
Sofia Rodrigues
f97d86cf4b feat: trim headers 2026-02-20 16:40:00 -03:00
Sofia Rodrigues
781b9f561e Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-20 16:30:28 -03:00
Sofia Rodrigues
a9ac33d994 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-20 16:24:06 -03:00
Sofia Rodrigues
c457a98d6a fix: test 2026-02-20 16:23:24 -03:00
Sofia Rodrigues
8d8439bf0b fix: status code 2026-02-20 16:19:55 -03:00
Sofia Rodrigues
7cf419491a Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 16:11:33 -03:00
Sofia Rodrigues
4cbdb39211 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-20 16:11:27 -03:00
Sofia Rodrigues
54ac93fb32 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-20 16:11:09 -03:00
Sofia Rodrigues
eddb5e139d Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-20 16:10:13 -03:00
Sofia Rodrigues
5a53207723 feat: remove direct access 2026-02-20 16:10:06 -03:00
Sofia Rodrigues
0d3f6e5481 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-20 16:09:16 -03:00
Sofia Rodrigues
96a017262c feat: reason phrase in custom status code 2026-02-20 16:08:26 -03:00
Sofia Rodrigues
04c73b64a5 test: fuzz 2026-02-20 16:05:09 -03:00
Sofia Rodrigues
02adf1fae0 fix: dedup 2026-02-20 16:04:56 -03:00
Sofia Rodrigues
9291e925ff fix: commment 2026-02-20 15:33:14 -03:00
Sofia Rodrigues
1d0e26e494 tests: dedu 2026-02-20 15:33:07 -03:00
Sofia Rodrigues
5528f97c8f fix: dedup 2026-02-20 14:12:00 -03:00
Sofia Rodrigues
32d42b52e9 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 14:07:10 -03:00
Sofia Rodrigues
f1ed971f26 fix: omit body 2026-02-20 14:06:15 -03:00
Sofia Rodrigues
b5610a43db feat: test 2026-02-20 14:04:39 -03:00
Sofia Rodrigues
a182a6652e feat: omit body 2026-02-20 14:04:22 -03:00
Sofia Rodrigues
cf51a32ffb fix: space sequence 2026-02-20 14:04:03 -03:00
Sofia Rodrigues
11cc11bc2f fix: test 2026-02-20 13:42:54 -03:00
Sofia Rodrigues
8cef903224 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 13:40:46 -03:00
Sofia Rodrigues
f5492db7fa fix: host validation and rreasonphrase 2026-02-20 13:40:39 -03:00
Sofia Rodrigues
cf603cdc7c Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 13:05:01 -03:00
Sofia Rodrigues
d07e1a6341 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-20 13:04:56 -03:00
Sofia Rodrigues
549e16f069 feat: add reader 2026-02-20 13:04:51 -03:00
Sofia Rodrigues
2e1406b683 fix: connection handler 2026-02-20 13:04:41 -03:00
Sofia Rodrigues
bfdfabd4a5 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 12:33:37 -03:00
Sofia Rodrigues
004c076236 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-20 12:33:30 -03:00
Sofia Rodrigues
93a6ecbbbc feat: reader features to write only things 2026-02-20 12:33:23 -03:00
Sofia Rodrigues
3c877f9604 feat: body 2026-02-20 12:32:51 -03:00
Sofia Rodrigues
d317c0208b feat: body 2026-02-20 12:32:40 -03:00
Sofia Rodrigues
4716725e81 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 12:01:57 -03:00
Sofia Rodrigues
4f15fe36e0 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-20 12:01:51 -03:00
Sofia Rodrigues
8bcc838f47 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-20 12:01:46 -03:00
Sofia Rodrigues
462e3d02dd fix: import coe 2026-02-20 12:01:21 -03:00
Sofia Rodrigues
541f9b2dc9 fix: rendezvouz stream 2026-02-20 12:01:02 -03:00
Sofia Rodrigues
86107e2b5a feat: discard reason-phrase 2026-02-20 11:49:30 -03:00
Sofia Rodrigues
5cc0026f3d Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-20 11:46:15 -03:00
Sofia Rodrigues
c5db47444e fix: bodyt ests 2026-02-20 11:46:08 -03:00
Sofia Rodrigues
fffc2b5633 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 11:40:58 -03:00
Sofia Rodrigues
637f260529 feat: discard reason-phrase 2026-02-20 11:40:52 -03:00
Sofia Rodrigues
469f466832 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-20 11:39:17 -03:00
Sofia Rodrigues
ecb7480b37 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-20 11:37:42 -03:00
Sofia Rodrigues
42800e4037 feat: body type class 2026-02-20 11:37:35 -03:00
Sofia Rodrigues
b52bbc9ae4 fix: rfc expected 2026-02-19 17:14:53 -03:00
Sofia Rodrigues
eaa1390a36 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-19 14:21:41 -03:00
Sofia Rodrigues
b38f01ef51 feat: ignore reasonphrase 2026-02-19 14:01:21 -03:00
Sofia Rodrigues
73bf2b5e04 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-19 13:36:29 -03:00
Sofia Rodrigues
c8c92fcf92 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-19 13:13:28 -03:00
Sofia Rodrigues
cf6b159da5 feat: validation in reasonPhrase 2026-02-19 13:13:22 -03:00
Sofia Rodrigues
330e1c5340 fix: config 2026-02-19 11:45:49 -03:00
Sofia Rodrigues
b40bc2e89c Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-19 11:39:36 -03:00
Sofia Rodrigues
e8347e9e9b fix: comments 2026-02-19 11:25:44 -03:00
Sofia Rodrigues
d051b967ed fix: method limit and comments 2026-02-19 11:18:18 -03:00
Sofia Rodrigues
cf4776ef92 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-19 10:51:52 -03:00
Sofia Rodrigues
b1ff312ef5 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-19 10:51:39 -03:00
Sofia Rodrigues
319214cfb3 feat: config URI 2026-02-19 10:51:32 -03:00
Sofia Rodrigues
e75049b604 feat: remove identity 2026-02-19 10:19:06 -03:00
Sofia Rodrigues
836cdf47a5 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-18 21:19:51 -03:00
Sofia Rodrigues
01f9c257e8 fix: comments 2026-02-18 21:19:29 -03:00
Sofia Rodrigues
3d07f4fd56 fix: comments 2026-02-18 21:19:08 -03:00
Sofia Rodrigues
7dc97a02fd fix: comments 2026-02-18 21:18:51 -03:00
Sofia Rodrigues
afd2f12242 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-18 20:56:42 -03:00
Sofia Rodrigues
5faf0572f6 feat: improve manyItems 2026-02-18 20:56:35 -03:00
Sofia Rodrigues
8d349ccbaa Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-18 19:46:46 -03:00
Sofia Rodrigues
9c35a91e0f feat: better validation in parser 2026-02-18 19:43:57 -03:00
Sofia Rodrigues
2da4e1b572 feat: connection lmit 2026-02-18 19:25:07 -03:00
Sofia Rodrigues
5368b134bb fix: test 2026-02-18 14:14:58 -03:00
Sofia Rodrigues
d1f090ee98 fix: test 2026-02-18 13:48:45 -03:00
Sofia Rodrigues
f311c9594f feat: unsuppoted method 2026-02-18 12:22:50 -03:00
Sofia Rodrigues
c6a3ab0a77 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-18 12:22:26 -03:00
Sofia Rodrigues
ba25ab3490 fix: method validation 2026-02-18 12:22:12 -03:00
Sofia Rodrigues
1095ebbeed fix: config 2026-02-18 11:49:45 -03:00
Sofia Rodrigues
299b15c8e9 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-18 11:47:39 -03:00
Sofia Rodrigues
091cb00ab9 feat: add max bytes 2026-02-18 11:47:33 -03:00
Sofia Rodrigues
2b408d2699 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-18 11:18:34 -03:00
Sofia Rodrigues
702efcacca refactor: remove duplication 2026-02-18 11:18:25 -03:00
Sofia Rodrigues
98ba01dc49 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-18 11:04:55 -03:00
Sofia Rodrigues
e1225efa03 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-18 11:03:40 -03:00
Sofia Rodrigues
37c7b1e22c feat: reuse char 2026-02-18 11:03:35 -03:00
Sofia Rodrigues
eea8e06d6b Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-18 10:57:12 -03:00
Sofia Rodrigues
c4234961bc Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-18 10:56:29 -03:00
Sofia Rodrigues
42cfda23f3 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-18 10:56:23 -03:00
Sofia Rodrigues
78316b9ade fix: isToken 2026-02-18 10:55:55 -03:00
Sofia Rodrigues
dd09289d2b fix: duplication 2026-02-18 10:36:17 -03:00
Sofia Rodrigues
10a66e9f9a fix: uri duplication checkers 2026-02-18 10:35:04 -03:00
Sofia Rodrigues
ad4719399d fix: trailing peridos 2026-02-18 10:11:47 -03:00
Sofia Rodrigues
892ab921b7 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-18 09:46:00 -03:00
Sofia Rodrigues
6551c32f6b Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-18 09:45:50 -03:00
Sofia Rodrigues
b8eac648ab Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-18 09:45:13 -03:00
Sofia Rodrigues
53fb1a25b3 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-18 09:45:02 -03:00
Sofia Rodrigues
3fdaf2df0c Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-18 09:44:01 -03:00
Sofia Rodrigues
4ba722f51c fix: reasonPhrase 2026-02-18 09:40:25 -03:00
Sofia Rodrigues
42b726c376 fix: misleading comment 2026-02-18 09:26:59 -03:00
Sofia Rodrigues
8bec5f4b98 fix: comments 2026-02-18 09:25:49 -03:00
Sofia Rodrigues
9a8bc523c5 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-18 08:53:23 -03:00
Sofia Rodrigues
59253973ce Merge branch 'sofia/async-http-body' into sofia/async-http-server 2026-02-18 08:53:19 -03:00
Sofia Rodrigues
205149a884 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-18 08:53:10 -03:00
Sofia Rodrigues
a89a69e7da fix: queue test 2026-02-18 08:52:59 -03:00
Sofia Rodrigues
9bb429d4e7 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-18 08:43:58 -03:00
Sofia Rodrigues
542a3a4e71 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-18 08:43:49 -03:00
Sofia Rodrigues
3646590506 feat: queue 2026-02-18 08:43:33 -03:00
Sofia Rodrigues
cf87c9594c feat: failure gate 2026-02-18 08:37:59 -03:00
Sofia Rodrigues
71420f6c81 feat: tests 2026-02-18 08:37:46 -03:00
Sofia Rodrigues
b6fdd8adc3 feat: failure gate 2026-02-18 08:37:13 -03:00
Sofia Rodrigues
45747bd2ef Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-17 23:46:33 -03:00
Sofia Rodrigues
69c75c1b56 fix: incomplete fixed chung 2026-02-17 23:46:28 -03:00
Sofia Rodrigues
bed5d8567c feat: incomplete chunks 2026-02-17 23:42:53 -03:00
Sofia Rodrigues
0c5d25a763 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-17 23:37:36 -03:00
Sofia Rodrigues
c324ee8347 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-17 23:37:29 -03:00
Sofia Rodrigues
193bbddb4e feat: incomlpete chunks 2026-02-17 23:37:16 -03:00
Sofia Rodrigues
6821bb82db feat: close after generate 2026-02-17 21:25:06 -03:00
Sofia Rodrigues
1cbd0569eb Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-17 21:09:13 -03:00
Sofia Rodrigues
14dbb661f8 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-17 21:08:50 -03:00
Sofia Rodrigues
ea5a986693 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-17 21:08:38 -03:00
Sofia Rodrigues
37ec94e2f0 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-17 21:08:30 -03:00
Sofia Rodrigues
157e3b032d Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-17 21:08:21 -03:00
Sofia Rodrigues
910c71954e feat: reason phrase 2026-02-17 21:07:48 -03:00
Sofia Rodrigues
27107066e3 fix: small issues with framing 2026-02-17 21:03:00 -03:00
Sofia Rodrigues
fd1843e120 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-17 12:12:11 -03:00
Sofia Rodrigues
dd2ab67d2b Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-17 11:51:41 -03:00
Sofia Rodrigues
9dd5634759 Merge branch 'sofia/async-http-headers' into sofia/async-http-uri 2026-02-17 11:47:57 -03:00
Sofia Rodrigues
a521ba3abd fix: typos and connection token 2026-02-17 11:47:46 -03:00
Sofia Rodrigues
6b0f05d075 Merge branch 'sofia/async-http-data' into sofia/async-http-headers 2026-02-17 11:12:22 -03:00
Sofia Rodrigues
61d6c02ecd fix: typos and compareName 2026-02-17 11:12:13 -03:00
Sofia Rodrigues
b7d4e12fbf Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-17 03:02:56 -03:00
Sofia Rodrigues
dc6d015870 fix: get-size 2026-02-17 03:02:53 -03:00
Sofia Rodrigues
07a05a3995 fix: 100-expect 2026-02-17 03:02:44 -03:00
Sofia Rodrigues
182625774d Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-17 03:00:43 -03:00
Sofia Rodrigues
b4684a2406 fix: 100-expect case, quoted parser and te 2026-02-17 03:00:14 -03:00
Sofia Rodrigues
ecc0ec05bd fix: typos 2026-02-17 02:55:21 -03:00
Sofia Rodrigues
5193b739ca fix: typos 2026-02-17 02:54:19 -03:00
Sofia Rodrigues
70c0a902f4 fix: api 2026-02-17 02:05:42 -03:00
Sofia Rodrigues
7f29fd0fcd Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-17 02:01:27 -03:00
Sofia Rodrigues
239536f1d8 fix: transfer-encoding gzip 2026-02-17 02:00:11 -03:00
Sofia Rodrigues
71be391dd3 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-17 01:55:33 -03:00
Sofia Rodrigues
df738acaa4 fix: direction 2026-02-17 01:55:26 -03:00
Sofia Rodrigues
8ed56677e5 feat: stale 2026-02-17 01:26:21 -03:00
Sofia Rodrigues
60d0b7c97a feat: server 2026-02-17 01:25:53 -03:00
Sofia Rodrigues
17a2c9e0c2 feat: server 2026-02-17 01:25:42 -03:00
Sofia Rodrigues
7ee37564d3 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-16 23:56:03 -03:00
Sofia Rodrigues
2ee7513f80 feat: pull-based body 2026-02-16 23:55:58 -03:00
Sofia Rodrigues
7d6505d296 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-16 22:33:45 -03:00
Sofia Rodrigues
8722e50897 feat: pull-based body 2026-02-16 22:33:35 -03:00
Sofia Rodrigues
fa8d76fa37 fix: frameCancellation with error 2026-02-16 01:22:00 -03:00
Sofia Rodrigues
c50fca363a fix: comments 2026-02-16 01:12:49 -03:00
Sofia Rodrigues
e8ff308154 fix: handler 2026-02-16 01:10:19 -03:00
Sofia Rodrigues
cdcb9db4ba Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-14 06:14:38 -03:00
Sofia Rodrigues
a8e405ac5d fix: knownsize 2026-02-14 06:13:39 -03:00
Sofia Rodrigues
b6705cceb2 fix: server 2026-02-14 05:59:54 -03:00
Sofia Rodrigues
af58b4f286 Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-14 05:51:28 -03:00
Sofia Rodrigues
02dc048ad2 feat: improve h1 2026-02-14 05:51:03 -03:00
Sofia Rodrigues
a981d91552 Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-13 11:58:23 -03:00
Sofia Rodrigues
96ffa3e354 Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-13 11:56:52 -03:00
Sofia Rodrigues
1c564ed5f7 fix: comment 2026-02-13 11:56:44 -03:00
Sofia Rodrigues
9dd5f62e0e Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-13 11:53:16 -03:00
Sofia Rodrigues
c4737fb66a fix: parse ab-empty 2026-02-13 11:52:53 -03:00
Sofia Rodrigues
43d3b2df91 merge: 'sofia/async-http-headers' 2026-02-13 11:43:55 -03:00
Sofia Rodrigues
87c5488c20 merge: 'sofia/async-http-headers' 2026-02-13 11:39:09 -03:00
Sofia Rodrigues
e0d5596e63 fix: typo 2026-02-13 11:37:08 -03:00
Sofia Rodrigues
1f2671db3d merge: branch 'sofia/async-http-data' 2026-02-13 11:35:33 -03:00
Sofia Rodrigues
940ab9bdb5 fix: typos 2026-02-13 11:33:18 -03:00
Sofia Rodrigues
8017d39c4e Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-13 10:29:22 -03:00
Sofia Rodrigues
25bb4ee812 feat: protocol 2026-02-13 10:28:48 -03:00
Sofia Rodrigues
7c1aff34e2 Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-13 10:28:13 -03:00
Sofia Rodrigues
28670d4420 Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-13 10:26:13 -03:00
Sofia Rodrigues
30f3a3520e Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-13 10:22:47 -03:00
Sofia Rodrigues
9acca40aaf revert: h1 2026-02-13 10:21:57 -03:00
Sofia Rodrigues
bf2ed2c87a revert: h1 2026-02-13 10:20:35 -03:00
Sofia Rodrigues
3561d58203 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-13 10:14:03 -03:00
Sofia Rodrigues
1d80616068 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-13 10:13:55 -03:00
Sofia Rodrigues
61c93a7f57 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-13 10:13:40 -03:00
Sofia Rodrigues
b042b8efbd fix: parser path 2026-02-13 10:13:00 -03:00
Sofia Rodrigues
8c00ba48ae fix: parser 2026-02-13 10:12:22 -03:00
Sofia Rodrigues
991a27b7f2 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-13 10:11:30 -03:00
Sofia Rodrigues
69e38e9495 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-13 10:11:23 -03:00
Sofia Rodrigues
16d0162ef0 Merge branch 'sofia/async-http-uri' into sofia/async-http-body 2026-02-13 10:11:10 -03:00
Sofia Rodrigues
d07f5c502f feat: specialize encodedstrings 2026-02-13 10:10:34 -03:00
Sofia Rodrigues
5b1493507d feat: body channel should close on completion 2026-02-13 02:53:16 -03:00
Sofia Rodrigues
1180572926 fix: test 2026-02-13 02:29:55 -03:00
Sofia Rodrigues
6dc19ef871 Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-13 02:23:20 -03:00
Sofia Rodrigues
4a641fc498 revert: bytearray parser 2026-02-13 02:22:43 -03:00
Sofia Rodrigues
2a04014fa7 Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-13 02:18:18 -03:00
Sofia Rodrigues
4f20a815ec fix: extension name 2026-02-13 02:18:09 -03:00
Sofia Rodrigues
4906e14e51 Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-13 02:13:50 -03:00
Sofia Rodrigues
c9296c7371 Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-13 02:12:07 -03:00
Sofia Rodrigues
4db36b214b feat: improve parser 2026-02-13 02:11:38 -03:00
Sofia Rodrigues
a6d94c7504 Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-13 01:58:16 -03:00
Sofia Rodrigues
045abb48bb Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-13 01:57:40 -03:00
Sofia Rodrigues
10337c620b fix: test 2026-02-13 01:57:23 -03:00
Sofia Rodrigues
698f557aa3 Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-13 01:56:37 -03:00
Sofia Rodrigues
692c7c1a09 fix: test 2026-02-13 01:56:29 -03:00
Sofia Rodrigues
1bdfdcdb38 Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-13 01:55:05 -03:00
Sofia Rodrigues
cacfe00c1d fix: test 2026-02-13 01:54:52 -03:00
Sofia Rodrigues
0fd0fa9c73 fix: test 2026-02-13 01:54:26 -03:00
Sofia Rodrigues
52fdc0f734 Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-13 01:52:49 -03:00
Sofia Rodrigues
451c11d5a1 fix: make strict 2026-02-13 01:52:04 -03:00
Sofia Rodrigues
e92fcf6d46 Merge branch 'sofia/async-http-headers' of https://github.com/leanprover/lean4 into sofia/async-http-uri 2026-02-13 01:41:20 -03:00
Sofia Rodrigues
07140aceb8 Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-13 01:39:32 -03:00
Sofia Rodrigues
2cc32928a4 feat: add parser features to path 2026-02-13 01:39:12 -03:00
Sofia Rodrigues
153513d5e2 fix: typos 2026-02-13 01:29:12 -03:00
Sofia Rodrigues
94308408a9 Merge branch 'sofia/async-http-data' of https://github.com/leanprover/lean4 into sofia/async-http-headers 2026-02-13 01:20:31 -03:00
Sofia Rodrigues
1ae6970b77 fix: comment 2026-02-13 01:19:51 -03:00
Sofia Rodrigues
0704f877f5 fix: tests 2026-02-13 01:07:32 -03:00
Sofia Rodrigues
7ff0e6f9c0 feat: 100-continue 2026-02-13 00:56:08 -03:00
Sofia Rodrigues
5b4498ac9d Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-13 00:47:12 -03:00
Sofia Rodrigues
976cc79b0c feat: 100-continue 2026-02-13 00:45:38 -03:00
Sofia Rodrigues
8d6ff0d727 feat: handler 2026-02-13 00:19:36 -03:00
Sofia Rodrigues
26c0e4dac4 feat: date header 2026-02-13 00:06:41 -03:00
Sofia Rodrigues
9ce1821be0 feat: add trailers some type of headers 2026-02-12 12:46:15 -03:00
Sofia Rodrigues
eeff4847fe Merge branch 'sofia/async-http-data' of https://github.com/leanprover/lean4 into sofia/async-http-headers 2026-02-12 12:31:30 -03:00
Sofia Rodrigues
2956f88050 refactor: move trailers 2026-02-12 12:30:59 -03:00
Sofia Rodrigues
26d9c1c07b feat: add extension handling of quotes and ExtensionName 2026-02-12 12:21:47 -03:00
Sofia Rodrigues
73af014cbd fix: documentation 2026-02-12 11:55:15 -03:00
Sofia Rodrigues
d206f437ef Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-12 10:47:19 -03:00
Sofia Rodrigues
d099586632 Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-12 10:46:56 -03:00
Sofia Rodrigues
058d95e441 feat: maximum size in readAll 2026-02-12 10:46:43 -03:00
Sofia Rodrigues
b40ac55755 Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-11 19:42:01 -03:00
Sofia Rodrigues
43aa88e5a6 Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-11 19:40:58 -03:00
Sofia Rodrigues
8fe2d519d2 revert: chunk changes 2026-02-11 19:40:34 -03:00
Sofia Rodrigues
07ed645f45 Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-11 19:35:43 -03:00
Sofia Rodrigues
9485e8f5eb revert: add toString head 2026-02-11 19:35:31 -03:00
Sofia Rodrigues
dc96616781 Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-11 19:30:41 -03:00
Sofia Rodrigues
0c44b4ae05 Merge branch 'sofia/async-http-headers' of https://github.com/leanprover/lean4 into sofia/async-http-uri 2026-02-11 19:29:54 -03:00
Sofia Rodrigues
3568464ca7 Merge branch 'sofia/async-http-data' of https://github.com/leanprover/lean4 into sofia/async-http-headers 2026-02-11 19:23:59 -03:00
Sofia Rodrigues
8e5296c71a fix: chunked 2026-02-11 19:22:30 -03:00
Sofia Rodrigues
eee971e3ef Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-11 19:10:02 -03:00
Sofia Rodrigues
7a1f8b2d30 fix: readAll 2026-02-11 19:09:45 -03:00
Sofia Rodrigues
157e122891 Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-11 18:51:21 -03:00
Sofia Rodrigues
b12ab7eae4 Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-11 18:49:17 -03:00
Sofia Rodrigues
10c8a923e6 feat: readAll functions 2026-02-11 18:48:10 -03:00
Sofia Rodrigues
2b91589750 Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-11 18:08:30 -03:00
Sofia Rodrigues
3e9674eaa9 feat: avoid more than one host 2026-02-11 18:08:16 -03:00
Sofia Rodrigues
d902c6a9f4 fix: mock double close 2026-02-11 18:07:58 -03:00
Sofia Rodrigues
04a17e8c55 fix: fail event should end everything 2026-02-11 18:06:16 -03:00
Sofia Rodrigues
1b6cd457d3 Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-11 17:40:54 -03:00
Sofia Rodrigues
2bc2080fbe fix: bad request behavior 2026-02-11 17:40:19 -03:00
Sofia Rodrigues
6b6425e8d7 fix: close mock bidirectionaly and fix test 2026-02-11 17:39:48 -03:00
Sofia Rodrigues
fb0e95d8ce fix: avoid gate errors 2026-02-11 17:25:26 -03:00
Sofia Rodrigues
4e4702a31f Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-11 17:22:53 -03:00
Sofia Rodrigues
5a2ad22f97 fix: selectable.one can used to change register/unregister order causing double register 2026-02-11 17:22:37 -03:00
Sofia Rodrigues
f02139f7ce fix: skipBytes 2026-02-11 17:14:52 -03:00
Sofia Rodrigues
d004e175e2 fix: error message 2026-02-11 17:03:27 -03:00
Sofia Rodrigues
7928a95c34 tests: add more tests 2026-02-11 16:53:11 -03:00
Sofia Rodrigues
202e6c5228 fix: transport, add explicit close that is no-op for tp 2026-02-11 14:54:08 -03:00
Sofia Rodrigues
0aeaa5e71d Merge branch 'sofia/async-http-h1' of https://github.com/leanprover/lean4 into sofia/async-http-server 2026-02-10 17:30:08 -03:00
Sofia Rodrigues
9ad4ee304b fix: imports 2026-02-10 17:29:04 -03:00
Sofia Rodrigues
5bd280553d Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-10 17:25:08 -03:00
Sofia Rodrigues
7e215c8220 Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-10 17:24:00 -03:00
Sofia Rodrigues
2c23680163 fix: imports 2026-02-10 17:23:14 -03:00
Sofia Rodrigues
c4f179daa0 Merge branch 'sofia/async-http-headers' of https://github.com/leanprover/lean4 into sofia/async-http-uri 2026-02-10 17:15:31 -03:00
Sofia Rodrigues
c2f657a15a Merge branch 'sofia/async-http-data' of https://github.com/leanprover/lean4 into sofia/async-http-headers 2026-02-10 17:12:45 -03:00
Sofia Rodrigues
9332081875 fix: import 2026-02-10 17:12:20 -03:00
Sofia Rodrigues
1cec97568b fix: imports 2026-02-10 17:11:11 -03:00
Sofia Rodrigues
b567713641 Merge branch 'sofia/async-http-data' of https://github.com/leanprover/lean4 into sofia/async-http-headers 2026-02-10 17:03:44 -03:00
Sofia Rodrigues
de776c1f32 fix: interpolation 2026-02-10 17:03:02 -03:00
Sofia Rodrigues
c498ea74ec Merge branch 'sofia/async-http-headers' of https://github.com/leanprover/lean4 into sofia/async-http-uri 2026-02-10 16:47:08 -03:00
Sofia Rodrigues
f4aad3a494 Merge branch 'sofia/async-http-data' of https://github.com/leanprover/lean4 into sofia/async-http-headers 2026-02-10 16:46:48 -03:00
Sofia Rodrigues
1cebf576c3 Merge branch 'master' of https://github.com/leanprover/lean4 into sofia/async-http-data 2026-02-10 16:44:20 -03:00
Sofia Rodrigues
25dac2e239 fix: test 2026-02-09 22:25:14 -03:00
Sofia Rodrigues
4a9de7094c feat: new body 2026-02-09 22:20:05 -03:00
Sofia Rodrigues
c4eab3b677 Merge branch 'sofia/async-http-h1' into sofia/async-http-server 2026-02-09 21:58:26 -03:00
Sofia Rodrigues
dd125c7999 Merge branch 'sofia/async-http-body' into sofia/async-http-h1 2026-02-09 21:57:42 -03:00
Sofia Rodrigues
5e3dce8088 fix: chunk stream will only deal with content-size of the chunks not with the wireFormatSize 2026-02-09 21:57:26 -03:00
Sofia Rodrigues
4c64f2c2e8 fix: suggestions 2026-02-09 21:55:38 -03:00
Sofia Rodrigues
aa6e11dfc0 Merge branch 'sofia/async-http-body' of https://github.com/leanprover/lean4 into sofia/async-http-h1 2026-02-09 21:25:02 -03:00
Sofia Rodrigues
e7d1e7dd54 Merge branch 'sofia/async-http-uri' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-09 21:09:55 -03:00
Sofia Rodrigues
03843fd3f0 fix: suggestions 2026-02-09 21:09:38 -03:00
Sofia Rodrigues
294e9900ea feat: unify all in stream 2026-02-09 20:29:18 -03:00
Sofia Rodrigues
f13651979e fix: wireFormatSize 2026-02-09 19:31:41 -03:00
Sofia Rodrigues
3d8ba4d09b Merge branch 'sofia/async-http-headers' of https://github.com/leanprover/lean4 into sofia/async-http-body 2026-02-09 12:30:01 -03:00
Sofia Rodrigues
63984c8dda fix: header value 2026-02-09 12:01:25 -03:00
Sofia Rodrigues
e2fd8a5835 Merge branch 'sofia/async-http-data' of https://github.com/leanprover/lean4 into sofia/async-http-headers 2026-02-09 11:28:52 -03:00
Sofia Rodrigues
a0263870b9 fix: extensions 2026-02-09 11:24:48 -03:00
Sofia Rodrigues
3c4ae58aff feat: add extensions 2026-02-09 11:18:24 -03:00
Sofia Rodrigues
5965707575 fix: apply suggestions 2026-02-09 11:17:03 -03:00
Sofia Rodrigues
dbe0140578 fix: enforce validations 2026-02-09 10:28:43 -03:00
Sofia Rodrigues
bc21289793 feat: http docs 2026-01-25 12:48:06 -03:00
Sofia Rodrigues
f11bd0928d feat: server basics 2026-01-25 12:48:06 -03:00
Sofia Rodrigues
6ffd5ad2a4 fix: incremental parsing 2026-01-25 12:42:29 -03:00
Sofia Rodrigues
7ce8cbc01c feat: remove toString instances 2026-01-25 12:42:29 -03:00
Sofia Rodrigues
12a7603c77 fix: orphan module 2026-01-25 12:42:29 -03:00
Sofia Rodrigues
53a6355074 feat: H1 protocol 2026-01-25 12:42:29 -03:00
Sofia Rodrigues
f8ad249e42 test: wrong test 2026-01-25 12:40:41 -03:00
Sofia Rodrigues
3c41d3961e feat: empty body and constructors 2026-01-25 12:39:43 -03:00
Sofia Rodrigues
18bc715bad feat: remove useless functions 2026-01-25 12:39:43 -03:00
Sofia Rodrigues
3349d20663 feat: body 2026-01-25 12:39:41 -03:00
Sofia Rodrigues
bad70e3eab feat: request type has request target 2026-01-25 12:36:36 -03:00
Sofia Rodrigues
21286eb163 fix: domain name comment 2026-01-25 12:36:36 -03:00
Sofia Rodrigues
0e5f07558c feat: introduce data type for HTTP 2026-01-25 12:36:36 -03:00
Sofia Rodrigues
6e26b901e4 fix: encoding 2026-01-25 12:33:07 -03:00
Sofia Rodrigues
81c67c8f12 revert: levenshtein test 2026-01-25 12:29:37 -03:00
Sofia Rodrigues
990e21eefc fix: namespace 2026-01-25 12:27:16 -03:00
Sofia Rodrigues
7141144a2f fix: remove native_decide 2026-01-25 12:27:16 -03:00
Sofia Rodrigues
8c343501c1 fix: apply suggestions
Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2026-01-25 12:27:16 -03:00
Sofia Rodrigues
44f08686cd feat: connection values 2026-01-25 12:27:16 -03:00
Sofia Rodrigues
65883f8c2a fix: levenshtein test is using the new Decidable instance 2026-01-25 12:27:16 -03:00
Sofia Rodrigues
bd28a8fad5 fix: tests and type class 2026-01-25 12:27:16 -03:00
Sofia Rodrigues
8ba86c2c67 fix: case and usage of native_decide 2026-01-25 12:24:52 -03:00
Sofia Rodrigues
d3cddf9e44 fix: Headers.Basic comment 2026-01-25 12:24:52 -03:00
Sofia Rodrigues
5f3babee5c feat: headers data structure 2026-01-25 12:24:51 -03:00
Sofia Rodrigues
26dfc9a872 refactor: remove headers 2026-01-25 12:16:13 -03:00
Sofia Rodrigues
e47439e8be fix: default size 2026-01-25 11:26:03 -03:00
Sofia Rodrigues
1ef53758be fix: apply suggestions
Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
2026-01-25 11:21:06 -03:00
Sofia Rodrigues
8544042789 fix: status and chunkedbuffer 2026-01-25 11:19:34 -03:00
Sofia Rodrigues
f564d43d98 feat: basic headers structure to more structured approach 2026-01-23 17:58:44 -03:00
Sofia Rodrigues
32fa0666c9 feat: data components 2026-01-23 17:14:53 -03:00
494 changed files with 21398 additions and 1859 deletions

View File

@@ -1,7 +1,12 @@
(In the following, use `sysctl -n hw.logicalcpu` instead of `nproc` on macOS)
## Building
To build Lean you should use `make -j$(nproc) -C build/release`.
The build uses `ccache`, and in a sandbox `ccache` may complain about read-only file systems.
Use `CCACHE_READONLY` and `CCACHE_TEMPDIR` instead of disabling ccache completely.
## Running Tests
See `tests/README.md` for full documentation. Quick reference:
@@ -20,9 +25,24 @@ CTEST_PARALLEL_LEVEL="$(nproc)" CTEST_OUTPUT_ON_FAILURE=1 \
make -C build/release -j "$(nproc)" test ARGS='--rerun-failed'
# Single test from tests/foo/bar/ (quick check during development)
cd tests/foo/bar && ./run_test example_test.lean
CTEST_PARALLEL_LEVEL="$(nproc)" CTEST_OUTPUT_ON_FAILURE=1 \
make -C build/release -j "$(nproc)" test ARGS=-R testname'
```
## Testing stage 2
When requested to test stage 2, build it as follows:
```
make -C build/release stage2 -j$(nproc)
```
Stage 2 is *not* automatically invalidated by changes to `src/` which allows for faster iteration
when fixing a specific file in the stage 2 build but for invalidating any files that already passed
the stage 2 build as well as for final validation,
```
make -C build/release/stage2 clean-stdlib
```
must be run manually before building.
## New features
When asked to implement new features:

View File

@@ -79,7 +79,7 @@ if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
endif()
find_program(LEANTAR leantar)
if(NOT LEANTAR)
set(LEANTAR_VERSION v0.1.18)
set(LEANTAR_VERSION v0.1.19)
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
set(LEANTAR_ARCHIVE_SUFFIX .zip)
set(LEANTAR_TARGET x86_64-pc-windows-msvc)

View File

@@ -1,6 +1,3 @@
#!/usr/bin/env bash
source ../../../tests/env_test.sh
leanmake --always-make bin
capture ./build/bin/test hello world

5
doc/examples/run_test → doc/examples/run_test.sh Executable file → Normal file
View File

@@ -1,7 +1,4 @@
#!/usr/bin/env bash
source ../../tests/env_test.sh
capture_only "$1" \
lean -Dlinter.all=false "$1"
check_exit_is_success
check_out_file
check_exit_is_success

View File

@@ -67,5 +67,5 @@
oldGlibc = devShellWithDist pkgsDist-old;
oldGlibcAArch = devShellWithDist pkgsDist-old-aarch;
};
}) ["x86_64-linux" "aarch64-linux"]);
}) ["x86_64-linux" "aarch64-linux" "aarch64-darwin"]);
}

View File

@@ -492,8 +492,9 @@ def execute_release_steps(repo, version, config):
'ROOT_REV=$(jq -r \'.packages[] | select(.name == "subverso") | .rev\' lake-manifest.json); '
'SUBVERSO_URL=$(jq -r \'.packages[] | select(.name == "subverso") | .url\' lake-manifest.json); '
'DEMOD_REV=$(git ls-remote "$SUBVERSO_URL" "refs/tags/no-modules/$ROOT_REV" | awk \'{print $1}\'); '
'find test-projects -name lake-manifest.json -print0 | '
'xargs -0 -I{} sh -c \'jq --arg rev "$DEMOD_REV" \'.packages |= map(if .name == "subverso" then .rev = $rev else . end)\' "{}" > /tmp/lm_tmp.json && mv /tmp/lm_tmp.json "{}"\''
'find test-projects -name lake-manifest.json -print0 | while IFS= read -r -d \'\' f; do '
'jq --arg rev "$DEMOD_REV" \'.packages |= map(if .name == "subverso" then .rev = $rev else . end)\' "$f" > /tmp/lm_tmp.json && mv /tmp/lm_tmp.json "$f"; '
'done'
)
run_command(sync_script, cwd=repo_path)
print(green("Synced de-modulized subverso rev to all test-project sub-manifests"))

View File

@@ -118,6 +118,9 @@ option(USE_LAKE_CACHE "Use the Lake artifact cache for stage 1 builds (requires
set(LEAN_EXTRA_MAKE_OPTS "" CACHE STRING "extra options to lean --make")
set(LEANC_CC ${CMAKE_C_COMPILER} CACHE STRING "C compiler to use in `leanc`")
# Temporary, core-only flags. Must be synced with stdlib_flags.h.
string(APPEND LEAN_EXTRA_MAKE_OPTS " -Dbackward.do.legacy=false")
if(LAZY_RC MATCHES "ON")
set(LEAN_LAZY_RC "#define LEAN_LAZY_RC")
endif()
@@ -942,6 +945,7 @@ install(
PATTERN "*.hash" EXCLUDE
PATTERN "*.trace" EXCLUDE
PATTERN "*.rsp" EXCLUDE
PATTERN "*.filelist" EXCLUDE
)
# symlink source into expected installation location for go-to-definition, if file system allows it

View File

@@ -254,8 +254,8 @@ instance : LawfulMonad Id := by
@[simp, grind =] theorem run_bind (x : Id α) (f : α Id β) : (x >>= f).run = (f x.run).run := rfl
@[simp, grind =] theorem run_pure (a : α) : (pure a : Id α).run = a := rfl
@[simp, grind =] theorem pure_run (a : Id α) : pure a.run = a := rfl
@[simp] theorem run_seqRight (x y : Id α) : (x *> y).run = y.run := rfl
@[simp] theorem run_seqLeft (x y : Id α) : (x <* y).run = x.run := rfl
@[simp] theorem run_seqRight (x : Id α) (y : Id β) : (x *> y).run = y.run := rfl
@[simp] theorem run_seqLeft (x : Id α) (y : Id β) : (x <* y).run = x.run := rfl
@[simp] theorem run_seq (f : Id (α β)) (x : Id α) : (f <*> x).run = f.run x.run := rfl
end Id

View File

@@ -60,9 +60,6 @@ with functions defined via well-founded recursion or partial fixpoints.
The proofs produced by `cbv` only use the three standard axioms.
In particular, they do not require trust in the correctness of the code
generator.
This tactic is experimental and its behavior is likely to change in upcoming
releases of Lean.
-/
syntax (name := cbv) "cbv" : conv

View File

@@ -98,7 +98,7 @@ well-founded recursion mechanism to prove that the function terminates.
@[simp] theorem pmap_push {P : α Prop} (f : a, P a β) (a : α) (xs : Array α) (h : b xs.push a, P b) :
pmap f (xs.push a) h =
(pmap f xs (fun a m => by simp at h; exact h a (.inl m))).push (f a (h a (by simp))) := by
(pmap f xs (fun a m => by simp [forall_or_eq_imp] at h; exact h.1 _ m)).push (f a (h a (by simp))) := by
simp [pmap]
@[simp] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
@@ -153,7 +153,7 @@ theorem attachWith_congr {xs ys : Array α} (w : xs = ys) {P : α → Prop} {H :
@[simp] theorem attachWith_push {a : α} {xs : Array α} {P : α Prop} {H : x xs.push a, P x} :
(xs.push a).attachWith P H =
(xs.attachWith P (fun x h => by simp at H; exact H x (.inl h))).push a, H a (by simp) := by
(xs.attachWith P (fun x h => by simp [forall_or_eq_imp] at H; exact H.1 _ h)).push a, H a (by simp) := by
cases xs
simp

View File

@@ -559,9 +559,9 @@ def modifyOp (xs : Array α) (idx : Nat) (f : αα) : Array α :=
xs.modify idx f
/--
We claim this unsafe implementation is correct because an array cannot have more than `usizeSz` elements in our runtime.
We claim this unsafe implementation is correct because an array cannot have more than `USize.size` elements in our runtime.
This kind of low level trick can be removed with a little bit of compiler support. For example, if the compiler simplifies `as.size < usizeSz` to true. -/
This kind of low level trick can be removed with a little bit of compiler support. For example, if the compiler simplifies `as.size < USize.size` to true. -/
@[inline] unsafe def forIn'Unsafe {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (as : Array α) (b : β) (f : (a : α) a as β m (ForInStep β)) : m β :=
let sz := as.usize
let rec @[specialize] loop (i : USize) (b : β) : m β := do

View File

@@ -622,12 +622,12 @@ theorem findIdx?_eq_some_le_of_findIdx?_eq_some {xs : Array α} {p q : α → Bo
/-! ### findFinIdx? -/
@[grind =]
theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := by simp; rfl
theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p #[] = none := by simp
@[grind =]
theorem findFinIdx?_singleton {a : α} {p : α Bool} :
#[a].findFinIdx? p = if p a then some 0, by simp else none := by
simp; rfl
simp
-- We can't mark this as a `@[congr]` lemma since the head of the RHS is not `findFinIdx?`.
theorem findFinIdx?_congr {p : α Bool} {xs ys : Array α} (w : xs = ys) :
@@ -801,7 +801,7 @@ theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : Array α} {a : α} :
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
simp [idxOf?, finIdxOf?]
@[grind =] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp; rfl
@[grind =] theorem finIdxOf?_empty [BEq α] : (#[] : Array α).finIdxOf? a = none := by simp
@[simp, grind =] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {xs : Array α} {a : α} :
xs.finIdxOf? a = none a xs := by

View File

@@ -36,6 +36,8 @@ theorem BEq.symm [BEq α] [Std.Symm (α := α) (· == ·)] {a b : α} : a == b
theorem BEq.comm [BEq α] [PartialEquivBEq α] {a b : α} : (a == b) = (b == a) :=
Bool.eq_iff_iff.2 BEq.symm, BEq.symm
theorem bne_eq [BEq α] {a b : α} : (a != b) = !(a == b) := rfl
theorem bne_comm [BEq α] [PartialEquivBEq α] {a b : α} : (a != b) = (b != a) := by
rw [bne, BEq.comm, bne]

View File

@@ -664,3 +664,6 @@ but may be used locally.
@[simp] theorem Bool.not'_eq_not (a : Bool) : a.not' = a.not := by
cases a <;> simp [Bool.not']
theorem Bool.rec_eq {α : Sort _} (b : Bool) {x y : α} : Bool.rec y x b = if b then x else y := by
cases b <;> simp

View File

@@ -86,4 +86,16 @@ theorem toUInt8_val {c : Char} : c.val.toUInt8 = c.toUInt8 := rfl
@[simp]
theorem toString_eq_singleton {c : Char} : c.toString = String.singleton c := rfl
@[simp]
theorem toNat_val {c : Char} : c.val.toNat = c.toNat := rfl
theorem val_inj {c d : Char} : c.val = d.val c = d :=
Char.ext_iff.symm
theorem toNat_inj {c d : Char} : c.toNat = d.toNat c = d := by
simp [ toNat_val, val_inj, UInt32.toNat_inj]
theorem isDigit_iff_toNat {c : Char} : c.isDigit '0'.toNat c.toNat c.toNat '9'.toNat := by
simp [isDigit, UInt32.le_iff_toNat_le]
end Char

View File

@@ -217,7 +217,7 @@ theorem succ?_eq {c : Char} : c.succ? = (c.ordinal.addNat? 1).map Char.ofOrdinal
Nat.reduceLeDiff, UInt32.left_eq_add]
grind [UInt32.lt_iff_toNat_lt]
· grind
· simp [coe_ordinal]
· simp [coe_ordinal, -toNat_val]
grind [UInt32.lt_iff_toNat_lt]
| case2 =>
rw [Fin.addNat?_eq_some]

View File

@@ -118,16 +118,19 @@ theorem toNat_pow_of_nonneg {x : Int} (h : 0 ≤ x) (k : Nat) : (x ^ k).toNat =
| succ k ih =>
rw [Int.pow_succ, Int.toNat_mul (Int.pow_nonneg h) h, ih, Nat.pow_succ]
protected theorem sq_nonnneg (m : Int) : 0 m ^ 2 := by
protected theorem sq_nonneg (m : Int) : 0 m ^ 2 := by
rw [Int.pow_succ, Int.pow_one]
cases m
· apply Int.mul_nonneg <;> simp
· apply Int.mul_nonneg_of_nonpos_of_nonpos <;> exact negSucc_le_zero _
@[deprecated Int.sq_nonneg (since := "2026-03-13")]
protected theorem sq_nonnneg (m : Int) : 0 m ^ 2 := Int.sq_nonneg m
protected theorem pow_nonneg_of_even {m : Int} {n : Nat} (h : n % 2 = 0) : 0 m ^ n := by
rw [ Nat.mod_add_div n 2, h, Nat.zero_add, Int.pow_mul]
apply Int.pow_nonneg
exact Int.sq_nonnneg m
exact Int.sq_nonneg m
protected theorem neg_pow {m : Int} {n : Nat} : (-m)^n = (-1)^(n % 2) * m^n := by
rw [Int.neg_eq_neg_one_mul, Int.mul_pow]

View File

@@ -435,8 +435,9 @@ theorem Iter.forIn_filterMapWithPostcondition
match (f out).run with
| some c => g c acc
| none => return .yield acc) := by
simp +instances [Iter.forIn_eq_forIn_toIterM, filterMapWithPostcondition, IterM.forIn_filterMapWithPostcondition,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]; rfl
simp only [filterMapWithPostcondition, IterM.forIn_filterMapWithPostcondition, forIn_eq_forIn_toIterM]
rw [instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
rfl -- expressions are equal up to different matchers
theorem Iter.forIn_filterMapM
[Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
@@ -448,8 +449,9 @@ theorem Iter.forIn_filterMapM
match f out with
| some c => g c acc
| none => return .yield acc) := by
simp +instances [filterMapM, forIn_eq_forIn_toIterM, IterM.forIn_filterMapM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]; rfl
simp [filterMapM, forIn_eq_forIn_toIterM, IterM.forIn_filterMapM]
rw [instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
rfl
theorem Iter.forIn_filterMap
[Monad n] [LawfulMonad n] [Finite α Id]
@@ -469,8 +471,8 @@ theorem Iter.forIn_mapWithPostcondition
{g : β₂ γ o (ForInStep γ)} :
forIn (it.mapWithPostcondition f) init g =
forIn it init (fun out acc => do g ( (f out).run) acc) := by
simp +instances [mapWithPostcondition, forIn_eq_forIn_toIterM, IterM.forIn_mapWithPostcondition,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
simp only [mapWithPostcondition, forIn_eq_forIn_toIterM, IterM.forIn_mapWithPostcondition]
rw [instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.forIn_mapM
[Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
@@ -498,8 +500,8 @@ theorem Iter.forIn_filterWithPostcondition
haveI : MonadLift n o := monadLift
forIn (it.filterWithPostcondition f) init g =
forIn it init (fun out acc => do if ( (f out).run).down then g out acc else return .yield acc) := by
simp +instances [filterWithPostcondition, forIn_eq_forIn_toIterM, IterM.forIn_filterWithPostcondition,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
simp only [filterWithPostcondition, forIn_eq_forIn_toIterM, IterM.forIn_filterWithPostcondition]
rw [instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.forIn_filterM
[Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
@@ -508,8 +510,8 @@ theorem Iter.forIn_filterM
[IteratorLoop α Id o] [LawfulIteratorLoop α Id o]
{it : Iter (α := α) β} {f : β n (ULift Bool)} {init : γ} {g : β γ o (ForInStep γ)} :
forIn (it.filterM f) init g = forIn it init (fun out acc => do if ( f out).down then g out acc else return .yield acc) := by
simp +instances [filterM, forIn_eq_forIn_toIterM, IterM.forIn_filterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
simp only [filterM, forIn_eq_forIn_toIterM, IterM.forIn_filterM]
rw [instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.forIn_filter
[Monad n] [LawfulMonad n]
@@ -550,8 +552,9 @@ theorem Iter.foldM_filterMapM {α β γ δ : Type w}
it.foldM (init := init) (fun d b => do
let some c f b | pure d
g d c) := by
simp +instances [filterMapM, IterM.foldM_filterMapM, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]; rfl
simp only [filterMapM, IterM.foldM_filterMapM, foldM_eq_foldM_toIterM]
rw [instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
rfl
theorem Iter.foldM_mapWithPostcondition {α β γ δ : Type w}
{n : Type w Type w''} {o : Type w Type w'''}
@@ -563,8 +566,8 @@ theorem Iter.foldM_mapWithPostcondition {α β γ δ : Type w}
{f : β PostconditionT n γ} {g : δ γ o δ} {init : δ} {it : Iter (α := α) β} :
(it.mapWithPostcondition f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do let c (f b).run; g d c) := by
simp +instances [mapWithPostcondition, IterM.foldM_mapWithPostcondition, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
simp only [mapWithPostcondition, IterM.foldM_mapWithPostcondition, foldM_eq_foldM_toIterM]
rw [instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.foldM_mapM {α β γ δ : Type w}
{n : Type w Type w''} {o : Type w Type w'''}
@@ -578,8 +581,8 @@ theorem Iter.foldM_mapM {α β γ δ : Type w}
haveI : MonadLift n o := MonadLiftT.monadLift
(it.mapM f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do let c f b; g d c) := by
simp +instances [mapM, IterM.foldM_mapM, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
simp only [mapM, IterM.foldM_mapM, foldM_eq_foldM_toIterM]
rw [instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.foldM_filterWithPostcondition {α β δ : Type w}
{n : Type w Type w''} {o : Type w Type w'''}
@@ -591,8 +594,8 @@ theorem Iter.foldM_filterWithPostcondition {α β δ : Type w}
{f : β PostconditionT n (ULift Bool)} {g : δ β o δ} {init : δ} {it : Iter (α := α) β} :
(it.filterWithPostcondition f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do if ( (f b).run).down then g d b else pure d) := by
simp +instances [filterWithPostcondition, IterM.foldM_filterWithPostcondition, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
simp only [filterWithPostcondition, IterM.foldM_filterWithPostcondition, foldM_eq_foldM_toIterM]
rw [instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.foldM_filterM {α β δ : Type w}
{n : Type w Type w''} {o : Type w Type w'''}
@@ -605,8 +608,8 @@ theorem Iter.foldM_filterM {α β δ : Type w}
{f : β n (ULift Bool)} {g : δ β o δ} {init : δ} {it : Iter (α := α) β} :
(it.filterM f).foldM (init := init) g =
it.foldM (init := init) (fun d b => do if ( f b).down then g d b else pure d) := by
simp +instances [filterM, IterM.foldM_filterM, foldM_eq_foldM_toIterM,
instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
simp only [filterM, IterM.foldM_filterM, foldM_eq_foldM_toIterM]
rw [instMonadLiftTOfMonadLift_instMonadLiftTOfPure]
theorem Iter.foldM_filterMap {α β γ δ : Type w} {n : Type w Type w''}
[Iterator α Id β] [Finite α Id] [Monad n] [LawfulMonad n]

View File

@@ -232,7 +232,6 @@ public theorem Iter.toArray_flatMapM {α α₂ β γ : Type w} {m : Type w → T
(it₁.flatMapM f).toArray = Array.flatten <$> (it₁.mapM fun b => do ( f b).toArray).toArray := by
simp [flatMapM, toArray_flatMapAfterM]
set_option backward.isDefEq.respectTransparency false in
public theorem Iter.toList_flatMapAfter {α α₂ β γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
[Finite α Id] [Finite α₂ Id]
{f : β Iter (α := α₂) γ} {it₁ : Iter (α := α) β} {it₂ : Option (Iter (α := α₂) γ)} :
@@ -241,9 +240,9 @@ public theorem Iter.toList_flatMapAfter {α α₂ β γ : Type w} [Iterator α I
| some it₂ => it₂.toList ++
(it₁.map fun b => (f b).toList).toList.flatten := by
simp only [flatMapAfter, Iter.toList, toIterM_toIter, IterM.toList_flatMapAfter]
cases it₂ <;> simp [map, IterM.toList_map_eq_toList_mapM, - IterM.toList_map]
unfold Iter.toList
cases it₂ <;> simp [map]
set_option backward.isDefEq.respectTransparency false in
public theorem Iter.toArray_flatMapAfter {α α₂ β γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]
[Finite α Id] [Finite α₂ Id]
{f : β Iter (α := α₂) γ} {it₁ : Iter (α := α) β} {it₂ : Option (Iter (α := α₂) γ)} :
@@ -252,6 +251,7 @@ public theorem Iter.toArray_flatMapAfter {α α₂ β γ : Type w} [Iterator α
| some it₂ => it₂.toArray ++
(it₁.map fun b => (f b).toArray).toArray.flatten := by
simp only [flatMapAfter, Iter.toArray, toIterM_toIter, IterM.toArray_flatMapAfter]
unfold Iter.toArray
cases it₂ <;> simp [map, IterM.toArray_map_eq_toArray_mapM, - IterM.toArray_map]
public theorem Iter.toList_flatMap {α α₂ β γ : Type w} [Iterator α Id β] [Iterator α₂ Id γ]

View File

@@ -374,7 +374,6 @@ theorem IterM.toList_map_eq_toList_filterMapM {α β γ : Type w} {m : Type w
simp [toList_map_eq_toList_mapM, toList_mapM_eq_toList_filterMapM]
congr <;> simp
set_option backward.whnf.reducibleClassField false in
/--
Variant of `toList_filterMapWithPostcondition_filterMapWithPostcondition` that is intended to be
used with the `apply` tactic. Because neither the LHS nor the RHS determine all implicit parameters,
@@ -395,7 +394,7 @@ private theorem IterM.toList_filterMapWithPostcondition_filterMapWithPostconditi
(it.filterMapWithPostcondition (n := o) fg).toList := by
induction it using IterM.inductSteps with | step it ihy ihs
letI : MonadLift n o := monadLift
haveI : LawfulMonadLift n o := by simp +instances [this], by simp +instances [this]
haveI : LawfulMonadLift n o := LawfulMonadLiftT.monadLift_pure, LawfulMonadLiftT.monadLift_bind
rw [toList_eq_match_step, toList_eq_match_step, step_filterMapWithPostcondition,
bind_assoc, step_filterMapWithPostcondition, step_filterMapWithPostcondition]
simp only [bind_assoc, liftM_bind]
@@ -602,7 +601,6 @@ theorem IterM.toList_map_mapM {α β γ δ : Type w}
toList_filterMapM_mapM]
congr <;> simp
set_option backward.isDefEq.respectTransparency false in
@[simp]
theorem IterM.toList_filterMapWithPostcondition {α β γ : Type w} {m : Type w Type w'}
[Monad m] [LawfulMonad m]
@@ -626,7 +624,6 @@ theorem IterM.toList_filterMapWithPostcondition {α β γ : Type w} {m : Type w
· simp [ihs _, heq]
· simp [heq]
set_option backward.isDefEq.respectTransparency false in
@[simp]
theorem IterM.toList_mapWithPostcondition {α β γ : Type w} {m : Type w Type w'}
[Monad m] [LawfulMonad m] [Iterator α Id β] [Finite α Id]
@@ -647,25 +644,25 @@ theorem IterM.toList_mapWithPostcondition {α β γ : Type w} {m : Type w → Ty
· simp [ihs _, heq]
· simp [heq]
set_option backward.isDefEq.respectTransparency false in
@[simp]
theorem IterM.toList_filterMapM {α β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α Id β] [Finite α Id]
{f : β m (Option γ)} (it : IterM (α := α) Id β) :
(it.filterMapM f).toList = it.toList.run.filterMapM f := by
simp [toList_filterMapM_eq_toList_filterMapWithPostcondition, toList_filterMapWithPostcondition,
PostconditionT.attachLift, PostconditionT.run_eq_map, WeaklyLawfulMonadAttach.map_attach]
simp only [toList_filterMapM_eq_toList_filterMapWithPostcondition,
toList_filterMapWithPostcondition, PostconditionT.run_eq_map]
simp [PostconditionT.attachLift, WeaklyLawfulMonadAttach.map_attach]
set_option backward.isDefEq.respectTransparency false in
@[simp]
theorem IterM.toList_mapM {α β γ : Type w} {m : Type w Type w'}
[Monad m] [MonadAttach m] [LawfulMonad m] [WeaklyLawfulMonadAttach m]
[Iterator α Id β] [Finite α Id]
{f : β m γ} (it : IterM (α := α) Id β) :
(it.mapM f).toList = it.toList.run.mapM f := by
simp [toList_mapM_eq_toList_mapWithPostcondition, toList_mapWithPostcondition,
PostconditionT.attachLift, PostconditionT.run_eq_map, WeaklyLawfulMonadAttach.map_attach]
simp only [toList_mapM_eq_toList_mapWithPostcondition, toList_mapWithPostcondition,
PostconditionT.run_eq_map]
simp [PostconditionT.attachLift, WeaklyLawfulMonadAttach.map_attach]
@[simp]
theorem IterM.toList_filterMap {α β γ : Type w} {m : Type w Type w'}
@@ -1303,7 +1300,6 @@ theorem IterM.forIn_filterMap
rw [filterMap, forIn_filterMapWithPostcondition]
simp [PostconditionT.run_eq_map]
set_option backward.isDefEq.respectTransparency false in
theorem IterM.forIn_mapWithPostcondition
[Monad m] [LawfulMonad m] [Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
[MonadLiftT m n] [LawfulMonadLiftT m n] [MonadLiftT n o] [LawfulMonadLiftT n o]
@@ -1314,9 +1310,9 @@ theorem IterM.forIn_mapWithPostcondition
haveI : MonadLift n o := monadLift
forIn (it.mapWithPostcondition f) init g =
forIn it init (fun out acc => do g ( (f out).run) acc) := by
rw [mapWithPostcondition, InternalCombinators.map, InternalCombinators.filterMap,
filterMapWithPostcondition, forIn_filterMapWithPostcondition]
simp [PostconditionT.run_eq_map]
unfold mapWithPostcondition InternalCombinators.map Map.instIterator Map.instIteratorLoop Map
rw [ InternalCombinators.filterMap, filterMapWithPostcondition, forIn_filterMapWithPostcondition]
simp
theorem IterM.forIn_mapM
[Monad m] [LawfulMonad m] [Monad n] [LawfulMonad n] [Monad o] [LawfulMonad o]
@@ -1480,7 +1476,7 @@ theorem IterM.foldM_filterM {α β δ : Type w}
simp [filterM, foldM_filterMapWithPostcondition, PostconditionT.run_attachLift]
congr 1; ext out acc
apply bind_congr; intro fx
cases fx.down <;> simp [PostconditionT.run_eq_map]
cases fx.down <;> simp
theorem IterM.foldM_filterMap {α β γ δ : Type w} {m : Type w Type w'} {n : Type w Type w''}
[Iterator α m β] [Finite α m] [Monad m] [Monad n] [LawfulMonad m] [LawfulMonad n]

View File

@@ -32,11 +32,12 @@ theorem Iter.forIn'_eq {α β : Type w} [Iterator α Id β] [Finite α Id]
IterM.DefaultConsumers.forIn' (n := m) (fun _ _ f x => f x.run) γ (fun _ _ _ => True)
it.toIterM init _ (fun _ => id)
(fun out h acc => return f out (Iter.isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM.mpr h) acc, trivial) := by
simp +instances only [ForIn'.forIn']
simp only [ForIn'.forIn']
have : a b c, f a b c = (Subtype.val <$> (·, trivial) <$> f a b c) := by simp
simp +singlePass only [this]
rw [hl.lawful (fun _ _ f x => f x.run) (wf := IteratorLoop.wellFounded_of_finite)]
simp +instances [IteratorLoop.defaultImplementation]
simp only [IteratorLoop.forIn, Functor.map_map, id_map',
bind_pure_comp]
theorem Iter.forIn_eq {α β : Type w} [Iterator α Id β] [Finite α Id]
{m : Type x Type x'} [Monad m] [LawfulMonad m] [IteratorLoop α Id m]
@@ -81,7 +82,7 @@ theorem Iter.forIn'_eq_forIn'_toIterM {α β : Type w} [Iterator α Id β]
letI : ForIn' m (IterM (α := α) Id β) β _ := IterM.instForIn'
ForIn'.forIn' it.toIterM init
(fun out h acc => f out (isPlausibleIndirectOutput_iff_isPlausibleIndirectOutput_toIterM.mpr h) acc) := by
simp +instances [ForIn'.forIn', monadLift]
simp [ForIn'.forIn', monadLift]
theorem Iter.forIn_eq_forIn_toIterM {α β : Type w} [Iterator α Id β]
[Finite α Id] {m : Type w Type w''} [Monad m] [LawfulMonad m]

View File

@@ -109,10 +109,10 @@ theorem IterM.forIn'_eq {α β : Type w} {m : Type w → Type w'} [Iterator α m
letI : ForIn' n (IterM (α := α) m β) β _ := IterM.instForIn'
ForIn'.forIn' (α := β) (m := n) it init f = IterM.DefaultConsumers.forIn' (n := n)
(fun _ _ f x => monadLift x >>= f) γ (fun _ _ _ => True) it init _ (fun _ => id) (return f · · ·, trivial) := by
simp +instances only [ForIn'.forIn']
simp only [ForIn'.forIn']
have : f = (Subtype.val <$> (·, trivial) <$> f · · ·) := by simp
rw [this, hl.lawful (fun _ _ f x => monadLift x >>= f) (wf := IteratorLoop.wellFounded_of_finite)]
simp +instances [IteratorLoop.defaultImplementation]
simp [IteratorLoop.forIn]
try rfl
theorem IterM.forIn_eq {α β : Type w} {m : Type w Type w'} [Iterator α m β] [Finite α m]

View File

@@ -272,6 +272,12 @@ theorem PostconditionT.run_bind' {m : Type w → Type w'} [Monad m] [LawfulMonad
(x >>= f).run = x.run >>= (f · |>.run) :=
run_bind
@[simp]
protected theorem PostconditionT.run_pure {m : Type w Type w'} [Monad m] [LawfulMonad m]
{α : Type w} {x : α} :
(pure x : PostconditionT m α).run = pure x := by
simp [run_eq_map]
@[simp]
theorem PostconditionT.property_lift {m : Type w Type w'} [Functor m] {α : Type w}
{x : m α} : (lift x : PostconditionT m α).Property = (fun _ => True) := by

View File

@@ -1050,7 +1050,7 @@ theorem findFinIdx?_append {xs ys : List α} {p : α → Bool} :
@[simp, grind =] theorem findFinIdx?_singleton {a : α} {p : α Bool} :
[a].findFinIdx? p = if p a then some 0, by simp else none := by
simp [findFinIdx?_cons, findFinIdx?_nil]; rfl
simp [findFinIdx?_cons, findFinIdx?_nil]
@[simp, grind =] theorem findFinIdx?_eq_none_iff {l : List α} {p : α Bool} :
l.findFinIdx? p = none x l, ¬ p x := by

View File

@@ -877,6 +877,11 @@ theorem getLast_eq_iff_getLast?_eq_some {xs : List α} (h) :
theorem getLast?_cons {a : α} : (a::l).getLast? = some (l.getLast?.getD a) := by
cases l <;> simp [getLast?, getLast]
theorem getLast?_cons_of_ne_nil {x : α} {xs : List α} (h : xs []) : (x::xs).getLast? = xs.getLast? := by
cases xs with
| nil => contradiction
| cons => simp [getLast?_cons]
@[simp] theorem getLast?_cons_cons : (a :: b :: l).getLast? = (b :: l).getLast? := by
simp [getLast?_cons]
@@ -1283,6 +1288,13 @@ theorem filter_eq_self {l} : filter p l = l ↔ ∀ a ∈ l, p a := by
cases h : p a <;> simp [*]
intro h; exact Nat.lt_irrefl _ (h length_filter_le p l)
theorem filter_bne_eq_self_of_not_mem [BEq α] [LawfulBEq α] {a : α} {l : List α} (h : a l) :
l.filter (· != a) = l := by
rw [List.filter_eq_self]
intro c hc
simp only [bne_iff_ne, ne_eq]
exact fun heq => absurd (heq hc) h
@[simp]
theorem length_filter_eq_length_iff {l} : (filter p l).length = l.length a l, p a := by
induction l with
@@ -1336,6 +1348,16 @@ theorem foldl_filter {p : α → Bool} {f : β → α → β} {l : List α} {ini
simp only [filter_cons, foldl_cons]
split <;> simp [ih]
theorem foldl_ite_left {P : α Prop} [DecidablePred P] {l : List α} {f : β α β} {init : β} :
(l.foldl (init := init) fun sofar a => if P a then f sofar a else sofar) = (l.filter P).foldl (init := init) f := by
simp [List.foldl_filter]
theorem foldl_ite_right {P : α Prop} [DecidablePred P] {l : List α} {f : β α β} {init : β} :
(l.foldl (init := init) fun sofar a => if P a then sofar else f sofar a) =
(l.filter (fun a => ¬ P a)).foldl (init := init) f := by
simp +singlePass only [ ite_not]
rw [foldl_ite_left]
theorem foldr_filter {p : α Bool} {f : α β β} {l : List α} {init : β} :
(l.filter p).foldr f init = l.foldr (fun x y => if p x then f x y else y) init := by
induction l generalizing init with

View File

@@ -311,7 +311,7 @@ theorem drop_length_cons {l : List α} (h : l ≠ []) (a : α) :
| nil =>
cases h rfl
| cons y l ih =>
simp only [drop, length]
simp only [drop]
by_cases h₁ : l = []
· simp [h₁]
rw [getLast_cons h₁]

View File

@@ -182,7 +182,6 @@ private theorem mergeSortTR_run_eq_mergeSort : {n : Nat} → (l : { l : List α
simp only [mergeSortTR.run, mergeSortTR.run, mergeSort]
rw [merge_eq_mergeTR]
rw [mergeSortTR_run_eq_mergeSort, mergeSortTR_run_eq_mergeSort]
rfl
-- We don't make this a `@[csimp]` lemma because `mergeSort_eq_mergeSortTR₂` is faster.
theorem mergeSort_eq_mergeSortTR : @mergeSort = @mergeSortTR := by

View File

@@ -706,6 +706,11 @@ theorem infix_refl (l : List α) : l <:+: l := prefix_rfl.isInfix
grind_pattern suffix_cons => _ <:+ a :: l
@[simp]
theorem suffix_cons_append {a : α} {l₁ l₂ : List α} : l₂ <:+ a :: (l₁ ++ l₂) := by
rw [ List.cons_append]
exact List.suffix_append (a :: l₁) l₂
theorem infix_cons : l₁ <:+: l₂ l₁ <:+: a :: l₂ := fun l₁', l₂', h => a :: l₁', l₂', h rfl
theorem infix_concat : l₁ <:+: l₂ l₁ <:+: concat l₂ a := fun l₁', l₂', h =>
@@ -1299,6 +1304,121 @@ theorem prefix_iff_eq_take : l₁ <+: l₂ ↔ l₁ = take (length l₁) l₂ :=
fun h => append_cancel_right <| (prefix_iff_eq_append.1 h).trans (take_append_drop _ _).symm,
fun e => e.symm take_prefix _ _
theorem prefix_iff_exists_append_eq {l₁ l₂ : List α} : l₁ <+: l₂ l₃, l₁ ++ l₃ = l₂ :=
Iff.rfl
theorem prefix_iff_exists_eq_append {l₁ l₂ : List α} : l₁ <+: l₂ l₃, l₂ = l₁ ++ l₃ := by
simp [prefix_iff_exists_append_eq, eq_comm]
-- See `Init.Data.List.Nat.Sublist` for `suffix_iff_eq_append`, `prefix_take_iff`, and `suffix_iff_eq_drop`.
theorem suffix_iff_exists_append_eq {l₁ l₂ : List α} : l₁ <:+ l₂ l₃, l₃ ++ l₁ = l₂ :=
Iff.rfl
theorem suffix_iff_exists_eq_append {l₁ l₂ : List α} : l₁ <:+ l₂ l₃, l₂ = l₃ ++ l₁ := by
simp [suffix_iff_exists_append_eq, eq_comm]
theorem suffix_append_self_iff {l₁ l₂ l₃ : List α} : l₁ ++ l₃ <:+ l₂ ++ l₃ l₁ <:+ l₂ := by
constructor
· rintro t, h
exact t, List.append_cancel_right (by rwa [ List.append_assoc] at h)
· rintro t, h
exact t, by rw [ List.append_assoc, h]
theorem prefix_self_append_iff {l₁ l₂ l₃ : List α} : l₃ ++ l₁ <+: l₃ ++ l₂ l₁ <+: l₂ := by
constructor
· rintro t, h
exact t, List.append_cancel_left (by rwa [List.append_assoc] at h)
· rintro t, h
exact t, by rw [List.append_assoc, h]
theorem suffix_append_inj_of_length_eq {l₁ l₂ s₁ s₂ : List α} (hs : s₁.length = s₂.length) :
l₁ ++ s₁ <:+ l₂ ++ s₂ l₁ <:+ l₂ s₁ = s₂ := by
simp only [suffix_iff_exists_eq_append]
refine ?_, ?_
· rintro l₃, h
rw [ List.append_assoc] at h
obtain rfl, rfl := List.append_inj' h hs.symm
refine l₃, by simp, by simp
· rintro l₃, rfl, rfl
refine l₃, by simp
theorem prefix_append_inj_of_length_eq {l₁ l₂ s₁ s₂ : List α} (hs : s₁.length = s₂.length) :
s₁ ++ l₁ <+: s₂ ++ l₂ s₁ = s₂ l₁ <+: l₂ := by
constructor
· rintro t, h
rw [List.append_assoc] at h
obtain rfl, rfl := List.append_inj h.symm hs.symm
exact rfl, t, rfl
· rintro rfl, t, rfl
exact t, by simp
theorem singleton_suffix_iff_getLast?_eq_some {a : α} {l : List α} : [a] <:+ l l.getLast? = some a := by
rw [suffix_iff_exists_eq_append, getLast?_eq_some_iff]
theorem singleton_prefix_iff_head?_eq_some {a : α} {l : List α} : [a] <+: l l.head? = some a := by
simp [prefix_iff_exists_eq_append, head?_eq_some_iff]
theorem singleton_prefix_cons_iff {a b : α} {l : List α} : [a] <+: b :: l a = b := by
simp
@[simp]
theorem singleton_suffix_append_singleton_iff {a b : α} {l : List α} :
[a] <:+ l ++ [b] a = b := by
refine fun h => Eq.symm ?_, by rintro rfl; simp
simpa [List.suffix_iff_exists_eq_append] using h
@[simp]
theorem singleton_suffix_cons_append_singleton_iff {a b c : α} {l : List α} :
[a] <:+ b :: (l ++ [c]) a = c := by
rw [ List.cons_append]
exact singleton_suffix_append_singleton_iff
theorem infix_append_iff {α : Type u} {l xs ys : List α} : l <:+: xs ++ ys
l <:+: xs l <:+: ys ( l₁ l₂, l = l₁ ++ l₂ l₁ <:+ xs l₂ <+: ys) := by
constructor
· rintro s, t, ht
rcases List.append_eq_append_iff.mp ht with as, hxs, _ | bs, hsl, hys
· exact Or.inl s, as, hxs.symm
· rcases List.append_eq_append_iff.mp hsl with cs, hxs', hl | ds, _, hbs
· exact Or.inr (Or.inr cs, bs, hl,
List.suffix_iff_exists_eq_append.mpr s, hxs',
List.prefix_iff_exists_eq_append.mpr t, hys)
· exact Or.inr (Or.inl ds, t, by rw [hys, hbs])
· rintro (s, t, ht | s, t, ht | l₁, l₂, rfl, hl₁, hl₂)
· exact s, t ++ ys, by rw [ List.append_assoc, ht]
· exact xs ++ s, t, by
rw [List.append_assoc] at ht
rw [List.append_assoc (xs ++ s), List.append_assoc xs, ht]
· rw [List.suffix_iff_exists_eq_append] at hl₁
rw [List.prefix_iff_exists_eq_append] at hl₂
obtain s, hxs := hl₁
obtain t, hys := hl₂
exact s, t, by rw [ List.append_assoc s l₁, List.append_assoc (s ++ l₁), hxs, hys]
theorem infix_append_iff_ne_nil {α : Type u} {l xs ys : List α} : l <:+: xs ++ ys
l <:+: xs l <:+: ys ( l₁ l₂, l₁ [] l₂ [] l = l₁ ++ l₂ l₁ <:+ xs l₂ <+: ys) := by
rw [List.infix_append_iff]
constructor
· rintro (h | h | l₁, l₂, hl, hl₁, hl₂)
· exact Or.inl h
· exact Or.inr (Or.inl h)
· cases l₁ with
| nil =>
simp only [List.nil_append] at hl
subst hl
exact Or.inr (Or.inl hl₂.isInfix)
| cons hd tl =>
cases l₂ with
| nil =>
simp only [List.append_nil] at hl
subst hl
exact Or.inl hl₁.isInfix
| cons hd' tl' =>
exact Or.inr (Or.inr _, _, List.cons_ne_nil _ _, List.cons_ne_nil _ _, hl, hl₁, hl₂)
· rintro (h | h | l₁, l₂, -, -, hl, hl₁, hl₂)
· exact Or.inl h
· exact Or.inr (Or.inl h)
· exact Or.inr (Or.inr l₁, l₂, hl, hl₁, hl₂)
end List

View File

@@ -297,6 +297,14 @@ theorem dropWhile_cons :
(a :: l).dropWhile p = a :: l := by
simp [dropWhile_cons, h]
theorem dropWhile_beq_eq_self_of_head?_ne [BEq α] [LawfulBEq α] {a : α} {l : List α}
(h : l.head? some a) : l.dropWhile (· == a) = l := by
cases l with
| nil => simp
| cons hd tl =>
rw [List.dropWhile_cons_of_neg]
simpa [beq_iff_eq] using h
theorem head?_takeWhile {p : α Bool} {l : List α} : (l.takeWhile p).head? = l.head?.filter p := by
cases l with
| nil => rfl

View File

@@ -102,6 +102,12 @@ instance : XorOp Nat := ⟨Nat.xor⟩
instance : ShiftLeft Nat := Nat.shiftLeft
instance : ShiftRight Nat := Nat.shiftRight
@[simp] theorem land_eq {m n : Nat} : m.land n = m &&& n := rfl
@[simp] theorem lor_eq {m n : Nat} : m.lor n = m ||| n := rfl
@[simp] theorem xor_eq {m n : Nat} : m.xor n = m ^^^ n := rfl
@[simp] theorem shiftLeft_eq' {m n : Nat} : m.shiftLeft n = m <<< n := rfl
@[simp] theorem shiftRight_eq' {m n : Nat} : m.shiftRight n = m >>> n := rfl
theorem shiftLeft_eq (a b : Nat) : a <<< b = a * 2 ^ b :=
match b with
| 0 => (Nat.mul_one _).symm

View File

@@ -867,7 +867,7 @@ theorem and_le_right {n m : Nat} : n &&& m ≤ m :=
le_of_testBit (by simp)
theorem left_le_or {n m : Nat} : n n ||| m :=
le_of_testBit (by simpa using fun i => Or.inl)
le_of_testBit (by simp [imp_or_left_iff_true])
theorem right_le_or {n m : Nat} : m n ||| m :=
le_of_testBit (by simpa using fun i => Or.inr)
le_of_testBit (by simp [imp_or_right_iff_true])

View File

@@ -253,4 +253,16 @@ theorem ext_div_mod {n a b : Nat} (h0 : a / n = b / n) (h1 : a % n = b % n) : a
theorem ext_div_mod_iff (n a b : Nat) : a = b a / n = b / n a % n = b % n :=
fun h => h rfl, h rfl, fun h0, h1 => ext_div_mod h0 h1
/-- An induction principle mirroring the base-`b` representation of the number. -/
theorem base_induction {P : Nat Prop} {n : Nat} (b : Nat) (hb : 1 < b) (single : m, m < b P m)
(digit : m k, k < b 0 < m P m P (b * m + k)) : P n := by
induction n using Nat.strongRecOn with | ind n ih
rcases Nat.lt_or_ge n b with hn | hn
· exact single _ hn
· have := div_add_mod n b
rw [ this]
apply digit _ _ (mod_lt _ (by omega)) _ (ih _ _)
· exact Nat.div_pos_iff.mpr by omega, hn
· exact div_lt_self (by omega) (by omega)
end Nat

View File

@@ -19,6 +19,7 @@ import Init.Data.Nat.Bitwise
import Init.Data.Nat.Simproc
import Init.WFTactics
import Init.Data.Char.Lemmas
import Init.Data.Nat.Div.Lemmas
public section
@@ -37,6 +38,71 @@ theorem isDigit_digitChar : n.digitChar.isDigit = decide (n < 10) :=
simp only [digitChar, reduceIte, Nat.reduceEqDiff]
(repeat' split) <;> simp
private theorem digitChar_iff_aux :
n, (n.digitChar = '0' n = 0) (n.digitChar = '1' n = 1)
(n.digitChar = '2' n = 2) (n.digitChar = '3' n = 3)
(n.digitChar = '4' n = 4) (n.digitChar = '5' n = 5)
(n.digitChar = '6' n = 6) (n.digitChar = '7' n = 7)
(n.digitChar = '8' n = 8) (n.digitChar = '9' n = 9)
(n.digitChar = 'a' n = 10) (n.digitChar = 'b' n = 11)
(n.digitChar = 'c' n = 12) (n.digitChar = 'd' n = 13)
(n.digitChar = 'e' n = 14) (n.digitChar = 'f' n = 15)
(n.digitChar = '*' 16 n)
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | _ + 16 => by simp [digitChar]
@[simp] theorem digitChar_eq_zero : n.digitChar = '0' n = 0 := (digitChar_iff_aux n).1
@[simp] theorem digitChar_eq_one : n.digitChar = '1' n = 1 := (digitChar_iff_aux n).2.1
@[simp] theorem digitChar_eq_two : n.digitChar = '2' n = 2 := (digitChar_iff_aux n).2.2.1
@[simp] theorem digitChar_eq_three : n.digitChar = '3' n = 3 := (digitChar_iff_aux n).2.2.2.1
@[simp] theorem digitChar_eq_four : n.digitChar = '4' n = 4 := (digitChar_iff_aux n).2.2.2.2.1
@[simp] theorem digitChar_eq_five : n.digitChar = '5' n = 5 := (digitChar_iff_aux n).2.2.2.2.2.1
@[simp] theorem digitChar_eq_six : n.digitChar = '6' n = 6 := (digitChar_iff_aux n).2.2.2.2.2.2.1
@[simp] theorem digitChar_eq_seven : n.digitChar = '7' n = 7 := (digitChar_iff_aux n).2.2.2.2.2.2.2.1
@[simp] theorem digitChar_eq_eight : n.digitChar = '8' n = 8 := (digitChar_iff_aux n).2.2.2.2.2.2.2.2.1
@[simp] theorem digitChar_eq_nine : n.digitChar = '9' n = 9 := (digitChar_iff_aux n).2.2.2.2.2.2.2.2.2.1
@[simp] theorem digitChar_eq_a : n.digitChar = 'a' n = 10 := (digitChar_iff_aux n).2.2.2.2.2.2.2.2.2.2.1
@[simp] theorem digitChar_eq_b : n.digitChar = 'b' n = 11 := (digitChar_iff_aux n).2.2.2.2.2.2.2.2.2.2.2.1
@[simp] theorem digitChar_eq_c : n.digitChar = 'c' n = 12 := (digitChar_iff_aux n).2.2.2.2.2.2.2.2.2.2.2.2.1
@[simp] theorem digitChar_eq_d : n.digitChar = 'd' n = 13 := (digitChar_iff_aux n).2.2.2.2.2.2.2.2.2.2.2.2.2.1
@[simp] theorem digitChar_eq_e : n.digitChar = 'e' n = 14 := (digitChar_iff_aux n).2.2.2.2.2.2.2.2.2.2.2.2.2.2.1
@[simp] theorem digitChar_eq_f : n.digitChar = 'f' n = 15 := (digitChar_iff_aux n).2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.1
@[simp] theorem digitChar_eq_star : n.digitChar = '*' 16 n := (digitChar_iff_aux n).2.2.2.2.2.2.2.2.2.2.2.2.2.2.2.2
@[simp] theorem zero_eq_digitChar : '0' = n.digitChar n = 0 := digitChar_eq_zero |> eq_comm.trans
@[simp] theorem one_eq_digitChar : '1' = n.digitChar n = 1 := digitChar_eq_one |> eq_comm.trans
@[simp] theorem two_eq_digitChar : '2' = n.digitChar n = 2 := digitChar_eq_two |> eq_comm.trans
@[simp] theorem three_eq_digitChar : '3' = n.digitChar n = 3 := digitChar_eq_three |> eq_comm.trans
@[simp] theorem four_eq_digitChar : '4' = n.digitChar n = 4 := digitChar_eq_four |> eq_comm.trans
@[simp] theorem five_eq_digitChar : '5' = n.digitChar n = 5 := digitChar_eq_five |> eq_comm.trans
@[simp] theorem six_eq_digitChar : '6' = n.digitChar n = 6 := digitChar_eq_six |> eq_comm.trans
@[simp] theorem seven_eq_digitChar : '7' = n.digitChar n = 7 := digitChar_eq_seven |> eq_comm.trans
@[simp] theorem eight_eq_digitChar : '8' = n.digitChar n = 8 := digitChar_eq_eight |> eq_comm.trans
@[simp] theorem nine_eq_digitChar : '9' = n.digitChar n = 9 := digitChar_eq_nine |> eq_comm.trans
@[simp] theorem a_eq_digitChar : 'a' = n.digitChar n = 10 := digitChar_eq_a |> eq_comm.trans
@[simp] theorem b_eq_digitChar : 'b' = n.digitChar n = 11 := digitChar_eq_b |> eq_comm.trans
@[simp] theorem c_eq_digitChar : 'c' = n.digitChar n = 12 := digitChar_eq_c |> eq_comm.trans
@[simp] theorem d_eq_digitChar : 'd' = n.digitChar n = 13 := digitChar_eq_d |> eq_comm.trans
@[simp] theorem e_eq_digitChar : 'e' = n.digitChar n = 14 := digitChar_eq_e |> eq_comm.trans
@[simp] theorem f_eq_digitChar : 'f' = n.digitChar n = 15 := digitChar_eq_f |> eq_comm.trans
@[simp] theorem star_eq_digitChar : '*' = n.digitChar 16 n := digitChar_eq_star |> eq_comm.trans
/-- Auxiliary theorem for `Nat.reduceDigitCharEq` simproc. -/
protected theorem digitChar_ne {n : Nat} (c : Char)
(h : c != '0' && c != '1' && c != '2' && c != '3' && c != '4' && c != '5' &&
c != '6' && c != '7' && c != '8' && c != '9' && c != 'a' && c != 'b' &&
c != 'c' && c != 'd' && c != 'e' && c != 'f' && c != '*') : n.digitChar c := by
rintro rfl
match n with
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | _ + 16 => simp [digitChar] at h
theorem toNat_digitChar_of_lt_ten {n : Nat} (hn : n < 10) : n.digitChar.toNat = 48 + n :=
match n with
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 => by simp [digitChar]
| _ + 10 => by omega
theorem toNat_digitChar_sub_48_of_lt_ten {n : Nat} (hn : n < 10) : n.digitChar.toNat - 48 = n := by
simp [toNat_digitChar_of_lt_ten hn]
private theorem isDigit_of_mem_toDigitsCore
(hc : c cs c.isDigit) (hb₁ : 0 < b) (hb₂ : b 10) (h : c toDigitsCore b fuel n cs) :
c.isDigit := by
@@ -53,6 +119,11 @@ private theorem isDigit_of_mem_toDigitsCore
theorem isDigit_of_mem_toDigits (hb₁ : 0 < b) (hb₂ : b 10) (hc : c toDigits b n) : c.isDigit :=
isDigit_of_mem_toDigitsCore (fun _ => by contradiction) hb₁ hb₂ hc
@[simp]
theorem underscore_not_in_toDigits {n : Nat} : ¬'_' Nat.toDigits 10 n := by
intro h
simpa using isDigit_of_mem_toDigits (by decide) (by decide) h
private theorem toDigitsCore_of_lt_base (hb : n < b) (hf : n < fuel) :
toDigitsCore b fuel n cs = n.digitChar :: cs := by
unfold toDigitsCore
@@ -129,6 +200,11 @@ theorem length_toDigits_pos {b n : Nat} :
· rw [toDigitsCore_eq_toDigitsCore_nil_append]
simp
@[simp]
theorem toDigits_ne_nil {n b : Nat} : Nat.toDigits b n [] := by
rw [ List.length_pos_iff]
exact Nat.length_toDigits_pos
theorem length_toDigits_le_iff {n k : Nat} (hb : 1 < b) (h : 0 < k) :
(Nat.toDigits b n).length k n < b ^ k := by
match k with
@@ -154,6 +230,14 @@ theorem repr_eq_ofList_toDigits {n : Nat} :
n.repr = .ofList (toDigits 10 n) :=
(rfl)
@[simp]
theorem toList_repr {n : Nat} : n.repr.toList = Nat.toDigits 10 n := by
simp [repr_eq_ofList_toDigits]
@[simp]
theorem repr_ne_empty {n : Nat} : n.repr "" := by
simp [ String.toList_inj]
theorem toString_eq_ofList_toDigits {n : Nat} :
toString n = .ofList (toDigits 10 n) :=
(rfl)
@@ -194,4 +278,57 @@ theorem length_repr_le_iff {n k : Nat} (h : 0 < k) :
n.repr.length k n < 10 ^ k := by
simpa [repr_eq_ofList_toDigits] using length_toDigits_le_iff (by omega) h
/--
Transforms a list of characters into a natural number, *assuming that all characters are in the
range from `'0'` to `'9'`*.
-/
def ofDigitChars (b : Nat) (l : List Char) (init : Nat) : Nat :=
l.foldl (init := init) (fun sofar c => b * sofar + (c.toNat - '0'.toNat))
theorem ofDigitChars_eq_foldl {b : Nat} {l : List Char} {init : Nat} :
ofDigitChars b l init = l.foldl (init := init) (fun sofar c => b * sofar + (c.toNat - '0'.toNat)) :=
(rfl)
@[simp]
theorem ofDigitChars_nil {init : Nat} : ofDigitChars b [] init = init := by
simp [ofDigitChars]
theorem ofDigitChars_cons {c : Char} {cs : List Char} {init : Nat} :
ofDigitChars b (c::cs) init = ofDigitChars b cs (b * init + (c.toNat - '0'.toNat)) := by
simp [ofDigitChars]
theorem ofDigitChars_cons_digitChar_of_lt_ten {n : Nat} (hn : n < 10) {cs : List Char} {init : Nat} :
ofDigitChars 10 (n.digitChar :: cs) init = ofDigitChars 10 cs (10 * init + n) := by
simp [ofDigitChars_cons, Nat.toNat_digitChar_sub_48_of_lt_ten hn]
theorem ofDigitChars_eq_ofDigitChars_zero {l : List Char} {init : Nat} :
ofDigitChars 10 l init = 10 ^ l.length * init + ofDigitChars 10 l 0 := by
induction l generalizing init with
| nil => simp [ofDigitChars]
| cons hd tl ih =>
simp only [ofDigitChars, Char.isValue, Char.reduceToNat, List.foldl_cons, List.length_cons,
Nat.mul_zero, Nat.zero_add] at ih
rw [ih, ih (init := hd.toNat - 48), Nat.pow_succ, Nat.mul_add, Nat.mul_assoc, Nat.add_assoc]
theorem ofDigitChars_append {l m : List Char} (init : Nat) :
ofDigitChars b (l ++ m) init = ofDigitChars b m (ofDigitChars b l init) := by
simp [ofDigitChars]
@[simp]
theorem ofDigitChars_replicate_zero {n : Nat} : ofDigitChars b (List.replicate n '0') init = b ^ n * init := by
induction n generalizing init with
| zero => simp
| succ n ih => simp [List.replicate_succ, ofDigitChars_cons, ih, Nat.pow_succ, Nat.mul_assoc]
@[simp]
theorem ofDigitChars_toDigits {n : Nat} : ofDigitChars 10 (toDigits 10 n) 0 = n := by
have : 1 < 10 := by decide
induction n using base_induction 10 this with
| single m hm =>
simp [Nat.toDigits_of_lt_base hm, ofDigitChars_cons_digitChar_of_lt_ten hm]
| digit m k hk hm ih =>
rw [ Nat.toDigits_append_toDigits this hm hk,
ofDigitChars_append, ih, Nat.toDigits_of_lt_base hk,
ofDigitChars_cons_digitChar_of_lt_ten hk, ofDigitChars_nil]
end Nat

View File

@@ -208,7 +208,7 @@ public instance LawfulOrderLT.of_lt {α : Type u} [LT α] [i : Asymm (α := α)
haveI := LE.ofLT α
LawfulOrderLT α :=
letI := LE.ofLT α
{ lt_iff a b := by simp +instances [LE.le]; apply Asymm.asymm }
{ lt_iff a b := by simp [LE.le]; apply Asymm.asymm }
/--
If an `LT α` instance is asymmetric and its negation is transitive, then `LE.ofLT α` represents a
@@ -253,8 +253,7 @@ public theorem LawfulOrderInf.of_lt {α : Type u} [Min α] [LT α]
letI := LE.ofLT α
{ le_min_iff a b c := by
open Classical in
simp +instances only [LE.le]
simp [ not_or, Decidable.not_iff_not]
simp only [LE.le, not_or, Decidable.not_iff_not]
simpa [Decidable.imp_iff_not_or] using min_lt_iff a b c }
/--
@@ -283,8 +282,7 @@ public theorem LawfulOrderSup.of_lt {α : Type u} [Max α] [LT α]
letI := LE.ofLT α
{ max_le_iff a b c := by
open Classical in
simp +instances only [LE.le]
simp [ not_or, Decidable.not_iff_not]
simp only [LE.le, not_or, Decidable.not_iff_not]
simpa [Decidable.imp_iff_not_or] using lt_max_iff a b c }
/--

View File

@@ -287,7 +287,7 @@ scoped instance (priority := low) instLawfulOrderLTOpposite {il : LE α} {it : L
letI := il.opposite
letI := it.opposite
{ lt_iff a b := by
simp +instances only [LE.opposite, LT.opposite]
simp only [LE.le, LT.lt]
letI := il; letI := it
exact LawfulOrderLT.lt_iff b a }
@@ -297,7 +297,7 @@ scoped instance (priority := low) instLawfulOrderBEqOpposite {il : LE α} {ib :
LawfulOrderBEq α :=
letI := il.opposite
{ beq_iff_le_and_ge a b := by
simp +instances only [LE.opposite]
simp only [LE.le]
letI := il; letI := ib
rw [LawfulOrderBEq.beq_iff_le_and_ge]
exact and_comm }
@@ -310,7 +310,7 @@ scoped instance (priority := low) instLawfulOrderInfOpposite {il : LE α} {im :
letI := il.opposite
letI := im.oppositeMax
{ max_le_iff a b c := by
simp +instances only [LE.opposite, Min.oppositeMax]
simp only [LE.le, Max.max]
letI := il; letI := im
exact LawfulOrderInf.le_min_iff c a b }
@@ -322,11 +322,11 @@ scoped instance (priority := low) instLawfulOrderMinOpposite {il : LE α} {im :
letI := il.opposite
letI := im.oppositeMax
{ max_eq_or a b := by
simp +instances only [Min.oppositeMax]
simp only [Max.max]
letI := il; letI := im
exact MinEqOr.min_eq_or a b
max_le_iff a b c := by
simp +instances only [LE.opposite, Min.oppositeMax]
simp only [LE.le, Max.max]
letI := il; letI := im
exact LawfulOrderInf.le_min_iff c a b }
@@ -338,7 +338,7 @@ scoped instance (priority := low) instLawfulOrderSupOpposite {il : LE α} {im :
letI := il.opposite
letI := im.oppositeMin
{ le_min_iff a b c := by
simp +instances only [LE.opposite, Max.oppositeMin]
simp only [LE.le, Min.min]
letI := il; letI := im
exact LawfulOrderSup.max_le_iff b c a }
@@ -350,11 +350,11 @@ scoped instance (priority := low) instLawfulOrderMaxOpposite {il : LE α} {im :
letI := il.opposite
letI := im.oppositeMin
{ min_eq_or a b := by
simp +instances only [Max.oppositeMin]
simp only [Min.min]
letI := il; letI := im
exact MaxEqOr.max_eq_or a b
le_min_iff a b c := by
simp +instances only [LE.opposite, Max.oppositeMin]
simp only [LE.le, Min.min]
letI := il; letI := im
exact LawfulOrderSup.max_le_iff b c a }
@@ -366,11 +366,11 @@ scoped instance (priority := low) instLawfulOrderLeftLeaningMinOpposite {il : LE
letI := il.opposite
letI := im.oppositeMax
{ max_eq_left a b hab := by
simp +instances only [Min.oppositeMax]
simp only [Max.max]
letI := il; letI := im
exact LawfulOrderLeftLeaningMin.min_eq_left a b hab
max_eq_right a b hab := by
simp +instances only [Min.oppositeMax]
simp only [Max.max]
letI := il; letI := im
exact LawfulOrderLeftLeaningMin.min_eq_right a b hab }
@@ -382,11 +382,11 @@ scoped instance (priority := low) instLawfulOrderLeftLeaningMaxOpposite {il : LE
letI := il.opposite
letI := im.oppositeMin
{ min_eq_left a b hab := by
simp +instances only [Max.oppositeMin]
simp only [Min.min]
letI := il; letI := im
exact LawfulOrderLeftLeaningMax.max_eq_left a b hab
min_eq_right a b hab := by
simp +instances only [Max.oppositeMin]
simp only [Min.min]
letI := il; letI := im
exact LawfulOrderLeftLeaningMax.max_eq_right a b hab }

View File

@@ -796,7 +796,6 @@ automatically. If it fails, it is necessary to provide some of the fields manual
@[inline, expose, implicit_reducible]
public def LinearOrderPackage.ofOrd (α : Type u)
(args : Packages.LinearOrderOfOrdArgs α := by exact {}) : LinearOrderPackage α :=
-- set_option backward.isDefEq.respectTransparency false in
letI := LinearPreorderPackage.ofOrd α args.toLinearPreorderOfOrdArgs
haveI : LawfulEqOrd α := args.eq_of_compare _ _
letI : Min α := args.min

View File

@@ -597,7 +597,7 @@ instance Iterator.instLawfulIteratorLoop [UpwardEnumerable α] [LE α] [Decidabl
LawfulIteratorLoop (Rxc.Iterator α) Id n where
lawful := by
intro lift instLawfulMonadLiftFunction γ it init Pl wf f
simp +instances only [IteratorLoop.forIn, IterM.DefaultConsumers.forIn'_eq_wf Pl wf]
simp only [IteratorLoop.forIn, IterM.DefaultConsumers.forIn'_eq_wf Pl wf]
rw [IterM.DefaultConsumers.forIn'.wf]
split; rotate_left
· simp only [IterM.step_eq,
@@ -636,7 +636,7 @@ The pure function mapping a range iterator of type {name}`IterM` to the next ste
This function is prefixed with {lit}`Monadic` in order to disambiguate it from the version for iterators
of type {name}`Iter`.
-/
@[inline]
@[inline, implicit_reducible]
def Iterator.Monadic.step [UpwardEnumerable α] [LT α] [DecidableLT α]
(it : IterM (α := Rxo.Iterator α) Id α) :
IterStep (IterM (α := Rxo.Iterator α) Id α) α :=
@@ -1113,7 +1113,6 @@ private theorem Iterator.instIteratorLoop.loop_eq_wf [UpwardEnumerable α] [LT
· rw [WellFounded.fix_eq]
simp_all
set_option backward.isDefEq.respectTransparency false in
private theorem Iterator.instIteratorLoop.loopWf_eq [UpwardEnumerable α] [LT α] [DecidableLT α]
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α]
{n : Type u Type w} [Monad n] [LawfulMonad n] (γ : Type u)
@@ -1165,14 +1164,13 @@ termination_by IteratorLoop.WithWF.mk ⟨⟨some next, upperBound⟩⟩ acc (hwf
decreasing_by
simp [IteratorLoop.rel, Monadic.isPlausibleStep_iff, Monadic.step, *]
set_option backward.isDefEq.respectTransparency false in
instance Iterator.instLawfulIteratorLoop [UpwardEnumerable α] [LT α] [DecidableLT α]
[LawfulUpwardEnumerable α] [LawfulUpwardEnumerableLT α]
{n : Type u Type w} [Monad n] [LawfulMonad n] :
LawfulIteratorLoop (Rxo.Iterator α) Id n where
lawful := by
intro lift instLawfulMonadLiftFunction γ it init Pl wf f
simp +instances only [IteratorLoop.forIn, IterM.DefaultConsumers.forIn'_eq_wf Pl wf]
simp only [IteratorLoop.forIn, IterM.DefaultConsumers.forIn'_eq_wf Pl wf]
rw [IterM.DefaultConsumers.forIn'.wf]
split; rotate_left
· simp [IterM.step_eq, Monadic.step, Internal.LawfulMonadLiftBindFunction.liftBind_pure (liftBind := lift)]
@@ -1637,7 +1635,7 @@ instance Iterator.instLawfulIteratorLoop [UpwardEnumerable α]
LawfulIteratorLoop (Rxi.Iterator α) Id n where
lawful := by
intro lift instLawfulMonadLiftFunction γ it init Pl wf f
simp +instances only [IteratorLoop.forIn, IterM.DefaultConsumers.forIn'_eq_wf Pl wf]
simp only [IteratorLoop.forIn, IterM.DefaultConsumers.forIn'_eq_wf Pl wf]
rw [IterM.DefaultConsumers.forIn'.wf]
split; rotate_left
· simp [Monadic.step_eq_step, Monadic.step, Internal.LawfulMonadLiftBindFunction.liftBind_pure]

View File

@@ -248,7 +248,16 @@ instance : HasModel Int8 (BitVec 8) where
le_iff_encode_le := by simp [LE.le, Int8.le]
lt_iff_encode_lt := by simp [LT.lt, Int8.lt]
set_option backward.whnf.reducibleClassField false in
private theorem succ?_eq_minValueSealed {x : Int8} :
UpwardEnumerable.succ? x = if x + 1 = minValueSealed then none else some (x + 1) :=
(rfl)
private theorem succMany?_eq_maxValueSealed {i : Int8} :
UpwardEnumerable.succMany? n i =
have := i.minValue_le_toInt
if h : i.toInt + n maxValueSealed.toInt then some (.ofIntLE _ (by omega) (maxValueSealed_def h)) else none :=
(rfl)
theorem instUpwardEnumerable_eq :
instUpwardEnumerable = HasModel.instUpwardEnumerable := by
apply UpwardEnumerable.ext
@@ -256,16 +265,16 @@ theorem instUpwardEnumerable_eq :
apply HasModel.succ?_eq_of_technicalCondition
simp [HasModel.encode, succ?, Int8.toBitVec_inj, toBitVec_minValueSealed_eq_intMinSealed]
· ext
simp +instances [HasModel.succMany?_eq, instUpwardEnumerable, HasModel.encode, HasModel.decode,
simp [HasModel.succMany?_eq, succMany?_eq_maxValueSealed, HasModel.encode, HasModel.decode,
toInt_toBitVec, toBitVec_maxValueSealed_eq_intMaxSealed, ofIntLE_eq_ofInt]
instance : LawfulUpwardEnumerable Int8 := by
simp +instances only [instUpwardEnumerable_eq]
rw [instUpwardEnumerable_eq]
infer_instance
instance : LawfulUpwardEnumerableLE Int8 := by
simp +instances only [instUpwardEnumerable_eq]
rw [instUpwardEnumerable_eq]
infer_instance
public instance instRxcHasSize : Rxc.HasSize Int8 where
@@ -277,7 +286,7 @@ theorem instRxcHasSize_eq :
toInt_toBitVec, HasModel.toNat_toInt_add_one_sub_toInt (Nat.zero_lt_succ _)]
public instance instRxcLawfulHasSize : Rxc.LawfulHasSize Int8 := by
simp +instances only [instUpwardEnumerable_eq, instRxcHasSize_eq]
rw [instUpwardEnumerable_eq, instRxcHasSize_eq]
infer_instance
public instance : Rxc.IsAlwaysFinite Int8 := by exact inferInstance
@@ -294,7 +303,7 @@ theorem instRxiHasSize_eq :
HasModel.encode, HasModel.toNat_two_pow_sub_one_sub_toInt (show 8 > 0 by omega)]
public instance instRxiLawfulHasSize : Rxi.LawfulHasSize Int8 := by
simp +instances only [instUpwardEnumerable_eq, instRxiHasSize_eq]
rw [instUpwardEnumerable_eq, instRxiHasSize_eq]
infer_instance
public instance instRxiIsAlwaysFinite : Rxi.IsAlwaysFinite Int8 := by exact inferInstance
@@ -344,7 +353,6 @@ instance : HasModel Int16 (BitVec 16) where
le_iff_encode_le := by simp [LE.le, Int16.le]
lt_iff_encode_lt := by simp [LT.lt, Int16.lt]
set_option backward.whnf.reducibleClassField false in
theorem instUpwardEnumerable_eq :
instUpwardEnumerable = HasModel.instUpwardEnumerable := by
apply UpwardEnumerable.ext
@@ -440,7 +448,6 @@ instance : HasModel Int32 (BitVec 32) where
le_iff_encode_le := by simp [LE.le, Int32.le]
lt_iff_encode_lt := by simp [LT.lt, Int32.lt]
set_option backward.whnf.reducibleClassField false in
theorem instUpwardEnumerable_eq :
instUpwardEnumerable = HasModel.instUpwardEnumerable := by
apply UpwardEnumerable.ext
@@ -536,7 +543,6 @@ instance : HasModel Int64 (BitVec 64) where
le_iff_encode_le := by simp [LE.le, Int64.le]
lt_iff_encode_lt := by simp [LT.lt, Int64.lt]
set_option backward.whnf.reducibleClassField false in
theorem instUpwardEnumerable_eq :
instUpwardEnumerable = HasModel.instUpwardEnumerable := by
apply UpwardEnumerable.ext
@@ -637,7 +643,6 @@ instance : HasModel ISize (BitVec System.Platform.numBits) where
le_iff_encode_le := by simp [LE.le, ISize.le]
lt_iff_encode_lt := by simp [LT.lt, ISize.lt]
set_option backward.whnf.reducibleClassField false in
theorem instUpwardEnumerable_eq :
instUpwardEnumerable = HasModel.instUpwardEnumerable := by
apply UpwardEnumerable.ext

View File

@@ -26,7 +26,7 @@ variable {shape : RangeShape} {α : Type u}
structure SubarrayIterator (α : Type u) where
xs : Subarray α
@[inline, expose]
@[inline, expose, implicit_reducible]
def SubarrayIterator.step :
IterM (α := SubarrayIterator α) Id α IterStep (IterM (α := SubarrayIterator α) m α) α
| xs =>

View File

@@ -28,7 +28,6 @@ open Std Std.Iterators Std.PRange Std.Slice
namespace SubarrayIterator
set_option backward.isDefEq.respectTransparency false in
theorem step_eq {it : Iter (α := SubarrayIterator α) α} :
it.step = if h : it.1.xs.start < it.1.xs.stop then
haveI := it.1.xs.start_le_stop
@@ -215,7 +214,6 @@ public theorem Array.stop_toSubarray {xs : Array α} {lo hi : Nat} :
(xs.toSubarray lo hi).stop = min hi xs.size := by
simp [toSubarray_eq_min, Subarray.stop]
set_option backward.whnf.reducibleClassField false in
public theorem Subarray.toList_eq {xs : Subarray α} :
xs.toList = (xs.array.extract xs.start xs.stop).toList := by
let aslice := xs

View File

@@ -70,7 +70,6 @@ end ListSlice
namespace List
set_option backward.whnf.reducibleClassField false in
@[simp, grind =]
public theorem toList_mkSlice_rco {xs : List α} {lo hi : Nat} :
xs[lo...hi].toList = (xs.take hi).drop lo := by
@@ -78,9 +77,9 @@ public theorem toList_mkSlice_rco {xs : List α} {lo hi : Nat} :
simp only [Std.Rco.Sliceable.mkSlice, toSlice, ListSlice.toList_eq]
by_cases h : lo < hi
· have : lo hi := by omega
simp +instances [h, List.take_drop, Nat.add_sub_cancel' _, List.take_eq_take_min]
simp [h, List.take_drop, Nat.add_sub_cancel' _, List.take_eq_take_min]
· have : min hi xs.length lo := by omega
simp +instances [h, Nat.min_eq_right this]
simp [h, Nat.min_eq_right this]
@[simp, grind =]
public theorem toArray_mkSlice_rco {xs : List α} {lo hi : Nat} :
@@ -111,12 +110,11 @@ public theorem size_mkSlice_rcc {xs : List α} {lo hi : Nat} :
xs[lo...=hi].size = min (hi + 1) xs.length - lo := by
simp [ length_toList_eq_size]
set_option backward.whnf.reducibleClassField false in
@[simp, grind =]
public theorem toList_mkSlice_rci {xs : List α} {lo : Nat} :
xs[lo...*].toList = xs.drop lo := by
rw [List.drop_eq_drop_min]
simp +instances [ListSlice.toList_eq, Std.Rci.Sliceable.mkSlice, List.toUnboundedSlice]
simp [ListSlice.toList_eq, Std.Rci.Sliceable.mkSlice, List.toUnboundedSlice]
@[simp, grind =]
public theorem toArray_mkSlice_rci {xs : List α} {lo : Nat} :
@@ -290,11 +288,11 @@ section ListSubslices
namespace ListSlice
set_option backward.whnf.reducibleClassField false in
@[simp, grind =]
public theorem toList_mkSlice_rco {xs : ListSlice α} {lo hi : Nat} :
xs[lo...hi].toList = (xs.toList.take hi).drop lo := by
simp +instances only [instSliceableListSliceNat_1, List.toList_mkSlice_rco, ListSlice.toList_eq (xs := xs)]
rw [instSliceableListSliceNat_1]
simp only [List.toList_mkSlice_rco, ListSlice.toList_eq (xs := xs)]
obtain xs, stop := xs
cases stop
· simp
@@ -329,13 +327,13 @@ public theorem size_mkSlice_rcc {xs : ListSlice α} {lo hi : Nat} :
xs[lo...=hi].size = min (hi + 1) xs.size - lo := by
simp [ length_toList_eq_size]
set_option backward.whnf.reducibleClassField false in
@[simp, grind =]
public theorem toList_mkSlice_rci {xs : ListSlice α} {lo : Nat} :
xs[lo...*].toList = xs.toList.drop lo := by
simp +instances only [instSliceableListSliceNat_2, ListSlice.toList_eq (xs := xs)]
rw [instSliceableListSliceNat_2]
simp only [ListSlice.toList_eq (xs := xs)]
obtain xs, stop := xs
simp +instances only
simp only
split <;> simp
@[simp, grind =]

View File

@@ -1266,9 +1266,11 @@ theorem Pos.toSlice_comp_ofToSlice {s : String} :
theorem Pos.ofToSlice_comp_toSlice {s : String} :
Pos.ofToSlice (toSlice (s := s)) = id := by ext; simp
@[simp]
theorem Pos.toSlice_inj {s : String} {p q : s.Pos} : p.toSlice = q.toSlice p = q :=
fun h => by simpa using congrArg Pos.ofToSlice h, (· rfl)
@[simp]
theorem Pos.ofToSlice_inj {s : String} {p q : s.toSlice.Pos} : ofToSlice p = ofToSlice q p = q :=
fun h => by simpa using congrArg Pos.toSlice h, (· rfl)
@@ -1687,7 +1689,7 @@ def Pos.next {s : @& String} (pos : @& s.Pos) (h : pos ≠ s.endPos) : s.Pos :=
@[simp]
theorem Pos.ofToSlice_next_toSlice {s : String} {pos : s.Pos} {h} :
ofToSlice (Slice.Pos.next pos.toSlice h) = pos.next (ne_of_apply_ne Pos.toSlice (by simpa)) :=
ofToSlice (Slice.Pos.next pos.toSlice h) = pos.next (ne_of_apply_ne Pos.toSlice (by simpa using h)) :=
rfl
@[simp]
@@ -1922,7 +1924,7 @@ theorem Pos.toSlice_next {s : String} {p : s.Pos} {h} :
simp [next, -ofToSlice_next_toSlice]
theorem Pos.next_toSlice {s : String} {p : s.Pos} {h} :
p.toSlice.next h = (p.next (ne_of_apply_ne Pos.toSlice (by simpa))).toSlice := by
p.toSlice.next h = (p.next (ne_of_apply_ne Pos.toSlice (by simpa using h))).toSlice := by
simp [Pos.toSlice_next]
theorem Pos.byteIdx_lt_utf8ByteSize {s : String} (p : s.Pos) (h : p s.endPos) :

View File

@@ -55,9 +55,11 @@ end String
namespace String.Internal
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_posof"]
opaque posOf (s : String) (c : Char) : Pos.Raw
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_offsetofpos"]
opaque offsetOfPos (s : String) (pos : Pos.Raw) : Nat
@@ -67,6 +69,7 @@ opaque extract : (@& String) → (@& Pos.Raw) → (@& Pos.Raw) → String
@[extern "lean_string_length"]
opaque length : (@& String) Nat
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_pushn"]
opaque pushn (s : String) (c : Char) (n : Nat) : String
@@ -76,45 +79,57 @@ opaque append : String → (@& String) → String
@[extern "lean_string_utf8_next"]
opaque next (s : @& String) (p : @& Pos.Raw) : Pos.Raw
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_isempty"]
opaque isEmpty (s : String) : Bool
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_foldl"]
opaque foldl (f : String Char String) (init : String) (s : String) : String
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_isprefixof"]
opaque isPrefixOf (p : String) (s : String) : Bool
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_any"]
opaque any (s : String) (p : Char Bool) : Bool
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_contains"]
opaque contains (s : String) (c : Char) : Bool
@[extern "lean_string_utf8_get"]
opaque get (s : @& String) (p : @& Pos.Raw) : Char
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_capitalize"]
opaque capitalize (s : String) : String
@[extern "lean_string_utf8_at_end"]
opaque atEnd : (@& String) (@& Pos.Raw) Bool
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_nextwhile"]
opaque nextWhile (s : String) (p : Char Bool) (i : String.Pos.Raw) : String.Pos.Raw
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_trim"]
opaque trim (s : String) : String
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_intercalate"]
opaque intercalate (s : String) : List String String
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_front"]
opaque front (s : String) : Char
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_drop"]
opaque drop (s : String) (n : Nat) : String
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_dropright"]
opaque dropRight (s : String) (n : Nat) : String
@@ -141,33 +156,43 @@ def List.asString (s : List Char) : String :=
namespace Substring.Raw.Internal
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_substring_tostring"]
opaque toString : Substring.Raw String
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_substring_drop"]
opaque drop : Substring.Raw Nat Substring.Raw
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_substring_front"]
opaque front (s : Substring.Raw) : Char
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_substring_takewhile"]
opaque takeWhile : Substring.Raw (Char Bool) Substring.Raw
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_substring_extract"]
opaque extract : Substring.Raw String.Pos.Raw String.Pos.Raw Substring.Raw
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_substring_all"]
opaque all (s : Substring.Raw) (p : Char Bool) : Bool
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_substring_beq"]
opaque beq (ss1 ss2 : Substring.Raw) : Bool
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_substring_isempty"]
opaque isEmpty (ss : Substring.Raw) : Bool
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_substring_get"]
opaque get : Substring.Raw String.Pos.Raw Char
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_substring_prev"]
opaque prev : Substring.Raw String.Pos.Raw String.Pos.Raw
@@ -175,9 +200,11 @@ end Substring.Raw.Internal
namespace String.Pos.Raw.Internal
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_pos_sub"]
opaque sub : String.Pos.Raw String.Pos.Raw String.Pos.Raw
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_string_pos_min"]
opaque min (p₁ p₂ : Pos.Raw) : Pos.Raw

View File

@@ -64,7 +64,7 @@ public theorem Char.utf8Size_eq (c : Char) : c.utf8Size = 1 c.utf8Size = 2
match c.utf8Size, c.utf8Size_pos, c.utf8Size_le_four with
| 1, _, _ | 2, _, _ | 3, _, _ | 4, _, _ => simp
theorem Char.toNat_val_le {c : Char} : c.val.toNat 0x10ffff := by
theorem Char.toNat_le {c : Char} : c.toNat 0x10ffff := by
have := c.valid
simp [UInt32.isValidChar, Nat.isValidChar] at this
omega
@@ -193,10 +193,10 @@ theorem helper₄ (s : Nat) (c : BitVec w₀) (v : BitVec w') (w : Nat) :
-- TODO: possibly it makes sense to factor out this proof
theorem String.toBitVec_getElem_utf8EncodeChar_zero_of_utf8Size_eq_one {c : Char} (h : c.utf8Size = 1) :
((String.utf8EncodeChar c)[0]'(by simp [h])).toBitVec = 0#1 ++ c.val.toBitVec.extractLsb' 0 7 := by
have h₀ : c.val.toNat < 128 := by
suffices c.val.toNat 127 by omega
have h₀ : c.toNat < 128 := by
suffices c.toNat 127 by omega
simpa [Char.utf8Size_eq_one_iff, UInt32.le_iff_toNat_le] using h
have h₁ : c.val.toNat < 256 := by omega
have h₁ : c.toNat < 256 := by omega
rw [ BitVec.toNat_inj, BitVec.toNat_append]
simp [-Char.toUInt8_val, utf8EncodeChar_eq_singleton h, Nat.mod_eq_of_lt h₀, Nat.mod_eq_of_lt h₁]
@@ -977,9 +977,9 @@ theorem assemble₄_eq_some_iff_utf8EncodeChar_eq {w x y z : UInt8} {c : Char} :
BitVec.extractLsb'_append_extractLsb'_eq_extractLsb' (by simp),
BitVec.extractLsb'_append_extractLsb'_eq_extractLsb' (by simp),
BitVec.setWidth_eq_extractLsb' (by simp), BitVec.setWidth_setWidth_eq_self]
have := c.toNat_val_le
have := c.toNat_le
simp only [Nat.reduceAdd, BitVec.lt_def, UInt32.toNat_toBitVec, BitVec.toNat_twoPow,
Nat.reducePow, Nat.reduceMod, gt_iff_lt]
Nat.reducePow, Nat.reduceMod, gt_iff_lt, Char.toNat_val]
omega
theorem verify₄_eq_isSome_assemble₄ {w x y z : UInt8} :

View File

@@ -230,7 +230,7 @@ Examples:
* `"empty".isEmpty = false`
* `" ".isEmpty = false`
-/
@[inline] def isEmpty (s : String) : Bool :=
@[inline, expose] def isEmpty (s : String) : Bool :=
s.utf8ByteSize == 0
@[export lean_string_isempty]

View File

@@ -462,4 +462,32 @@ def revBytes (s : String) :=
instance {m : Type u Type v} [Monad m] : ForIn m String Char where
forIn s b f := ForIn.forIn s.toSlice b f
/--
Folds a function over a string from the start, accumulating a value starting with {name}`init`. The
accumulated value is combined with each character in order, using {name}`f`.
Examples:
* {lean}`"coffee tea water".foldl (fun n c => if c.isWhitespace then n + 1 else n) 0 = 2`
* {lean}`"coffee tea and water".foldl (fun n c => if c.isWhitespace then n + 1 else n) 0 = 3`
* {lean}`"coffee tea water".foldl (·.push ·) "" = "coffee tea water"`
-/
@[inline] def foldl {α : Type u} (f : α Char α) (init : α) (s : String) : α :=
s.toSlice.foldl f init
@[export lean_string_foldl]
def Internal.foldlImpl (f : String Char String) (init : String) (s : String) : String :=
String.foldl f init s
/--
Folds a function over a string from the right, accumulating a value starting with {lean}`init`. The
accumulated value is combined with each character in reverse order, using {lean}`f`.
Examples:
* {lean}`"coffee tea water".foldr (fun c n => if c.isWhitespace then n + 1 else n) 0 = 2`
* {lean}`"coffee tea and water".foldr (fun c n => if c.isWhitespace then n + 1 else n) 0 = 3`
* {lean}`"coffee tea water".foldr (fun c s => s.push c) "" = "retaw aet eeffoc"`
-/
@[inline] def foldr {α : Type u} (f : Char α α) (init : α) (s : String) : α :=
s.toSlice.foldr f init
end String

View File

@@ -78,7 +78,7 @@ theorem getUTF8Byte_toSlice {s : String} {p : String.Pos.Raw} {h} :
@[simp]
theorem Pos.byte_toSlice {s : String} {p : s.Pos} {h} :
p.toSlice.byte h = p.byte (ne_of_apply_ne Pos.toSlice (by simpa)) := by
p.toSlice.byte h = p.byte (ne_of_apply_ne Pos.toSlice (by simpa using h)) := by
simp [byte]
theorem Pos.byte_eq_byte_toSlice {s : String} {p : s.Pos} {h} :
@@ -181,6 +181,22 @@ theorem sliceTo_slice {s : String} {p₁ p₂ h p} :
(s.slice p₁ p₂ h).sliceTo p = s.slice p₁ (Pos.ofSlice p) Pos.le_ofSlice := by
ext <;> simp
@[simp]
theorem Slice.sliceFrom_startPos {s : Slice} : s.sliceFrom s.startPos = s := by
ext <;> simp
@[simp]
theorem Slice.sliceTo_endPos {s : Slice} : s.sliceTo s.endPos = s := by
ext <;> simp
@[simp]
theorem sliceFrom_startPos {s : String} : s.sliceFrom s.startPos = s := by
ext <;> simp
@[simp]
theorem sliceTo_endPos {s : String} : s.sliceTo s.endPos = s := by
ext <;> simp
end Iterate
theorem Slice.copy_eq_copy_slice {s : Slice} {pos₁ pos₂ : s.Pos} {h} :

View File

@@ -190,6 +190,17 @@ theorem forIn_eq_forIn_toList {m : Type u → Type v} [Monad m] [LawfulMonad m]
ForIn.forIn s b f = ForIn.forIn s.copy.toList b f := by
rw [forIn_eq_forIn_chars, Std.Iter.forIn_toList, toList_chars]
@[simp]
theorem foldl_eq_foldl_toList {α : Type u} {f : α Char α} {init : α} {s : Slice} :
s.foldl f init = s.copy.toList.foldl f init := by
rw [foldl, Std.Iter.foldl_toList, toList_chars]
@[simp]
theorem foldr_eq_foldr_toList {α : Type u} {f : Char α α} {init : α} {s : Slice} :
s.foldr f init = s.copy.toList.foldr f init := by
rw [foldr, Std.Iter.foldl_toList, toList_revChars, List.foldl_reverse]
congr
end Slice
/--
@@ -355,4 +366,14 @@ theorem forIn_eq_forIn_toList {m : Type u → Type v} [Monad m] [LawfulMonad m]
ForIn.forIn s b f = ForIn.forIn s.toList b f := by
rw [forIn_eq_forIn_chars, Std.Iter.forIn_toList, toList_chars]
@[simp]
theorem foldl_eq_foldl_toList {α : Type u} {f : α Char α} {init : α} {s : String} :
s.foldl f init = s.toList.foldl f init := by
simp [foldl]
@[simp]
theorem foldr_eq_foldr_toList {α : Type u} {f : Char α α} {init : α} {s : String} :
s.foldr f init = s.toList.foldr f init := by
simp [foldr]
end String

View File

@@ -57,4 +57,14 @@ theorem length_map {f : Char → Char} {s : String} : (s.map f).length = s.lengt
theorem map_eq_empty {f : Char Char} {s : String} : s.map f = "" s = "" := by
simp [ toList_eq_nil_iff]
@[simp]
theorem map_map {f g : Char Char} {s : String} : String.map g (String.map f s) = String.map (g f) s := by
apply String.ext
simp [List.map_map]
@[simp]
theorem map_id {s : String} : String.map id s = s := by
apply String.ext
simp [List.map_id]
end String

View File

@@ -13,3 +13,4 @@ public import Init.Data.String.Lemmas.Pattern.Char
public import Init.Data.String.Lemmas.Pattern.String
public import Init.Data.String.Lemmas.Pattern.Split
public import Init.Data.String.Lemmas.Pattern.Find
public import Init.Data.String.Lemmas.Pattern.TakeDrop

View File

@@ -193,6 +193,13 @@ theorem IsLongestMatch.isLongestMatchAt_ofSliceFrom {pat : ρ} [ForwardPatternMo
le := Slice.Pos.le_ofSliceFrom
isLongestMatch_sliceFrom := by simpa
@[simp]
theorem isLongestMatchAt_startPos_iff {pat : ρ} [ForwardPatternModel pat] {s : Slice} {endPos : s.Pos} :
IsLongestMatchAt pat s.startPos endPos IsLongestMatch pat endPos := by
simpa [isLongestMatchAt_iff] using
fun h => isLongestMatch_of_eq (by simp) (by simp) h,
fun h => isLongestMatch_of_eq (by simp) (by simp) h
/--
Predicate stating that there is a (longest) match starting at the given position.
-/
@@ -272,18 +279,24 @@ supplied by the {name}`ForwardPatternModel` instance.
-/
class LawfulForwardPatternModel {ρ : Type} (pat : ρ) [ForwardPattern pat]
[ForwardPatternModel pat] : Prop extends LawfulForwardPattern pat where
dropPrefix?_eq_some_iff (pos) : ForwardPattern.dropPrefix? pat s = some pos IsLongestMatch pat pos
skipPrefix?_eq_some_iff (pos) : ForwardPattern.skipPrefix? pat s = some pos IsLongestMatch pat pos
open Classical in
theorem LawfulForwardPatternModel.dropPrefix?_eq_none_iff {ρ : Type} {pat : ρ} [ForwardPattern pat] [ForwardPatternModel pat]
theorem LawfulForwardPatternModel.skipPrefix?_sliceFrom_eq_none_iff {ρ : Type} {pat : ρ} [ForwardPattern pat] [ForwardPatternModel pat]
[LawfulForwardPatternModel pat] {s : Slice} {p₀ : s.Pos} :
ForwardPattern.dropPrefix? pat (s.sliceFrom p₀) = none ¬ MatchesAt pat p₀ := by
ForwardPattern.skipPrefix? pat (s.sliceFrom p₀) = none ¬ MatchesAt pat p₀ := by
rw [ Decidable.not_iff_not]
simp [Option.ne_none_iff_exists', LawfulForwardPatternModel.dropPrefix?_eq_some_iff]
simp [Option.ne_none_iff_exists', LawfulForwardPatternModel.skipPrefix?_eq_some_iff]
refine fun p, hp => ?_, fun p, hp => ?_
· exact Slice.Pos.ofSliceFrom p, hp.isLongestMatchAt_ofSliceFrom
· exact p₀.sliceFrom p hp.le, hp.isLongestMatch_sliceFrom
theorem LawfulForwardPatternModel.skipPrefix?_eq_none_iff {ρ : Type} {pat : ρ} [ForwardPattern pat] [ForwardPatternModel pat]
[LawfulForwardPatternModel pat] {s : Slice} :
ForwardPattern.skipPrefix? pat s = none ¬ MatchesAt pat s.startPos := by
conv => lhs; rw [ sliceFrom_startPos (s := s)]
simp [skipPrefix?_sliceFrom_eq_none_iff]
/--
Inductive predicate stating that a list of search steps represents a valid search from a given
position in a slice.
@@ -358,8 +371,8 @@ theorem LawfulToForwardSearcherModel.defaultImplementation {pat : ρ} [ForwardPa
Std.PlausibleIterStep.yield, Std.IterStep.yield.injEq] at heq
rw [ heq.1, heq.2]
apply IsValidSearchFrom.matched
· rw [LawfulForwardPattern.dropPrefixOfNonempty?_eq,
LawfulForwardPatternModel.dropPrefix?_eq_some_iff] at heq'
· rw [LawfulForwardPattern.skipPrefixOfNonempty?_eq,
LawfulForwardPatternModel.skipPrefix?_eq_some_iff] at heq'
exact heq'.isLongestMatchAt_ofSliceFrom
· simp only [Std.IterM.toIter]
apply ih
@@ -372,8 +385,8 @@ theorem LawfulToForwardSearcherModel.defaultImplementation {pat : ρ} [ForwardPa
apply IsValidSearchFrom.mismatched (by simp) _ (ih _ (by simp))
intro p' hp' hp''
obtain rfl : pos = p' := Std.le_antisymm hp' (by simpa using hp'')
rwa [LawfulForwardPattern.dropPrefixOfNonempty?_eq,
LawfulForwardPatternModel.dropPrefix?_eq_none_iff] at heq'
rwa [LawfulForwardPattern.skipPrefixOfNonempty?_eq,
LawfulForwardPatternModel.skipPrefix?_sliceFrom_eq_none_iff] at heq'
· split at heq <;> simp at heq
· split at heq <;> simp at heq

View File

@@ -57,8 +57,8 @@ theorem isLongestMatchAt_of_get_eq {c : Char} {s : Slice} {pos : s.Pos} {h : pos
isLongestMatchAt_iff.2 h, by simp [hc]
instance {c : Char} : LawfulForwardPatternModel c where
dropPrefix?_eq_some_iff {s} pos := by
simp [isLongestMatch_iff, ForwardPattern.dropPrefix?, and_comm, eq_comm (b := pos)]
skipPrefix?_eq_some_iff {s} pos := by
simp [isLongestMatch_iff, ForwardPattern.skipPrefix?, and_comm, eq_comm (b := pos)]
theorem toSearcher_eq {c : Char} {s : Slice} :
ToForwardSearcher.toSearcher c s = ToForwardSearcher.toSearcher (· == c) s := (rfl)
@@ -136,42 +136,36 @@ theorem dropPrefix?_char_eq_dropPrefix?_beq {c : Char} {s : Slice} :
theorem dropPrefix_char_eq_dropPrefix_beq {c : Char} {s : Slice} :
s.dropPrefix c = s.dropPrefix (· == c) := (rfl)
theorem Pattern.ForwardPattern.dropPrefix?_char_eq_dropPrefix?_beq {c : Char} {s : Slice} :
dropPrefix? c s = dropPrefix? (· == c) s := (rfl)
theorem skipPrefix?_char_eq_skipPrefix?_beq {c : Char} {s : Slice} :
s.skipPrefix? c = s.skipPrefix? (· == c) := (rfl)
private theorem dropWhileGo_eq {c : Char} {s : Slice} (curr : s.Pos) :
dropWhile.go s c curr = dropWhile.go s (· == c) curr := by
fun_induction dropWhile.go s c curr with
theorem Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq {c : Char} {s : Slice} :
skipPrefix? c s = skipPrefix? (· == c) s := (rfl)
theorem Pos.skipWhile_char_eq_skipWhile_beq {c : Char} {s : Slice} (curr : s.Pos) :
Pos.skipWhile curr c = Pos.skipWhile curr (· == c) := by
fun_induction Pos.skipWhile curr c with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [dropWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_char_eq_dropPrefix?_beq, h₁, h₂, ih]
conv => rhs; rw [Pos.skipWhile]
simp [ Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [dropWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_char_eq_dropPrefix?_beq, h, ih]
conv => rhs; rw [Pos.skipWhile]
simp [ Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq, h, ih]
| case3 pos h =>
conv => rhs; rw [dropWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_char_eq_dropPrefix?_beq]
conv => rhs; rw [Pos.skipWhile]
simp [ Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq]
theorem skipPrefixWhile_char_eq_skipPrefixWhile_beq {c : Char} {s : Slice} :
s.skipPrefixWhile c = s.skipPrefixWhile (· == c) :=
Pos.skipWhile_char_eq_skipWhile_beq s.startPos
theorem dropWhile_char_eq_dropWhile_beq {c : Char} {s : Slice} :
s.dropWhile c = s.dropWhile (· == c) := by
simpa only [dropWhile] using dropWhileGo_eq s.startPos
private theorem takeWhileGo_eq {c : Char} {s : Slice} (curr : s.Pos) :
takeWhile.go s c curr = takeWhile.go s (· == c) curr := by
fun_induction takeWhile.go s c curr with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [takeWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_char_eq_dropPrefix?_beq, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [takeWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_char_eq_dropPrefix?_beq, h, ih]
| case3 pos h =>
conv => rhs; rw [takeWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_char_eq_dropPrefix?_beq]
simp only [dropWhile]; exact congrArg _ skipPrefixWhile_char_eq_skipPrefixWhile_beq
theorem takeWhile_char_eq_takeWhile_beq {c : Char} {s : Slice} :
s.takeWhile c = s.takeWhile (· == c) := by
simp only [takeWhile]; exact takeWhileGo_eq s.startPos
simp only [takeWhile]; exact congrArg _ skipPrefixWhile_char_eq_skipPrefixWhile_beq
theorem all_char_eq_all_beq {c : Char} {s : Slice} :
s.all c = s.all (· == c) := by
@@ -192,47 +186,41 @@ theorem contains_char_eq_contains_beq {c : Char} {s : Slice} :
theorem endsWith_char_eq_endsWith_beq {c : Char} {s : Slice} :
s.endsWith c = s.endsWith (· == c) := (rfl)
theorem skipSuffix?_char_eq_skipSuffix?_beq {c : Char} {s : Slice} :
s.skipSuffix? c = s.skipSuffix? (· == c) := (rfl)
theorem dropSuffix?_char_eq_dropSuffix?_beq {c : Char} {s : Slice} :
s.dropSuffix? c = s.dropSuffix? (· == c) := (rfl)
theorem dropSuffix_char_eq_dropSuffix_beq {c : Char} {s : Slice} :
s.dropSuffix c = s.dropSuffix (· == c) := (rfl)
theorem Pattern.BackwardPattern.dropSuffix?_char_eq_dropSuffix?_beq {c : Char} {s : Slice} :
dropSuffix? c s = dropSuffix? (· == c) s := (rfl)
theorem Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq {c : Char} {s : Slice} :
skipSuffix? c s = skipSuffix? (· == c) s := (rfl)
private theorem dropEndWhileGo_eq {c : Char} {s : Slice} (curr : s.Pos) :
dropEndWhile.go s c curr = dropEndWhile.go s (· == c) curr := by
fun_induction dropEndWhile.go s c curr with
theorem Pos.revSkipWhile_char_eq_revSkipWhile_beq {c : Char} {s : Slice} (curr : s.Pos) :
Pos.revSkipWhile curr c = Pos.revSkipWhile curr (· == c) := by
fun_induction Pos.revSkipWhile curr c with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [dropEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_char_eq_dropSuffix?_beq, h₁, h₂, ih]
conv => rhs; rw [Pos.revSkipWhile]
simp [ Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [dropEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_char_eq_dropSuffix?_beq, h, ih]
conv => rhs; rw [Pos.revSkipWhile]
simp [ Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq, h, ih]
| case3 pos h =>
conv => rhs; rw [dropEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_char_eq_dropSuffix?_beq]
conv => rhs; rw [Pos.revSkipWhile]
simp [ Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq]
theorem skipSuffixWhile_char_eq_skipSuffixWhile_beq {c : Char} {s : Slice} :
s.skipSuffixWhile c = s.skipSuffixWhile (· == c) :=
Pos.revSkipWhile_char_eq_revSkipWhile_beq s.endPos
theorem dropEndWhile_char_eq_dropEndWhile_beq {c : Char} {s : Slice} :
s.dropEndWhile c = s.dropEndWhile (· == c) := by
simpa only [dropEndWhile] using dropEndWhileGo_eq s.endPos
private theorem takeEndWhileGo_eq {c : Char} {s : Slice} (curr : s.Pos) :
takeEndWhile.go s c curr = takeEndWhile.go s (· == c) curr := by
fun_induction takeEndWhile.go s c curr with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [takeEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_char_eq_dropSuffix?_beq, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [takeEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_char_eq_dropSuffix?_beq, h, ih]
| case3 pos h =>
conv => rhs; rw [takeEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_char_eq_dropSuffix?_beq]
simp only [dropEndWhile]; exact congrArg _ skipSuffixWhile_char_eq_skipSuffixWhile_beq
theorem takeEndWhile_char_eq_takeEndWhile_beq {c : Char} {s : Slice} :
s.takeEndWhile c = s.takeEndWhile (· == c) := by
simpa only [takeEndWhile] using takeEndWhileGo_eq s.endPos
simp only [takeEndWhile]; exact congrArg _ skipSuffixWhile_char_eq_skipSuffixWhile_beq
end String.Slice

View File

@@ -57,8 +57,8 @@ theorem isLongestMatchAt_of_get {p : Char → Bool} {s : Slice} {pos : s.Pos} {h
isLongestMatchAt_iff.2 h, by simp [hc]
instance {p : Char Bool} : LawfulForwardPatternModel p where
dropPrefix?_eq_some_iff {s} pos := by
simp [isLongestMatch_iff, ForwardPattern.dropPrefix?, and_comm, eq_comm (b := pos)]
skipPrefix?_eq_some_iff {s} pos := by
simp [isLongestMatch_iff, ForwardPattern.skipPrefix?, and_comm, eq_comm (b := pos)]
instance {p : Char Bool} : LawfulToForwardSearcherModel p :=
.defaultImplementation
@@ -126,13 +126,13 @@ theorem matchAt?_eq_matchAt?_decide {p : Char → Prop} [DecidablePred p] {s : S
ext endPos
simp [isLongestMatchAt_iff_isLongestMatchAt_decide]
theorem dropPrefix?_eq_dropPrefix?_decide {p : Char Prop} [DecidablePred p] :
ForwardPattern.dropPrefix? p = ForwardPattern.dropPrefix? (decide <| p ·) := rfl
theorem skipPrefix?_eq_skipPrefix?_decide {p : Char Prop} [DecidablePred p] :
ForwardPattern.skipPrefix? p = ForwardPattern.skipPrefix? (decide <| p ·) := rfl
instance {p : Char Prop} [DecidablePred p] : LawfulForwardPatternModel p where
dropPrefix?_eq_some_iff {s} pos := by
rw [dropPrefix?_eq_dropPrefix?_decide, isLongestMatch_iff_isLongestMatch_decide]
exact LawfulForwardPatternModel.dropPrefix?_eq_some_iff ..
skipPrefix?_eq_some_iff {s} pos := by
rw [skipPrefix?_eq_skipPrefix?_decide, isLongestMatch_iff_isLongestMatch_decide]
exact LawfulForwardPatternModel.skipPrefix?_eq_some_iff ..
theorem toSearcher_eq {p : Char Prop} [DecidablePred p] {s : Slice} :
ToForwardSearcher.toSearcher p s = ToForwardSearcher.toSearcher (decide <| p ·) s := (rfl)
@@ -181,43 +181,39 @@ theorem dropPrefix?_prop_eq_dropPrefix?_decide {p : Char → Prop} [DecidablePre
theorem dropPrefix_prop_eq_dropPrefix_decide {p : Char Prop} [DecidablePred p] {s : Slice} :
s.dropPrefix p = s.dropPrefix (decide <| p ·) := (rfl)
theorem Pattern.ForwardPattern.dropPrefix?_prop_eq_dropPrefix?_decide
{p : Char Prop} [DecidablePred p] {s : Slice} :
dropPrefix? p s = dropPrefix? (decide <| p ·) s := (rfl)
theorem skipPrefix?_prop_eq_skipPrefix?_decide {p : Char Prop} [DecidablePred p] {s : Slice} :
s.skipPrefix? p = s.skipPrefix? (decide <| p ·) := (rfl)
private theorem dropWhileGo_eq {p : Char Prop} [DecidablePred p] {s : Slice} (curr : s.Pos) :
dropWhile.go s p curr = dropWhile.go s (decide <| p ·) curr := by
fun_induction dropWhile.go s p curr with
theorem Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide
{p : Char Prop} [DecidablePred p] {s : Slice} :
skipPrefix? p s = skipPrefix? (decide <| p ·) s := (rfl)
theorem Pos.skipWhile_prop_eq_skipWhile_decide {p : Char Prop} [DecidablePred p] {s : Slice}
(curr : s.Pos) :
Pos.skipWhile curr p = Pos.skipWhile curr (decide <| p ·) := by
fun_induction Pos.skipWhile curr p with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [dropWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_prop_eq_dropPrefix?_decide, h₁, h₂, ih]
conv => rhs; rw [Pos.skipWhile]
simp [ Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [dropWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_prop_eq_dropPrefix?_decide, h, ih]
conv => rhs; rw [Pos.skipWhile]
simp [ Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide, h, ih]
| case3 pos h =>
conv => rhs; rw [dropWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_prop_eq_dropPrefix?_decide]
conv => rhs; rw [Pos.skipWhile]
simp [ Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide]
theorem skipPrefixWhile_prop_eq_skipPrefixWhile_decide {p : Char Prop} [DecidablePred p]
{s : Slice} :
s.skipPrefixWhile p = s.skipPrefixWhile (decide <| p ·) :=
Pos.skipWhile_prop_eq_skipWhile_decide s.startPos
theorem dropWhile_prop_eq_dropWhile_decide {p : Char Prop} [DecidablePred p] {s : Slice} :
s.dropWhile p = s.dropWhile (decide <| p ·) := by
simpa only [dropWhile] using dropWhileGo_eq s.startPos
private theorem takeWhileGo_eq {p : Char Prop} [DecidablePred p] {s : Slice} (curr : s.Pos) :
takeWhile.go s p curr = takeWhile.go s (decide <| p ·) curr := by
fun_induction takeWhile.go s p curr with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [takeWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_prop_eq_dropPrefix?_decide, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [takeWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_prop_eq_dropPrefix?_decide, h, ih]
| case3 pos h =>
conv => rhs; rw [takeWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_prop_eq_dropPrefix?_decide]
simp only [dropWhile]; exact congrArg _ skipPrefixWhile_prop_eq_skipPrefixWhile_decide
theorem takeWhile_prop_eq_takeWhile_decide {p : Char Prop} [DecidablePred p] {s : Slice} :
s.takeWhile p = s.takeWhile (decide <| p ·) := by
simp only [takeWhile]; exact takeWhileGo_eq s.startPos
simp only [takeWhile]; exact congrArg _ skipPrefixWhile_prop_eq_skipPrefixWhile_decide
theorem all_prop_eq_all_decide {p : Char Prop} [DecidablePred p] {s : Slice} :
s.all p = s.all (decide <| p ·) := by
@@ -239,52 +235,46 @@ theorem contains_prop_eq_contains_decide {p : Char → Prop} [DecidablePred p] {
theorem endsWith_prop_eq_endsWith_decide {p : Char Prop} [DecidablePred p] {s : Slice} :
s.endsWith p = s.endsWith (decide <| p ·) := (rfl)
theorem skipSuffix?_prop_eq_skipSuffix?_decide {p : Char Prop} [DecidablePred p] {s : Slice} :
s.skipSuffix? p = s.skipSuffix? (decide <| p ·) := (rfl)
theorem dropSuffix?_prop_eq_dropSuffix?_decide {p : Char Prop} [DecidablePred p] {s : Slice} :
s.dropSuffix? p = s.dropSuffix? (decide <| p ·) := (rfl)
theorem dropSuffix_prop_eq_dropSuffix_decide {p : Char Prop} [DecidablePred p] {s : Slice} :
s.dropSuffix p = s.dropSuffix (decide <| p ·) := (rfl)
theorem Pattern.BackwardPattern.dropSuffix?_prop_eq_dropSuffix?_decide
theorem Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide
{p : Char Prop} [DecidablePred p] {s : Slice} :
dropSuffix? p s = dropSuffix? (decide <| p ·) s := (rfl)
skipSuffix? p s = skipSuffix? (decide <| p ·) s := (rfl)
private theorem dropEndWhileGo_eq {p : Char Prop} [DecidablePred p] {s : Slice}
(curr : s.Pos) :
dropEndWhile.go s p curr = dropEndWhile.go s (decide <| p ·) curr := by
fun_induction dropEndWhile.go s p curr with
theorem Pos.revSkipWhile_prop_eq_revSkipWhile_decide {p : Char Prop} [DecidablePred p]
{s : Slice} (curr : s.Pos) :
Pos.revSkipWhile curr p = Pos.revSkipWhile curr (decide <| p ·) := by
fun_induction Pos.revSkipWhile curr p with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [dropEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_prop_eq_dropSuffix?_decide, h₁, h₂, ih]
conv => rhs; rw [Pos.revSkipWhile]
simp [ Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [dropEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_prop_eq_dropSuffix?_decide, h, ih]
conv => rhs; rw [Pos.revSkipWhile]
simp [ Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide, h, ih]
| case3 pos h =>
conv => rhs; rw [dropEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_prop_eq_dropSuffix?_decide]
conv => rhs; rw [Pos.revSkipWhile]
simp [ Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide]
theorem skipSuffixWhile_prop_eq_skipSuffixWhile_decide {p : Char Prop} [DecidablePred p]
{s : Slice} :
s.skipSuffixWhile p = s.skipSuffixWhile (decide <| p ·) :=
Pos.revSkipWhile_prop_eq_revSkipWhile_decide s.endPos
theorem dropEndWhile_prop_eq_dropEndWhile_decide {p : Char Prop} [DecidablePred p]
{s : Slice} :
s.dropEndWhile p = s.dropEndWhile (decide <| p ·) := by
simpa only [dropEndWhile] using dropEndWhileGo_eq s.endPos
private theorem takeEndWhileGo_eq {p : Char Prop} [DecidablePred p] {s : Slice}
(curr : s.Pos) :
takeEndWhile.go s p curr = takeEndWhile.go s (decide <| p ·) curr := by
fun_induction takeEndWhile.go s p curr with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [takeEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_prop_eq_dropSuffix?_decide, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [takeEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_prop_eq_dropSuffix?_decide, h, ih]
| case3 pos h =>
conv => rhs; rw [takeEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_prop_eq_dropSuffix?_decide]
simp only [dropEndWhile]; exact congrArg _ skipSuffixWhile_prop_eq_skipSuffixWhile_decide
theorem takeEndWhile_prop_eq_takeEndWhile_decide {p : Char Prop} [DecidablePred p]
{s : Slice} :
s.takeEndWhile p = s.takeEndWhile (decide <| p ·) := by
simpa only [takeEndWhile] using takeEndWhileGo_eq s.endPos
simp only [takeEndWhile]; exact congrArg _ skipSuffixWhile_prop_eq_skipSuffixWhile_decide
end String.Slice

View File

@@ -55,6 +55,12 @@ theorem isLongestMatchAt_iff_splits {pat s : Slice} {pos₁ pos₂ : s.Pos} (h :
pos₂.Splits (t₁ ++ pat.copy) t₂ := by
simp only [isLongestMatchAt_iff h, copy_slice_eq_iff_splits]
theorem isLongestMatch_iff_splits {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
IsLongestMatch pat pos t, pos.Splits pat.copy t := by
simp only [ isLongestMatchAt_startPos_iff, isLongestMatchAt_iff_splits h, splits_startPos_iff,
and_assoc, exists_and_left, exists_eq_left, empty_append]
exact fun h, _, h' => h, h', fun h, h' => h, h'.eq_append.symm, h'
theorem isLongestMatchAt_iff_extract {pat s : Slice} {pos₁ pos₂ : s.Pos} (h : pat.isEmpty = false) :
IsLongestMatchAt pat pos₁ pos₂
s.copy.toByteArray.extract pos₁.offset.byteIdx pos₂.offset.byteIdx = pat.copy.toByteArray := by

View File

@@ -39,9 +39,9 @@ theorem startsWith_iff {pat s : Slice} : startsWith pat s ↔ ∃ t, s.copy = pa
· rintro t, rfl
simp [-size_toByteArray, ByteArray.extract_append]
theorem dropPrefix?_eq_some_iff {pat s : Slice} {pos : s.Pos} :
dropPrefix? pat s = some pos (s.sliceTo pos).copy = pat.copy := by
fun_cases dropPrefix? with
theorem skipPrefix?_eq_some_iff {pat s : Slice} {pos : s.Pos} :
skipPrefix? pat s = some pos (s.sliceTo pos).copy = pat.copy := by
fun_cases skipPrefix? with
| case1 h =>
simp only [offset_startPos, Pos.Raw.offsetBy_zero, Option.some.injEq]
obtain t, ht := startsWith_iff.1 h
@@ -58,8 +58,17 @@ theorem dropPrefix?_eq_some_iff {pat s : Slice} {pos : s.Pos} :
have := h (s.sliceFrom pos).copy
simp [ heq, pos.splits.eq_append] at this
theorem isSome_dropPrefix? {pat s : Slice} : (dropPrefix? pat s).isSome = startsWith pat s := by
fun_cases dropPrefix? <;> simp_all
theorem isSome_skipPrefix? {pat s : Slice} : (skipPrefix? pat s).isSome = startsWith pat s := by
fun_cases skipPrefix? <;> simp_all
public theorem startsWith_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
ForwardPattern.startsWith pat s = true := by
suffices pat.copy = "" by simp [ForwardPattern.startsWith, startsWith_iff, this]
simpa
public theorem skipPrefix?_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
ForwardPattern.skipPrefix? pat s = some s.startPos := by
simpa [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff]
end ForwardSliceSearcher
@@ -69,10 +78,10 @@ open Pattern.ForwardSliceSearcher
public theorem lawfulForwardPatternModel {pat : Slice} (hpat : pat.isEmpty = false) :
LawfulForwardPatternModel pat where
dropPrefixOfNonempty?_eq h := rfl
startsWith_eq s := isSome_dropPrefix?.symm
dropPrefix?_eq_some_iff pos := by
simp [ForwardPattern.dropPrefix?, dropPrefix?_eq_some_iff, isLongestMatch_iff hpat]
skipPrefixOfNonempty?_eq h := rfl
startsWith_eq s := isSome_skipPrefix?.symm
skipPrefix?_eq_some_iff pos := by
simp [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff, isLongestMatch_iff hpat]
end Model.ForwardSliceSearcher
@@ -82,10 +91,10 @@ open Pattern.ForwardSliceSearcher
public theorem lawfulForwardPatternModel {pat : String} (hpat : pat "") :
LawfulForwardPatternModel pat where
dropPrefixOfNonempty?_eq h := rfl
startsWith_eq s := isSome_dropPrefix?.symm
dropPrefix?_eq_some_iff pos := by
simp [ForwardPattern.dropPrefix?, dropPrefix?_eq_some_iff, isLongestMatch_iff hpat]
skipPrefixOfNonempty?_eq h := rfl
startsWith_eq s := isSome_skipPrefix?.symm
skipPrefix?_eq_some_iff pos := by
simp [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff, isLongestMatch_iff hpat]
end Model.ForwardStringSearcher
@@ -100,43 +109,38 @@ public theorem dropPrefix?_string_eq_dropPrefix?_toSlice {pat : String} {s : Sli
public theorem dropPrefix_string_eq_dropPrefix_toSlice {pat : String} {s : Slice} :
s.dropPrefix pat = s.dropPrefix pat.toSlice := (rfl)
public theorem Pattern.ForwardPattern.dropPrefix?_string_eq_dropPrefix?_toSlice
{pat : String} {s : Slice} :
dropPrefix? pat s = dropPrefix? pat.toSlice s := (rfl)
public theorem skipPrefix?_string_eq_skipPrefix?_toSlice {pat : String} {s : Slice} :
s.skipPrefix? pat = s.skipPrefix? pat.toSlice := (rfl)
private theorem dropWhileGo_string_eq {pat : String} {s : Slice} (curr : s.Pos) :
dropWhile.go s pat curr = dropWhile.go s pat.toSlice curr := by
fun_induction dropWhile.go s pat curr with
public theorem Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice
{pat : String} {s : Slice} :
skipPrefix? pat s = skipPrefix? pat.toSlice s := (rfl)
public theorem Pos.skipWhile_string_eq_skipWhile_toSlice {pat : String} {s : Slice}
(curr : s.Pos) :
Pos.skipWhile curr pat = Pos.skipWhile curr pat.toSlice := by
fun_induction Pos.skipWhile curr pat with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [dropWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_string_eq_dropPrefix?_toSlice, h₁, h₂, ih]
conv => rhs; rw [Pos.skipWhile]
simp [ Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [dropWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_string_eq_dropPrefix?_toSlice, h, ih]
conv => rhs; rw [Pos.skipWhile]
simp [ Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice, h, ih]
| case3 pos h =>
conv => rhs; rw [dropWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_string_eq_dropPrefix?_toSlice]
conv => rhs; rw [Pos.skipWhile]
simp [ Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice]
public theorem skipPrefixWhile_string_eq_skipPrefixWhile_toSlice {pat : String} {s : Slice} :
s.skipPrefixWhile pat = s.skipPrefixWhile pat.toSlice :=
Pos.skipWhile_string_eq_skipWhile_toSlice s.startPos
public theorem dropWhile_string_eq_dropWhile_toSlice {pat : String} {s : Slice} :
s.dropWhile pat = s.dropWhile pat.toSlice := by
simpa only [dropWhile] using dropWhileGo_string_eq s.startPos
private theorem takeWhileGo_string_eq {pat : String} {s : Slice} (curr : s.Pos) :
takeWhile.go s pat curr = takeWhile.go s pat.toSlice curr := by
fun_induction takeWhile.go s pat curr with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [takeWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_string_eq_dropPrefix?_toSlice, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [takeWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_string_eq_dropPrefix?_toSlice, h, ih]
| case3 pos h =>
conv => rhs; rw [takeWhile.go]
simp [ Pattern.ForwardPattern.dropPrefix?_string_eq_dropPrefix?_toSlice]
simp only [dropWhile]; exact congrArg _ skipPrefixWhile_string_eq_skipPrefixWhile_toSlice
public theorem takeWhile_string_eq_takeWhile_toSlice {pat : String} {s : Slice} :
s.takeWhile pat = s.takeWhile pat.toSlice := by
simp only [takeWhile]; exact takeWhileGo_string_eq s.startPos
simp only [takeWhile]; exact congrArg _ skipPrefixWhile_string_eq_skipPrefixWhile_toSlice
public theorem all_string_eq_all_toSlice {pat : String} {s : Slice} :
s.all pat = s.all pat.toSlice := by
@@ -145,48 +149,43 @@ public theorem all_string_eq_all_toSlice {pat : String} {s : Slice} :
public theorem endsWith_string_eq_endsWith_toSlice {pat : String} {s : Slice} :
s.endsWith pat = s.endsWith pat.toSlice := (rfl)
public theorem skipSuffix?_string_eq_skipSuffix?_toSlice {pat : String} {s : Slice} :
s.skipSuffix? pat = s.skipSuffix? pat.toSlice := (rfl)
public theorem dropSuffix?_string_eq_dropSuffix?_toSlice {pat : String} {s : Slice} :
s.dropSuffix? pat = s.dropSuffix? pat.toSlice := (rfl)
public theorem dropSuffix_string_eq_dropSuffix_toSlice {pat : String} {s : Slice} :
s.dropSuffix pat = s.dropSuffix pat.toSlice := (rfl)
public theorem Pattern.BackwardPattern.dropSuffix?_string_eq_dropSuffix?_toSlice
public theorem Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice
{pat : String} {s : Slice} :
dropSuffix? pat s = dropSuffix? pat.toSlice s := (rfl)
skipSuffix? pat s = skipSuffix? pat.toSlice s := (rfl)
private theorem dropEndWhileGo_string_eq {pat : String} {s : Slice} (curr : s.Pos) :
dropEndWhile.go s pat curr = dropEndWhile.go s pat.toSlice curr := by
fun_induction dropEndWhile.go s pat curr with
public theorem Pos.revSkipWhile_string_eq_revSkipWhile_toSlice {pat : String} {s : Slice}
(curr : s.Pos) :
Pos.revSkipWhile curr pat = Pos.revSkipWhile curr pat.toSlice := by
fun_induction Pos.revSkipWhile curr pat with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [dropEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_string_eq_dropSuffix?_toSlice, h₁, h₂, ih]
conv => rhs; rw [Pos.revSkipWhile]
simp [ Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [dropEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_string_eq_dropSuffix?_toSlice, h, ih]
conv => rhs; rw [Pos.revSkipWhile]
simp [ Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice, h, ih]
| case3 pos h =>
conv => rhs; rw [dropEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_string_eq_dropSuffix?_toSlice]
conv => rhs; rw [Pos.revSkipWhile]
simp [ Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice]
public theorem skipSuffixWhile_string_eq_skipSuffixWhile_toSlice {pat : String} {s : Slice} :
s.skipSuffixWhile pat = s.skipSuffixWhile pat.toSlice :=
Pos.revSkipWhile_string_eq_revSkipWhile_toSlice s.endPos
public theorem dropEndWhile_string_eq_dropEndWhile_toSlice {pat : String} {s : Slice} :
s.dropEndWhile pat = s.dropEndWhile pat.toSlice := by
simpa only [dropEndWhile] using dropEndWhileGo_string_eq s.endPos
private theorem takeEndWhileGo_string_eq {pat : String} {s : Slice} (curr : s.Pos) :
takeEndWhile.go s pat curr = takeEndWhile.go s pat.toSlice curr := by
fun_induction takeEndWhile.go s pat curr with
| case1 pos nextCurr h₁ h₂ ih =>
conv => rhs; rw [takeEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_string_eq_dropSuffix?_toSlice, h₁, h₂, ih]
| case2 pos nextCurr h ih =>
conv => rhs; rw [takeEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_string_eq_dropSuffix?_toSlice, h, ih]
| case3 pos h =>
conv => rhs; rw [takeEndWhile.go]
simp [ Pattern.BackwardPattern.dropSuffix?_string_eq_dropSuffix?_toSlice]
simp only [dropEndWhile]; exact congrArg _ skipSuffixWhile_string_eq_skipSuffixWhile_toSlice
public theorem takeEndWhile_string_eq_takeEndWhile_toSlice {pat : String} {s : Slice} :
s.takeEndWhile pat = s.takeEndWhile pat.toSlice := by
simpa only [takeEndWhile] using takeEndWhileGo_string_eq s.endPos
simp only [takeEndWhile]; exact congrArg _ skipSuffixWhile_string_eq_skipSuffixWhile_toSlice
end String.Slice

View File

@@ -0,0 +1,12 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
module
prelude
public import Init.Data.String.Lemmas.Pattern.TakeDrop.Basic
public import Init.Data.String.Lemmas.Pattern.TakeDrop.Char
public import Init.Data.String.Lemmas.Pattern.TakeDrop.Pred
public import Init.Data.String.Lemmas.Pattern.TakeDrop.String

View File

@@ -0,0 +1,71 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.String.Slice
public import Init.Data.String.TakeDrop
public import Init.Data.String.Lemmas.Pattern.Basic
import all Init.Data.String.Slice
import all Init.Data.String.TakeDrop
public section
open String.Slice Pattern Model
namespace String
namespace Slice
theorem skipPrefix?_eq_forwardPatternSkipPrefix? {ρ : Type} {pat : ρ} [ForwardPattern pat] {s : Slice} :
s.skipPrefix? pat = ForwardPattern.skipPrefix? pat s := (rfl)
theorem startsWith_eq_forwardPatternStartsWith {ρ : Type} {pat : ρ} [ForwardPattern pat] {s : Slice} :
s.startsWith pat = ForwardPattern.startsWith pat s := (rfl)
theorem Pattern.Model.skipPrefix?_eq_some_iff {ρ : Type} {pat : ρ} [ForwardPatternModel pat] [ForwardPattern pat]
[LawfulForwardPatternModel pat] {s : Slice} {pos : s.Pos} :
s.skipPrefix? pat = some pos IsLongestMatch pat pos := by
rw [skipPrefix?_eq_forwardPatternSkipPrefix?, LawfulForwardPatternModel.skipPrefix?_eq_some_iff]
theorem Pattern.Model.skipPrefix?_eq_none_iff {ρ : Type} {pat : ρ} [ForwardPatternModel pat] [ForwardPattern pat]
[LawfulForwardPatternModel pat] {s : Slice} :
s.skipPrefix? pat = none ¬ MatchesAt pat s.startPos := by
rw [skipPrefix?_eq_forwardPatternSkipPrefix?, LawfulForwardPatternModel.skipPrefix?_eq_none_iff]
@[simp]
theorem isSome_skipPrefix? {ρ : Type} {pat : ρ} [ForwardPattern pat] [LawfulForwardPattern pat] {s : Slice} :
(s.skipPrefix? pat).isSome = s.startsWith pat := by
rw [startsWith_eq_forwardPatternStartsWith, skipPrefix?, LawfulForwardPattern.startsWith_eq]
theorem Pattern.Model.startsWith_eq_false_iff {ρ : Type} {pat : ρ} [ForwardPatternModel pat] [ForwardPattern pat]
[LawfulForwardPatternModel pat] {s : Slice} :
s.startsWith pat = false ¬ MatchesAt pat s.startPos := by
rw [ Pattern.Model.skipPrefix?_eq_none_iff, Option.isNone_iff_eq_none,
isSome_skipPrefix?, Option.isSome_eq_false_iff]
theorem Pattern.Model.startsWith_iff {ρ : Type} {pat : ρ} [ForwardPatternModel pat] [ForwardPattern pat]
[LawfulForwardPatternModel pat] {s : Slice} :
s.startsWith pat = true MatchesAt pat s.startPos := by
rw [ Bool.not_eq_false, startsWith_eq_false_iff, Classical.not_not]
theorem dropPrefix?_eq_map_skipPrefix? {ρ : Type} {pat : ρ} [ForwardPattern pat] {s : Slice} :
s.dropPrefix? pat = (s.skipPrefix? pat).map s.sliceFrom := (rfl)
@[simp]
theorem skipPrefix?_eq_none_iff {ρ : Type} {pat : ρ} [ForwardPattern pat] [LawfulForwardPattern pat]
{s : Slice} : s.skipPrefix? pat = none s.startsWith pat = false := by
rw [ Option.isNone_iff_eq_none, Option.isSome_eq_false_iff, isSome_skipPrefix?]
end Slice
theorem skipPrefix?_eq_skipPrefix?_toSlice {ρ : Type} {pat : ρ} [ForwardPattern pat] {s : String} :
s.skipPrefix? pat = (s.toSlice.skipPrefix? pat).map Pos.ofToSlice := (rfl)
theorem startsWith_eq_startsWith_toSlice {ρ : Type} {pat : ρ} [ForwardPattern pat] {s : String} :
s.startsWith pat = s.toSlice.startsWith pat := (rfl)
end String

View File

@@ -0,0 +1,62 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.String.Slice
public import Init.Data.String.TakeDrop
import Init.Data.String.Lemmas.Pattern.TakeDrop.Basic
import Init.Data.String.Lemmas.Pattern.Char
import Init.Data.Option.Lemmas
public section
open String.Slice Pattern Model
namespace String
namespace Slice
theorem skipPrefix?_char_eq_some_iff {c : Char} {s : Slice} {pos : s.Pos} :
s.skipPrefix? c = some pos h, pos = s.startPos.next h s.startPos.get h = c := by
rw [Pattern.Model.skipPrefix?_eq_some_iff, Char.isLongestMatch_iff]
theorem startsWith_char_iff_get {c : Char} {s : Slice} :
s.startsWith c h, s.startPos.get h = c := by
simp [Pattern.Model.startsWith_iff, Char.matchesAt_iff]
theorem startsWith_char_eq_false_iff_get {c : Char} {s : Slice} :
s.startsWith c = false h, s.startPos.get h c := by
simp [Pattern.Model.startsWith_eq_false_iff, Char.matchesAt_iff]
theorem startsWith_char_eq_head? {c : Char} {s : Slice} :
s.startsWith c = (s.copy.toList.head? == some c) := by
rw [Bool.eq_iff_iff, Pattern.Model.startsWith_iff, Char.matchesAt_iff_splits]
simp only [splits_startPos_iff, exists_and_left, exists_eq_left, beq_iff_eq]
refine fun t, ht => by simp [ ht], fun h => ?_
simp only [ toList_inj, toList_append, toList_singleton, List.cons_append, List.nil_append]
cases h : s.copy.toList <;> simp_all [ ofList_inj]
end Slice
theorem skipPrefix?_char_eq_some_iff {c : Char} {s : String} {pos : s.Pos} :
s.skipPrefix? c = some pos h, pos = s.startPos.next h s.startPos.get h = c := by
simp [skipPrefix?_eq_skipPrefix?_toSlice, Slice.skipPrefix?_char_eq_some_iff, Pos.toSlice_inj,
Pos.toSlice_next]
theorem startsWith_char_iff_get {c : Char} {s : String} :
s.startsWith c h, s.startPos.get h = c := by
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_char_iff_get]
theorem startsWith_char_eq_false_iff_get {c : Char} {s : String} :
s.startsWith c = false h, s.startPos.get h c := by
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_char_eq_false_iff_get]
theorem startsWith_char_eq_head? {c : Char} {s : String} :
s.startsWith c = (s.toList.head? == some c) := by
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_char_eq_head?]
end String

View File

@@ -0,0 +1,98 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.String.Slice
public import Init.Data.String.TakeDrop
import Init.Data.String.Lemmas.Pattern.TakeDrop.Basic
import Init.Data.String.Lemmas.Pattern.Pred
import Init.Data.Option.Lemmas
import Init.ByCases
public section
open String.Slice Pattern Model
namespace String
namespace Slice
theorem skipPrefix?_bool_eq_some_iff {p : Char Bool} {s : Slice} {pos : s.Pos} :
s.skipPrefix? p = some pos h, pos = s.startPos.next h p (s.startPos.get h) = true := by
rw [Pattern.Model.skipPrefix?_eq_some_iff, CharPred.isLongestMatch_iff]
theorem startsWith_bool_iff_get {p : Char Bool} {s : Slice} :
s.startsWith p h, p (s.startPos.get h) = true := by
simp [Pattern.Model.startsWith_iff, CharPred.matchesAt_iff]
theorem startsWith_bool_eq_false_iff_get {p : Char Bool} {s : Slice} :
s.startsWith p = false h, p (s.startPos.get h) = false := by
simp [Pattern.Model.startsWith_eq_false_iff, CharPred.matchesAt_iff]
theorem startsWith_bool_eq_head? {p : Char Bool} {s : Slice} :
s.startsWith p = s.copy.toList.head?.any p := by
rw [Bool.eq_iff_iff, Pattern.Model.startsWith_iff, CharPred.matchesAt_iff]
by_cases h : s.startPos = s.endPos
· refine fun h', _ => by simp_all, ?_
have : s.copy = "" := by simp_all [Slice.startPos_eq_endPos_iff]
simp [this]
· obtain t, ht := s.splits_startPos.exists_eq_singleton_append h
simp [h, ht]
theorem skipPrefix?_prop_eq_some_iff {P : Char Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
s.skipPrefix? P = some pos h, pos = s.startPos.next h P (s.startPos.get h) := by
simp [skipPrefix?_prop_eq_skipPrefix?_decide, skipPrefix?_bool_eq_some_iff]
theorem startsWith_prop_iff_get {P : Char Prop} [DecidablePred P] {s : Slice} :
s.startsWith P h, P (s.startPos.get h) := by
simp [startsWith_prop_eq_startsWith_decide, startsWith_bool_iff_get]
theorem startsWith_prop_eq_false_iff_get {P : Char Prop} [DecidablePred P] {s : Slice} :
s.startsWith P = false h, ¬ P (s.startPos.get h) := by
simp [startsWith_prop_eq_startsWith_decide, startsWith_bool_eq_false_iff_get]
theorem startsWith_prop_eq_head? {P : Char Prop} [DecidablePred P] {s : Slice} :
s.startsWith P = s.copy.toList.head?.any (decide <| P ·) := by
simp [startsWith_prop_eq_startsWith_decide, startsWith_bool_eq_head?]
end Slice
theorem skipPrefix?_bool_eq_some_iff {p : Char Bool} {s : String} {pos : s.Pos} :
s.skipPrefix? p = some pos h, pos = s.startPos.next h p (s.startPos.get h) = true := by
simp [skipPrefix?_eq_skipPrefix?_toSlice, Slice.skipPrefix?_bool_eq_some_iff, Pos.toSlice_inj,
Pos.toSlice_next]
theorem startsWith_bool_iff_get {p : Char Bool} {s : String} :
s.startsWith p h, p (s.startPos.get h) = true := by
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_bool_iff_get]
theorem startsWith_bool_eq_false_iff_get {p : Char Bool} {s : String} :
s.startsWith p = false h, p (s.startPos.get h) = false := by
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_bool_eq_false_iff_get]
theorem startsWith_bool_eq_head? {p : Char Bool} {s : String} :
s.startsWith p = s.toList.head?.any p := by
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_bool_eq_head?]
theorem skipPrefix?_prop_eq_some_iff {P : Char Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
s.skipPrefix? P = some pos h, pos = s.startPos.next h P (s.startPos.get h) := by
simp [skipPrefix?_eq_skipPrefix?_toSlice, Slice.skipPrefix?_prop_eq_some_iff, Pos.toSlice_inj,
Pos.toSlice_next]
theorem startsWith_prop_iff_get {P : Char Prop} [DecidablePred P] {s : String} :
s.startsWith P h, P (s.startPos.get h) := by
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_prop_iff_get]
theorem startsWith_prop_eq_false_iff_get {P : Char Prop} [DecidablePred P] {s : String} :
s.startsWith P = false h, ¬ P (s.startPos.get h) := by
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_prop_eq_false_iff_get]
theorem startsWith_prop_eq_head? {P : Char Prop} [DecidablePred P] {s : String} :
s.startsWith P = s.toList.head?.any (decide <| P ·) := by
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_prop_eq_head?]
end String

View File

@@ -0,0 +1,110 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Markus Himmel
-/
module
prelude
public import Init.Data.String.Slice
public import Init.Data.String.TakeDrop
public import Init.Data.String.Lemmas.Splits
import Init.Data.String.Lemmas.Pattern.TakeDrop.Basic
import Init.Data.String.Lemmas.Pattern.String
import Init.Data.List.Sublist
import Init.Data.Option.Lemmas
public section
open String.Slice Pattern Model
namespace String
namespace Slice
@[simp]
theorem skipPrefix?_slice_eq_some_iff {pat s : Slice} {pos : s.Pos} :
s.skipPrefix? pat = some pos t, pos.Splits pat.copy t := by
match h : pat.isEmpty with
| false =>
have := ForwardSliceSearcher.lawfulForwardPatternModel h
rw [Pattern.Model.skipPrefix?_eq_some_iff, ForwardSliceSearcher.isLongestMatch_iff_splits h]
| true =>
rw [skipPrefix?_eq_forwardPatternSkipPrefix?, ForwardSliceSearcher.skipPrefix?_of_isEmpty h]
simp [(show pat.copy = "" by simpa), eq_comm]
@[simp]
theorem startsWith_slice_iff {pat s : Slice} :
s.startsWith pat pat.copy.toList <+: s.copy.toList := by
match h : pat.isEmpty with
| false =>
have := ForwardSliceSearcher.lawfulForwardPatternModel h
simp only [Model.startsWith_iff, ForwardSliceSearcher.matchesAt_iff_splits h,
splits_startPos_iff, exists_and_left, exists_eq_left]
simp only [ toList_inj, toList_append, List.prefix_iff_exists_append_eq]
exact fun t, ht => t.toList, by simp [ht], fun t, ht => String.ofList t, by simp [ ht]
| true =>
rw [startsWith_eq_forwardPatternStartsWith, ForwardSliceSearcher.startsWith_of_isEmpty h]
simp [(show pat.copy = "" by simpa)]
@[simp]
theorem startsWith_slice_eq_false_iff {pat s : Slice} :
s.startsWith pat = false ¬ (pat.copy.toList <+: s.copy.toList) := by
simp [ Bool.not_eq_true, startsWith_slice_iff]
@[simp]
theorem skipPrefix?_string_eq_some_iff {pat : String} {s : Slice} {pos : s.Pos} :
s.skipPrefix? pat = some pos t, pos.Splits pat t := by
simp [skipPrefix?_string_eq_skipPrefix?_toSlice]
@[simp]
theorem startsWith_string_iff {pat : String} {s : Slice} :
s.startsWith pat pat.toList <+: s.copy.toList := by
simp [startsWith_string_eq_startsWith_toSlice]
@[simp]
theorem startsWith_string_eq_false_iff {pat : String} {s : Slice} :
s.startsWith pat = false ¬ (pat.toList <+: s.copy.toList) := by
simp [startsWith_string_eq_startsWith_toSlice]
end Slice
@[simp]
theorem skipPrefix?_slice_eq_some_iff {pat : Slice} {s : String} {pos : s.Pos} :
s.skipPrefix? pat = some pos t, pos.Splits pat.copy t := by
simp only [skipPrefix?_eq_skipPrefix?_toSlice, Option.map_eq_some_iff,
Slice.skipPrefix?_slice_eq_some_iff]
refine ?_, fun h => pos.toSlice, by simpa
rintro pos, h, rfl
simpa
@[simp]
theorem startsWith_slice_iff {pat : Slice} {s : String} :
s.startsWith pat pat.copy.toList <+: s.toList := by
simp [startsWith_eq_startsWith_toSlice]
@[simp]
theorem startsWith_slice_eq_false_iff {pat : Slice} {s : String} :
s.startsWith pat = false ¬ (pat.copy.toList <+: s.toList) := by
simp [startsWith_eq_startsWith_toSlice]
@[simp]
theorem skipPrefix?_string_eq_some_iff {pat s : String} {pos : s.Pos} :
s.skipPrefix? pat = some pos t, pos.Splits pat t := by
simp only [skipPrefix?_eq_skipPrefix?_toSlice, Option.map_eq_some_iff,
Slice.skipPrefix?_string_eq_some_iff]
refine ?_, fun h => pos.toSlice, by simpa
rintro pos, h, rfl
simpa
@[simp]
theorem startsWith_string_iff {pat s : String} :
s.startsWith pat pat.toList <+: s.toList := by
simp [startsWith_eq_startsWith_toSlice]
@[simp]
theorem startsWith_string_eq_false_iff {pat s : String} :
s.startsWith pat = false ¬ (pat.toList <+: s.toList) := by
simp [startsWith_eq_startsWith_toSlice]
end String

View File

@@ -201,6 +201,26 @@ theorem Slice.Pos.Splits.eq_startPos_iff {s : Slice} {p : s.Pos} (h : p.Splits t
p = s.startPos t₁ = "" := by
rw [ copy_inj, startPos_copy, h.copy.eq_startPos_iff]
@[simp]
theorem Pos.splits_empty_left_iff {s : String} {p : s.Pos} {t : String} :
p.Splits "" t p = s.startPos t = s :=
fun h => by rw [h.eq_startPos_iff]; simp [h.eq_append], by rintro rfl, rfl; simp
@[simp]
theorem Pos.splits_empty_right_iff {s : String} {p : s.Pos} {t : String} :
p.Splits t "" p = s.endPos t = s :=
fun h => by rw [h.eq_endPos_iff]; simp [h.eq_append], by rintro rfl, rfl; simp
@[simp]
theorem Slice.Pos.splits_empty_left_iff {s : Slice} {p : s.Pos} {t : String} :
p.Splits "" t p = s.startPos t = s.copy :=
fun h => by rw [h.eq_startPos_iff]; simp [h.eq_append], by rintro rfl, rfl; simp
@[simp]
theorem Slice.Pos.splits_empty_right_iff {s : Slice} {p : s.Pos} {t : String} :
p.Splits t "" p = s.endPos t = s.copy :=
fun h => by rw [h.eq_endPos_iff]; simp [h.eq_append], by rintro rfl, rfl; simp
theorem Pos.splits_next_right {s : String} (p : s.Pos) (hp : p s.endPos) :
p.Splits (s.sliceTo p).copy (singleton (p.get hp) ++ (s.sliceFrom (p.next hp)).copy) where
eq_append := by simpa [ append_assoc] using p.eq_copy_sliceTo_append_get hp

View File

@@ -229,7 +229,7 @@ Examples:
* `"Orange".toLower = "orange"`
* `"ABc123".toLower = "abc123"`
-/
@[inline] def toLower (s : String) : String :=
@[inline, expose] def toLower (s : String) : String :=
s.map Char.toLower
/--

View File

@@ -106,20 +106,24 @@ class ForwardPattern {ρ : Type} (pat : ρ) where
Checks whether the slice starts with the pattern. If it does, the slice is returned with the
prefix removed; otherwise the result is {name}`none`.
-/
dropPrefix? : (s : Slice) Option s.Pos
skipPrefix? : (s : Slice) Option s.Pos
/--
Checks whether the slice starts with the pattern. If it does, the slice is returned with the
prefix removed; otherwise the result is {name}`none`.
-/
dropPrefixOfNonempty? : (s : Slice) (h : s.isEmpty = false) Option s.Pos := fun s _ => dropPrefix? s
skipPrefixOfNonempty? : (s : Slice) (h : s.isEmpty = false) Option s.Pos := fun s _ => skipPrefix? s
/--
Checks whether the slice starts with the pattern.
-/
startsWith : (s : Slice) Bool := fun s => (dropPrefix? s).isSome
startsWith : (s : Slice) Bool := fun s => (skipPrefix? s).isSome
@[deprecated ForwardPattern.dropPrefix? (since := "2026-03-19")]
def ForwardPattern.dropPrefix? {ρ : Type} (pat : ρ) [ForwardPattern pat] (s : Slice) : Option s.Pos :=
ForwardPattern.skipPrefix? pat s
/--
A lawful forward pattern is one where the three functions {name}`ForwardPattern.dropPrefix?`,
{name}`ForwardPattern.dropPrefixOfNonempty?` and {name}`ForwardPattern.startsWith` agree for any
A lawful forward pattern is one where the three functions {name}`ForwardPattern.skipPrefix?`,
{name}`ForwardPattern.skipPrefixOfNonempty?` and {name}`ForwardPattern.startsWith` agree for any
given input slice.
Note that this is a relatively weak condition. It is non-uniform in the sense that the functions
@@ -127,14 +131,14 @@ can still return completely different results on different slices, even if they
string.
There is a stronger lawfulness typeclass {lit}`LawfulForwardPatternModel` that asserts that the
{name}`ForwardPattern.dropPrefix?` function behaves like a function that drops the longest prefix
{name}`ForwardPattern.skipPrefix?` function behaves like a function that drops the longest prefix
according to some notion of matching.
-/
class LawfulForwardPattern {ρ : Type} (pat : ρ) [ForwardPattern pat] : Prop where
dropPrefixOfNonempty?_eq {s : Slice} (h) :
ForwardPattern.dropPrefixOfNonempty? pat s h = ForwardPattern.dropPrefix? pat s
skipPrefixOfNonempty?_eq {s : Slice} (h) :
ForwardPattern.skipPrefixOfNonempty? pat s h = ForwardPattern.skipPrefix? pat s
startsWith_eq (s : Slice) :
ForwardPattern.startsWith pat s = (ForwardPattern.dropPrefix? pat s).isSome
ForwardPattern.startsWith pat s = (ForwardPattern.skipPrefix? pat s).isSome
/--
A strict forward pattern is one which never drops an empty prefix.
@@ -144,7 +148,7 @@ iterator.
-/
class StrictForwardPattern {ρ : Type} (pat : ρ) [ForwardPattern pat] : Prop where
ne_startPos {s : Slice} (h) (q) :
ForwardPattern.dropPrefixOfNonempty? pat s h = some q q s.startPos
ForwardPattern.skipPrefixOfNonempty? pat s h = some q q s.startPos
/--
Provides a conversion from a pattern to an iterator of {name}`SearchStep` that searches for matches
@@ -188,11 +192,11 @@ instance (s : Slice) [ForwardPattern pat] :
Std.Iterator (DefaultForwardSearcher pat s) Id (SearchStep s) where
IsPlausibleStep it
| .yield it' (.rejected p₁ p₂) => (h : it.internalState.currPos s.endPos),
ForwardPattern.dropPrefixOfNonempty? pat (s.sliceFrom it.internalState.currPos) (by simpa) = none
ForwardPattern.skipPrefixOfNonempty? pat (s.sliceFrom it.internalState.currPos) (by simpa) = none
p₁ = it.internalState.currPos p₂ = it.internalState.currPos.next h
it'.internalState.currPos = it.internalState.currPos.next h
| .yield it' (.matched p₁ p₂) => (h : it.internalState.currPos s.endPos), pos,
ForwardPattern.dropPrefixOfNonempty? pat (s.sliceFrom it.internalState.currPos) (by simpa) = some pos
ForwardPattern.skipPrefixOfNonempty? pat (s.sliceFrom it.internalState.currPos) (by simpa) = some pos
p₁ = it.internalState.currPos p₂ = Slice.Pos.ofSliceFrom pos
it'.internalState.currPos = Slice.Pos.ofSliceFrom pos
| .done => it.internalState.currPos = s.endPos
@@ -201,7 +205,7 @@ instance (s : Slice) [ForwardPattern pat] :
if h : it.internalState.currPos = s.endPos then
pure (.deflate .done, by simp [h])
else
match h' : ForwardPattern.dropPrefixOfNonempty? pat (s.sliceFrom it.internalState.currPos) (by simpa) with
match h' : ForwardPattern.skipPrefixOfNonempty? pat (s.sliceFrom it.internalState.currPos) (by simpa) with
| some pos =>
pure (.deflate .yield Slice.Pos.ofSliceFrom pos
(.matched it.internalState.currPos (Slice.Pos.ofSliceFrom pos)), by simp [h, h'])
@@ -312,20 +316,20 @@ class BackwardPattern {ρ : Type} (pat : ρ) where
Checks whether the slice ends with the pattern. If it does, the slice is returned with the
suffix removed; otherwise the result is {name}`none`.
-/
dropSuffix? : (s : Slice) Option s.Pos
skipSuffix? : (s : Slice) Option s.Pos
/--
Checks whether the slice ends with the pattern. If it does, the slice is returned with the
suffix removed; otherwise the result is {name}`none`.
-/
dropSuffixOfNonempty? : (s : Slice) (h : s.isEmpty = false) Option s.Pos := fun s _ => dropSuffix? s
skipSuffixOfNonempty? : (s : Slice) (h : s.isEmpty = false) Option s.Pos := fun s _ => skipSuffix? s
/--
Checks whether the slice ends with the pattern.
-/
endsWith : Slice Bool := fun s => (dropSuffix? s).isSome
endsWith : Slice Bool := fun s => (skipSuffix? s).isSome
/--
A lawful backward pattern is one where the three functions {name}`BackwardPattern.dropSuffix?`,
{name}`BackwardPattern.dropSuffixOfNonempty?` and {name}`BackwardPattern.endsWith` agree for any
A lawful backward pattern is one where the three functions {name}`BackwardPattern.skipSuffix?`,
{name}`BackwardPattern.skipSuffixOfNonempty?` and {name}`BackwardPattern.endsWith` agree for any
given input slice.
Note that this is a relatively weak condition. It is non-uniform in the sense that the functions
@@ -333,14 +337,14 @@ can still return completely different results on different slices, even if they
string.
There is a stronger lawfulness typeclass {lit}`LawfulBackwardPatternModel` that asserts that the
{name}`BackwardPattern.dropSuffix?` function behaves like a function that drops the longest prefix
{name}`BackwardPattern.skipSuffix?` function behaves like a function that skips the longest prefix
according to some notion of matching.
-/
class LawfulBackwardPattern {ρ : Type} (pat : ρ) [BackwardPattern pat] : Prop where
dropSuffixOfNonempty?_eq {s : Slice} (h) :
BackwardPattern.dropSuffixOfNonempty? pat s h = BackwardPattern.dropSuffix? pat s
skipSuffixOfNonempty?_eq {s : Slice} (h) :
BackwardPattern.skipSuffixOfNonempty? pat s h = BackwardPattern.skipSuffix? pat s
endsWith_eq (s : Slice) :
BackwardPattern.endsWith pat s = (BackwardPattern.dropSuffix? pat s).isSome
BackwardPattern.endsWith pat s = (BackwardPattern.skipSuffix? pat s).isSome
/--
A strict backward pattern is one which never drops an empty suffix.
@@ -350,7 +354,7 @@ iterator.
-/
class StrictBackwardPattern {ρ : Type} (pat : ρ) [BackwardPattern pat] : Prop where
ne_endPos {s : Slice} (h) (q) :
BackwardPattern.dropSuffixOfNonempty? pat s h = some q q s.endPos
BackwardPattern.skipSuffixOfNonempty? pat s h = some q q s.endPos
/--
Provides a conversion from a pattern to an iterator of {name}`SearchStep` searching for matches of
@@ -394,11 +398,11 @@ instance (s : Slice) [BackwardPattern pat] :
Std.Iterator (DefaultBackwardSearcher pat s) Id (SearchStep s) where
IsPlausibleStep it
| .yield it' (.rejected p₁ p₂) => (h : it.internalState.currPos s.startPos),
BackwardPattern.dropSuffixOfNonempty? pat (s.sliceTo it.internalState.currPos) (by simpa) = none
BackwardPattern.skipSuffixOfNonempty? pat (s.sliceTo it.internalState.currPos) (by simpa) = none
p₁ = it.internalState.currPos.prev h p₂ = it.internalState.currPos
it'.internalState.currPos = it.internalState.currPos.prev h
| .yield it' (.matched p₁ p₂) => (h : it.internalState.currPos s.startPos), pos,
BackwardPattern.dropSuffixOfNonempty? pat (s.sliceTo it.internalState.currPos) (by simpa) = some pos
BackwardPattern.skipSuffixOfNonempty? pat (s.sliceTo it.internalState.currPos) (by simpa) = some pos
p₁ = Slice.Pos.ofSliceTo pos p₂ = it.internalState.currPos
it'.internalState.currPos = Slice.Pos.ofSliceTo pos
| .done => it.internalState.currPos = s.startPos
@@ -407,7 +411,7 @@ instance (s : Slice) [BackwardPattern pat] :
if h : it.internalState.currPos = s.startPos then
pure (.deflate .done, by simp [h])
else
match h' : BackwardPattern.dropSuffixOfNonempty? pat (s.sliceTo it.internalState.currPos) (by simpa) with
match h' : BackwardPattern.skipSuffixOfNonempty? pat (s.sliceTo it.internalState.currPos) (by simpa) with
| some pos =>
pure (.deflate .yield Slice.Pos.ofSliceTo pos
(.matched (Slice.Pos.ofSliceTo pos) it.internalState.currPos), by simp [h, h'])

View File

@@ -21,30 +21,30 @@ namespace String.Slice.Pattern
namespace Char
instance {c : Char} : ForwardPattern c where
dropPrefixOfNonempty? s h := ForwardPattern.dropPrefixOfNonempty? (· == c) s h
dropPrefix? s := ForwardPattern.dropPrefix? (· == c) s
skipPrefixOfNonempty? s h := ForwardPattern.skipPrefixOfNonempty? (· == c) s h
skipPrefix? s := ForwardPattern.skipPrefix? (· == c) s
startsWith s := ForwardPattern.startsWith (· == c) s
instance {c : Char} : StrictForwardPattern c where
ne_startPos h q := StrictForwardPattern.ne_startPos (pat := (· == c)) h q
instance {c : Char} : LawfulForwardPattern c where
dropPrefixOfNonempty?_eq h := LawfulForwardPattern.dropPrefixOfNonempty?_eq (pat := (· == c)) h
skipPrefixOfNonempty?_eq h := LawfulForwardPattern.skipPrefixOfNonempty?_eq (pat := (· == c)) h
startsWith_eq s := LawfulForwardPattern.startsWith_eq (pat := (· == c)) s
instance {c : Char} : ToForwardSearcher c (ToForwardSearcher.DefaultForwardSearcher (· == c)) where
toSearcher s := ToForwardSearcher.toSearcher (· == c) s
instance {c : Char} : BackwardPattern c where
dropSuffixOfNonempty? s h := BackwardPattern.dropSuffixOfNonempty? (· == c) s h
dropSuffix? s := BackwardPattern.dropSuffix? (· == c) s
skipSuffixOfNonempty? s h := BackwardPattern.skipSuffixOfNonempty? (· == c) s h
skipSuffix? s := BackwardPattern.skipSuffix? (· == c) s
endsWith s := BackwardPattern.endsWith (· == c) s
instance {c : Char} : StrictBackwardPattern c where
ne_endPos h q := StrictBackwardPattern.ne_endPos (pat := (· == c)) h q
instance {c : Char} : LawfulBackwardPattern c where
dropSuffixOfNonempty?_eq h := LawfulBackwardPattern.dropSuffixOfNonempty?_eq (pat := (· == c)) h
skipSuffixOfNonempty?_eq h := LawfulBackwardPattern.skipSuffixOfNonempty?_eq (pat := (· == c)) h
endsWith_eq s := LawfulBackwardPattern.endsWith_eq (pat := (· == c)) s
instance {c : Char} : ToBackwardSearcher c (ToBackwardSearcher.DefaultBackwardSearcher c) :=

View File

@@ -30,12 +30,12 @@ namespace CharPred
@[default_instance]
instance {p : Char Bool} : ForwardPattern p where
dropPrefixOfNonempty? s h :=
skipPrefixOfNonempty? s h :=
if p (s.startPos.get (Slice.startPos_ne_endPos h)) then
some (s.startPos.next (Slice.startPos_ne_endPos h))
else
none
dropPrefix? s :=
skipPrefix? s :=
if h : s.startPos = s.endPos then
none
else if p (s.startPos.get h) then
@@ -50,17 +50,17 @@ instance {p : Char → Bool} : ForwardPattern p where
instance {p : Char Bool} : StrictForwardPattern p where
ne_startPos {s h q} := by
simp only [ForwardPattern.dropPrefixOfNonempty?, Option.ite_none_right_eq_some,
simp only [ForwardPattern.skipPrefixOfNonempty?, Option.ite_none_right_eq_some,
Option.some.injEq, ne_eq, and_imp]
rintro _ rfl
simp
instance {p : Char Bool} : LawfulForwardPattern p where
dropPrefixOfNonempty?_eq {s} h := by
simp [ForwardPattern.dropPrefixOfNonempty?, ForwardPattern.dropPrefix?,
skipPrefixOfNonempty?_eq {s} h := by
simp [ForwardPattern.skipPrefixOfNonempty?, ForwardPattern.skipPrefix?,
Slice.startPos_eq_endPos_iff, h]
startsWith_eq s := by
simp only [ForwardPattern.startsWith, ForwardPattern.dropPrefix?]
simp only [ForwardPattern.startsWith, ForwardPattern.skipPrefix?]
split <;> (try split) <;> simp_all
@[default_instance]
@@ -70,15 +70,15 @@ instance {p : Char → Bool} : ToForwardSearcher p (ToForwardSearcher.DefaultFor
namespace Decidable
instance {p : Char Prop} [DecidablePred p] : ForwardPattern p where
dropPrefixOfNonempty? s h := ForwardPattern.dropPrefixOfNonempty? (decide <| p ·) s h
dropPrefix? s := ForwardPattern.dropPrefix? (decide <| p ·) s
skipPrefixOfNonempty? s h := ForwardPattern.skipPrefixOfNonempty? (decide <| p ·) s h
skipPrefix? s := ForwardPattern.skipPrefix? (decide <| p ·) s
startsWith s := ForwardPattern.startsWith (decide <| p ·) s
instance {p : Char Prop} [DecidablePred p] : StrictForwardPattern p where
ne_startPos h q := StrictForwardPattern.ne_startPos (pat := (decide <| p ·)) h q
instance {p : Char Prop} [DecidablePred p] : LawfulForwardPattern p where
dropPrefixOfNonempty?_eq h := LawfulForwardPattern.dropPrefixOfNonempty?_eq (pat := (decide <| p ·)) h
skipPrefixOfNonempty?_eq h := LawfulForwardPattern.skipPrefixOfNonempty?_eq (pat := (decide <| p ·)) h
startsWith_eq s := LawfulForwardPattern.startsWith_eq (pat := (decide <| p ·)) s
instance {p : Char Prop} [DecidablePred p] : ToForwardSearcher p (ToForwardSearcher.DefaultForwardSearcher (decide <| p ·)) where
@@ -88,12 +88,12 @@ end Decidable
@[default_instance]
instance {p : Char Bool} : BackwardPattern p where
dropSuffixOfNonempty? s h :=
skipSuffixOfNonempty? s h :=
if p ((s.endPos.prev (Ne.symm (by exact Slice.startPos_ne_endPos h))).get (by simp)) then
some (s.endPos.prev (Ne.symm (by exact Slice.startPos_ne_endPos h)))
else
none
dropSuffix? s :=
skipSuffix? s :=
if h : s.endPos = s.startPos then
none
else if p ((s.endPos.prev h).get (by simp)) then
@@ -108,17 +108,17 @@ instance {p : Char → Bool} : BackwardPattern p where
instance {p : Char Bool} : StrictBackwardPattern p where
ne_endPos {s h q} := by
simp only [BackwardPattern.dropSuffixOfNonempty?, Option.ite_none_right_eq_some,
simp only [BackwardPattern.skipSuffixOfNonempty?, Option.ite_none_right_eq_some,
Option.some.injEq, ne_eq, and_imp]
rintro _ rfl
simp
instance {p : Char Bool} : LawfulBackwardPattern p where
dropSuffixOfNonempty?_eq {s h} := by
simp [BackwardPattern.dropSuffixOfNonempty?, BackwardPattern.dropSuffix?,
skipSuffixOfNonempty?_eq {s h} := by
simp [BackwardPattern.skipSuffixOfNonempty?, BackwardPattern.skipSuffix?,
Eq.comm (a := s.endPos), Slice.startPos_eq_endPos_iff, h]
endsWith_eq {s} := by
simp only [BackwardPattern.endsWith, BackwardPattern.dropSuffix?]
simp only [BackwardPattern.endsWith, BackwardPattern.skipSuffix?]
split <;> (try split) <;> simp_all
@[default_instance]
@@ -128,15 +128,15 @@ instance {p : Char → Bool} : ToBackwardSearcher p (ToBackwardSearcher.DefaultB
namespace Decidable
instance {p : Char Prop} [DecidablePred p] : BackwardPattern p where
dropSuffixOfNonempty? s h := BackwardPattern.dropSuffixOfNonempty? (decide <| p ·) s h
dropSuffix? s := BackwardPattern.dropSuffix? (decide <| p ·) s
skipSuffixOfNonempty? s h := BackwardPattern.skipSuffixOfNonempty? (decide <| p ·) s h
skipSuffix? s := BackwardPattern.skipSuffix? (decide <| p ·) s
endsWith s := BackwardPattern.endsWith (decide <| p ·) s
instance {p : Char Prop} [DecidablePred p] : StrictBackwardPattern p where
ne_endPos h q := StrictBackwardPattern.ne_endPos (pat := (decide <| p ·)) h q
instance {p : Char Prop} [DecidablePred p] : LawfulBackwardPattern p where
dropSuffixOfNonempty?_eq h := LawfulBackwardPattern.dropSuffixOfNonempty?_eq (pat := (decide <| p ·)) h
skipSuffixOfNonempty?_eq h := LawfulBackwardPattern.skipSuffixOfNonempty?_eq (pat := (decide <| p ·)) h
endsWith_eq s := LawfulBackwardPattern.endsWith_eq (pat := (decide <| p ·)) s
instance {p : Char Prop} [DecidablePred p] : ToBackwardSearcher p (ToBackwardSearcher.DefaultBackwardSearcher p) :=

View File

@@ -280,7 +280,7 @@ def startsWith (pat : Slice) (s : Slice) : Bool :=
false
@[inline]
def dropPrefix? (pat : Slice) (s : Slice) : Option s.Pos :=
def skipPrefix? (pat : Slice) (s : Slice) : Option s.Pos :=
if startsWith pat s then
some <| s.pos! <| pat.rawEndPos.offsetBy s.startPos.offset
else
@@ -288,14 +288,14 @@ def dropPrefix? (pat : Slice) (s : Slice) : Option s.Pos :=
instance {pat : Slice} : ForwardPattern pat where
startsWith := startsWith pat
dropPrefix? := dropPrefix? pat
skipPrefix? := skipPrefix? pat
instance {pat : String} : ToForwardSearcher pat ForwardSliceSearcher where
toSearcher := iter pat.toSlice
instance {pat : String} : ForwardPattern pat where
startsWith := startsWith pat.toSlice
dropPrefix? := dropPrefix? pat.toSlice
skipPrefix? := skipPrefix? pat.toSlice
end ForwardSliceSearcher
@@ -316,7 +316,7 @@ def endsWith (pat : Slice) (s : Slice) : Bool :=
false
@[inline]
def dropSuffix? (pat : Slice) (s : Slice) : Option s.Pos :=
def skipSuffix? (pat : Slice) (s : Slice) : Option s.Pos :=
if endsWith pat s then
some <| s.pos! <| s.endPos.offset.unoffsetBy pat.rawEndPos
else
@@ -324,11 +324,11 @@ def dropSuffix? (pat : Slice) (s : Slice) : Option s.Pos :=
instance {pat : Slice} : BackwardPattern pat where
endsWith := endsWith pat
dropSuffix? := dropSuffix? pat
skipSuffix? := skipSuffix? pat
instance {pat : String} : BackwardPattern pat where
endsWith := endsWith pat.toSlice
dropSuffix? := dropSuffix? pat.toSlice
skipSuffix? := skipSuffix? pat.toSlice
end BackwardSliceSearcher

View File

@@ -278,43 +278,14 @@ def splitInclusive (s : String) (pat : ρ) [ToForwardSearcher pat σ] :=
def foldlAux {α : Type u} (f : α Char α) (s : String) (stopPos : Pos.Raw) (i : Pos.Raw) (a : α) : α :=
s.slice! (s.pos! i) (s.pos! stopPos) |>.foldl f a
/--
Folds a function over a string from the start, accumulating a value starting with {name}`init`. The
accumulated value is combined with each character in order, using {name}`f`.
Examples:
* {lean}`"coffee tea water".foldl (fun n c => if c.isWhitespace then n + 1 else n) 0 = 2`
* {lean}`"coffee tea and water".foldl (fun n c => if c.isWhitespace then n + 1 else n) 0 = 3`
* {lean}`"coffee tea water".foldl (·.push ·) "" = "coffee tea water"`
-/
@[inline] def foldl {α : Type u} (f : α Char α) (init : α) (s : String) : α :=
s.toSlice.foldl f init
@[export lean_string_foldl]
def Internal.foldlImpl (f : String Char String) (init : String) (s : String) : String :=
String.foldl f init s
@[deprecated String.Slice.foldr (since := "2025-11-25")]
def foldrAux {α : Type u} (f : Char α α) (a : α) (s : String) (i begPos : Pos.Raw) : α :=
s.slice! (s.pos! begPos) (s.pos! i) |>.foldr f a
/--
Folds a function over a string from the right, accumulating a value starting with {lean}`init`. The
accumulated value is combined with each character in reverse order, using {lean}`f`.
Examples:
* {lean}`"coffee tea water".foldr (fun c n => if c.isWhitespace then n + 1 else n) 0 = 2`
* {lean}`"coffee tea and water".foldr (fun c n => if c.isWhitespace then n + 1 else n) 0 = 3`
* {lean}`"coffee tea water".foldr (fun c s => s.push c) "" = "retaw aet eeffoc"`
-/
@[inline] def foldr {α : Type u} (f : Char α α) (init : α) (s : String) : α :=
s.toSlice.foldr f init
@[deprecated String.Slice.any (since := "2025-11-25")]
def anyAux (s : String) (stopPos : Pos.Raw) (p : Char Bool) (i : Pos.Raw) : Bool :=
s.slice! (s.pos! i) (s.pos! stopPos) |>.any p
/--
Checks whether a string has a match of the pattern {name}`pat` anywhere.

View File

@@ -328,6 +328,26 @@ def splitInclusive (s : Slice) (pat : ρ) [ToForwardSearcher pat σ] :
Std.Iter (α := SplitInclusiveIterator pat s) Slice :=
{ internalState := .operating s.startPos (ToForwardSearcher.toSearcher pat s) }
/--
If {name}`pat` matches a prefix of {name}`s`, returns the position at the start of the remainder.
Returns {name}`none` otherwise.
This function is generic over all currently supported patterns.
-/
@[inline]
def skipPrefix? (s : Slice) (pat : ρ) [ForwardPattern pat] : Option s.Pos :=
ForwardPattern.skipPrefix? pat s
/--
If {name}`pat` matches a prefix at {name}`pos`, returns the position after the end of the match.
Returns {name}`none` otherwise.
This function is generic over all currently supported patterns.
-/
@[inline]
def Pos.skip? {s : Slice} (pos : s.Pos) (pat : ρ) [ForwardPattern pat] : Option s.Pos :=
((s.sliceFrom pos).skipPrefix? pat).map Pos.ofSliceFrom
/--
If {name}`pat` matches a prefix of {name}`s`, returns the remainder. Returns {name}`none` otherwise.
@@ -344,7 +364,7 @@ Examples:
-/
@[inline]
def dropPrefix? (s : Slice) (pat : ρ) [ForwardPattern pat] : Option Slice :=
(ForwardPattern.dropPrefix? pat s).map s.sliceFrom
(s.skipPrefix? pat).map s.sliceFrom
/--
If {name}`pat` matches a prefix of {name}`s`, returns the remainder. Returns {name}`s` unmodified
@@ -400,6 +420,28 @@ Examples:
def drop (s : Slice) (n : Nat) : Slice :=
s.sliceFrom (s.startPos.nextn n)
/--
Advances {name}`pos` as long as {name}`pat` matches.
-/
@[specialize pat]
def Pos.skipWhile {s : Slice} (pos : s.Pos) (pat : ρ) [ForwardPattern pat] : s.Pos :=
if let some nextCurr := ForwardPattern.skipPrefix? pat (s.sliceFrom pos) then
if pos < Pos.ofSliceFrom nextCurr then
skipWhile (Pos.ofSliceFrom nextCurr) pat
else
pos
else
pos
termination_by pos
/--
Returns the position after the longest prefix of {name}`s` for which {name}`pat` matches
(potentially repeatedly).
-/
@[inline]
def skipPrefixWhile (s : Slice) (pat : ρ) [ForwardPattern pat] : s.Pos :=
s.startPos.skipWhile pat
/--
Creates a new slice that contains the longest prefix of {name}`s` for which {name}`pat` matched
(potentially repeatedly).
@@ -412,18 +454,7 @@ Examples:
-/
@[inline]
def dropWhile (s : Slice) (pat : ρ) [ForwardPattern pat] : Slice :=
go s.startPos
where
@[specialize pat]
go (curr : s.Pos) : Slice :=
if let some nextCurr := ForwardPattern.dropPrefix? pat (s.sliceFrom curr) then
if curr < Pos.ofSliceFrom nextCurr then
go (Pos.ofSliceFrom nextCurr)
else
s.sliceFrom curr
else
s.sliceFrom curr
termination_by curr
s.sliceFrom (s.skipPrefixWhile pat)
/--
Removes leading whitespace from a slice by moving its start position to the first non-whitespace
@@ -472,18 +503,7 @@ Examples:
-/
@[inline]
def takeWhile (s : Slice) (pat : ρ) [ForwardPattern pat] : Slice :=
go s.startPos
where
@[specialize pat]
go (curr : s.Pos) : Slice :=
if let some nextCurr := ForwardPattern.dropPrefix? pat (s.sliceFrom curr) then
if curr < Pos.ofSliceFrom nextCurr then
go (Pos.ofSliceFrom nextCurr)
else
s.sliceTo curr
else
s.sliceTo curr
termination_by curr
s.sliceTo (s.skipPrefixWhile pat)
/--
Finds the position of the first match of the pattern {name}`pat` in a slice {name}`s`. If there
@@ -668,6 +688,26 @@ def revSplit (s : Slice) (pat : ρ) [ToBackwardSearcher pat σ] :
Std.Iter (α := RevSplitIterator pat s) Slice :=
{ internalState := .operating s.endPos (ToBackwardSearcher.toSearcher pat s) }
/--
If {name}`pat` matches a suffix of {name}`s`, returns the position at the beginning of the suffix.
Returns {name}`none` otherwise.
This function is generic over all currently supported patterns.
-/
@[inline]
def skipSuffix? (s : Slice) (pat : ρ) [BackwardPattern pat] : Option s.Pos :=
BackwardPattern.skipSuffix? pat s
/--
If {name}`pat` matches a suffix at {name}`pos`, returns the position at the beginning of the match.
Returns {name}`none` otherwise.
This function is generic over all currently supported patterns.
-/
@[inline]
def Pos.revSkip? {s : Slice} (pos : s.Pos) (pat : ρ) [ForwardPattern pat] : Option s.Pos :=
((s.sliceFrom pos).skipPrefix? pat).map Pos.ofSliceFrom
/--
If {name}`pat` matches a suffix of {name}`s`, returns the remainder. Returns {name}`none` otherwise.
@@ -684,7 +724,7 @@ Examples:
-/
@[inline]
def dropSuffix? (s : Slice) (pat : ρ) [BackwardPattern pat] : Option Slice :=
(BackwardPattern.dropSuffix? pat s).map s.sliceTo
(s.skipSuffix? pat).map s.sliceTo
/--
If {name}`pat` matches a suffix of {name}`s`, returns the remainder. Returns {name}`s` unmodified
@@ -719,6 +759,28 @@ Examples:
def dropEnd (s : Slice) (n : Nat) : Slice :=
s.sliceTo (s.endPos.prevn n)
/--
Rewinds {name}`pos` as long as {name}`pat` matches.
-/
@[specialize pat]
def Pos.revSkipWhile {s : Slice} (pos : s.Pos) (pat : ρ) [BackwardPattern pat] : s.Pos :=
if let some nextCurr := BackwardPattern.skipSuffix? pat (s.sliceTo pos) then
if Pos.ofSliceTo nextCurr < pos then
revSkipWhile (Pos.ofSliceTo nextCurr) pat
else
pos
else
pos
termination_by pos.down
/--
Returns the position a the start of the longest suffix of {name}`s` for which {name}`pat` matches
(potentially repeatedly).
-/
@[inline]
def skipSuffixWhile (s : Slice) (pat : ρ) [BackwardPattern pat] : s.Pos :=
s.endPos.revSkipWhile pat
/--
Creates a new slice that contains the longest suffix of {name}`s` for which {name}`pat` matched
(potentially repeatedly).
@@ -730,18 +792,7 @@ Examples:
-/
@[inline]
def dropEndWhile (s : Slice) (pat : ρ) [BackwardPattern pat] : Slice :=
go s.endPos
where
@[specialize pat]
go (curr : s.Pos) : Slice :=
if let some nextCurr := BackwardPattern.dropSuffix? pat (s.sliceTo curr) then
if Pos.ofSliceTo nextCurr < curr then
go (Pos.ofSliceTo nextCurr)
else
s.sliceTo curr
else
s.sliceTo curr
termination_by curr.down
s.sliceTo (s.skipSuffixWhile pat)
/--
Removes trailing whitespace from a slice by moving its end position to the last non-whitespace
@@ -789,18 +840,7 @@ Examples:
-/
@[inline]
def takeEndWhile (s : Slice) (pat : ρ) [BackwardPattern pat] : Slice :=
go s.endPos
where
@[specialize pat]
go (curr : s.Pos) : Slice :=
if let some nextCurr := BackwardPattern.dropSuffix? pat (s.sliceTo curr) then
if Pos.ofSliceTo nextCurr < curr then
go (Pos.ofSliceTo nextCurr)
else
s.sliceFrom curr
else
s.sliceFrom curr
termination_by curr.down
s.sliceFrom (s.skipSuffixWhile pat)
/--
Finds the position of the first match of the pattern {name}`pat` in a slice, starting
@@ -906,21 +946,20 @@ Examples:
* {lean}`"12__34".toSlice.isNat = false`
-/
@[inline]
def isNat (s : Slice) : Bool :=
if s.isEmpty then
false
else
-- Track: isFirst, lastWasUnderscore, lastCharWasDigit, valid
let result := s.foldl (fun (isFirst, lastWasUnderscore, _lastCharWasDigit, valid) c =>
let isDigit := c.isDigit
let isUnderscore := c = '_'
let newValid := valid && (isDigit || isUnderscore) &&
!(isFirst && isUnderscore) && -- Cannot start with underscore
!(lastWasUnderscore && isUnderscore) -- No consecutive underscores
(false, isUnderscore, isDigit, newValid))
(true, false, false, true)
-- Must be valid and last character must have been a digit (not underscore)
result.2.2.2 && result.2.2.1
def isNat (s : Slice) : Bool := Id.run do
let mut lastWasDigit := false
for c in s do
if c = '_' then
if !lastWasDigit then
return false
lastWasDigit := false
else if c.isDigit then
lastWasDigit := true
else
return false
return lastWasDigit
/--
Interprets a slice as the decimal representation of a natural number, returning it. Returns

View File

@@ -208,6 +208,39 @@ def dropRightWhile (s : String) (p : Char → Bool) : String :=
def Slice.dropRightWhile (s : Slice) (p : Char Bool) : Slice :=
s.dropEndWhile p
/--
If {name}`pat` matches a prefix of {name}`s`, returns the position at the start of the remainder.
Returns {name}`none` otherwise.
This function is generic over all currently supported patterns.
-/
@[inline] def skipPrefix? (s : String) (pat : ρ) [ForwardPattern pat] : Option s.Pos :=
(s.toSlice.skipPrefix? pat).map Pos.ofToSlice
/--
Returns the position after the longest prefix of {name}`s` for which {name}`pat` matches
(potentially repeatedly).
-/
@[inline] def skipPrefixWhile (s : String) (pat : ρ) [ForwardPattern pat] : s.Pos :=
Pos.ofToSlice (s.toSlice.skipPrefixWhile pat)
/--
If {name}`pat` matches at {name}`pos`, returns the position after the end of the match.
Returns {name}`none` otherwise.
This function is generic over all currently supported patterns.
-/
@[inline]
def Pos.skip? {s : String} (pos : s.Pos) (pat : ρ) [ForwardPattern pat] : Option s.Pos :=
(pos.toSlice.skip? pat).map Pos.ofToSlice
/--
Advances {name}`pos` as long as {name}`pat` matches.
-/
@[inline]
def Pos.skipWhile {s : String} (pos : s.Pos) (pat : ρ) [ForwardPattern pat] : s.Pos :=
Pos.ofToSlice (pos.toSlice.skipWhile pat)
/--
Checks whether the first string ({name}`s`) begins with the pattern ({name}`pat`).
@@ -258,6 +291,39 @@ Examples:
@[inline] def endsWith (s : String) (pat : ρ) [BackwardPattern pat] : Bool :=
s.toSlice.endsWith pat
/--
If {name}`pat` matches a suffix of {name}`s`, returns the position at the beginning of the suffix.
Returns {name}`none` otherwise.
This function is generic over all currently supported patterns.
-/
@[inline] def skipSuffix? (s : String) (pat : ρ) [BackwardPattern pat] : Option s.Pos :=
(s.toSlice.skipSuffix? pat).map Pos.ofToSlice
/--
Returns the position at the start of the longest suffix of {name}`s` for which {name}`pat` matches
(potentially repeatedly).
-/
@[inline] def skipSuffixWhile (s : String) (pat : ρ) [BackwardPattern pat] : s.Pos :=
Pos.ofToSlice (s.toSlice.skipSuffixWhile pat)
/--
If {name}`pat` matches at {name}`pos`, returns the position after the end of the match.
Returns {name}`none` otherwise.
This function is generic over all currently supported patterns.
-/
@[inline]
def Pos.revSkip? {s : String} (pos : s.Pos) (pat : ρ) [ForwardPattern pat] : Option s.Pos :=
(pos.toSlice.revSkip? pat).map Pos.ofToSlice
/--
Rewinds {name}`pos` as long as {name}`pat` matches.
-/
@[inline]
def Pos.revSkipWhile {s : String} (pos : s.Pos) (pat : ρ) [BackwardPattern pat] : s.Pos :=
Pos.ofToSlice (pos.toSlice.revSkipWhile pat)
/--
Removes trailing whitespace from a string by returning a slice whose end position is the last
non-whitespace character, or the start position if there is no non-whitespace character.

View File

@@ -100,7 +100,7 @@ Unsafe implementation of `attachWith`, taking advantage of the fact that the rep
@[simp] theorem pmap_push {P : α Prop} {f : a, P a β} {a : α} {xs : Vector α n} {h : b xs.push a, P b} :
pmap f (xs.push a) h =
(pmap f xs (fun a m => by simp at h; exact h a (.inl m))).push (f a (h a (by simp))) := by
(pmap f xs (fun a m => by simp [forall_or_eq_imp] at h; exact h.1 _ m)).push (f a (h a (by simp))) := by
simp [pmap]
@[simp] theorem attach_empty : (#v[] : Vector α 0).attach = #v[] := rfl
@@ -147,7 +147,7 @@ theorem attachWith_congr {xs ys : Vector α n} (w : xs = ys) {P : α → Prop} {
@[simp] theorem attachWith_push {a : α} {xs : Vector α n} {P : α Prop} {H : x xs.push a, P x} :
(xs.push a).attachWith P H =
(xs.attachWith P (fun x h => by simp at H; exact H x (.inl h))).push a, H a (by simp) := by
(xs.attachWith P (fun x h => by simp [forall_or_eq_imp] at H; exact H.1 _ h)).push a, H a (by simp) := by
rcases xs with xs, rfl
simp

View File

@@ -303,12 +303,12 @@ theorem find?_eq_some_iff_getElem {xs : Vector α n} {p : α → Bool} {b : α}
/-! ### findFinIdx? -/
@[grind =]
theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p (#v[] : Vector α 0) = none := by simp; rfl
theorem findFinIdx?_empty {p : α Bool} : findFinIdx? p (#v[] : Vector α 0) = none := by simp
@[grind =]
theorem findFinIdx?_singleton {a : α} {p : α Bool} :
#[a].findFinIdx? p = if p a then some 0, by simp else none := by
simp; rfl
simp
@[congr] theorem findFinIdx?_congr {p : α Bool} {xs : Vector α n} {ys : Vector α n} (w : xs = ys) :
findFinIdx? p xs = findFinIdx? p ys := by

View File

@@ -53,7 +53,7 @@ theorem mapM_pure [Monad m] [LawfulMonad m] {xs : Vector α n} (f : α → β) :
(mk #[] rfl).mapM f = pure #v[] := by
unfold mapM
unfold mapM.go
simp; rfl
simp
@[simp, grind =] theorem mapM_append [Monad m] [LawfulMonad m]
{f : α m β} {xs : Vector α n} {ys : Vector α n'} :

View File

@@ -262,5 +262,47 @@ Adds new case-splits using model-based theory combination.
-/
syntax (name := mbtc) "mbtc" : grind
/-- `intro x₁ ... xₙ` introduces binders and internalizes them into the E-graph.
Only available in `sym =>` mode.
`intro` with no arguments introduces one binder with an inaccessible name.
Use `intro (internalize := false)` or `intro~` to skip internalization. -/
syntax (name := symIntro) "intro" (ppSpace "(" &"internalize" " := " (&"true" <|> &"false") ")")? (ppSpace colGt binderIdent)* : grind
/-- `intro~ x₁ ... xₙ` is shorthand for `intro (internalize := false)`. -/
syntax (name := symIntroLight) "intro" noWs "~" (ppSpace colGt binderIdent)* : grind
macro_rules
| `(grind| intro~ $ids*) => `(grind| intro (internalize := false) $ids*)
/-- `intros` introduces all remaining binders and internalizes them.
Only available in `sym =>` mode.
Use `intros (internalize := false)` or `intros~` to skip internalization. -/
syntax (name := symIntros) "intros" (ppSpace "(" &"internalize" " := " (&"true" <|> &"false") ")")? : grind
/-- `intros~` is shorthand for `intros (internalize := false)`. -/
syntax (name := symIntrosLight) "intros" noWs "~" : grind
macro_rules
| `(grind| intros~) => `(grind| intros (internalize := false))
/-- `apply t` applies theorem `t` as a backward rule.
Only available in `sym =>` mode.
When used with `repeat`, the backward rule is cached for efficiency. -/
syntax (name := symApply) "apply " term : grind
/-- `internalize` internalizes hypotheses into the grind E-graph.
Only available in `sym =>` mode.
- `internalize` internalizes the next hypothesis.
- `internalize <num>` internalizes the next `<num>` hypotheses. -/
syntax (name := symInternalize) "internalize" (ppSpace num)? : grind
/-- `internalize_all` internalizes all pending hypotheses into the grind E-graph.
Only available in `sym =>` mode. -/
syntax (name := symInternalizeAll) "internalize_all" : grind
/-- `by_contra` applies proof by contradiction, negating the target and making it `False`.
Only available in `sym =>` mode. -/
syntax (name := symByContra) "by_contra" : grind
end Grind
end Lean.Parser.Tactic

View File

@@ -295,6 +295,24 @@ syntax (name := grindTrace)
(" [" withoutPosition(grindParam,*) "]")?
: tactic
/--
`sym` enters an interactive symbolic simulation mode built on `grind`.
Unlike `grind =>`, it does not eagerly introduce hypotheses or apply by-contradiction,
giving the user explicit control over `intro`, `apply`, and `internalize` steps.
Example:
```
example (x : Nat) : myP x → myQ x := by
sym [myP_myQ] =>
intro h
finish
```
-/
syntax (name := sym)
"sym" optConfig (&" only")?
(" [" withoutPosition(grindParam,*) "]")?
" => " grindSeq : tactic
/--
`cutsat` solves linear integer arithmetic goals.

View File

@@ -722,7 +722,7 @@ syntax (name := runElab) "run_elab " doSeq : command
/--
The `run_meta doSeq` command executes code in `MetaM Unit`.
This is the same as `#eval show MetaM Unit from do discard doSeq`.
This is the same as `#eval show MetaM Unit from discard do doSeq`.
(This is effectively a synonym for `run_elab` since `MetaM` lifts to `TermElabM`.)
-/

View File

@@ -3004,7 +3004,7 @@ Examples:
* `([] : List String).length = 0`
* `["green", "brown"].length = 2`
-/
def List.length : List α Nat
@[implicit_reducible] def List.length : List α Nat
| nil => 0
| cons _ as => HAdd.hAdd (length as) 1
@@ -4674,7 +4674,7 @@ inductive Name where
/-- The "anonymous" name. -/
| anonymous : Name
/--
A string name. The name `Lean.Meta.run` is represented at
A string name. The name `Lean.Meta.run` is represented as
```lean
.str (.str (.str .anonymous "Lean") "Meta") "run"
```

View File

@@ -72,6 +72,12 @@ theorem and_and_right : (a ∧ b) ∧ c ↔ (a ∧ c) ∧ b ∧ c := by rw [and_
theorem and_iff_left (hb : b) : a b a := Iff.intro And.left (And.intro · hb)
theorem and_iff_right (ha : a) : a b b := Iff.intro And.right (And.intro ha ·)
@[simp] theorem and_imp_left_iff_true {P Q : Prop} : (P Q P) True := by
simpa using And.left
@[simp] theorem and_imp_right_iff_true {P Q : Prop} : (P Q Q) True := by
simp
/-! ## or -/
theorem or_self_iff : a a a := or_self _ .rfl
@@ -94,6 +100,12 @@ theorem or_rotate : a b c ↔ b c a := by simp only [or_left_com
theorem or_iff_left (hb : ¬b) : a b a := or_iff_left_iff_imp.mpr hb.elim
theorem or_iff_right (ha : ¬a) : a b b := or_iff_right_iff_imp.mpr ha.elim
theorem imp_or_left_iff_true {P Q : Prop} : (P P Q) True := by
simpa using Or.inl
theorem imp_or_right_iff_true {P Q : Prop} : (Q P Q) True := by
simpa using Or.inr
/-! ## distributivity -/
theorem not_imp_of_and_not : a ¬b ¬(a b)
@@ -344,12 +356,34 @@ theorem not_forall_of_exists_not {p : α → Prop} : (∃ x, ¬p x) → ¬∀ x,
@[simp] theorem exists_prop_eq' {p : (a : α) a' = a Prop} :
( (a : α) (h : a' = a), p a h) p a' rfl := by simp [@eq_comm _ a']
@[simp] theorem forall_eq_or_imp : ( a, a = a' q a p a) p a' a, q a p a := by
@[simp] theorem forall_eq_or_imp {P Q : α Prop} :
( a, a = a' Q a P a) P a' a, Q a P a := by
simp only [or_imp, forall_and, forall_eq]
theorem forall_eq_or_imp' {P Q : α Prop} {a' : α} :
( (a : α), a' = a Q a P a) P a' (a : α), Q a P a := by
simp only [or_imp, forall_and, forall_eq']
theorem forall_or_eq_imp {P Q : α Prop} :
( a, Q a a = a' P a) ( a, Q a P a) P a' := by
simp only [or_imp, forall_and, forall_eq]
theorem forall_or_eq_imp' {P Q : α Prop} :
( a, Q a a' = a P a) ( a, Q a P a) P a' := by
simp only [or_imp, forall_and, forall_eq']
@[simp] theorem exists_eq_or_imp : ( a, (a = a' q a) p a) p a' a, q a p a := by
simp only [or_and_right, exists_or, exists_eq_left]
@[simp] theorem exists_eq_or_imp' : ( a, (a' = a q a) p a) p a' a, q a p a := by
simp only [or_and_right, exists_or, exists_eq_left']
@[simp] theorem exists_or_eq_imp : ( a, (q a a = a') p a) ( a, q a p a) p a' := by
simp only [or_and_right, exists_or, exists_eq_left]
@[simp] theorem exists_or_eq_imp' : ( a, (q a a' = a) p a) ( a, q a p a) p a' := by
simp only [or_and_right, exists_or, exists_eq_left']
@[simp] theorem exists_eq_right_right : ( (a : α), p a q a a = a') p a' q a' := by
simp [ and_assoc]
@@ -404,6 +438,22 @@ theorem forall_prop_of_false {p : Prop} {q : p → Prop} (hn : ¬p) : (∀ h' :
@[simp] theorem forall_self_imp (P : Prop) (Q : P Prop) : ( p : P, P Q p) p, Q p :=
fun h p => h p p, fun h _ p => h p
theorem forall_or_imp_or_self_right_right {P Q R : α Prop} :
( a, P a Q a R a Q a) ( a, P a R a Q a) := by
simp only [or_imp, imp_or_right_iff_true, and_true]
theorem forall_or_imp_or_self_right_left {P Q R : α Prop} :
( a, P a Q a Q a R a) ( a, P a Q a R a) := by
simp only [or_imp, imp_or_left_iff_true, and_true]
theorem forall_or_imp_or_self_left_right {P Q R : α Prop} :
( a, Q a P a R a Q a) ( a, P a R a Q a) := by
simp only [or_imp, imp_or_right_iff_true, true_and]
theorem forall_or_imp_or_self_left_left {P Q R : α Prop} :
( a, Q a P a Q a R a) ( a, P a Q a R a) := by
simp only [or_imp, imp_or_left_iff_true, true_and]
end quantifiers
/-! ## membership -/

View File

@@ -2353,9 +2353,6 @@ after reduction to close the goal.
The proofs produced by `cbv` only use the three standard axioms.
In particular, they do not require trust in the correctness of the code
generator.
This tactic is experimental and its behavior is likely to change in upcoming
releases of Lean.
-/
syntax (name := cbv) "cbv" (location)? : tactic
@@ -2373,9 +2370,6 @@ fails.
The proofs produced by `decide_cbv` only use the three standard axioms.
In particular, they do not require trust in the correctness of the code
generator.
This tactic is experimental and its behavior is likely to change in upcoming
releases of Lean.
-/
syntax (name := decide_cbv) "decide_cbv" : tactic

View File

@@ -56,6 +56,7 @@ private def syntaxToExternAttrData (stx : Syntax) : AttrM ExternAttrData := do
return { entries := entries.toList }
-- Forward declaration
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_add_extern"]
opaque addExtern (declName : Name) (externAttrData : ExternAttrData) : CoreM Unit

View File

@@ -81,7 +81,10 @@ where
ps.map fun p => { p with borrow := p.type.isPossibleRef }
initParamsIfNotExported (exported : Bool) (ps : Array (Param .impure)) : Array (Param .impure) :=
if exported then ps else initParams ps
if exported then
ps.map ({ · with borrow := false })
else
initParams ps
goCode (declName : Name) (code : Code .impure) : InitM Unit := do
match code with

View File

@@ -333,11 +333,7 @@ public def demangleBtLine (line : String) : Option String := do
return pfx ++ demangled ++ sfx
@[export lean_demangle_bt_line_cstr]
def demangleBtLineCStr (line : @& String) : String :=
def demangleBtLineCStr (line : String) : String :=
(demangleBtLine line).getD ""
@[export lean_demangle_symbol_cstr]
def demangleSymbolCStr (symbol : @& String) : String :=
(demangleSymbol symbol).getD ""
end Lean.Name.Demangle

View File

@@ -235,8 +235,8 @@ def idbgCompileAndEval (α : Type) [Nonempty α]
| .error msg => throw (.userError s!"idbg evalConst failed: {msg}")
/-- Connect to the debug server, receive expressions, evaluate, send results. Loops forever. -/
@[nospecialize, export lean_idbg_client_loop] def idbgClientLoopImpl {α : Type} [Nonempty α]
(siteId : String) (imports : Array Import) (apply : α String) : IO Unit := do
@[nospecialize, export lean_idbg_client_loop] def idbgClientLoopImpl
(siteId : String) (imports : Array Import) (apply : NonScalar String) : IO Unit := do
let baseEnv idbgLoadEnv imports
let port := idbgPort siteId
let addr := SocketAddressV4.mk (.ofParts 127 0 0 1) port
@@ -249,7 +249,7 @@ def idbgCompileAndEval (α : Type) [Nonempty α]
let json IO.ofExcept (Json.parse msg)
let type IO.ofExcept (exprFromJson? ( IO.ofExcept (json.getObjVal? "type")))
let value IO.ofExcept (exprFromJson? ( IO.ofExcept (json.getObjVal? "value")))
let fnVal idbgCompileAndEval α baseEnv type value
let fnVal idbgCompileAndEval NonScalar baseEnv type value
let result := apply fnVal
sendMsg client result
let t client.shutdown |>.toIO

View File

@@ -50,7 +50,7 @@ private def mkHeader (kind : String) (id : Name) (levelParams : List Name) (type
let id' match privateToUserName? id with
| some id' =>
m := m ++ "private "
pure id'
if getPPPrivateNames ( getOptions) then pure id else pure id'
| none =>
pure id

View File

@@ -18,7 +18,7 @@ open Lean.Meta.Tactic.Cbv
@[builtin_tactic Lean.Parser.Tactic.cbv] def evalCbv : Tactic := fun stx => withMainContext do
if cbv.warning.get ( getOptions) then
logWarningAt stx "The `cbv` tactic is experimental and still under development. Avoid using it in production projects"
logWarningAt stx "The `cbv` usage warning option is enabled. Disable it by setting `set_option cbv.warning false`."
let (fvarIds, simplifyTarget) match expandOptLocation stx[1] with
| Location.targets hyps simplifyTarget => pure ( getFVarIds hyps, simplifyTarget)
| Location.wildcard => pure ( ( getMainGoal).getNondepPropHyps, true)
@@ -31,7 +31,7 @@ open Lean.Meta.Tactic.Cbv
match stx with
| `(tactic| decide_cbv) => withMainContext do
if cbv.warning.get ( getOptions) then
logWarningAt stx "The `decide_cbv` tactic is experimental and still under development. Avoid using it in production projects"
logWarningAt stx "The `decide_cbv` usage warning option is enabled. Disable it by setting `set_option cbv.warning false`."
liftMetaFinishingTactic fun mvar => do
let [mvar'] mvar.applyConst ``of_decide_eq_true | throwError "Could not apply `of_decide_eq_true`"
cbvDecideGoal mvar'

View File

@@ -18,7 +18,7 @@ open Lean.Meta.Tactic.Cbv
@[builtin_tactic Lean.Parser.Tactic.Conv.cbv] public def evalCbv : Tactic := fun stx => withMainContext do
if cbv.warning.get ( getOptions) then
logWarningAt stx "The `cbv` tactic is experimental and still under development. Avoid using it in production projects"
logWarningAt stx "The `cbv` usage warning option is enabled. Disable it by setting `set_option cbv.warning false`."
let lhs getLhs
let evalResult cbvEntry lhs
match evalResult with

View File

@@ -15,3 +15,4 @@ public import Lean.Elab.Tactic.Grind.Config
public import Lean.Elab.Tactic.Grind.Lint
public import Lean.Elab.Tactic.Grind.LintExceptions
public import Lean.Elab.Tactic.Grind.Annotated
public import Lean.Elab.Tactic.Grind.Sym

View File

@@ -8,6 +8,8 @@ prelude
public import Lean.Elab.Tactic.Basic
public import Lean.Meta.Tactic.Grind.Main
import Lean.Meta.Tactic.Grind.Intro
public import Lean.Meta.Sym.Apply
public import Lean.Meta.Sym.Util
import Init.Omega
public section
namespace Lean.Elab.Tactic.Grind
@@ -18,13 +20,21 @@ structure Context extends Tactic.Context where
sctx : Meta.Sym.Context
methods : Grind.Methods
params : Grind.Params
sym : Bool := false
open Meta.Grind (Goal)
structure Cache where
/-- Cache for `BackwardRule`s created from declaration names (sym mode only). -/
backwardRuleName : PHashMap Name Sym.BackwardRule := {}
/-- Cache for `BackwardRule`s created from elaborated terms, keyed by syntax byte position range (sym mode only). -/
backwardRuleSyntax : PHashMap (Nat × Nat) Sym.BackwardRule := {}
structure State where
symState : Meta.Sym.State
grindState : Meta.Grind.State
goals : List Goal
cache : Cache := {}
structure SavedState where
term : Term.SavedState
@@ -377,7 +387,7 @@ def mkEvalTactic' (elaborator : Name) (params : Params) : TermElabM (Goal → TS
def mkEvalTactic (params : Params) : TacticM (Goal TSyntax `grind GrindM (List Goal)) := do
mkEvalTactic' ( read).elaborator params
def GrindTacticM.runAtGoal (mvarId : MVarId) (params : Params) (k : GrindTacticM α) : TacticM (α × State) := do
def GrindTacticM.runAtGoal (mvarId : MVarId) (params : Params) (k : GrindTacticM α) (sym : Bool := false) : TacticM (α × State) := do
let evalTactic mkEvalTactic params
/-
**Note**: We don't want to close branches using `sorry` after applying `intros + assertAll`.
@@ -385,10 +395,17 @@ def GrindTacticM.runAtGoal (mvarId : MVarId) (params : Params) (k : GrindTacticM
-/
let params' := { params with config.useSorry := false }
let (methods, ctx, sctx, state) liftMetaM <| GrindM.runAtGoal mvarId params' (evalTactic? := some evalTactic) fun goal => do
let a : Action := Action.intros 0 >> Action.assertAll
let goals match ( a.run goal) with
| .closed _ => pure []
| .stuck gs => pure gs
let goals
if sym then
/- In sym mode, skip eager intros + by-contradiction. The user controls intro/internalize.
Preprocess for maximal term sharing, required by Sym operations (introN, BackwardRule.apply, etc.). -/
let mvarId Sym.preprocessMVar goal.mvarId
pure [{ goal with mvarId }]
else
let a : Action := Action.intros 0 >> Action.assertAll
match ( a.run goal) with
| .closed _ => pure []
| .stuck gs => pure gs
let methods getMethods
let ctx readThe Meta.Grind.Context
/- Restore original config -/
@@ -398,6 +415,6 @@ def GrindTacticM.runAtGoal (mvarId : MVarId) (params : Params) (k : GrindTacticM
let symState getThe Sym.State
return (methods, ctx, sctx, { grindState, symState, goals })
let tctx read
k { tctx with methods, ctx, sctx, params } |>.run state
k { tctx with methods, ctx, sctx, params, sym } |>.run state
end Lean.Elab.Tactic.Grind

View File

@@ -102,6 +102,12 @@ If the goal is not inconsistent and progress has been made,
-/
def evalCheck (tacticName : Name) (k : GoalM Bool)
(pp? : Goal MetaM (Option MessageData)) : GrindTacticM Unit := do
/- In sym mode, introduce remaining binders + by-contradiction + internalize
so that satellite solvers (lia, ring, linarith) see all hypotheses.
This matches the behavior of these tactics in default tactic mode
where `lia` can close `x > 1 → x + y + z > 0` directly. -/
if ( read).sym then
liftAction <| Action.intros 0 >> Action.assertAll
let recover := ( read).recover
liftGoalM do
let progress k

View File

@@ -242,15 +242,21 @@ def mkGrindParams
params := { params with config.clean := false }
return params
def checkTerminalAsSorry (mvarId : MVarId) : TacticM Bool := do
if debug.terminalTacticsAsSorry.get ( getOptions) then
mvarId.admit
replaceMainGoal []
return true
else
return false
def grind
(mvarId : MVarId) (config : Grind.Config)
(only : Bool)
(ps : TSyntaxArray ``Parser.Tactic.grindParam)
(seq? : Option (TSyntax `Lean.Parser.Tactic.Grind.grindSeq))
: TacticM Unit := do
if debug.terminalTacticsAsSorry.get ( getOptions) then
mvarId.admit
return ()
if ( checkTerminalAsSorry mvarId) then return ()
mvarId.withContext do
let params mkGrindParams config only ps mvarId
let params := if Grind.grind.unusedLemmaThreshold.get ( getOptions) > 0 then
@@ -260,7 +266,7 @@ def grind
let finalize (result : Grind.Result) : TacticM Unit := do
if result.hasFailed then
throwError "`grind` failed\n{← result.toMessageData}"
return ()
replaceMainGoal []
if let some seq := seq? then
let (result, _) Grind.GrindTacticM.runAtGoal mvarId' params do
Grind.evalGrindTactic seq
@@ -286,9 +292,7 @@ def evalGrindCore
let params := if let some params := params? then params.getElems else #[]
if Grind.grind.warning.get ( getOptions) then
logWarningAt ref "The `grind` tactic is new and its behavior may change in the future. This project has used `set_option grind.warning true` to discourage its use."
withMainContext do
grind ( getMainGoal) config only params seq?
replaceMainGoal []
grind ( getMainGoal) config only params seq?
/-- Position for the `[..]` child syntax in the `grind` tactic. -/
def grindParamsPos := 3
@@ -343,6 +347,25 @@ private def elabGrindConfig' (config : TSyntax ``Lean.Parser.Tactic.optConfig) (
let config elabGrindConfig' config interactive
evalGrindCore stx config only params seq
@[builtin_tactic Lean.Parser.Tactic.sym] def evalSym : Tactic := fun stx => do
recordExtraModUse (isMeta := false) `Init.Grind.Tactics
let `(tactic| sym $config:optConfig $[only%$only]? $[ [$params:grindParam,*] ]? => $seq:grindSeq) := stx
| throwUnsupportedSyntax
let config elabGrindConfig' config true
let only' := only.isSome
let params := if let some params := params then params.getElems else #[]
let mvarId getMainGoal
if ( checkTerminalAsSorry mvarId) then return ()
mvarId.withContext do
let params mkGrindParams config only' params mvarId
Grind.withProtectedMCtx config mvarId fun mvarId' => do
let (result, _) Grind.GrindTacticM.runAtGoal mvarId' params (sym := true) do
Grind.evalGrindTactic seq
let goal? := if let goal :: _ := ( get).goals then some goal else none
Grind.liftGrindM <| Grind.mkResult params goal?
if result.hasFailed then
throwError "`sym` failed\n{← result.toMessageData}"
def evalGrindTraceCore (stx : Syntax) (trace := true) (verbose := true) (useSorry := true) : TacticM (Array (TSyntax `tactic)) := withMainContext do
let `(tactic| grind? $configStx:optConfig $[only%$only]? $[ [$params?:grindParam,*] ]?) := stx
| throwUnsupportedSyntax

View File

@@ -0,0 +1,138 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
import Lean.Elab.Tactic.Grind.Basic
import Lean.Meta.Sym.Grind
import Lean.Meta.Tactic.Apply
import Lean.Elab.SyntheticMVars
namespace Lean.Elab.Tactic.Grind
open Meta Grind
private def ensureSym : GrindTacticM Unit := do
unless ( read).sym do
throwError "tactic is only available in `sym =>` mode"
/-- Lift a `SymM` computation into `GrindTacticM`. -/
private def liftSymM (k : Sym.SymM α) : GrindTacticM α := do
-- GrindM := ... Sym.SymM, so SymM auto-lifts to GrindM
liftGrindM k
private def evalIntroCore (internalize : Bool) (ids : TSyntaxArray `Lean.binderIdent) : GrindTacticM Unit := do
ensureSym
let goal getMainGoal
let goal
if ids.isEmpty then
match ( liftSymM <| Grind.Goal.introN goal 1) with
| .goal _ goal => pure goal
| .failed => throwError "`intro` failed, no binders to introduce"
else
let names ids.mapM fun id => match id with
| `(binderIdent| $name:ident) => pure name.getId
| `(binderIdent| $_) => mkFreshBinderNameForTactic `h
match ( liftSymM <| Grind.Goal.intros goal names) with
| .goal _ goal => pure goal
| .failed => throwError "`intro` failed"
let goal if internalize then liftGrindM <| Grind.Goal.internalizeAll goal else pure goal
replaceMainGoal [goal]
@[builtin_grind_tactic Parser.Tactic.Grind.symIntro] def evalSymIntro : GrindTactic := fun stx => do
-- syntax: "intro" ("(" &"internalize" " := " (&"true" <|> &"false") ")")? binderIdent*
match stx with
| `(grind| intro $ids:binderIdent*) => evalIntroCore true ids
| `(grind| intro (internalize := false) $ids:binderIdent*) => evalIntroCore false ids
| `(grind| intro (internalize := true) $ids:binderIdent*) => evalIntroCore true ids
| _ => throwUnsupportedSyntax
private def evalIntrosCore (internalize : Bool) : GrindTacticM Unit := do
ensureSym
let goal getMainGoal
match ( liftSymM <| Grind.Goal.intros goal #[]) with
| .goal _ goal =>
let goal if internalize then liftGrindM <| Grind.Goal.internalizeAll goal else pure goal
replaceMainGoal [goal]
| .failed => throwError "`intros` failed"
@[builtin_grind_tactic Parser.Tactic.Grind.symIntros] def evalSymIntros : GrindTactic := fun stx => do
match stx with
| `(grind| intros) => evalIntrosCore true
| `(grind| intros (internalize := false)) => evalIntrosCore false
| `(grind| intros (internalize := true)) => evalIntrosCore true
| _ => throwUnsupportedSyntax
/-- Get or create a `BackwardRule` for a declaration, using the name cache. -/
private def getOrCreateBackwardRule (declName : Name) : GrindTacticM Sym.BackwardRule := do
if let some rule := ( get).cache.backwardRuleName.find? declName then
return rule
let rule Sym.mkBackwardRuleFromDecl declName
modify fun s => { s with cache.backwardRuleName := s.cache.backwardRuleName.insert declName rule }
return rule
/-- Get or create a `BackwardRule` for a term, using the syntax position cache. -/
private def getOrCreateBackwardRuleFromTerm (term : Syntax) : GrindTacticM Sym.BackwardRule := do
let startPos := term.getPos?.map (·.byteIdx) |>.getD 0
let endPos := term.getTailPos?.map (·.byteIdx) |>.getD 0
let pos := (startPos, endPos)
if let some rule := ( get).cache.backwardRuleSyntax.find? pos then
return rule
let e withMainContext do
let e Term.elabTerm term none
Term.synthesizeSyntheticMVars (postpone := .no)
instantiateMVars e
let rule Sym.mkBackwardRuleFromExpr e
modify fun s => { s with cache.backwardRuleSyntax := s.cache.backwardRuleSyntax.insert pos rule }
return rule
@[builtin_grind_tactic Parser.Tactic.Grind.symApply] def evalSymApply : GrindTactic := fun stx => do
ensureSym
let `(grind| apply $term:term) := stx | throwUnsupportedSyntax
let goal getMainGoal
goal.withContext do
-- Try to interpret as a declaration name for efficient caching
let rule match term with
| `($id:ident) =>
try
let declName realizeGlobalConstNoOverload id
getOrCreateBackwardRule declName
catch _ =>
getOrCreateBackwardRuleFromTerm term
| _ =>
getOrCreateBackwardRuleFromTerm term
match ( liftSymM <| Grind.Goal.apply goal rule) with
| .goals subgoals => replaceMainGoal subgoals
| .failed => throwError "`apply` failed, rule is not applicable"
@[builtin_grind_tactic Parser.Tactic.Grind.symInternalize] def evalSymInternalize : GrindTactic := fun stx => do
ensureSym
let goal getMainGoal
let num := if stx[1].isNone then 1 else stx[1][0].toNat
let goal liftGrindM <| Grind.Goal.internalize goal num
replaceMainGoal [goal]
@[builtin_grind_tactic Parser.Tactic.Grind.symInternalizeAll] def evalSymInternalizeAll : GrindTactic := fun _ => do
ensureSym
let goal getMainGoal
let goal liftGrindM <| Grind.Goal.internalizeAll goal
replaceMainGoal [goal]
@[builtin_grind_tactic Parser.Tactic.Grind.symByContra] def evalSymByContra : GrindTactic := fun _ => do
ensureSym
let goal getMainGoal
let target goal.mvarId.getType
if target.isFalse then
throwError "`by_contra` failed, target is already `False`"
-- If target is not a proposition, apply exfalso first
let mvarId if ( isProp target) then pure goal.mvarId else goal.mvarId.exfalso
let some mvarId mvarId.byContra?
| throwError "`by_contra` failed"
-- byContra? produces `⊢ ¬target → False`, introduce the negated hypothesis
let (_, mvarId) mvarId.intro1
let goal := { goal with mvarId }
-- Internalize the negated hypothesis so the E-graph can detect contradictions
let goal liftGrindM <| Grind.Goal.internalizeAll goal
replaceMainGoal [goal]
end Lean.Elab.Tactic.Grind

View File

@@ -999,9 +999,13 @@ def evalInduction : Tactic := fun stx =>
match expandInduction? stx with
| some stxNew => withMacroExpansion stx stxNew <| evalTactic stxNew
| _ => focus do
let (targets, toTag) elabElimTargets stx[1].getSepArgs
let elimInfo withMainContext <| getElimNameInfo stx[2] targets (induction := true)
let targets withMainContext <| addImplicitTargets elimInfo targets
-- Disable tactic incrementality during setup to prevent nested `by` blocks (e.g. in `using`)
-- from consuming the snapshot meant for `evalAlts`.
let (targets, toTag, elimInfo) Term.withoutTacticIncrementality true do
let (targets, toTag) elabElimTargets stx[1].getSepArgs
let elimInfo withMainContext <| getElimNameInfo stx[2] targets (induction := true)
let targets withMainContext <| addImplicitTargets elimInfo targets
return (targets, toTag, elimInfo)
evalInductionCore stx elimInfo targets toTag
@@ -1081,8 +1085,10 @@ def evalFunInduction : Tactic := fun stx =>
match expandInduction? stx with
| some stxNew => withMacroExpansion stx stxNew <| evalTactic stxNew
| _ => focus do
let (elimInfo, targets) elabFunTarget (cases := false) stx[1]
let targets generalizeTargets targets
let (elimInfo, targets) Term.withoutTacticIncrementality true do
let (elimInfo, targets) elabFunTarget (cases := false) stx[1]
let targets generalizeTargets targets
return (elimInfo, targets)
evalInductionCore stx elimInfo targets
/--
@@ -1122,9 +1128,11 @@ def evalCases : Tactic := fun stx =>
| some stxNew => withMacroExpansion stx stxNew <| evalTactic stxNew
| _ => focus do
-- syntax (name := cases) "cases " elimTarget,+ (" using " term)? (inductionAlts)? : tactic
let (targets, toTag) elabElimTargets stx[1].getSepArgs
let elimInfo withMainContext <| getElimNameInfo stx[2] targets (induction := false)
let targets withMainContext <| addImplicitTargets elimInfo targets
let (targets, toTag, elimInfo) Term.withoutTacticIncrementality true do
let (targets, toTag) elabElimTargets stx[1].getSepArgs
let elimInfo withMainContext <| getElimNameInfo stx[2] targets (induction := false)
let targets withMainContext <| addImplicitTargets elimInfo targets
return (targets, toTag, elimInfo)
evalCasesCore stx elimInfo targets toTag
@[builtin_tactic Lean.Parser.Tactic.funCases, builtin_incremental]
@@ -1132,8 +1140,10 @@ def evalFunCases : Tactic := fun stx =>
match expandInduction? stx with
| some stxNew => withMacroExpansion stx stxNew <| evalTactic stxNew
| _ => focus do
let (elimInfo, targets) elabFunTarget (cases := true) stx[1]
let targets generalizeTargets targets
let (elimInfo, targets) Term.withoutTacticIncrementality true do
let (elimInfo, targets) elabFunTarget (cases := true) stx[1]
let targets generalizeTargets targets
return (elimInfo, targets)
evalCasesCore stx elimInfo targets
builtin_initialize

View File

@@ -623,6 +623,7 @@ private def evalSuggestSimpAllTrace : TryTactic := fun tac => do
| _ => throwUnsupportedSyntax
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_eval_suggest_tactic"] -- forward definition to avoid mutual block
opaque evalSuggest : TryTactic

View File

@@ -752,6 +752,7 @@ private def lakeAdd (env : Environment) (cinfo : ConstantInfo) : Environment :=
}
-- forward reference due to too many cyclic dependencies
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_is_reserved_name"]
private opaque isReservedName (env : Environment) (name : Name) : Bool
@@ -1768,6 +1769,7 @@ private def looksLikeOldCodegenName : Name → Bool
| .str _ s => s.startsWith "_cstage" || s.startsWith "_spec_" || s.startsWith "_elambda"
| _ => false
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_get_ir_extra_const_names"]
private opaque getIRExtraConstNames (env : Environment) (level : OLeanLevel) (includeDecls := false) : Array Name
@@ -1804,6 +1806,7 @@ def mkModuleData (env : Environment) (level : OLeanLevel := .private) : IO Modul
constNames, constants, entries
}
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_ir_export_entries"]
private opaque exportIREntries (env : Environment) : Array (Name × Array EnvExtensionEntry)
@@ -1862,6 +1865,7 @@ private def setImportedEntries (states : Array EnvExtensionState) (mods : Array
{ s with importedEntries := s.importedEntries.set! modIdx entries }
return states
set_option compiler.ignoreBorrowAnnotation true in
/--
"Forward declaration" needed for updating the attribute table with user-defined attributes.
User-defined attributes are declared using the `initialize` command. The `initialize` command is just syntax sugar for the `init` attribute.
@@ -1872,9 +1876,12 @@ private def setImportedEntries (states : Array EnvExtensionState) (mods : Array
Later, we set this method with code that adds the user-defined attributes that were imported after we initialized `attributeExtension`.
-/
@[extern "lean_update_env_attributes"] opaque updateEnvAttributes : Environment IO Environment
set_option compiler.ignoreBorrowAnnotation true in
/-- "Forward declaration" for retrieving the number of builtin attributes. -/
@[extern "lean_get_num_attributes"] opaque getNumBuiltinAttributes : IO Nat
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_run_init_attrs"]
private opaque runInitAttrs (env : Environment) (opts : Options) : IO Unit
@@ -2399,6 +2406,7 @@ def displayStats (env : Environment) : IO Unit := do
@[extern "lean_eval_const"]
private unsafe opaque evalConstCore (α) (env : @& Environment) (opts : @& Options) (constName : @& Name) : Except String α
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_eval_check_meta"]
private opaque evalCheckMeta (env : Environment) (constName : Name) : Except String Unit

View File

@@ -253,7 +253,7 @@ instance : EmptyCollection (FVarIdMap α) := inferInstanceAs (EmptyCollection (S
instance : Inhabited (FVarIdMap α) where
default := {}
/-- Universe metavariable Id -/
/-- Expression metavariable Id -/
structure MVarId where
name : Name
deriving Inhabited, BEq, Hashable
@@ -301,8 +301,8 @@ inductive Expr where
of a variable in the expression where there is a variable binder
above it (i.e. introduced by a `lam`, `forallE`, or `letE`).
The `deBruijnIndex` parameter is the *de-Bruijn* index for the bound
variable. See [the Wikipedia page on de-Bruijn indices](https://en.wikipedia.org/wiki/De_Bruijn_index)
The `deBruijnIndex` parameter is the *de Bruijn* index for the bound
variable. See [the Wikipedia page on de Bruijn indices](https://en.wikipedia.org/wiki/De_Bruijn_index)
for additional information.
For example, consider the expression `fun x : Nat => forall y : Nat, x = y`.
@@ -321,16 +321,16 @@ inductive Expr where
| bvar (deBruijnIndex : Nat)
/--
The `fvar` constructor represent free variables. These *free* variable
occurrences are not bound by an earlier `lam`, `forallE`, or `letE`
The `fvar` constructor represents free variables. Such a *free* variable
occurrence is not bound by an earlier `lam`, `forallE`, or `letE`
constructor and its binder exists in a local context only.
Note that Lean uses the *locally nameless approach*. See [McBride and McKinna](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.365.2479&rep=rep1&type=pdf)
Note that Lean uses the *locally nameless approach*. See [McBride and McKinna](https://doi.org/10.1145/1017472.1017477)
for additional details.
When "visiting" the body of a binding expression (i.e. `lam`, `forallE`, or `letE`),
bound variables are converted into free variables using a unique identifier,
and their user-facing name, type, value (for `LetE`), and binder annotation
and their user-facing name, type, value (for `letE`), and binder annotation
are stored in the `LocalContext`.
-/
| fvar (fvarId : FVarId)
@@ -363,7 +363,7 @@ inductive Expr where
A function application.
For example, the natural number one, i.e. `Nat.succ Nat.zero` is represented as
``Expr.app (.const `Nat.succ []) (.const .zero [])``.
``Expr.app (.const `Nat.succ []) (.const `Nat.zero [])``.
Note that multiple arguments are represented using partial application.
For example, the two argument application `f x y` is represented as
@@ -383,7 +383,7 @@ inductive Expr where
| lam (binderName : Name) (binderType : Expr) (body : Expr) (binderInfo : BinderInfo)
/--
A dependent arrow `(a : α) → β)` (aka forall-expression) where `β` may dependent
A dependent arrow `(a : α) → β)` (aka forall-expression) where `β` may depend
on `a`. Note that this constructor is also used to represent non-dependent arrows
where `β` does not depend on `a`.

View File

@@ -760,6 +760,7 @@ have to hard-code the true arity of these definitions here, and make sure the C
We have used another hack based on `IO.Ref`s in the past, it was safer but less efficient.
-/
set_option compiler.ignoreBorrowAnnotation true in
/--
Reduces an expression to its *weak head normal form*.
This is when the "head" of the top-level expression has been fully reduced.
@@ -768,6 +769,7 @@ The result may contain subexpressions that have not been reduced.
See `Lean.Meta.whnfImp` for the implementation.
-/
@[extern "lean_whnf"] opaque whnf : Expr MetaM Expr
set_option compiler.ignoreBorrowAnnotation true in
/--
Returns the inferred type of the given expression. Assumes the expression is type-correct.
@@ -822,8 +824,11 @@ def e3 : Expr := .app (.const ``Nat.zero []) (.const ``Nat.zero [])
See `Lean.Meta.inferTypeImp` for the implementation of `inferType`.
-/
@[extern "lean_infer_type"] opaque inferType : Expr MetaM Expr
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_is_expr_def_eq"] opaque isExprDefEqAux : Expr Expr MetaM Bool
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_is_level_def_eq"] opaque isLevelDefEqAux : Level Level MetaM Bool
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_synth_pending"] protected opaque synthPending : MVarId MetaM Bool
def whnfForall (e : Expr) : MetaM Expr := do
@@ -2498,6 +2503,7 @@ def isDefEqD (t s : Expr) : MetaM Bool :=
def isDefEqI (t s : Expr) : MetaM Bool :=
withReducibleAndInstances <| isDefEq t s
set_option compiler.ignoreBorrowAnnotation true in
/--
Returns `true` if `mvarId := val` was successfully assigned.
This method uses the same assignment validation performed by `isDefEq`, but it does not check whether the types match.
@@ -2710,7 +2716,14 @@ where
-- catch all exceptions
let _ : MonadExceptOf _ MetaM := MonadAlwaysExcept.except
observing do
withDeclNameForAuxNaming constName do
-- Re-privatize private `constName` under the current module so that auxiliary
-- declarations generated during realization get names scoped to the realizing module,
-- not the original defining module. This prevents name collisions when the same
-- constant is realized independently from two modules that are later imported together
-- (diamond import pattern).
let namePrefix :=
if isPrivateName constName then mkPrivateName env constName else constName
withDeclNameForAuxNaming namePrefix do
withoutExporting (when := isPrivateName constName) do
realize
-- Meta code working on a non-exported declaration should usually do so inside

View File

@@ -46,12 +46,14 @@ Forward definition of `getEquationsForImpl`.
We want to use `getEquationsFor` in the simplifier,
getEquationsFor` depends on `mkEquationsFor` which uses the simplifier.
-/
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_get_match_equations_for"]
opaque getEquationsFor (matchDeclName : Name) : MetaM MatchEqns
/-
Forward definition of `genMatchCongrEqnsImpl`.
-/
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_get_congr_match_equations_for"]
opaque genMatchCongrEqns (matchDeclName : Name) : MetaM (Array Name)

View File

@@ -30,12 +30,12 @@ then returns `f`. Otherwise, returns `e`.
Returns the original expression when not reducible to enable pointer equality checks.
-/
public def etaReduce (e : Expr) : Expr :=
go e 0
if e.isLambda then go e 0 else e
where
go (body : Expr) (n : Nat) : Expr :=
match body with
| .lam _ _ b _ => go b (n+1)
| _ => if n == 0 then e else etaReduceAux body n 0 e
| _ => etaReduceAux body n 0 e
/-- Returns `true` if `e` can be eta-reduced. Uses pointer equality for efficiency. -/
public def isEtaReducible (e : Expr) : Bool :=

View File

@@ -17,7 +17,7 @@ import Lean.Meta.Sym.ProofInstInfo
import Lean.Meta.Sym.AlphaShareBuilder
import Lean.Meta.Sym.Offset
import Lean.Meta.Sym.Eta
import Lean.Meta.AbstractMVars
import Lean.Meta.Sym.Util
import Init.Data.List.MapIdx
import Init.Data.Nat.Linear
import Std.Do.Triple.Basic
@@ -31,7 +31,9 @@ framework (`Sym`). The design prioritizes performance by using a two-phase appro
# Phase 1 (Syntactic Matching)
- Patterns use de Bruijn indices for expression variables and renamed level params (`_uvar.0`, `_uvar.1`, ...) for universe variables
- Matching is purely structural after reducible definitions are unfolded during preprocessing
- Universe levels treat `max` and `imax` as uninterpreted functions (no AC reasoning)
- Universe levels are eagerly normalized in patterns and normalized on the target side during matching
- `tryApproxMaxMax` handles `max` commutativity when one argument matches structurally. It is relevant
for constraints such as `max ?u a =?= max a b`
- Binders and term metavariables are deferred to Phase 2
# Phase 2 (Pending Constraints)
@@ -107,6 +109,7 @@ def preprocessDeclPattern (declName : Name) : MetaM (List Name × Expr) := do
let us := levelParams.map mkLevelParam
let type instantiateTypeLevelParams info.toConstantVal us
let type preprocessType type
let type normalizeLevels type
return (levelParams, type)
def preprocessExprPattern (e : Expr) (levelParams₀ : List Name) : MetaM (List Name × Expr) := do
@@ -115,6 +118,7 @@ def preprocessExprPattern (e : Expr) (levelParams₀ : List Name) : MetaM (List
let us := levelParams.map mkLevelParam
let type := type.instantiateLevelParams levelParams₀ us
let type preprocessType type
let type normalizeLevels type
return (levelParams, type)
/--
@@ -261,45 +265,6 @@ public def mkEqPatternFromDecl (declName : Name) : MetaM (Pattern × Expr) := do
let_expr Eq _ lhs rhs := type | throwError "conclusion is not a equality{indentExpr type}"
return (lhs, rhs)
/--
Like `mkPatternCore` but takes a lambda expression instead of a forall type.
Uses `lambdaBoundedTelescope` to open binders and detect instance/proof arguments.
-/
def mkPatternCoreFromLambda (lam : Expr) (levelParams : List Name)
(varTypes : Array Expr) (pattern : Expr) : MetaM Pattern := do
let fnInfos mkProofInstInfoMapFor pattern
let checkTypeMask := mkCheckTypeMask pattern varTypes.size
let checkTypeMask? := if checkTypeMask.all (· == false) then none else some checkTypeMask
let varInfos? lambdaBoundedTelescope lam varTypes.size fun xs _ =>
mkProofInstArgInfo? xs
return { levelParams, varTypes, pattern, fnInfos, varInfos?, checkTypeMask? }
def mkPatternFromLambda (levelParams : List Name) (lam : Expr) : MetaM Pattern := do
let rec go (pattern : Expr) (varTypes : Array Expr) : MetaM Pattern := do
if let .lam _ d b _ := pattern then
return ( go b (varTypes.push d))
let pattern preprocessType pattern
mkPatternCoreFromLambda lam levelParams varTypes pattern
go lam #[]
/--
Creates a `Pattern` from an expression containing metavariables.
Metavariables in `e` become pattern variables (wildcards). For example,
`Nat.succ ?m` produces a pattern matching `Nat.succ _` with discrimination
tree keys `[Nat.succ, *]`.
This is used for user-registered simproc patterns where the user provides
an expression with underscores (elaborated as metavariables) to specify
what the simproc should match.
-/
public def mkSimprocPatternFromExpr (e : Expr) : MetaM Pattern := do
let result abstractMVars e
let levelParams := result.paramNames.mapIdx fun i _ => Name.num uvarPrefix i
let us := levelParams.toList.map mkLevelParam
let expr := result.expr.instantiateLevelParamsArray result.paramNames us.toArray
mkPatternFromLambda levelParams.toList expr
structure UnifyM.Context where
pattern : Pattern
unify : Bool := true
@@ -365,35 +330,42 @@ def assignLevel (uidx : Nat) (u : Level) : UnifyM Bool := do
modify fun s => { s with uAssignment := s.uAssignment.set! uidx (some u) }
return true
def processLevel (u : Level) (v : Level) : UnifyM Bool := do
match u, v with
| .zero, .zero => return true
| .succ u, .succ v => processLevel u v
| .zero, .succ _ => return false
| .succ _, .zero => return false
| .zero, .max v₁ v₂ => processLevel .zero v <&&> processLevel .zero v
| .max u₁ u₂, .zero => processLevel u₁ .zero <&&> processLevel u₂ .zero
| .zero, .imax _ v => processLevel .zero v
| .imax _ u, .zero => processLevel u .zero
| .max u₁ u₂, .max v₁ v₂ => processLevel u₁ v₁ <&&> processLevel u₂ v₂
| .imax u₁ u₂, .imax v₁ v => processLevel u₁ v₁ <&&> processLevel u₂ v
| .param uName, _ =>
if let some uidx := isUVar? uName then
assignLevel uidx v
else if u == v then
return true
else if v.isMVar && ( read).unify then
pushLevelPending u v
return true
else
return false
| .mvar _, _ | _, .mvar _ =>
if ( read).unify then
pushLevelPending u v
return true
else
return false
| _, _ => return false
def processLevel (u : Level) (v : Level) : UnifyM Bool :=
go u v.normalize
where
go (u : Level) (v : Level) : UnifyM Bool := do
match u, v with
| .zero, .zero => return true
| .succ u, .succ v => go u v
| .zero, .succ _ => return false
| .succ _, .zero => return false
| .zero, .max v₁ v₂ => go .zero v₁ <&&> go .zero v₂
| .max u₁ u₂, .zero => go u₁ .zero <&&> go u₂ .zero
| .zero, .imax _ v => go .zero v
| .imax _ u, .zero => go u .zero
| .max u₁ u, .max v₁ v₂ =>
-- tryApproxMaxMax: find a shared concrete arg between both sides
if u == v then go u₁ v₂
else if u₁ == v₂ then go u₂ v₁
else go u₁ v₁ <&&> go u₂ v₂
| .imax u₁ u₂, .imax v₁ v₂ => go u v <&&> go u₂ v₂
| .param uName, _ =>
if let some uidx := isUVar? uName then
assignLevel uidx v
else if u == v then
return true
else if v.isMVar && ( read).unify then
pushLevelPending u v
return true
else
return false
| .mvar _, _ | _, .mvar _ =>
if ( read).unify then
pushLevelPending u v
return true
else
return false
| _, _ => return false
def processLevels (us : List Level) (vs : List Level) : UnifyM Bool := do
match us, vs with
@@ -542,7 +514,7 @@ def tryAssignLevelMVar (u : Level) (v : Level) : MetaM Bool := do
/--
Structural definitional equality for universe levels.
Treats `max` and `imax` as uninterpreted functions (no AC reasoning).
Uses `tryApproxMaxMax` to handle `max` commutativity when one argument matches structurally.
Attempts metavariable assignment in both directions if structural matching fails.
-/
def isLevelDefEqS (u : Level) (v : Level) : MetaM Bool := do
@@ -556,7 +528,10 @@ def isLevelDefEqS (u : Level) (v : Level) : MetaM Bool := do
| .max u₁ u₂, .zero => isLevelDefEqS u₁ .zero <&&> isLevelDefEqS u₂ .zero
| .zero, .imax _ v => isLevelDefEqS .zero v
| .imax _ u, .zero => isLevelDefEqS u .zero
| .max u₁ u₂, .max v₁ v₂ => isLevelDefEqS u₁ v₁ <&&> isLevelDefEqS u₂ v₂
| .max u₁ u₂, .max v₁ v₂ =>
if u₂ == v₁ then isLevelDefEqS u₁ v₂
else if u₁ == v₂ then isLevelDefEqS u₂ v₁
else isLevelDefEqS u₁ v₁ <&&> isLevelDefEqS u₂ v₂
| .imax u₁ u₂, .imax v₁ v₂ => isLevelDefEqS u₁ v₁ <&&> isLevelDefEqS u₂ v₂
| _, _ => tryAssignLevelMVar u v <||> tryAssignLevelMVar v u
@@ -598,6 +573,7 @@ structure DefEqM.Context where
abbrev DefEqM := ReaderT DefEqM.Context SymM
set_option compiler.ignoreBorrowAnnotation true in
/--
Structural definitional equality. It is much cheaper than `isDefEq`.

View File

@@ -8,6 +8,7 @@ prelude
public import Lean.Meta.Sym.Pattern
public import Lean.Meta.DiscrTree.Basic
import Lean.Meta.Sym.Offset
import Lean.Meta.Sym.Eta
import Init.Omega
namespace Lean.Meta.Sym
open DiscrTree
@@ -154,7 +155,7 @@ partial def getMatchLoop (todo : Array Expr) (c : Trie α) (result : Array α) :
else if h : csize = 0 then
result
else
let e := todo.back!
let e := etaReduce todo.back!
let todo := todo.pop
let first := cs[0] /- Recall that `Key.star` is the minimal key -/
if csize = 1 then
@@ -184,6 +185,7 @@ public def getMatch (d : DiscrTree α) (e : Expr) : Array α :=
let result := match d.root.find? .star with
| none => .mkEmpty initCapacity
| some (.node vs _) => vs
let e := etaReduce e
match d.root.find? (getKey e) with
| none => result
| some c => getMatchLoop (pushArgsTodo #[] e) c result
@@ -196,6 +198,7 @@ This is useful for rewriting: if a pattern matches `f x` but `e` is `f x y z`, w
still apply the rewrite and return `(value, 2)` indicating 2 extra arguments.
-/
public partial def getMatchWithExtra (d : DiscrTree α) (e : Expr) : Array (α × Nat) :=
let e := etaReduce e
let result := getMatch d e
let result := result.map (·, 0)
if !e.isApp then

View File

@@ -227,6 +227,7 @@ def SimpM.run' (x : SimpM α) (methods : Methods := {}) (config : Config := {})
let initialLCtxSize := ( getLCtx).decls.size
x methods.toMethodsRef { initialLCtxSize, config } |>.run' {}
set_option compiler.ignoreBorrowAnnotation true in
@[extern "lean_sym_simp"] -- Forward declaration
opaque simp : Simproc

Some files were not shown because too many files have changed in this diff Show More