mirror of
https://github.com/leanprover/lean4.git
synced 2026-04-12 07:04:07 +00:00
Compare commits
1 Commits
nightly
...
release-co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30c59e81db |
@@ -66,8 +66,6 @@ To rebuild individual stage 2 modules without a full `make stage2`, use Lake dir
|
||||
cd build/release/stage2 && lake build Init.Prelude
|
||||
```
|
||||
|
||||
To run tests in stage2, replace `-C build/release` from above with `-C build/release/stage2`.
|
||||
|
||||
## New features
|
||||
|
||||
When asked to implement new features:
|
||||
|
||||
2
.github/workflows/build-template.yml
vendored
2
.github/workflows/build-template.yml
vendored
@@ -131,7 +131,7 @@ jobs:
|
||||
[ -d build ] || mkdir build
|
||||
cd build
|
||||
# arguments passed to `cmake`
|
||||
OPTIONS=(-DWFAIL=ON)
|
||||
OPTIONS=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
|
||||
if [[ -n '${{ matrix.release }}' ]]; then
|
||||
# this also enables githash embedding into stage 1 library, which prohibits reusing
|
||||
# `.olean`s across commits, so we don't do it in the fast non-release CI
|
||||
|
||||
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@@ -143,7 +143,7 @@ jobs:
|
||||
CMAKE_MAJOR=$(grep -E "^set\(LEAN_VERSION_MAJOR " src/CMakeLists.txt | grep -oE '[0-9]+')
|
||||
CMAKE_MINOR=$(grep -E "^set\(LEAN_VERSION_MINOR " src/CMakeLists.txt | grep -oE '[0-9]+')
|
||||
CMAKE_PATCH=$(grep -E "^set\(LEAN_VERSION_PATCH " src/CMakeLists.txt | grep -oE '[0-9]+')
|
||||
CMAKE_IS_RELEASE=$(grep -m 1 -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | grep -oE '[0-9]+' | head -1)
|
||||
CMAKE_IS_RELEASE=$(grep -m 1 -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | sed -nE 's/^set\(LEAN_VERSION_IS_RELEASE ([0-9]+)\).*/\1/p')
|
||||
|
||||
# Expected values from tag parsing
|
||||
TAG_MAJOR="${{ steps.set-release.outputs.LEAN_VERSION_MAJOR }}"
|
||||
@@ -305,8 +305,7 @@ jobs:
|
||||
"test": true,
|
||||
"CMAKE_PRESET": "reldebug",
|
||||
// * `elab_bench/big_do` crashes with exit code 134
|
||||
// * `compile_bench/channel` randomly segfaults
|
||||
"CTEST_OPTIONS": "-E 'elab_bench/big_do|compile_bench/channel'",
|
||||
"CTEST_OPTIONS": "-E 'elab_bench/big_do'",
|
||||
},
|
||||
{
|
||||
"name": "Linux fsanitize",
|
||||
|
||||
2
.github/workflows/update-stage0.yml
vendored
2
.github/workflows/update-stage0.yml
vendored
@@ -77,7 +77,7 @@ jobs:
|
||||
# sync options with `Linux Lake` to ensure cache reuse
|
||||
run: |
|
||||
mkdir -p build
|
||||
cmake --preset release -B build -DWFAIL=ON
|
||||
cmake --preset release -B build -DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true
|
||||
shell: 'nix develop -c bash -euxo pipefail {0}'
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: |
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -34,4 +34,3 @@ wdErr.txt
|
||||
wdIn.txt
|
||||
wdOut.txt
|
||||
downstream_releases/
|
||||
.claude/worktrees/
|
||||
|
||||
@@ -6,6 +6,6 @@ vscode:
|
||||
- leanprover.lean4
|
||||
|
||||
tasks:
|
||||
- name: Build
|
||||
init: cmake --preset dev
|
||||
- name: Release build
|
||||
init: cmake --preset release
|
||||
command: make -C build/release -j$(nproc || sysctl -n hw.logicalcpu)
|
||||
|
||||
9
.vscode/tasks.json
vendored
9
.vscode/tasks.json
vendored
@@ -11,15 +11,6 @@
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "build stage2",
|
||||
"type": "shell",
|
||||
"command": "make -C build/release stage2 -j$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)",
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "build-old",
|
||||
"type": "shell",
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
include(ExternalProject)
|
||||
include(FetchContent)
|
||||
|
||||
if(NOT CMAKE_GENERATOR MATCHES "Makefiles")
|
||||
message(FATAL_ERROR "Only makefile generators are supported")
|
||||
@@ -36,6 +34,7 @@ foreach(var ${vars})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
|
||||
if(NOT (DEFINED STAGE0_CMAKE_EXECUTABLE_SUFFIX))
|
||||
@@ -120,17 +119,17 @@ if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
|
||||
endif()
|
||||
|
||||
if(USE_MIMALLOC)
|
||||
FetchContent_Declare(
|
||||
ExternalProject_Add(
|
||||
mimalloc
|
||||
PREFIX mimalloc
|
||||
GIT_REPOSITORY https://github.com/microsoft/mimalloc
|
||||
GIT_TAG v2.2.3
|
||||
# Unnecessarily deep directory structure, but it saves us from a complicated
|
||||
# stage0 update for now. If we ever update the other dependencies like
|
||||
# cadical, it might be worth reorganizing the directory structure.
|
||||
SOURCE_DIR
|
||||
"${CMAKE_BINARY_DIR}/mimalloc/src/mimalloc"
|
||||
# just download, we compile it as part of each stage as it is small
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
FetchContent_MakeAvailable(mimalloc)
|
||||
list(APPEND EXTRA_DEPENDS mimalloc)
|
||||
endif()
|
||||
|
||||
if(NOT STAGE1_PREV_STAGE)
|
||||
|
||||
@@ -8,26 +8,16 @@
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "release",
|
||||
"displayName": "Release build config",
|
||||
"displayName": "Default development optimized build config",
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/release"
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"displayName": "Default development optimized build config",
|
||||
"cacheVariables": {
|
||||
"STRIP_BINARIES": "OFF"
|
||||
},
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/dev"
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"displayName": "Debug build config",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"LEAN_EXTRA_CXX_FLAGS": "-DLEAN_DEFAULT_THREAD_STACK_SIZE=16*1024*1024",
|
||||
"STRIP_BINARIES": "OFF"
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
},
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/debug"
|
||||
@@ -36,8 +26,7 @@
|
||||
"name": "reldebug",
|
||||
"displayName": "Release with assertions enabled",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithAssert",
|
||||
"STRIP_BINARIES": "OFF"
|
||||
"CMAKE_BUILD_TYPE": "RelWithAssert"
|
||||
},
|
||||
"generator": "Unix Makefiles",
|
||||
"binaryDir": "${sourceDir}/build/reldebug"
|
||||
@@ -49,7 +38,6 @@
|
||||
"LEAN_EXTRA_CXX_FLAGS": "-fsanitize=address,undefined -DLEAN_DEFAULT_THREAD_STACK_SIZE=16*1024*1024",
|
||||
"LEANC_EXTRA_CC_FLAGS": "-fsanitize=address,undefined",
|
||||
"LEAN_EXTRA_LINKER_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
|
||||
"STRIP_BINARIES": "OFF",
|
||||
"SMALL_ALLOCATOR": "OFF",
|
||||
"USE_MIMALLOC": "OFF",
|
||||
"BSYMBOLIC": "OFF",
|
||||
@@ -70,10 +58,6 @@
|
||||
"name": "release",
|
||||
"configurePreset": "release"
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"configurePreset": "dev"
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"configurePreset": "debug"
|
||||
@@ -97,11 +81,6 @@
|
||||
"configurePreset": "release",
|
||||
"output": {"outputOnFailure": true, "shortProgress": true}
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"configurePreset": "dev",
|
||||
"output": {"outputOnFailure": true, "shortProgress": true}
|
||||
},
|
||||
{
|
||||
"name": "debug",
|
||||
"configurePreset": "debug",
|
||||
|
||||
@@ -30,9 +30,6 @@ cd lean4
|
||||
cmake --preset release
|
||||
make -C build/release -j$(nproc || sysctl -n hw.logicalcpu)
|
||||
```
|
||||
|
||||
For development, `cmake --preset dev` is recommended instead.
|
||||
|
||||
You can replace `$(nproc || sysctl -n hw.logicalcpu)` with the desired parallelism amount.
|
||||
|
||||
The above commands will compile the Lean library and binaries into the
|
||||
|
||||
@@ -311,16 +311,16 @@ def check_cmake_version(repo_url, branch, version_major, version_minor, github_t
|
||||
print(f" ❌ Could not retrieve {cmake_file_path} from {branch}")
|
||||
return False
|
||||
|
||||
expected_patterns = [
|
||||
(f"LEAN_VERSION_MAJOR", rf"^set\(LEAN_VERSION_MAJOR\s+{version_major}[\s)]", f"set(LEAN_VERSION_MAJOR {version_major} ...)"),
|
||||
(f"LEAN_VERSION_MINOR", rf"^set\(LEAN_VERSION_MINOR\s+{version_minor}[\s)]", f"set(LEAN_VERSION_MINOR {version_minor} ...)"),
|
||||
(f"LEAN_VERSION_PATCH", rf"^set\(LEAN_VERSION_PATCH\s+0[\s)]", f"set(LEAN_VERSION_PATCH 0 ...)"),
|
||||
(f"LEAN_VERSION_IS_RELEASE", rf"^set\(LEAN_VERSION_IS_RELEASE\s+1[\s)]", f"set(LEAN_VERSION_IS_RELEASE 1 ...)"),
|
||||
expected_lines = [
|
||||
f"set(LEAN_VERSION_MAJOR {version_major})",
|
||||
f"set(LEAN_VERSION_MINOR {version_minor})",
|
||||
f"set(LEAN_VERSION_PATCH 0)",
|
||||
f"set(LEAN_VERSION_IS_RELEASE 1)"
|
||||
]
|
||||
|
||||
for name, pattern, display in expected_patterns:
|
||||
if not any(re.match(pattern, l.strip()) for l in content.splitlines()):
|
||||
print(f" ❌ Missing or incorrect line in {cmake_file_path}: {display}")
|
||||
for line in expected_lines:
|
||||
if not any(l.strip().startswith(line) for l in content.splitlines()):
|
||||
print(f" ❌ Missing or incorrect line in {cmake_file_path}: {line}")
|
||||
return False
|
||||
|
||||
print(f" ✅ CMake version settings are correct in {cmake_file_path}")
|
||||
@@ -343,11 +343,11 @@ def check_stage0_version(repo_url, branch, version_major, version_minor, github_
|
||||
for line in content.splitlines():
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("set(LEAN_VERSION_MAJOR "):
|
||||
actual = stripped.split()[1].rstrip(")")
|
||||
actual = stripped.split()[-1].rstrip(")")
|
||||
if actual != str(version_major):
|
||||
errors.append(f"LEAN_VERSION_MAJOR: expected {version_major}, found {actual}")
|
||||
elif stripped.startswith("set(LEAN_VERSION_MINOR "):
|
||||
actual = stripped.split()[1].rstrip(")")
|
||||
actual = stripped.split()[-1].rstrip(")")
|
||||
if actual != str(version_minor):
|
||||
errors.append(f"LEAN_VERSION_MINOR: expected {version_minor}, found {actual}")
|
||||
|
||||
|
||||
@@ -28,14 +28,6 @@ repositories:
|
||||
branch: main
|
||||
dependencies: []
|
||||
|
||||
- name: leansqlite
|
||||
url: https://github.com/leanprover/leansqlite
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies:
|
||||
- plausible
|
||||
|
||||
- name: verso
|
||||
url: https://github.com/leanprover/verso
|
||||
toolchain-tag: true
|
||||
@@ -108,7 +100,7 @@ repositories:
|
||||
toolchain-tag: true
|
||||
stable-branch: false
|
||||
branch: main
|
||||
dependencies: [lean4-cli, BibtexQuery, mathlib4, leansqlite]
|
||||
dependencies: [lean4-cli, BibtexQuery, mathlib4]
|
||||
|
||||
- name: cslib
|
||||
url: https://github.com/leanprover/cslib
|
||||
|
||||
@@ -481,9 +481,11 @@ def execute_release_steps(repo, version, config):
|
||||
run_command("lake update", cwd=repo_path, stream_output=True)
|
||||
elif repo_name == "verso":
|
||||
# verso has nested Lake projects in test-projects/ that each have their own
|
||||
# lake-manifest.json with a subverso pin and their own lean-toolchain.
|
||||
# After updating the root manifest via `lake update`, sync the de-modulized
|
||||
# subverso rev into all sub-manifests, and update their lean-toolchain files.
|
||||
# lake-manifest.json with a subverso pin. After updating the root manifest via
|
||||
# `lake update`, sync the de-modulized subverso rev into all sub-manifests.
|
||||
# The sub-projects use an old toolchain (v4.21.0) that doesn't support module/prelude
|
||||
# syntax, so they need the de-modulized version (tagged no-modules/<root-rev>).
|
||||
# The "SubVerso version consistency" CI check accepts either the root or de-modulized rev.
|
||||
run_command("lake update", cwd=repo_path, stream_output=True)
|
||||
print(blue("Syncing de-modulized subverso rev to test-project sub-manifests..."))
|
||||
sync_script = (
|
||||
@@ -496,15 +498,6 @@ def execute_release_steps(repo, version, config):
|
||||
)
|
||||
run_command(sync_script, cwd=repo_path)
|
||||
print(green("Synced de-modulized subverso rev to all test-project sub-manifests"))
|
||||
# Update all lean-toolchain files in test-projects/ to match the root
|
||||
print(blue("Updating lean-toolchain files in test-projects/..."))
|
||||
find_result = run_command("find test-projects -name lean-toolchain", cwd=repo_path)
|
||||
for tc_path in find_result.stdout.strip().splitlines():
|
||||
if tc_path:
|
||||
tc_file = repo_path / tc_path
|
||||
with open(tc_file, "w") as f:
|
||||
f.write(f"leanprover/lean4:{version}\n")
|
||||
print(green(f" Updated {tc_path}"))
|
||||
elif dependencies:
|
||||
run_command(f'perl -pi -e \'s/"v4\\.[0-9]+(\\.[0-9]+)?(-rc[0-9]+)?"/"' + version + '"/g\' lakefile.*', cwd=repo_path)
|
||||
run_command("lake update", cwd=repo_path, stream_output=True)
|
||||
@@ -666,61 +659,56 @@ def execute_release_steps(repo, version, config):
|
||||
# Fetch latest changes to ensure we have the most up-to-date nightly-testing branch
|
||||
print(blue("Fetching latest changes from origin..."))
|
||||
run_command("git fetch origin", cwd=repo_path)
|
||||
|
||||
# Check if nightly-testing branch exists on origin (use local ref after fetch for exact match)
|
||||
nightly_check = run_command("git show-ref --verify --quiet refs/remotes/origin/nightly-testing", cwd=repo_path, check=False)
|
||||
if nightly_check.returncode != 0:
|
||||
print(yellow("No nightly-testing branch found on origin, skipping merge"))
|
||||
else:
|
||||
try:
|
||||
print(blue("Merging origin/nightly-testing..."))
|
||||
run_command("git merge origin/nightly-testing", cwd=repo_path)
|
||||
print(green("Merge completed successfully"))
|
||||
except subprocess.CalledProcessError:
|
||||
# Merge failed due to conflicts - check which files are conflicted
|
||||
print(blue("Merge conflicts detected, checking which files are affected..."))
|
||||
|
||||
# Get conflicted files using git status
|
||||
status_result = run_command("git status --porcelain", cwd=repo_path)
|
||||
conflicted_files = []
|
||||
|
||||
for line in status_result.stdout.splitlines():
|
||||
if len(line) >= 2 and line[:2] in ['UU', 'AA', 'DD', 'AU', 'UA', 'DU', 'UD']:
|
||||
# Extract filename (skip the first 3 characters which are status codes)
|
||||
conflicted_files.append(line[3:])
|
||||
|
||||
# Filter out allowed files
|
||||
allowed_patterns = ['lean-toolchain', 'lake-manifest.json']
|
||||
problematic_files = []
|
||||
|
||||
|
||||
try:
|
||||
print(blue("Merging origin/nightly-testing..."))
|
||||
run_command("git merge origin/nightly-testing", cwd=repo_path)
|
||||
print(green("Merge completed successfully"))
|
||||
except subprocess.CalledProcessError:
|
||||
# Merge failed due to conflicts - check which files are conflicted
|
||||
print(blue("Merge conflicts detected, checking which files are affected..."))
|
||||
|
||||
# Get conflicted files using git status
|
||||
status_result = run_command("git status --porcelain", cwd=repo_path)
|
||||
conflicted_files = []
|
||||
|
||||
for line in status_result.stdout.splitlines():
|
||||
if len(line) >= 2 and line[:2] in ['UU', 'AA', 'DD', 'AU', 'UA', 'DU', 'UD']:
|
||||
# Extract filename (skip the first 3 characters which are status codes)
|
||||
conflicted_files.append(line[3:])
|
||||
|
||||
# Filter out allowed files
|
||||
allowed_patterns = ['lean-toolchain', 'lake-manifest.json']
|
||||
problematic_files = []
|
||||
|
||||
for file in conflicted_files:
|
||||
is_allowed = any(pattern in file for pattern in allowed_patterns)
|
||||
if not is_allowed:
|
||||
problematic_files.append(file)
|
||||
|
||||
if problematic_files:
|
||||
# There are conflicts in non-allowed files - fail
|
||||
print(red("❌ Merge failed!"))
|
||||
print(red(f"Merging nightly-testing resulted in conflicts in:"))
|
||||
for file in problematic_files:
|
||||
print(red(f" - {file}"))
|
||||
print(red("Please resolve these conflicts manually."))
|
||||
return
|
||||
else:
|
||||
# Only allowed files are conflicted - resolve them automatically
|
||||
print(green(f"✅ Only allowed files conflicted: {', '.join(conflicted_files)}"))
|
||||
print(blue("Resolving conflicts automatically..."))
|
||||
|
||||
# For lean-toolchain and lake-manifest.json, keep our versions
|
||||
for file in conflicted_files:
|
||||
is_allowed = any(pattern in file for pattern in allowed_patterns)
|
||||
if not is_allowed:
|
||||
problematic_files.append(file)
|
||||
|
||||
if problematic_files:
|
||||
# There are conflicts in non-allowed files - fail
|
||||
print(red("❌ Merge failed!"))
|
||||
print(red(f"Merging nightly-testing resulted in conflicts in:"))
|
||||
for file in problematic_files:
|
||||
print(red(f" - {file}"))
|
||||
print(red("Please resolve these conflicts manually."))
|
||||
return
|
||||
else:
|
||||
# Only allowed files are conflicted - resolve them automatically
|
||||
print(green(f"✅ Only allowed files conflicted: {', '.join(conflicted_files)}"))
|
||||
print(blue("Resolving conflicts automatically..."))
|
||||
|
||||
# For lean-toolchain and lake-manifest.json, keep our versions
|
||||
for file in conflicted_files:
|
||||
print(blue(f"Keeping our version of {file}"))
|
||||
run_command(f"git checkout --ours {file}", cwd=repo_path)
|
||||
|
||||
# Complete the merge
|
||||
run_command("git add .", cwd=repo_path)
|
||||
run_command("git commit --no-edit", cwd=repo_path)
|
||||
|
||||
print(green("✅ Merge completed successfully with automatic conflict resolution"))
|
||||
print(blue(f"Keeping our version of {file}"))
|
||||
run_command(f"git checkout --ours {file}", cwd=repo_path)
|
||||
|
||||
# Complete the merge
|
||||
run_command("git add .", cwd=repo_path)
|
||||
run_command("git commit --no-edit", cwd=repo_path)
|
||||
|
||||
print(green("✅ Merge completed successfully with automatic conflict resolution"))
|
||||
|
||||
# Build and test (skip for Mathlib)
|
||||
if repo_name not in ["mathlib4"]:
|
||||
|
||||
@@ -8,7 +8,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4 CACHE STRING "")
|
||||
set(LEAN_VERSION_MINOR 31 CACHE STRING "")
|
||||
set(LEAN_VERSION_MINOR 30 CACHE STRING "")
|
||||
set(LEAN_VERSION_PATCH 0 CACHE STRING "")
|
||||
set(LEAN_VERSION_IS_RELEASE 0 CACHE STRING "") # This number is 1 in the release revision, and 0 otherwise.
|
||||
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
|
||||
@@ -80,7 +80,6 @@ option(CCACHE "use ccache" ON)
|
||||
option(SPLIT_STACK "SPLIT_STACK" OFF)
|
||||
# When OFF we disable LLVM support
|
||||
option(LLVM "LLVM" OFF)
|
||||
option(STRIP_BINARIES "Strip produced binaries" ON)
|
||||
|
||||
# When ON we include githash in the version string
|
||||
option(USE_GITHASH "GIT_HASH" ON)
|
||||
@@ -116,19 +115,11 @@ option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current ver
|
||||
option(USE_LAKE "Use Lake instead of lean.mk for building core libs from language server" ON)
|
||||
option(USE_LAKE_CACHE "Use the Lake artifact cache for stage 1 builds (requires USE_LAKE)" OFF)
|
||||
|
||||
set(LEAN_EXTRA_OPTS "" CACHE STRING "extra options to lean (via lake or make)")
|
||||
set(LEAN_EXTRA_MAKE_OPTS "" CACHE STRING "extra options to leanmake")
|
||||
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_OPTS " -Dbackward.do.legacy=false")
|
||||
|
||||
# option used by the CI to fail on warnings
|
||||
option(WFAIL "Fail build if warnings are emitted by Lean" ON)
|
||||
if(WFAIL MATCHES "ON")
|
||||
string(APPEND LAKE_EXTRA_ARGS " --wfail")
|
||||
string(APPEND LEAN_EXTRA_MAKE_OPTS " -DwarningAsError=true")
|
||||
endif()
|
||||
string(APPEND LEAN_EXTRA_MAKE_OPTS " -Dbackward.do.legacy=false")
|
||||
|
||||
if(LAZY_RC MATCHES "ON")
|
||||
set(LEAN_LAZY_RC "#define LEAN_LAZY_RC")
|
||||
@@ -206,7 +197,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/lean")
|
||||
|
||||
# OSX default thread stack size is very small. Moreover, in Debug mode, each new stack frame consumes a lot of extra memory.
|
||||
if((MULTI_THREAD MATCHES "ON") AND (CMAKE_SYSTEM_NAME MATCHES "Darwin"))
|
||||
string(APPEND LEAN_EXTRA_OPTS " -s40000")
|
||||
string(APPEND LEAN_EXTRA_MAKE_OPTS " -s40000")
|
||||
endif()
|
||||
|
||||
# We want explicit stack probes in huge Lean stack frames for robust stack overflow detection
|
||||
@@ -678,9 +669,6 @@ else()
|
||||
set(LEAN_PATH_SEPARATOR ":")
|
||||
endif()
|
||||
|
||||
# inherit genral options for lean.mk.in and stdlib.make.in
|
||||
string(APPEND LEAN_EXTRA_MAKE_OPTS " ${LEAN_EXTRA_OPTS}")
|
||||
|
||||
# Version
|
||||
configure_file("${LEAN_SOURCE_DIR}/version.h.in" "${LEAN_BINARY_DIR}/include/lean/version.h")
|
||||
if(STAGE EQUAL 0)
|
||||
@@ -1065,7 +1053,7 @@ string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_CC "${LEANC_CC}")
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_FLAGS "${LEANC_INTERNAL_FLAGS}")
|
||||
string(REPLACE "ROOT" "${CMAKE_BINARY_DIR}" LEANC_INTERNAL_LINKER_FLAGS "${LEANC_INTERNAL_LINKER_FLAGS}")
|
||||
|
||||
toml_escape("${LEAN_EXTRA_OPTS}" LEAN_EXTRA_OPTS_TOML)
|
||||
toml_escape("${LEAN_EXTRA_MAKE_OPTS}" LEAN_EXTRA_OPTS_TOML)
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES "Debug|Release|RelWithDebInfo|MinSizeRel")
|
||||
set(CMAKE_BUILD_TYPE_TOML "${CMAKE_BUILD_TYPE}")
|
||||
|
||||
@@ -802,7 +802,6 @@ Examples:
|
||||
def firstM {α : Type u} {m : Type v → Type w} [Alternative m] (f : α → m β) (as : Array α) : m β :=
|
||||
go 0
|
||||
where
|
||||
@[specialize]
|
||||
go (i : Nat) : m β :=
|
||||
if hlt : i < as.size then
|
||||
f as[i] <|> go (i+1)
|
||||
@@ -1086,17 +1085,6 @@ Examples:
|
||||
def sum {α} [Add α] [Zero α] : Array α → α :=
|
||||
foldr (· + ·) 0
|
||||
|
||||
/--
|
||||
Computes the product of the elements of an array.
|
||||
|
||||
Examples:
|
||||
* `#[a, b, c].prod = a * (b * (c * 1))`
|
||||
* `#[1, 2, 5].prod = 10`
|
||||
-/
|
||||
@[inline, expose]
|
||||
def prod {α} [Mul α] [One α] : Array α → α :=
|
||||
foldr (· * ·) 1
|
||||
|
||||
/--
|
||||
Counts the number of elements in the array `as` that satisfy the Boolean predicate `p`.
|
||||
|
||||
@@ -1265,7 +1253,7 @@ Examples:
|
||||
-/
|
||||
@[inline, expose]
|
||||
def findIdx? {α : Type u} (p : α → Bool) (as : Array α) : Option Nat :=
|
||||
let rec @[specialize] loop (j : Nat) :=
|
||||
let rec loop (j : Nat) :=
|
||||
if h : j < as.size then
|
||||
if p as[j] then some j else loop (j + 1)
|
||||
else none
|
||||
|
||||
@@ -7,7 +7,6 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.List.Int.Sum
|
||||
public import Init.Data.List.Int.Prod
|
||||
public import Init.Data.Array.MinMax
|
||||
import Init.Data.Int.Lemmas
|
||||
|
||||
@@ -75,17 +74,4 @@ theorem sum_div_length_le_max_of_max?_eq_some_int {xs : Array Int} (h : xs.max?
|
||||
simpa [List.max?_toArray, List.sum_toArray] using
|
||||
List.sum_div_length_le_max_of_max?_eq_some_int (by simpa using h)
|
||||
|
||||
@[simp] theorem prod_replicate_int {n : Nat} {a : Int} : (replicate n a).prod = a ^ n := by
|
||||
rw [← List.toArray_replicate, List.prod_toArray]
|
||||
simp
|
||||
|
||||
theorem prod_append_int {as₁ as₂ : Array Int} : (as₁ ++ as₂).prod = as₁.prod * as₂.prod := by
|
||||
simp [prod_append]
|
||||
|
||||
theorem prod_reverse_int (xs : Array Int) : xs.reverse.prod = xs.prod := by
|
||||
simp [prod_reverse]
|
||||
|
||||
theorem prod_eq_foldl_int {xs : Array Int} : xs.prod = xs.foldl (init := 1) (· * ·) := by
|
||||
simp only [foldl_eq_foldr_reverse, Int.mul_comm, ← prod_eq_foldr, prod_reverse_int]
|
||||
|
||||
end Array
|
||||
|
||||
@@ -4380,47 +4380,6 @@ theorem sum_eq_foldl [Zero α] [Add α] [Std.Associative (α := α) (· + ·)]
|
||||
xs.sum = xs.foldl (init := 0) (· + ·) := by
|
||||
simp [← sum_toList, List.sum_eq_foldl]
|
||||
|
||||
/-! ### prod -/
|
||||
|
||||
@[simp, grind =] theorem prod_empty [Mul α] [One α] : (#[] : Array α).prod = 1 := rfl
|
||||
theorem prod_eq_foldr [Mul α] [One α] {xs : Array α} :
|
||||
xs.prod = xs.foldr (init := 1) (· * ·) :=
|
||||
rfl
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_toList [Mul α] [One α] {as : Array α} : as.toList.prod = as.prod := by
|
||||
cases as
|
||||
simp [Array.prod, List.prod]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_append [One α] [Mul α] [Std.Associative (α := α) (· * ·)]
|
||||
[Std.LawfulLeftIdentity (α := α) (· * ·) 1]
|
||||
{as₁ as₂ : Array α} : (as₁ ++ as₂).prod = as₁.prod * as₂.prod := by
|
||||
simp [← prod_toList, List.prod_append]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_singleton [Mul α] [One α] [Std.LawfulRightIdentity (· * ·) (1 : α)] {x : α} :
|
||||
#[x].prod = x := by
|
||||
simp [Array.prod_eq_foldr, Std.LawfulRightIdentity.right_id x]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_push [Mul α] [One α] [Std.Associative (α := α) (· * ·)]
|
||||
[Std.LawfulIdentity (· * ·) (1 : α)] {xs : Array α} {x : α} :
|
||||
(xs.push x).prod = xs.prod * x := by
|
||||
simp [Array.prod_eq_foldr, Std.LawfulRightIdentity.right_id, Std.LawfulLeftIdentity.left_id,
|
||||
← Array.foldr_assoc]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_reverse [One α] [Mul α] [Std.Associative (α := α) (· * ·)]
|
||||
[Std.Commutative (α := α) (· * ·)]
|
||||
[Std.LawfulLeftIdentity (α := α) (· * ·) 1] (xs : Array α) : xs.reverse.prod = xs.prod := by
|
||||
simp [← prod_toList, List.prod_reverse]
|
||||
|
||||
theorem prod_eq_foldl [One α] [Mul α] [Std.Associative (α := α) (· * ·)]
|
||||
[Std.LawfulIdentity (· * ·) (1 : α)] {xs : Array α} :
|
||||
xs.prod = xs.foldl (init := 1) (· * ·) := by
|
||||
simp [← prod_toList, List.prod_eq_foldl]
|
||||
|
||||
theorem foldl_toList_eq_flatMap {l : List α} {acc : Array β}
|
||||
{F : Array β → α → Array β} {G : α → List β}
|
||||
(H : ∀ acc a, (F acc a).toList = acc.toList ++ G a) :
|
||||
|
||||
@@ -8,7 +8,6 @@ module
|
||||
prelude
|
||||
public import Init.Data.Array.MinMax
|
||||
import Init.Data.List.Nat.Sum
|
||||
import Init.Data.List.Nat.Prod
|
||||
import Init.Data.Array.Lemmas
|
||||
|
||||
public section
|
||||
@@ -82,24 +81,4 @@ theorem sum_div_length_le_max_of_max?_eq_some_nat {xs : Array Nat} (h : xs.max?
|
||||
simpa [List.max?_toArray, List.sum_toArray] using
|
||||
List.sum_div_length_le_max_of_max?_eq_some_nat (by simpa using h)
|
||||
|
||||
protected theorem prod_pos_iff_forall_pos_nat {xs : Array Nat} : 0 < xs.prod ↔ ∀ x ∈ xs, 0 < x := by
|
||||
simp [← prod_toList, List.prod_pos_iff_forall_pos_nat]
|
||||
|
||||
protected theorem prod_eq_zero_iff_exists_zero_nat {xs : Array Nat} :
|
||||
xs.prod = 0 ↔ ∃ x ∈ xs, x = 0 := by
|
||||
simp [← prod_toList, List.prod_eq_zero_iff_exists_zero_nat]
|
||||
|
||||
@[simp] theorem prod_replicate_nat {n : Nat} {a : Nat} : (replicate n a).prod = a ^ n := by
|
||||
rw [← List.toArray_replicate, List.prod_toArray]
|
||||
simp
|
||||
|
||||
theorem prod_append_nat {as₁ as₂ : Array Nat} : (as₁ ++ as₂).prod = as₁.prod * as₂.prod := by
|
||||
simp [prod_append]
|
||||
|
||||
theorem prod_reverse_nat (xs : Array Nat) : xs.reverse.prod = xs.prod := by
|
||||
simp [prod_reverse]
|
||||
|
||||
theorem prod_eq_foldl_nat {xs : Array Nat} : xs.prod = xs.foldl (init := 1) (· * ·) := by
|
||||
simp only [foldl_eq_foldr_reverse, Nat.mul_comm, ← prod_eq_foldr, prod_reverse_nat]
|
||||
|
||||
end Array
|
||||
|
||||
@@ -20,20 +20,12 @@ universe u
|
||||
|
||||
namespace ByteArray
|
||||
|
||||
@[extern "lean_sarray_dec_eq"]
|
||||
def beq (lhs rhs : @& ByteArray) : Bool :=
|
||||
lhs.data == rhs.data
|
||||
|
||||
instance : BEq ByteArray where
|
||||
beq := beq
|
||||
deriving instance BEq for ByteArray
|
||||
|
||||
attribute [ext] ByteArray
|
||||
|
||||
@[extern "lean_sarray_dec_eq"]
|
||||
def decEq (lhs rhs : @& ByteArray) : Decidable (lhs = rhs) :=
|
||||
decidable_of_decidable_of_iff ByteArray.ext_iff.symm
|
||||
|
||||
instance : DecidableEq ByteArray := decEq
|
||||
instance : DecidableEq ByteArray :=
|
||||
fun _ _ => decidable_of_decidable_of_iff ByteArray.ext_iff.symm
|
||||
|
||||
instance : Inhabited ByteArray where
|
||||
default := empty
|
||||
|
||||
@@ -271,7 +271,7 @@ private def optionPelim' {α : Type u_1} (t : Option α) {β : Sort u_2}
|
||||
|
||||
/--
|
||||
Inserts an `Option` case distinction after the first computation of a call to `MonadAttach.pbind`.
|
||||
This lemma is useful for simplifying the second computation, which often involves `match` expressions
|
||||
This lemma is useful for simplifying the second computation, which often involes `match` expressions
|
||||
that use `pbind`'s proof term.
|
||||
-/
|
||||
private theorem pbind_eq_pbind_if_isSome [Monad m] [MonadAttach m] (x : m (Option α)) (f : (_ : _) → _ → m β) :
|
||||
|
||||
@@ -282,7 +282,6 @@ The lexicographic order with respect to `lt` is:
|
||||
* `as.lex [] = false` is `false`
|
||||
* `(a :: as).lex (b :: bs)` is true if `lt a b` or `a == b` and `lex lt as bs` is true.
|
||||
-/
|
||||
@[specialize]
|
||||
def lex [BEq α] (l₁ l₂ : List α) (lt : α → α → Bool := by exact (· < ·)) : Bool :=
|
||||
match l₁, l₂ with
|
||||
| [], _ :: _ => true
|
||||
@@ -1005,7 +1004,6 @@ Examples:
|
||||
* `[8, 3, 2, 4, 2, 7, 4].dropWhile (· < 4) = [8, 3, 2, 4, 2, 7, 4]`
|
||||
* `[8, 3, 2, 4, 2, 7, 4].dropWhile (· < 100) = []`
|
||||
-/
|
||||
@[specialize]
|
||||
def dropWhile (p : α → Bool) : List α → List α
|
||||
| [] => []
|
||||
| a::l => match p a with
|
||||
@@ -1462,11 +1460,9 @@ Examples:
|
||||
["circle", "square", "triangle"]
|
||||
```
|
||||
-/
|
||||
@[inline]
|
||||
def modifyTailIdx (l : List α) (i : Nat) (f : List α → List α) : List α :=
|
||||
go i l
|
||||
where
|
||||
@[specialize]
|
||||
go : Nat → List α → List α
|
||||
| 0, l => f l
|
||||
| _+1, [] => []
|
||||
@@ -1502,7 +1498,6 @@ Examples:
|
||||
* `[1, 2, 3].modify 2 (· * 10) = [1, 2, 30]`
|
||||
* `[1, 2, 3].modify 3 (· * 10) = [1, 2, 3]`
|
||||
-/
|
||||
@[inline]
|
||||
def modify (l : List α) (i : Nat) (f : α → α) : List α :=
|
||||
l.modifyTailIdx i (modifyHead f)
|
||||
|
||||
@@ -1609,7 +1604,6 @@ Examples:
|
||||
* `[7, 6, 5, 8, 1, 2, 6].find? (· < 5) = some 1`
|
||||
* `[7, 6, 5, 8, 1, 2, 6].find? (· < 1) = none`
|
||||
-/
|
||||
@[specialize]
|
||||
def find? (p : α → Bool) : List α → Option α
|
||||
| [] => none
|
||||
| a::as => match p a with
|
||||
@@ -1632,7 +1626,6 @@ Examples:
|
||||
* `[7, 6, 5, 8, 1, 2, 6].findSome? (fun x => if x < 5 then some (10 * x) else none) = some 10`
|
||||
* `[7, 6, 5, 8, 1, 2, 6].findSome? (fun x => if x < 1 then some (10 * x) else none) = none`
|
||||
-/
|
||||
@[specialize]
|
||||
def findSome? (f : α → Option β) : List α → Option β
|
||||
| [] => none
|
||||
| a::as => match f a with
|
||||
@@ -1656,7 +1649,6 @@ Examples:
|
||||
* `[7, 6, 5, 8, 1, 2, 6].findRev? (· < 5) = some 2`
|
||||
* `[7, 6, 5, 8, 1, 2, 6].findRev? (· < 1) = none`
|
||||
-/
|
||||
@[specialize]
|
||||
def findRev? (p : α → Bool) : List α → Option α
|
||||
| [] => none
|
||||
| a::as => match findRev? p as with
|
||||
@@ -1675,7 +1667,6 @@ Examples:
|
||||
* `[7, 6, 5, 8, 1, 2, 6].findSomeRev? (fun x => if x < 5 then some (10 * x) else none) = some 20`
|
||||
* `[7, 6, 5, 8, 1, 2, 6].findSomeRev? (fun x => if x < 1 then some (10 * x) else none) = none`
|
||||
-/
|
||||
@[specialize]
|
||||
def findSomeRev? (f : α → Option β) : List α → Option β
|
||||
| [] => none
|
||||
| a::as => match findSomeRev? f as with
|
||||
@@ -1726,11 +1717,9 @@ Examples:
|
||||
* `[7, 6, 5, 8, 1, 2, 6].findIdx (· < 5) = some 4`
|
||||
* `[7, 6, 5, 8, 1, 2, 6].findIdx (· < 1) = none`
|
||||
-/
|
||||
@[inline]
|
||||
def findIdx? (p : α → Bool) (l : List α) : Option Nat :=
|
||||
go l 0
|
||||
where
|
||||
@[specialize]
|
||||
go : List α → Nat → Option Nat
|
||||
| [], _ => none
|
||||
| a :: l, i => if p a then some i else go l (i + 1)
|
||||
@@ -1761,7 +1750,6 @@ Examples:
|
||||
@[inline] def findFinIdx? (p : α → Bool) (l : List α) : Option (Fin l.length) :=
|
||||
go l 0 (by simp)
|
||||
where
|
||||
@[specialize]
|
||||
go : (l' : List α) → (i : Nat) → (h : l'.length + i = l.length) → Option (Fin l.length)
|
||||
| [], _, _ => none
|
||||
| a :: l, i, h =>
|
||||
@@ -1898,7 +1886,7 @@ Examples:
|
||||
* `[2, 4, 5, 6].any (· % 2 = 0) = true`
|
||||
* `[2, 4, 5, 6].any (· % 2 = 1) = true`
|
||||
-/
|
||||
@[suggest_for List.some, specialize]
|
||||
@[suggest_for List.some]
|
||||
def any : (l : List α) → (p : α → Bool) → Bool
|
||||
| [], _ => false
|
||||
| h :: t, p => p h || any t p
|
||||
@@ -1918,7 +1906,7 @@ Examples:
|
||||
* `[2, 4, 6].all (· % 2 = 0) = true`
|
||||
* `[2, 4, 5, 6].all (· % 2 = 0) = false`
|
||||
-/
|
||||
@[suggest_for List.every, specialize]
|
||||
@[suggest_for List.every]
|
||||
def all : List α → (α → Bool) → Bool
|
||||
| [], _ => true
|
||||
| h :: t, p => p h && all t p
|
||||
@@ -2019,7 +2007,6 @@ Examples:
|
||||
* `[1, 2, 3].zipWithAll Prod.mk [5, 6] = [(some 1, some 5), (some 2, some 6), (some 3, none)]`
|
||||
* `[x₁, x₂].zipWithAll f [y] = [f (some x₁) (some y), f (some x₂) none]`
|
||||
-/
|
||||
@[specialize]
|
||||
def zipWithAll (f : Option α → Option β → γ) : List α → List β → List γ
|
||||
| [], bs => bs.map fun b => f none (some b)
|
||||
| a :: as, [] => (a :: as).map fun a => f (some a) none
|
||||
@@ -2069,20 +2056,6 @@ def sum {α} [Add α] [Zero α] : List α → α :=
|
||||
@[simp, grind =] theorem sum_cons [Add α] [Zero α] {a : α} {l : List α} : (a::l).sum = a + l.sum := rfl
|
||||
theorem sum_eq_foldr [Add α] [Zero α] {l : List α} : l.sum = l.foldr (· + ·) 0 := rfl
|
||||
|
||||
/--
|
||||
Computes the product of the elements of a list.
|
||||
|
||||
Examples:
|
||||
* `[a, b, c].prod = a * (b * (c * 1))`
|
||||
* `[1, 2, 5].prod = 10`
|
||||
-/
|
||||
def prod {α} [Mul α] [One α] : List α → α :=
|
||||
foldr (· * ·) 1
|
||||
|
||||
@[simp, grind =] theorem prod_nil [Mul α] [One α] : ([] : List α).prod = 1 := rfl
|
||||
@[simp, grind =] theorem prod_cons [Mul α] [One α] {a : α} {l : List α} : (a::l).prod = a * l.prod := rfl
|
||||
theorem prod_eq_foldr [Mul α] [One α] {l : List α} : l.prod = l.foldr (· * ·) 1 := rfl
|
||||
|
||||
/-! ### range -/
|
||||
|
||||
/--
|
||||
|
||||
@@ -444,8 +444,8 @@ theorem findM?_eq_findSomeM? [Monad m] [LawfulMonad m] {p : α → m Bool} {as :
|
||||
intro b
|
||||
cases b <;> simp
|
||||
|
||||
@[inline, expose] protected def forIn' {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : @& List α) (init : β) (f : (a : α) → a ∈ as → β → m (ForInStep β)) : m β :=
|
||||
let rec @[specialize] loop : (as' : @& List α) → (b : β) → Exists (fun bs => bs ++ as' = as) → m β
|
||||
@[inline, expose] protected def forIn' {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : List α) (init : β) (f : (a : α) → a ∈ as → β → m (ForInStep β)) : m β :=
|
||||
let rec @[specialize] loop : (as' : List α) → (b : β) → Exists (fun bs => bs ++ as' = as) → m β
|
||||
| [], b, _ => pure b
|
||||
| a::as', b, h => do
|
||||
have : a ∈ as := by
|
||||
|
||||
@@ -7,4 +7,3 @@ module
|
||||
|
||||
prelude
|
||||
public import Init.Data.List.Int.Sum
|
||||
public import Init.Data.List.Int.Prod
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
import Init.Data.Int.Lemmas
|
||||
public import Init.Data.Int.Pow
|
||||
public import Init.Data.List.Basic
|
||||
|
||||
public section
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace List
|
||||
|
||||
@[simp]
|
||||
theorem prod_replicate_int {n : Nat} {a : Int} : (replicate n a).prod = a ^ n := by
|
||||
induction n <;> simp_all [replicate_succ, Int.pow_succ, Int.mul_comm]
|
||||
|
||||
theorem prod_append_int {l₁ l₂ : List Int} : (l₁ ++ l₂).prod = l₁.prod * l₂.prod := by
|
||||
simp [prod_append]
|
||||
|
||||
theorem prod_reverse_int (xs : List Int) : xs.reverse.prod = xs.prod := by
|
||||
simp [prod_reverse]
|
||||
|
||||
end List
|
||||
@@ -1878,24 +1878,6 @@ theorem sum_reverse [Zero α] [Add α] [Std.Associative (α := α) (· + ·)]
|
||||
simp_all [sum_append, Std.Commutative.comm (α := α) _ 0,
|
||||
Std.LawfulLeftIdentity.left_id, Std.Commutative.comm]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_append [Mul α] [One α] [Std.LawfulLeftIdentity (α := α) (· * ·) 1]
|
||||
[Std.Associative (α := α) (· * ·)] {l₁ l₂ : List α} : (l₁ ++ l₂).prod = l₁.prod * l₂.prod := by
|
||||
induction l₁ generalizing l₂ <;> simp_all [Std.Associative.assoc, Std.LawfulLeftIdentity.left_id]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_singleton [Mul α] [One α] [Std.LawfulRightIdentity (· * ·) (1 : α)] {x : α} :
|
||||
[x].prod = x := by
|
||||
simp [List.prod_eq_foldr, Std.LawfulRightIdentity.right_id x]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_reverse [One α] [Mul α] [Std.Associative (α := α) (· * ·)]
|
||||
[Std.Commutative (α := α) (· * ·)]
|
||||
[Std.LawfulLeftIdentity (α := α) (· * ·) 1] (xs : List α) : xs.reverse.prod = xs.prod := by
|
||||
induction xs <;>
|
||||
simp_all [prod_append, Std.Commutative.comm (α := α) _ 1,
|
||||
Std.LawfulLeftIdentity.left_id, Std.Commutative.comm]
|
||||
|
||||
/-! ### concat
|
||||
|
||||
Note that `concat_eq_append` is a `@[simp]` lemma, so `concat` should usually not appear in goals.
|
||||
@@ -2802,11 +2784,6 @@ theorem sum_eq_foldl [Zero α] [Add α] [Std.Associative (α := α) (· + ·)]
|
||||
xs.sum = xs.foldl (init := 0) (· + ·) := by
|
||||
simp [sum_eq_foldr, foldl_eq_apply_foldr, Std.LawfulLeftIdentity.left_id]
|
||||
|
||||
theorem prod_eq_foldl [One α] [Mul α] [Std.Associative (α := α) (· * ·)]
|
||||
[Std.LawfulIdentity (· * ·) (1 : α)] {xs : List α} :
|
||||
xs.prod = xs.foldl (init := 1) (· * ·) := by
|
||||
simp [prod_eq_foldr, foldl_eq_apply_foldr, Std.LawfulLeftIdentity.left_id]
|
||||
|
||||
-- The argument `f : α₁ → α₂` is intentionally explicit, as it is sometimes not found by unification.
|
||||
theorem foldl_hom (f : α₁ → α₂) {g₁ : α₁ → β → α₁} {g₂ : α₂ → β → α₂} {l : List β} {init : α₁}
|
||||
(H : ∀ x y, g₂ (f x) y = f (g₁ x y)) : l.foldl g₂ (f init) = f (l.foldl g₁ init) := by
|
||||
|
||||
@@ -13,7 +13,6 @@ public import Init.Data.List.Nat.Sublist
|
||||
public import Init.Data.List.Nat.TakeDrop
|
||||
public import Init.Data.List.Nat.Count
|
||||
public import Init.Data.List.Nat.Sum
|
||||
public import Init.Data.List.Nat.Prod
|
||||
public import Init.Data.List.Nat.Erase
|
||||
public import Init.Data.List.Nat.Find
|
||||
public import Init.Data.List.Nat.BEq
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Kim Morrison
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
import Init.Data.List.Lemmas
|
||||
public import Init.BinderPredicates
|
||||
public import Init.NotationExtra
|
||||
import Init.Data.Nat.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
|
||||
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
|
||||
|
||||
namespace List
|
||||
|
||||
protected theorem prod_eq_zero_iff_exists_zero_nat {l : List Nat} : l.prod = 0 ↔ ∃ x ∈ l, x = 0 := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp [Nat.mul_eq_zero, ih, eq_comm (a := (0 : Nat))]
|
||||
|
||||
protected theorem prod_pos_iff_forall_pos_nat {l : List Nat} : 0 < l.prod ↔ ∀ x ∈ l, 0 < x := by
|
||||
induction l with
|
||||
| nil => simp
|
||||
| cons x xs ih =>
|
||||
simp only [prod_cons, mem_cons, forall_eq_or_imp, ← ih]
|
||||
constructor
|
||||
· intro h
|
||||
exact ⟨Nat.pos_of_mul_pos_right h, Nat.pos_of_mul_pos_left h⟩
|
||||
· exact fun ⟨hx, hxs⟩ => Nat.mul_pos hx hxs
|
||||
|
||||
@[simp]
|
||||
theorem prod_replicate_nat {n : Nat} {a : Nat} : (replicate n a).prod = a ^ n := by
|
||||
induction n <;> simp_all [replicate_succ, Nat.pow_succ, Nat.mul_comm]
|
||||
|
||||
theorem prod_append_nat {l₁ l₂ : List Nat} : (l₁ ++ l₂).prod = l₁.prod * l₂.prod := by
|
||||
simp [prod_append]
|
||||
|
||||
theorem prod_reverse_nat (xs : List Nat) : xs.reverse.prod = xs.prod := by
|
||||
simp [prod_reverse]
|
||||
|
||||
theorem prod_eq_foldl_nat {xs : List Nat} : xs.prod = xs.foldl (init := 1) (· * ·) := by
|
||||
simp only [foldl_eq_foldr_reverse, Nat.mul_comm, ← prod_eq_foldr, prod_reverse_nat]
|
||||
|
||||
end List
|
||||
@@ -606,13 +606,6 @@ theorem sum_nat {l₁ l₂ : List Nat} (h : l₁ ~ l₂) : l₁.sum = l₂.sum :
|
||||
| swap => simpa [List.sum_cons] using Nat.add_left_comm ..
|
||||
| trans _ _ ih₁ ih₂ => simp [ih₁, ih₂]
|
||||
|
||||
theorem prod_nat {l₁ l₂ : List Nat} (h : l₁ ~ l₂) : l₁.prod = l₂.prod := by
|
||||
induction h with
|
||||
| nil => simp
|
||||
| cons _ _ ih => simp [ih]
|
||||
| swap => simpa [List.prod_cons] using Nat.mul_left_comm ..
|
||||
| trans _ _ ih₁ ih₂ => simp [ih₁, ih₂]
|
||||
|
||||
theorem all_eq {l₁ l₂ : List α} {f : α → Bool} (hp : l₁.Perm l₂) : l₁.all f = l₂.all f := by
|
||||
rw [Bool.eq_iff_iff]; simp [hp.mem_iff]
|
||||
|
||||
@@ -622,9 +615,6 @@ theorem any_eq {l₁ l₂ : List α} {f : α → Bool} (hp : l₁.Perm l₂) : l
|
||||
grind_pattern Perm.sum_nat => l₁ ~ l₂, l₁.sum
|
||||
grind_pattern Perm.sum_nat => l₁ ~ l₂, l₂.sum
|
||||
|
||||
grind_pattern Perm.prod_nat => l₁ ~ l₂, l₁.prod
|
||||
grind_pattern Perm.prod_nat => l₁ ~ l₂, l₂.prod
|
||||
|
||||
end Perm
|
||||
|
||||
end List
|
||||
|
||||
@@ -213,9 +213,6 @@ theorem forM_toArray [Monad m] (l : List α) (f : α → m PUnit) :
|
||||
@[simp, grind =] theorem sum_toArray [Add α] [Zero α] (l : List α) : l.toArray.sum = l.sum := by
|
||||
simp [Array.sum, List.sum]
|
||||
|
||||
@[simp, grind =] theorem prod_toArray [Mul α] [One α] (l : List α) : l.toArray.prod = l.prod := by
|
||||
simp [Array.prod, List.prod]
|
||||
|
||||
@[simp, grind =] theorem append_toArray (l₁ l₂ : List α) :
|
||||
l₁.toArray ++ l₂.toArray = (l₁ ++ l₂).toArray := by
|
||||
apply ext'
|
||||
|
||||
@@ -60,7 +60,7 @@ theorem gcd_def (x y : Nat) : gcd x y = if x = 0 then y else gcd (y % x) x := by
|
||||
cases n with
|
||||
| zero => simp
|
||||
| succ n =>
|
||||
-- `simp [gcd_succ]` produces an invalid term unless `gcd_succ` is proved with `(rfl)` instead
|
||||
-- `simp [gcd_succ]` produces an invalid term unless `gcd_succ` is proved with `id rfl` instead
|
||||
rw [gcd_succ]
|
||||
exact gcd_zero_left _
|
||||
instance : Std.LawfulIdentity gcd 0 where
|
||||
|
||||
@@ -9,7 +9,7 @@ prelude
|
||||
public import Init.Data.Order.Ord
|
||||
public import Init.Data.String.Basic
|
||||
import Init.Data.Char.Lemmas
|
||||
import Init.Data.String.Lemmas.StringOrder
|
||||
import Init.Data.String.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
|
||||
@@ -243,10 +243,6 @@ public theorem lt_iff_le_and_ne [LE α] [LT α] [LawfulOrderLT α] [IsPartialOrd
|
||||
a < b ↔ a ≤ b ∧ a ≠ b := by
|
||||
simpa [le_iff_lt_or_eq, or_and_right] using Std.ne_of_lt
|
||||
|
||||
public theorem lt_trichotomy [LT α] [Std.Trichotomous (α := α) (· < ·)] (a b : α) :
|
||||
a < b ∨ a = b ∨ b < a :=
|
||||
Trichotomous.rel_or_eq_or_rel_swap
|
||||
|
||||
end LT
|
||||
end Std
|
||||
|
||||
|
||||
@@ -1718,7 +1718,7 @@ theorem toArray_roc_append_toArray_roc {l m n : Nat} (h : l ≤ m) (h' : m ≤ n
|
||||
@[simp]
|
||||
theorem getElem_toArray_roc {m n i : Nat} (_h : i < (m<...=n).toArray.size) :
|
||||
(m<...=n).toArray[i]'_h = m + 1 + i := by
|
||||
simp [toArray_roc_eq_toArray_rco]
|
||||
simp [toArray_roc_eq_toArray_rco]
|
||||
|
||||
theorem getElem?_toArray_roc {m n i : Nat} :
|
||||
(m<...=n).toArray[i]? = if i < n - m then some (m + 1 + i) else none := by
|
||||
|
||||
@@ -1386,11 +1386,6 @@ theorem Slice.copy_eq_copy_sliceTo {s : Slice} {pos : s.Pos} :
|
||||
rw [Nat.max_eq_right]
|
||||
exact pos.offset_str_le_offset_endExclusive
|
||||
|
||||
@[simp]
|
||||
theorem Slice.sliceTo_append_sliceFrom {s : Slice} {pos : s.Pos} :
|
||||
(s.sliceTo pos).copy ++ (s.sliceFrom pos).copy = s.copy :=
|
||||
copy_eq_copy_sliceTo.symm
|
||||
|
||||
/-- Given a slice `s` and a position on `s.copy`, obtain the corresponding position on `s`. -/
|
||||
@[inline]
|
||||
def Pos.ofCopy {s : Slice} (pos : s.copy.Pos) : s.Pos where
|
||||
@@ -1750,31 +1745,6 @@ theorem Slice.Pos.offset_cast {s t : Slice} {pos : s.Pos} {h : s.copy = t.copy}
|
||||
theorem Slice.Pos.cast_rfl {s : Slice} {pos : s.Pos} : pos.cast rfl = pos :=
|
||||
Slice.Pos.ext (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_cast {s t u : Slice} {hst : s.copy = t.copy} {htu : t.copy = u.copy}
|
||||
{pos : s.Pos} : (pos.cast hst).cast htu = pos.cast (hst.trans htu) :=
|
||||
Slice.Pos.ext (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_inj {s t : Slice} {hst : s.copy = t.copy} {p q : s.Pos} : p.cast hst = q.cast hst ↔ p = q := by
|
||||
simp [Slice.Pos.ext_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_startPos {s t : Slice} {hst : s.copy = t.copy} : s.startPos.cast hst = t.startPos :=
|
||||
Slice.Pos.ext (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_eq_startPos {s t : Slice} {p : s.Pos} {hst : s.copy = t.copy} : p.cast hst = t.startPos ↔ p = s.startPos := by
|
||||
rw [← cast_startPos (hst := hst), Pos.cast_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_endPos {s t : Slice} {hst : s.copy = t.copy} : s.endPos.cast hst = t.endPos :=
|
||||
Slice.Pos.ext (by simp [← rawEndPos_copy, hst])
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_eq_endPos {s t : Slice} {p : s.Pos} {hst : s.copy = t.copy} : p.cast hst = t.endPos ↔ p = s.endPos := by
|
||||
rw [← cast_endPos (hst := hst), Pos.cast_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_le_cast_iff {s t : Slice} {pos pos' : s.Pos} {h : s.copy = t.copy} :
|
||||
pos.cast h ≤ pos'.cast h ↔ pos ≤ pos' := by
|
||||
@@ -1785,22 +1755,6 @@ theorem Slice.Pos.cast_lt_cast_iff {s t : Slice} {pos pos' : s.Pos} {h : s.copy
|
||||
pos.cast h < pos'.cast h ↔ pos < pos' := by
|
||||
simp [Slice.Pos.lt_iff]
|
||||
|
||||
theorem Slice.Pos.cast_le_iff {s t : Slice} {pos : s.Pos} {pos' : t.Pos} {h : s.copy = t.copy} :
|
||||
pos.cast h ≤ pos' ↔ pos ≤ pos'.cast h.symm := by
|
||||
simp [Slice.Pos.le_iff]
|
||||
|
||||
theorem Slice.Pos.le_cast_iff {s t : Slice} {pos : t.Pos} {pos' : s.Pos} {h : s.copy = t.copy} :
|
||||
pos ≤ pos'.cast h ↔ pos.cast h.symm ≤ pos' := by
|
||||
simp [Slice.Pos.le_iff]
|
||||
|
||||
theorem Slice.Pos.cast_lt_iff {s t : Slice} {pos : s.Pos} {pos' : t.Pos} {h : s.copy = t.copy} :
|
||||
pos.cast h < pos' ↔ pos < pos'.cast h.symm := by
|
||||
simp [Slice.Pos.lt_iff]
|
||||
|
||||
theorem Slice.Pos.lt_cast_iff {s t : Slice} {pos : t.Pos} {pos' : s.Pos} {h : s.copy = t.copy} :
|
||||
pos < pos'.cast h ↔ pos.cast h.symm < pos' := by
|
||||
simp [Slice.Pos.lt_iff]
|
||||
|
||||
/-- Constructs a valid position on `t` from a valid position on `s` and a proof that `s = t`. -/
|
||||
@[inline]
|
||||
def Pos.cast {s t : String} (pos : s.Pos) (h : s = t) : t.Pos where
|
||||
@@ -1815,31 +1769,6 @@ theorem Pos.offset_cast {s t : String} {pos : s.Pos} {h : s = t} :
|
||||
theorem Pos.cast_rfl {s : String} {pos : s.Pos} : pos.cast rfl = pos :=
|
||||
Pos.ext (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_cast {s t u : String} {hst : s = t} {htu : t = u}
|
||||
{pos : s.Pos} : (pos.cast hst).cast htu = pos.cast (hst.trans htu) :=
|
||||
Pos.ext (by simp)
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_inj {s t : String} {hst : s = t} {p q : s.Pos} : p.cast hst = q.cast hst ↔ p = q := by
|
||||
simp [Pos.ext_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_startPos {s t : String} {hst : s = t} : s.startPos.cast hst = t.startPos := by
|
||||
subst hst; simp
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_eq_startPos {s t : String} {hst : s = t} {p : s.Pos} : p.cast hst = t.startPos ↔ p = s.startPos := by
|
||||
rw [← Pos.cast_startPos (hst := hst), Pos.cast_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_endPos {s t : String} {hst : s = t} : s.endPos.cast hst = t.endPos := by
|
||||
subst hst; simp
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_eq_endPos {s t : String} {hst : s = t} {p : s.Pos} : p.cast hst = t.endPos ↔ p = s.endPos := by
|
||||
rw [← Pos.cast_endPos (hst := hst), Pos.cast_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.cast_le_cast_iff {s t : String} {pos pos' : s.Pos} {h : s = t} :
|
||||
pos.cast h ≤ pos'.cast h ↔ pos ≤ pos' := by
|
||||
@@ -1850,22 +1779,6 @@ theorem Pos.cast_lt_cast_iff {s t : String} {pos pos' : s.Pos} {h : s = t} :
|
||||
pos.cast h < pos'.cast h ↔ pos < pos' := by
|
||||
cases h; simp
|
||||
|
||||
theorem Pos.cast_le_iff {s t : String} {pos : s.Pos} {pos' : t.Pos} {h : s = t} :
|
||||
pos.cast h ≤ pos' ↔ pos ≤ pos'.cast h.symm := by
|
||||
simp [Pos.le_iff]
|
||||
|
||||
theorem Pos.le_cast_iff {s t : String} {pos : t.Pos} {pos' : s.Pos} {h : s = t} :
|
||||
pos ≤ pos'.cast h ↔ pos.cast h.symm ≤ pos' := by
|
||||
simp [Pos.le_iff]
|
||||
|
||||
theorem Pos.cast_lt_iff {s t : String} {pos : s.Pos} {pos' : t.Pos} {h : s = t} :
|
||||
pos.cast h < pos' ↔ pos < pos'.cast h.symm := by
|
||||
simp [Pos.lt_iff]
|
||||
|
||||
theorem Pos.lt_cast_iff {s t : String} {pos : t.Pos} {pos' : s.Pos} {h : s = t} :
|
||||
pos < pos'.cast h ↔ pos.cast h.symm < pos' := by
|
||||
simp [Pos.lt_iff]
|
||||
|
||||
theorem Pos.copy_toSlice_eq_cast {s : String} (p : s.Pos) :
|
||||
p.toSlice.copy = p.cast copy_toSlice.symm :=
|
||||
Pos.ext (by simp)
|
||||
@@ -2141,10 +2054,6 @@ theorem Pos.le_ofToSlice_iff {s : String} {p : s.Pos} {q : s.toSlice.Pos} :
|
||||
theorem Pos.toSlice_lt_toSlice_iff {s : String} {p q : s.Pos} :
|
||||
p.toSlice < q.toSlice ↔ p < q := Iff.rfl
|
||||
|
||||
@[simp]
|
||||
theorem Pos.toSlice_le_toSlice_iff {s : String} {p q : s.Pos} :
|
||||
p.toSlice ≤ q.toSlice ↔ p ≤ q := Iff.rfl
|
||||
|
||||
theorem Pos.next_le_of_lt {s : String} {p q : s.Pos} {h} : p < q → p.next h ≤ q := by
|
||||
rw [next, Pos.ofToSlice_le_iff, ← Pos.toSlice_lt_toSlice_iff]
|
||||
exact Slice.Pos.next_le_of_lt
|
||||
|
||||
@@ -363,7 +363,7 @@ theorem toBitVec_eq_of_parseFirstByte_eq_threeMore {b : UInt8} (h : parseFirstBy
|
||||
public def isInvalidContinuationByte (b : UInt8) : Bool :=
|
||||
b &&& 0xc0 != 0x80
|
||||
|
||||
theorem isInvalidContinuationByte_eq_false_iff {b : UInt8} :
|
||||
theorem isInvalidContinutationByte_eq_false_iff {b : UInt8} :
|
||||
isInvalidContinuationByte b = false ↔ b &&& 0xc0 = 0x80 := by
|
||||
simp [isInvalidContinuationByte]
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ namespace Std
|
||||
/--
|
||||
Appends all the elements in the iterator, in order.
|
||||
-/
|
||||
@[inline]
|
||||
public def Iter.joinString {α β : Type} [Iterator α Id β] [ToString β]
|
||||
(it : Std.Iter (α := α) β) : String :=
|
||||
(it.map toString).fold (init := "") (· ++ ·)
|
||||
|
||||
@@ -20,4 +20,49 @@ public import Init.Data.String.Lemmas.Intercalate
|
||||
public import Init.Data.String.Lemmas.Iter
|
||||
public import Init.Data.String.Lemmas.Hashable
|
||||
public import Init.Data.String.Lemmas.TakeDrop
|
||||
public import Init.Data.String.Lemmas.StringOrder
|
||||
import Init.Data.Order.Lemmas
|
||||
public import Init.Data.String.Basic
|
||||
import Init.Data.Char.Lemmas
|
||||
import Init.Data.Char.Order
|
||||
import Init.Data.List.Lex
|
||||
|
||||
public section
|
||||
|
||||
open Std
|
||||
|
||||
namespace String
|
||||
|
||||
@[deprecated toList_inj (since := "2025-10-30")]
|
||||
protected theorem data_eq_of_eq {a b : String} (h : a = b) : a.toList = b.toList :=
|
||||
h ▸ rfl
|
||||
@[deprecated toList_inj (since := "2025-10-30")]
|
||||
protected theorem ne_of_data_ne {a b : String} (h : a.toList ≠ b.toList) : a ≠ b := by
|
||||
simpa [← toList_inj]
|
||||
|
||||
@[simp] protected theorem not_le {a b : String} : ¬ a ≤ b ↔ b < a := Decidable.not_not
|
||||
@[simp] protected theorem not_lt {a b : String} : ¬ a < b ↔ b ≤ a := Iff.rfl
|
||||
@[simp] protected theorem le_refl (a : String) : a ≤ a := List.le_refl _
|
||||
@[simp] protected theorem lt_irrefl (a : String) : ¬ a < a := List.lt_irrefl _
|
||||
|
||||
attribute [local instance] Char.notLTTrans Char.ltTrichotomous Char.ltAsymm
|
||||
|
||||
protected theorem le_trans {a b c : String} : a ≤ b → b ≤ c → a ≤ c := List.le_trans
|
||||
protected theorem lt_trans {a b c : String} : a < b → b < c → a < c := List.lt_trans
|
||||
protected theorem le_total (a b : String) : a ≤ b ∨ b ≤ a := List.le_total _ _
|
||||
protected theorem le_antisymm {a b : String} : a ≤ b → b ≤ a → a = b := fun h₁ h₂ => String.ext (List.le_antisymm (as := a.toList) (bs := b.toList) h₁ h₂)
|
||||
protected theorem lt_asymm {a b : String} (h : a < b) : ¬ b < a := List.lt_asymm h
|
||||
protected theorem ne_of_lt {a b : String} (h : a < b) : a ≠ b := by
|
||||
have := String.lt_irrefl a
|
||||
intro h; subst h; contradiction
|
||||
|
||||
instance instIsLinearOrder : IsLinearOrder String := by
|
||||
apply IsLinearOrder.of_le
|
||||
case le_antisymm => constructor; apply String.le_antisymm
|
||||
case le_trans => constructor; apply String.le_trans
|
||||
case le_total => constructor; apply String.le_total
|
||||
|
||||
instance : LawfulOrderLT String where
|
||||
lt_iff a b := by
|
||||
simp [← String.not_le, Decidable.imp_iff_not_or, Std.Total.total]
|
||||
|
||||
end String
|
||||
|
||||
@@ -22,10 +22,6 @@ public section
|
||||
|
||||
namespace String
|
||||
|
||||
@[simp]
|
||||
theorem singleton_inj {c d : Char} : singleton c = singleton d ↔ c = d := by
|
||||
simp [← toList_inj]
|
||||
|
||||
@[simp]
|
||||
theorem singleton_append_inj : singleton c ++ s = singleton d ++ t ↔ c = d ∧ s = t := by
|
||||
simp [← toList_inj]
|
||||
@@ -195,74 +191,18 @@ theorem sliceTo_slice {s : String} {p₁ p₂ h p} :
|
||||
theorem Slice.sliceFrom_startPos {s : Slice} : s.sliceFrom s.startPos = s := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem Slice.sliceFrom_eq_self_iff {s : Slice} {p : s.Pos} : s.sliceFrom p = s ↔ p = s.startPos := by
|
||||
refine ⟨?_, by rintro rfl; simp⟩
|
||||
rcases s with ⟨str, startInclusive, endExclusive, h⟩
|
||||
simp [sliceFrom, Slice.startPos, String.Pos.ext_iff, Pos.Raw.ext_iff, Slice.Pos.ext_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.sliceTo_endPos {s : Slice} : s.sliceTo s.endPos = s := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem Slice.sliceTo_eq_self_iff {s : Slice} {p : s.Pos} : s.sliceTo p = s ↔ p = s.endPos := by
|
||||
refine ⟨?_, by rintro rfl; simp⟩
|
||||
rcases s with ⟨str, startInclusive, endExclusive, h⟩
|
||||
simp [sliceTo, Slice.endPos, String.Pos.ext_iff, Pos.Raw.ext_iff, Slice.Pos.ext_iff,
|
||||
utf8ByteSize_eq]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem Slice.slice_startPos {s : Slice} {p : s.Pos} :
|
||||
s.slice s.startPos p (Pos.startPos_le _) = s.sliceTo p := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem Slice.slice_eq_self_iff {s : Slice} {p₁ p₂ : s.Pos} {h} :
|
||||
s.slice p₁ p₂ h = s ↔ p₁ = s.startPos ∧ p₂ = s.endPos := by
|
||||
refine ⟨?_, by rintro ⟨rfl, rfl⟩; simp⟩
|
||||
rcases s with ⟨str, startInclusive, endExclusive, h⟩
|
||||
simp [slice, Slice.endPos, String.Pos.ext_iff, Pos.Raw.ext_iff, Slice.Pos.ext_iff,
|
||||
utf8ByteSize_eq]
|
||||
omega
|
||||
|
||||
@[simp]
|
||||
theorem Slice.slice_endPos {s : Slice} {p : s.Pos} :
|
||||
s.slice p s.endPos (Pos.le_endPos _) = s.sliceFrom p := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem sliceFrom_startPos {s : String} : s.sliceFrom s.startPos = s := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem sliceFrom_eq_toSlice_iff {s : String} {p : s.Pos} : s.sliceFrom p = s.toSlice ↔ p = s.startPos := by
|
||||
simp [← sliceFrom_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem sliceTo_endPos {s : String} : s.sliceTo s.endPos = s := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem sliceTo_eq_toSlice_iff {s : String} {p : s.Pos} : s.sliceTo p = s.toSlice ↔ p = s.endPos := by
|
||||
simp [← sliceTo_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem slice_startPos {s : String} {p : s.Pos} :
|
||||
s.slice s.startPos p (Pos.startPos_le _) = s.sliceTo p := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem slice_endPos {s : String} {p : s.Pos} :
|
||||
s.slice p s.endPos (Pos.le_endPos _) = s.sliceFrom p := by
|
||||
ext <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem slice_eq_toSlice_iff {s : String} {p₁ p₂ : s.Pos} {h} :
|
||||
s.slice p₁ p₂ h = s.toSlice ↔ p₁ = s.startPos ∧ p₂ = s.endPos := by
|
||||
simp [← slice_toSlice]
|
||||
|
||||
end Iterate
|
||||
|
||||
theorem Slice.copy_eq_copy_slice {s : Slice} {pos₁ pos₂ : s.Pos} {h} :
|
||||
@@ -352,39 +292,4 @@ theorem nextn_endPos {s : String} : s.endPos.nextn n = s.endPos := by
|
||||
|
||||
end Pos
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.cast_toSlice_copy {s : Slice} {pos : s.Pos} :
|
||||
pos.copy.toSlice.cast (by simp) = pos := by
|
||||
ext; simp
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.sliceFrom_eq_startPos {s : Slice} {p : s.Pos} :
|
||||
(Pos.sliceFrom p p (Pos.le_refl _)) = Slice.startPos _ := by
|
||||
simp [← Pos.ofSliceFrom_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.sliceFrom_endPos {s : Slice} {p : s.Pos} :
|
||||
(Pos.sliceFrom p s.endPos (Pos.le_endPos _)) = Slice.endPos _ := by
|
||||
simp [← Pos.ofSliceFrom_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.sliceTo_startPos {s : Slice} {p : s.Pos} :
|
||||
(Pos.sliceTo p s.startPos (Pos.startPos_le _)) = Slice.startPos _ := by
|
||||
simp [← Pos.ofSliceTo_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.sliceTo_eq_endPos {s : Slice} {p : s.Pos} :
|
||||
(Pos.sliceTo p p (Pos.le_refl _)) = Slice.endPos _ := by
|
||||
simp [← Pos.ofSliceTo_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.slice_eq_startPos {s : Slice} {p₀ p₁ : s.Pos} {h} :
|
||||
(Pos.slice p₀ p₀ p₁ (Pos.le_refl _) h) = Slice.startPos _ := by
|
||||
simp [← Pos.ofSlice_inj]
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.slice_eq_endPos {s : Slice} {p₀ p₁ : s.Pos} {h} :
|
||||
(Pos.slice p₁ p₀ p₁ h (Pos.le_refl _)) = Slice.endPos _ := by
|
||||
simp [← Pos.ofSlice_inj]
|
||||
|
||||
end String
|
||||
|
||||
@@ -77,15 +77,6 @@ theorem join_cons : join (s :: l) = s ++ join l := by
|
||||
theorem toList_join {l : List String} : (String.join l).toList = l.flatMap String.toList := by
|
||||
induction l <;> simp_all
|
||||
|
||||
@[simp]
|
||||
theorem join_append {l m : List String} : String.join (l ++ m) = String.join l ++ String.join m := by
|
||||
simp [← toList_inj]
|
||||
|
||||
@[simp]
|
||||
theorem length_join {l : List String} : (String.join l).length = (l.map String.length).sum := by
|
||||
simp only [← length_toList, toList_join, List.length_flatMap]
|
||||
simp
|
||||
|
||||
namespace Slice
|
||||
|
||||
@[simp]
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Slice
|
||||
/--
|
||||
A list of all positions starting at {name}`p`.
|
||||
|
||||
This function is not meant to be used in actual programs. Actual programs should use
|
||||
This function is not meant to be used in actual progams. Actual programs should use
|
||||
{name}`Slice.positionsFrom` or {name}`Slice.positions`.
|
||||
-/
|
||||
protected def Model.positionsFrom {s : Slice} (p : s.Pos) : List { p : s.Pos // p ≠ s.endPos } :=
|
||||
@@ -206,7 +206,7 @@ end Slice
|
||||
/--
|
||||
A list of all positions starting at {name}`p`.
|
||||
|
||||
This function is not meant to be used in actual programs. Actual programs should use
|
||||
This function is not meant to be used in actual progams. Actual programs should use
|
||||
{name}`Slice.positionsFrom` or {name}`Slice.positions`.
|
||||
-/
|
||||
protected def Model.positionsFrom {s : String} (p : s.Pos) : List { p : s.Pos // p ≠ s.endPos } :=
|
||||
|
||||
@@ -368,41 +368,21 @@ theorem Slice.Pos.ofSliceTo_ne_endPos {s : Slice} {p₀ : s.Pos} {p : (s.sliceTo
|
||||
refine (lt_endPos_iff _).1 (Std.lt_of_lt_of_le ?_ (le_endPos p₀))
|
||||
simpa [← lt_endPos_iff, ← ofSliceTo_lt_ofSliceTo_iff] using h
|
||||
|
||||
theorem Slice.Pos.ne_endPos_of_sliceTo_ne_endPos {s : Slice} {p p₀ : s.Pos} {h₀}
|
||||
(h : Pos.sliceTo p₀ p h₀ ≠ Slice.endPos _) : p ≠ s.endPos := by
|
||||
rw [← Pos.ofSliceTo_sliceTo (h := h₀)]
|
||||
apply Pos.ofSliceTo_ne_endPos h
|
||||
|
||||
theorem Slice.Pos.ofSliceFrom_ne_startPos {s : Slice} {p₀ : s.Pos} {p : (s.sliceFrom p₀).Pos}
|
||||
(h : p ≠ (s.sliceFrom p₀).startPos) : Pos.ofSliceFrom p ≠ s.startPos := by
|
||||
refine (startPos_lt_iff _).1 (Std.lt_of_le_of_lt (startPos_le p₀) ?_)
|
||||
simpa [← startPos_lt_iff, ← ofSliceFrom_lt_ofSliceFrom_iff] using h
|
||||
|
||||
theorem Slice.Pos.ne_startPos_of_sliceFrom_ne_startPos {s : Slice} {p p₀ : s.Pos} {h₀}
|
||||
(h : Pos.sliceFrom p₀ p h₀ ≠ Slice.startPos _) : p ≠ s.startPos := by
|
||||
rw [← Pos.ofSliceFrom_sliceFrom (h := h₀)]
|
||||
apply Pos.ofSliceFrom_ne_startPos h
|
||||
|
||||
theorem Pos.ofSliceTo_ne_endPos {s : String} {p₀ : s.Pos} {p : (s.sliceTo p₀).Pos}
|
||||
(h : p ≠ (s.sliceTo p₀).endPos) : Pos.ofSliceTo p ≠ s.endPos := by
|
||||
refine (lt_endPos_iff _).1 (Std.lt_of_lt_of_le ?_ (le_endPos p₀))
|
||||
simpa [← Slice.Pos.lt_endPos_iff, ← ofSliceTo_lt_ofSliceTo_iff] using h
|
||||
|
||||
theorem Pos.ne_endPos_of_sliceTo_ne_endPos {s : String} {p p₀ : s.Pos} {h₀}
|
||||
(h : Pos.sliceTo p₀ p h₀ ≠ Slice.endPos _) : p ≠ s.endPos := by
|
||||
rw [← Pos.ofSliceTo_sliceTo (h := h₀)]
|
||||
apply Pos.ofSliceTo_ne_endPos h
|
||||
|
||||
theorem Pos.ofSliceFrom_ne_startPos {s : String} {p₀ : s.Pos} {p : (s.sliceFrom p₀).Pos}
|
||||
(h : p ≠ (s.sliceFrom p₀).startPos) : Pos.ofSliceFrom p ≠ s.startPos := by
|
||||
refine (startPos_lt_iff _).1 (Std.lt_of_le_of_lt (startPos_le p₀) ?_)
|
||||
simpa [← Slice.Pos.startPos_lt_iff, ← ofSliceFrom_lt_ofSliceFrom_iff] using h
|
||||
|
||||
theorem Pos.ne_startPos_of_sliceFrom_ne_startPos {s : String} {p p₀ : s.Pos} {h₀}
|
||||
(h : Pos.sliceFrom p₀ p h₀ ≠ Slice.startPos _) : p ≠ s.startPos := by
|
||||
rw [← Pos.ofSliceFrom_sliceFrom (h := h₀)]
|
||||
apply Pos.ofSliceFrom_ne_startPos h
|
||||
|
||||
theorem Slice.Pos.ofSliceTo_next {s : Slice} {p₀ : s.Pos} {p : (s.sliceTo p₀).Pos} {h} :
|
||||
Pos.ofSliceTo (p.next h) = (Pos.ofSliceTo p).next (ofSliceTo_ne_endPos h) := by
|
||||
rw [eq_comm, Pos.next_eq_iff]
|
||||
@@ -534,41 +514,21 @@ theorem Slice.Pos.ofSlice_ne_endPos {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.
|
||||
refine (lt_endPos_iff _).1 (Std.lt_of_lt_of_le ?_ (le_endPos p₁))
|
||||
simpa [← lt_endPos_iff, ← ofSlice_lt_ofSlice_iff] using h
|
||||
|
||||
theorem Slice.Pos.ne_endPos_of_slice_ne_endPos {s : Slice} {p p₀ p₁ : s.Pos} {h₁ h₂}
|
||||
(h : Pos.slice p p₀ p₁ h₁ h₂ ≠ Slice.endPos _) : p ≠ s.endPos := by
|
||||
rw [← Pos.ofSlice_slice (h₁ := h₁) (h₂ := h₂)]
|
||||
apply Pos.ofSlice_ne_endPos h
|
||||
|
||||
theorem Slice.Pos.ofSlice_ne_startPos {s : Slice} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos}
|
||||
(h : p ≠ (s.slice p₀ p₁ h).startPos) : Pos.ofSlice p ≠ s.startPos := by
|
||||
refine (startPos_lt_iff _).1 (Std.lt_of_le_of_lt (startPos_le p₀) ?_)
|
||||
simpa [← startPos_lt_iff, ← ofSlice_lt_ofSlice_iff] using h
|
||||
|
||||
theorem Slice.Pos.ne_startPos_of_slice_ne_startPos {s : Slice} {p p₀ p₁ : s.Pos} {h₁ h₂}
|
||||
(h : Pos.slice p p₀ p₁ h₁ h₂ ≠ Slice.startPos _) : p ≠ s.startPos := by
|
||||
rw [← Pos.ofSlice_slice (h₁ := h₁) (h₂ := h₂)]
|
||||
apply Pos.ofSlice_ne_startPos h
|
||||
|
||||
theorem Pos.ofSlice_ne_endPos {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos}
|
||||
(h : p ≠ (s.slice p₀ p₁ h).endPos) : Pos.ofSlice p ≠ s.endPos := by
|
||||
refine (lt_endPos_iff _).1 (Std.lt_of_lt_of_le ?_ (le_endPos p₁))
|
||||
simpa [← Slice.Pos.lt_endPos_iff, ← ofSlice_lt_ofSlice_iff] using h
|
||||
|
||||
theorem Pos.ne_endPos_of_slice_ne_endPos {s : String} {p p₀ p₁ : s.Pos} {h₁ h₂}
|
||||
(h : Pos.slice p p₀ p₁ h₁ h₂ ≠ Slice.endPos _) : p ≠ s.endPos := by
|
||||
rw [← Pos.ofSlice_slice (h₁ := h₁) (h₂ := h₂)]
|
||||
apply Pos.ofSlice_ne_endPos h
|
||||
|
||||
theorem Pos.ofSlice_ne_startPos {s : String} {p₀ p₁ : s.Pos} {h} {p : (s.slice p₀ p₁ h).Pos}
|
||||
(h : p ≠ (s.slice p₀ p₁ h).startPos) : Pos.ofSlice p ≠ s.startPos := by
|
||||
refine (startPos_lt_iff _).1 (Std.lt_of_le_of_lt (startPos_le p₀) ?_)
|
||||
simpa [← Slice.Pos.startPos_lt_iff, ← ofSlice_lt_ofSlice_iff] using h
|
||||
|
||||
theorem Pos.ne_startPos_of_slice_ne_startPos {s : String} {p p₀ p₁ : s.Pos} {h₁ h₂}
|
||||
(h : Pos.slice p p₀ p₁ h₁ h₂ ≠ Slice.startPos _) : p ≠ s.startPos := by
|
||||
rw [← Pos.ofSlice_slice (h₁ := h₁) (h₂ := h₂)]
|
||||
apply Pos.ofSlice_ne_startPos h
|
||||
|
||||
@[simp]
|
||||
theorem Slice.Pos.offset_le_rawEndPos {s : Slice} {p : s.Pos} :
|
||||
p.offset ≤ s.rawEndPos :=
|
||||
@@ -621,37 +581,21 @@ theorem Slice.Pos.get_eq_get_ofSliceTo {s : Slice} {p₀ : s.Pos} {pos : (s.slic
|
||||
pos.get h = (ofSliceTo pos).get (ofSliceTo_ne_endPos h) := by
|
||||
simp [Slice.Pos.get]
|
||||
|
||||
theorem Slice.Pos.get_sliceTo {s : Slice} {p₀ p : s.Pos} {h h'} :
|
||||
(Pos.sliceTo p₀ p h).get h' = p.get (ne_endPos_of_sliceTo_ne_endPos h') := by
|
||||
simp [get_eq_get_ofSliceTo]
|
||||
|
||||
theorem Pos.get_eq_get_ofSliceTo {s : String} {p₀ : s.Pos}
|
||||
{pos : (s.sliceTo p₀).Pos} {h} :
|
||||
pos.get h = (ofSliceTo pos).get (ofSliceTo_ne_endPos h) := by
|
||||
simp [Pos.get, Slice.Pos.get]
|
||||
|
||||
theorem Pos.get_sliceTo {s : String} {p₀ p : s.Pos} {h h'} :
|
||||
(Pos.sliceTo p₀ p h).get h' = p.get (ne_endPos_of_sliceTo_ne_endPos h') := by
|
||||
simp [get_eq_get_ofSliceTo]
|
||||
|
||||
theorem Slice.Pos.get_eq_get_ofSlice {s : Slice} {p₀ p₁ : s.Pos} {h}
|
||||
{pos : (s.slice p₀ p₁ h).Pos} {h'} :
|
||||
pos.get h' = (ofSlice pos).get (ofSlice_ne_endPos h') := by
|
||||
simp [Slice.Pos.get, Nat.add_assoc]
|
||||
|
||||
theorem Slice.Pos.get_slice {s : Slice} {p p₀ p₁ : s.Pos} {h₁ h₂ h} :
|
||||
(Pos.slice p p₀ p₁ h₁ h₂).get h = p.get (ne_endPos_of_slice_ne_endPos h) := by
|
||||
simp [get_eq_get_ofSlice]
|
||||
|
||||
theorem Pos.get_eq_get_ofSlice {s : String} {p₀ p₁ : s.Pos} {h}
|
||||
{pos : (s.slice p₀ p₁ h).Pos} {h'} :
|
||||
pos.get h' = (ofSlice pos).get (ofSlice_ne_endPos h') := by
|
||||
simp [Pos.get, Slice.Pos.get]
|
||||
|
||||
theorem Pos.get_slice {s : String} {p p₀ p₁ : s.Pos} {h₁ h₂ h} :
|
||||
(Pos.slice p p₀ p₁ h₁ h₂).get h = p.get (ne_endPos_of_slice_ne_endPos h) := by
|
||||
simp [get_eq_get_ofSlice]
|
||||
|
||||
theorem Slice.Pos.ofSlice_next {s : Slice} {p₀ p₁ : s.Pos} {h}
|
||||
{p : (s.slice p₀ p₁ h).Pos} {h'} :
|
||||
Pos.ofSlice (p.next h') = (Pos.ofSlice p).next (ofSlice_ne_endPos h') := by
|
||||
|
||||
@@ -12,7 +12,7 @@ public import Init.Data.Iterators.Consumers.Collect
|
||||
import all Init.Data.String.Pattern.Basic
|
||||
import Init.Data.String.OrderInstances
|
||||
import Init.Data.String.Lemmas.IsEmpty
|
||||
public import Init.Data.String.Lemmas.Basic
|
||||
import Init.Data.String.Lemmas.Basic
|
||||
import Init.Data.String.Lemmas.Order
|
||||
import Init.Data.String.Termination
|
||||
import Init.Data.Order.Lemmas
|
||||
@@ -40,7 +40,7 @@ framework.
|
||||
/--
|
||||
This data-carrying typeclass is used to give semantics to a pattern type that implements
|
||||
{name}`ForwardPattern` and/or {name}`ToForwardSearcher` by providing an abstract, not necessarily
|
||||
decidable {name}`PatternModel.Matches` predicate that implementations of {name}`ForwardPattern`
|
||||
decidable {name}`PatternModel.Matches` predicate that implementates of {name}`ForwardPattern`
|
||||
and {name}`ToForwardSearcher` can be validated against.
|
||||
|
||||
Correctness results for generic functions relying on the pattern infrastructure, for example the
|
||||
@@ -52,23 +52,19 @@ The corresponding compatibility typeclasses are
|
||||
{name (scope := "Init.Data.String.Lemmas.Pattern.Basic")}`String.Slice.Pattern.Model.LawfulForwardPatternModel`
|
||||
and
|
||||
{name (scope := "Init.Data.String.Lemmas.Pattern.Basic")}`String.Slice.Pattern.Model.LawfulToForwardSearcherModel`.
|
||||
|
||||
We include the condition that the empty string is not a match. This is necessary for the theory to
|
||||
work out as there is just no reasonable notion of searching that works for the empty string that is
|
||||
still specific enough to yield reasonably strong correctness results for operations based on
|
||||
searching.
|
||||
|
||||
This means that pattern types that allow searching for the empty string will have to special-case
|
||||
the empty string in their correctness statements.
|
||||
-/
|
||||
class PatternModel {ρ : Type} (pat : ρ) : Type where
|
||||
/-- The predicate that says which strings match the pattern. -/
|
||||
Matches : String → Prop
|
||||
|
||||
/--
|
||||
Type class for the condition that the empty string is not a match. This is necessary for the theory to
|
||||
work out as there is just no reasonable notion of searching that works for the empty string that is
|
||||
still specific enough to yield reasonably strong correctness results for operations based on
|
||||
searching.
|
||||
-/
|
||||
class StrictPatternModel {ρ : Type} (pat : ρ) [PatternModel pat] : Prop where
|
||||
not_matches_empty : ¬ PatternModel.Matches pat ""
|
||||
|
||||
theorem not_matches_empty {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat] :
|
||||
¬ PatternModel.Matches pat "" :=
|
||||
StrictPatternModel.not_matches_empty
|
||||
not_matches_empty : ¬ Matches ""
|
||||
|
||||
/--
|
||||
Predicate stating that the region between the start of the slice {name}`s` and the position
|
||||
@@ -78,10 +74,10 @@ Predicate stating that the region between the start of the slice {name}`s` and t
|
||||
structure IsMatch (pat : ρ) [PatternModel pat] {s : Slice} (endPos : s.Pos) : Prop where
|
||||
matches_copy : PatternModel.Matches pat (s.sliceTo endPos).copy
|
||||
|
||||
theorem IsMatch.ne_startPos {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} {pos : s.Pos}
|
||||
theorem IsMatch.ne_startPos {pat : ρ} [PatternModel pat] {s : Slice} {pos : s.Pos}
|
||||
(h : IsMatch pat pos) : pos ≠ s.startPos := by
|
||||
intro hc
|
||||
apply not_matches_empty (pat := pat)
|
||||
apply PatternModel.not_matches_empty (pat := pat)
|
||||
simpa [hc] using h.matches_copy
|
||||
|
||||
theorem isMatch_iff {pat : ρ} [PatternModel pat] {s : Slice} {pos : s.Pos} :
|
||||
@@ -94,21 +90,6 @@ theorem isMatch_iff_exists_splits {pat : ρ} [PatternModel pat] {s : Slice} {pos
|
||||
refine ⟨fun h => ⟨_, _, pos.splits, h⟩, fun ⟨t₁, t₂, h₁, h₂⟩ => ?_⟩
|
||||
rwa [h₁.eq_left pos.splits] at h₂
|
||||
|
||||
@[simp]
|
||||
theorem isMatch_cast_iff {pat : ρ} [PatternModel pat] {s t : Slice} (h : s.copy = t.copy) {pos : s.Pos} :
|
||||
IsMatch pat (pos.cast h) ↔ IsMatch pat pos := by
|
||||
simp [isMatch_iff]
|
||||
|
||||
@[simp]
|
||||
theorem isMatch_sliceTo_iff {pat : ρ} [PatternModel pat] {s : Slice} {pos p : s.Pos} {h} :
|
||||
IsMatch pat (Pos.sliceTo p pos h) ↔ IsMatch pat pos := by
|
||||
simp [isMatch_iff]
|
||||
|
||||
@[simp]
|
||||
theorem isMatch_ofSliceTo_iff {pat : ρ} [PatternModel pat] {s : Slice} {p : s.Pos} {pos : (s.sliceTo p).Pos} :
|
||||
IsMatch pat (Pos.ofSliceTo pos) ↔ IsMatch pat pos := by
|
||||
rw [← isMatch_sliceTo_iff (p := p) (h := Pos.ofSliceTo_le), Pos.sliceTo_ofSliceTo]
|
||||
|
||||
/--
|
||||
Predicate stating that the region between the position {name}`startPos` and the end of the slice
|
||||
{name}`s` matches the pattern {name}`pat`. Note that there might be a longer match.
|
||||
@@ -116,10 +97,10 @@ Predicate stating that the region between the position {name}`startPos` and the
|
||||
structure IsRevMatch (pat : ρ) [PatternModel pat] {s : Slice} (startPos : s.Pos) : Prop where
|
||||
matches_copy : PatternModel.Matches pat (s.sliceFrom startPos).copy
|
||||
|
||||
theorem IsRevMatch.ne_endPos {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} {pos : s.Pos}
|
||||
theorem IsRevMatch.ne_endPos {pat : ρ} [PatternModel pat] {s : Slice} {pos : s.Pos}
|
||||
(h : IsRevMatch pat pos) : pos ≠ s.endPos := by
|
||||
intro hc
|
||||
apply not_matches_empty (pat := pat)
|
||||
apply PatternModel.not_matches_empty (pat := pat)
|
||||
simpa [hc] using h.matches_copy
|
||||
|
||||
theorem isRevMatch_iff {pat : ρ} [PatternModel pat] {s : Slice} {pos : s.Pos} :
|
||||
@@ -132,21 +113,6 @@ theorem isRevMatch_iff_exists_splits {pat : ρ} [PatternModel pat] {s : Slice} {
|
||||
refine ⟨fun h => ⟨_, _, pos.splits, h⟩, fun ⟨t₁, t₂, h₁, h₂⟩ => ?_⟩
|
||||
rwa [h₁.eq_right pos.splits] at h₂
|
||||
|
||||
@[simp]
|
||||
theorem isRevMatch_cast_iff {pat : ρ} [PatternModel pat] {s t : Slice} (h : s.copy = t.copy) {pos : s.Pos} :
|
||||
IsRevMatch pat (pos.cast h) ↔ IsRevMatch pat pos := by
|
||||
simp [isRevMatch_iff]
|
||||
|
||||
@[simp]
|
||||
theorem isRevMatch_sliceFrom_iff {pat : ρ} [PatternModel pat] {s : Slice} {pos p : s.Pos} {h} :
|
||||
IsRevMatch pat (Pos.sliceFrom p pos h) ↔ IsRevMatch pat pos := by
|
||||
simp [isRevMatch_iff]
|
||||
|
||||
@[simp]
|
||||
theorem isRevMatch_ofSliceFrom_iff {pat : ρ} [PatternModel pat] {s : Slice} {p : s.Pos} {pos : (s.sliceFrom p).Pos} :
|
||||
IsRevMatch pat (Pos.ofSliceFrom pos) ↔ IsRevMatch pat pos := by
|
||||
rw [← isRevMatch_sliceFrom_iff (p := p) (h := Pos.le_ofSliceFrom), Pos.sliceFrom_ofSliceFrom]
|
||||
|
||||
/--
|
||||
Predicate stating that the region between the start of the slice {name}`s` and the position
|
||||
{name}`pos` matches the pattern {name}`pat`, and that there is no longer match starting at the
|
||||
@@ -159,19 +125,10 @@ structure IsLongestMatch (pat : ρ) [PatternModel pat] {s : Slice} (pos : s.Pos)
|
||||
isMatch : IsMatch pat pos
|
||||
not_isMatch : ∀ pos', pos < pos' → ¬ IsMatch pat pos'
|
||||
|
||||
theorem isLongestMatch_iff {pat : ρ} [PatternModel pat] {s : Slice} {pos : s.Pos} :
|
||||
IsLongestMatch pat pos ↔ IsMatch pat pos ∧ ∀ pos', pos < pos' → ¬ IsMatch pat pos' :=
|
||||
⟨fun ⟨h, h'⟩ => ⟨h, h'⟩, fun ⟨h, h'⟩ => ⟨h, h'⟩⟩
|
||||
|
||||
theorem IsLongestMatch.ne_startPos {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} {pos : s.Pos}
|
||||
theorem IsLongestMatch.ne_startPos {pat : ρ} [PatternModel pat] {s : Slice} {pos : s.Pos}
|
||||
(h : IsLongestMatch pat pos) : pos ≠ s.startPos :=
|
||||
h.isMatch.ne_startPos
|
||||
|
||||
@[simp]
|
||||
theorem not_isLongestMatch_startPos {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} :
|
||||
¬IsLongestMatch pat s.startPos :=
|
||||
fun h => h.ne_startPos rfl
|
||||
|
||||
theorem IsLongestMatch.eq {pat : ρ} [PatternModel pat] {s : Slice} {pos pos' : s.Pos}
|
||||
(h : IsLongestMatch pat pos) (h' : IsLongestMatch pat pos') : pos = pos' := by
|
||||
apply Std.le_antisymm
|
||||
@@ -192,37 +149,9 @@ theorem IsLongestMatch.le_of_isMatch {pat : ρ} [PatternModel pat] {s : Slice} {
|
||||
(h : IsLongestMatch pat pos) (h' : IsMatch pat pos') : pos' ≤ pos :=
|
||||
Std.not_lt.1 (fun hlt => h.not_isMatch _ hlt h')
|
||||
|
||||
@[simp]
|
||||
theorem isLongestMatch_cast_iff {pat : ρ} [PatternModel pat] {s t : Slice}
|
||||
(hst : s.copy = t.copy) {pos : s.Pos} :
|
||||
IsLongestMatch pat (pos.cast hst) ↔ IsLongestMatch pat pos := by
|
||||
simp only [isLongestMatch_iff, isMatch_cast_iff, and_congr_right_iff]
|
||||
refine fun _ => ⟨fun h p hp => ?_, fun h p hp => ?_⟩
|
||||
· rw [← isMatch_cast_iff hst]
|
||||
exact h _ (by simpa)
|
||||
· have : p = (p.cast hst.symm).cast hst := by simp
|
||||
rw [this, isMatch_cast_iff hst]
|
||||
exact h _ (by rwa [this, Pos.cast_lt_cast_iff] at hp)
|
||||
|
||||
theorem IsLongestMatch.of_eq {pat : ρ} [PatternModel pat] {s t : Slice} {pos : s.Pos} {pos' : t.Pos}
|
||||
(h : IsLongestMatch pat pos) (h₁ : s.copy = t.copy) (h₂ : pos.cast h₁ = pos') :
|
||||
IsLongestMatch pat pos' := by
|
||||
subst h₂; simpa
|
||||
|
||||
theorem IsLongestMatch.sliceTo {pat : ρ} [PatternModel pat] {s : Slice} {pos : s.Pos}
|
||||
(h : IsLongestMatch pat pos) (p : s.Pos) (hp : pos ≤ p) : IsLongestMatch pat (Pos.sliceTo p pos hp) := by
|
||||
simp [isLongestMatch_iff] at ⊢ h
|
||||
refine ⟨h.1, fun p hp => ?_⟩
|
||||
rw [← isMatch_ofSliceTo_iff]
|
||||
exact h.2 _ (by simpa [Pos.sliceTo_lt_iff] using hp)
|
||||
|
||||
theorem isLongestMatch_of_ofSliceTo {pat : ρ} [PatternModel pat] {s : Slice} {p : s.Pos} {pos : (s.sliceTo p).Pos}
|
||||
(h : IsLongestMatch pat (Pos.ofSliceTo pos)) : IsLongestMatch pat pos := by
|
||||
simpa using h.sliceTo p
|
||||
|
||||
/--
|
||||
Predicate stating that the region between the start of the slice {name}`s` and the position
|
||||
{name}`pos` matches the pattern {name}`pat`, and that there is no longer match starting at the
|
||||
{name}`pos` matches the patten {name}`pat`, and that there is no longer match starting at the
|
||||
beginning of the slice. This is what a correct matcher should match.
|
||||
|
||||
In some cases, being a match and being a longest match will coincide, see
|
||||
@@ -232,19 +161,10 @@ structure IsLongestRevMatch (pat : ρ) [PatternModel pat] {s : Slice} (pos : s.P
|
||||
isRevMatch : IsRevMatch pat pos
|
||||
not_isRevMatch : ∀ pos', pos' < pos → ¬ IsRevMatch pat pos'
|
||||
|
||||
theorem isLongestRevMatch_iff {pat : ρ} [PatternModel pat] {s : Slice} {pos : s.Pos} :
|
||||
IsLongestRevMatch pat pos ↔ IsRevMatch pat pos ∧ ∀ pos', pos' < pos → ¬ IsRevMatch pat pos' :=
|
||||
⟨fun ⟨h, h'⟩ => ⟨h, h'⟩, fun ⟨h, h'⟩ => ⟨h, h'⟩⟩
|
||||
|
||||
theorem IsLongestRevMatch.ne_endPos {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} {pos : s.Pos}
|
||||
theorem IsLongestRevMatch.ne_endPos {pat : ρ} [PatternModel pat] {s : Slice} {pos : s.Pos}
|
||||
(h : IsLongestRevMatch pat pos) : pos ≠ s.endPos :=
|
||||
h.isRevMatch.ne_endPos
|
||||
|
||||
@[simp]
|
||||
theorem not_isLongestRevMatch_endPos {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} :
|
||||
¬IsLongestRevMatch pat s.endPos :=
|
||||
fun h => h.ne_endPos rfl
|
||||
|
||||
theorem IsLongestRevMatch.eq {pat : ρ} [PatternModel pat] {s : Slice} {pos pos' : s.Pos}
|
||||
(h : IsLongestRevMatch pat pos) (h' : IsLongestRevMatch pat pos') : pos = pos' := by
|
||||
apply Std.le_antisymm
|
||||
@@ -265,34 +185,6 @@ theorem IsLongestRevMatch.le_of_isRevMatch {pat : ρ} [PatternModel pat] {s : Sl
|
||||
(h : IsLongestRevMatch pat pos) (h' : IsRevMatch pat pos') : pos ≤ pos' :=
|
||||
Std.not_lt.1 (fun hlt => h.not_isRevMatch _ hlt h')
|
||||
|
||||
@[simp]
|
||||
theorem isLongestRevMatch_cast_iff {pat : ρ} [PatternModel pat] {s t : Slice}
|
||||
(hst : s.copy = t.copy) {pos : s.Pos} :
|
||||
IsLongestRevMatch pat (pos.cast hst) ↔ IsLongestRevMatch pat pos := by
|
||||
simp only [isLongestRevMatch_iff, isRevMatch_cast_iff, and_congr_right_iff]
|
||||
refine fun _ => ⟨fun h p hp => ?_, fun h p hp => ?_⟩
|
||||
· rw [← isRevMatch_cast_iff hst]
|
||||
exact h _ (by simpa)
|
||||
· have : p = (p.cast hst.symm).cast hst := by simp
|
||||
rw [this, isRevMatch_cast_iff hst]
|
||||
exact h _ (by rwa [this, Pos.cast_lt_cast_iff] at hp)
|
||||
|
||||
theorem IsLongestRevMatch.of_eq {pat : ρ} [PatternModel pat] {s t : Slice} {pos : s.Pos} {pos' : t.Pos}
|
||||
(h : IsLongestRevMatch pat pos) (h₁ : s.copy = t.copy) (h₂ : pos.cast h₁ = pos') :
|
||||
IsLongestRevMatch pat pos' := by
|
||||
subst h₂; simpa
|
||||
|
||||
theorem IsLongestRevMatch.sliceFrom {pat : ρ} [PatternModel pat] {s : Slice} {pos : s.Pos}
|
||||
(h : IsLongestRevMatch pat pos) (p : s.Pos) (hp : p ≤ pos) : IsLongestRevMatch pat (Pos.sliceFrom p pos hp) := by
|
||||
simp [isLongestRevMatch_iff] at ⊢ h
|
||||
refine ⟨h.1, fun p' hp' => ?_⟩
|
||||
rw [← isRevMatch_ofSliceFrom_iff]
|
||||
exact h.2 _ (by simpa [Pos.lt_sliceFrom_iff] using hp')
|
||||
|
||||
theorem isLongestRevMatch_of_ofSliceFrom {pat : ρ} [PatternModel pat] {s : Slice} {p : s.Pos} {pos : (s.sliceFrom p).Pos}
|
||||
(h : IsLongestRevMatch pat (Pos.ofSliceFrom pos)) : IsLongestRevMatch pat pos := by
|
||||
simpa using h.sliceFrom p
|
||||
|
||||
/--
|
||||
Predicate stating that a match for a given pattern is never a proper prefix of another match.
|
||||
|
||||
@@ -336,7 +228,7 @@ theorem isLongestRevMatch_iff_isRevMatch {ρ : Type} (pat : ρ) [PatternModel pa
|
||||
exact ht₅ (NoSuffixPatternModel.eq_empty _ _ ht₂ (ht₅'' ▸ ht₂'))
|
||||
|
||||
/--
|
||||
Predicate stating that the slice formed by {name}`startPos` and {name}`endPos` contains a match
|
||||
Predicate stating that the slice formed by {name}`startPos` and {name}`endPos` contains is a match
|
||||
of {name}`pat` in {name}`s` and it is longest among matches starting at {name}`startPos`.
|
||||
-/
|
||||
structure IsLongestMatchAt (pat : ρ) [PatternModel pat] {s : Slice} (startPos endPos : s.Pos) : Prop where
|
||||
@@ -348,21 +240,12 @@ theorem isLongestMatchAt_iff {pat : ρ} [PatternModel pat] {s : Slice} {pos₁ p
|
||||
∃ (h : pos₁ ≤ pos₂), IsLongestMatch pat (Slice.Pos.sliceFrom _ _ h) :=
|
||||
⟨fun ⟨h, h'⟩ => ⟨h, h'⟩, fun ⟨h, h'⟩ => ⟨h, h'⟩⟩
|
||||
|
||||
theorem IsLongestMatchAt.lt {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
theorem IsLongestMatchAt.lt {pat : ρ} [PatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
(h : IsLongestMatchAt pat startPos endPos) : startPos < endPos := by
|
||||
have := h.isLongestMatch_sliceFrom.ne_startPos
|
||||
rw [← Pos.startPos_lt_iff, ← Slice.Pos.ofSliceFrom_lt_ofSliceFrom_iff] at this
|
||||
simpa
|
||||
|
||||
theorem IsLongestMatchAt.ne {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
(h : IsLongestMatchAt pat startPos endPos) : startPos ≠ endPos :=
|
||||
Std.ne_of_lt h.lt
|
||||
|
||||
@[simp]
|
||||
theorem not_isLongestMatchAt_self {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} {startPos : s.Pos} :
|
||||
¬IsLongestMatchAt pat startPos startPos :=
|
||||
fun h => h.ne rfl
|
||||
|
||||
theorem IsLongestMatchAt.eq {pat : ρ} [PatternModel pat] {s : Slice} {startPos endPos endPos' : s.Pos}
|
||||
(h : IsLongestMatchAt pat startPos endPos) (h' : IsLongestMatchAt pat startPos endPos') :
|
||||
endPos = endPos' := by
|
||||
@@ -399,77 +282,6 @@ theorem isLongestMatchAt_startPos_iff {pat : ρ} [PatternModel pat] {s : Slice}
|
||||
⟨fun h => isLongestMatch_of_eq (by simp) (by simp) h,
|
||||
fun h => isLongestMatch_of_eq (by simp) (by simp) h⟩
|
||||
|
||||
theorem isLongestMatch_iff_isLongestMatchAt_ofSliceFrom {pat : ρ} [PatternModel pat]
|
||||
{s : Slice} {base : s.Pos} (endPos : (s.sliceFrom base).Pos) :
|
||||
IsLongestMatch pat endPos ↔ IsLongestMatchAt pat base (Pos.ofSliceFrom endPos) := by
|
||||
simp [← isLongestMatchAt_startPos_iff, isLongestMatchAt_iff_isLongestMatchAt_ofSliceFrom]
|
||||
|
||||
theorem IsLongestMatchAt.matches_slice {pat : ρ} [PatternModel pat] {s : Slice}
|
||||
{startPos endPos : s.Pos} (h : IsLongestMatchAt pat startPos endPos) :
|
||||
PatternModel.Matches pat (s.slice startPos endPos h.le).copy := by
|
||||
simpa using h.isLongestMatch_sliceFrom.isMatch.matches_copy
|
||||
|
||||
@[simp]
|
||||
theorem isLongestMatchAt_cast_iff {pat : ρ} [PatternModel pat] {s t : Slice} (hst : s.copy = t.copy)
|
||||
{startPos endPos : s.Pos} :
|
||||
IsLongestMatchAt pat (startPos.cast hst) (endPos.cast hst) ↔ IsLongestMatchAt pat startPos endPos := by
|
||||
simp [isLongestMatchAt_iff, Pos.sliceFrom_cast]
|
||||
|
||||
theorem IsLongestMatchAt.of_eq {pat : ρ} [PatternModel pat] {s t : Slice} {s₁ e₁ : s.Pos} {s₂ e₂ : t.Pos}
|
||||
(h : IsLongestMatchAt pat s₁ e₁) (h₁ : s.copy = t.copy) (h₂ : s₁.cast h₁ = s₂) (h₃ : e₁.cast h₁ = e₂) :
|
||||
IsLongestMatchAt pat s₂ e₂ := by
|
||||
subst h₂ h₃; simpa
|
||||
|
||||
theorem IsLongestMatchAt.sliceTo {pat : ρ} [PatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
(h : IsLongestMatchAt pat startPos endPos) (p : s.Pos) (hp : endPos ≤ p) :
|
||||
IsLongestMatchAt pat (Pos.sliceTo p startPos (by exact Std.le_trans h.le hp)) (Pos.sliceTo p endPos hp) := by
|
||||
simp only [isLongestMatchAt_iff, Pos.sliceTo_le_sliceTo_iff] at ⊢ h
|
||||
obtain ⟨h, hp'⟩ := h
|
||||
exact ⟨h, (hp'.sliceTo (Pos.sliceFrom startPos p (Std.le_trans h hp)) (by simpa)).of_eq (by simp) (by ext; simp)⟩
|
||||
|
||||
theorem isLongestMatchAt_of_ofSliceTo {pat : ρ} [PatternModel pat] {s : Slice} {p : s.Pos} {startPos endPos : (s.sliceTo p).Pos}
|
||||
(h : IsLongestMatchAt pat (Pos.ofSliceTo startPos) (Pos.ofSliceTo endPos)) :
|
||||
IsLongestMatchAt pat startPos endPos := by
|
||||
simpa using h.sliceTo p Pos.ofSliceTo_le
|
||||
|
||||
/--
|
||||
Predicate stating that the range between two positions of {name}`s` can be covered by longest
|
||||
matches of the pattern within {name}`s`.
|
||||
-/
|
||||
inductive IsLongestMatchAtChain (pat : ρ) [PatternModel pat] {s : Slice} : s.Pos → s.Pos → Prop where
|
||||
| nil (p : s.Pos) : IsLongestMatchAtChain pat p p
|
||||
| cons (startPos middlePos endPos : s.Pos) : IsLongestMatchAt pat startPos middlePos →
|
||||
IsLongestMatchAtChain pat middlePos endPos → IsLongestMatchAtChain pat startPos endPos
|
||||
|
||||
attribute [simp] IsLongestMatchAtChain.nil
|
||||
|
||||
theorem IsLongestMatchAtChain.eq_of_isLongestMatchAt_self {pat : ρ} [PatternModel pat] {s : Slice}
|
||||
{startPos endPos : s.Pos} (h : IsLongestMatchAtChain pat startPos endPos) (h' : IsLongestMatchAt pat startPos startPos) :
|
||||
startPos = endPos := by
|
||||
induction h with
|
||||
| nil => rfl
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih =>
|
||||
obtain rfl : p₁ = p₂ := h'.eq h₁
|
||||
exact ih h₁
|
||||
|
||||
theorem IsLongestMatchAtChain.le {pat : ρ} [PatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
(h : IsLongestMatchAtChain pat startPos endPos) : startPos ≤ endPos := by
|
||||
induction h with
|
||||
| nil => exact Std.le_refl _
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih => exact Std.le_trans h₁.le ih
|
||||
|
||||
theorem IsLongestMatchAtChain.sliceTo {pat : ρ} [PatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
(h : IsLongestMatchAtChain pat startPos endPos) (p : s.Pos) (hp : endPos ≤ p) :
|
||||
IsLongestMatchAtChain pat (Pos.sliceTo p startPos (by exact Std.le_trans h.le hp)) (Pos.sliceTo p endPos hp) := by
|
||||
induction h with
|
||||
| nil => simp
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih => exact .cons _ _ _ (h₁.sliceTo p (Std.le_trans h₂.le hp)) (ih hp)
|
||||
|
||||
theorem isLongestMatchAtChain_of_ofSliceTo {pat : ρ} [PatternModel pat] {s : Slice} {p : s.Pos}
|
||||
{startPos endPos : (s.sliceTo p).Pos} (h : IsLongestMatchAtChain pat (Pos.ofSliceTo startPos) (Pos.ofSliceTo endPos)) :
|
||||
IsLongestMatchAtChain pat startPos endPos := by
|
||||
simpa using h.sliceTo p Pos.ofSliceTo_le
|
||||
|
||||
/--
|
||||
Predicate stating that the slice formed by {name}`startPos` and {name}`endPos` contains is a match
|
||||
of {name}`pat` in {name}`s` and it is longest among matches ending at {name}`endPos`.
|
||||
@@ -483,21 +295,12 @@ theorem isLongestRevMatchAt_iff {pat : ρ} [PatternModel pat] {s : Slice} {pos
|
||||
∃ (h : pos₁ ≤ pos₂), IsLongestRevMatch pat (Slice.Pos.sliceTo _ _ h) :=
|
||||
⟨fun ⟨h, h'⟩ => ⟨h, h'⟩, fun ⟨h, h'⟩ => ⟨h, h'⟩⟩
|
||||
|
||||
theorem IsLongestRevMatchAt.lt {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
theorem IsLongestRevMatchAt.lt {pat : ρ} [PatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
(h : IsLongestRevMatchAt pat startPos endPos) : startPos < endPos := by
|
||||
have := h.isLongestRevMatch_sliceTo.ne_endPos
|
||||
rw [← Pos.lt_endPos_iff, ← Slice.Pos.ofSliceTo_lt_ofSliceTo_iff] at this
|
||||
simpa
|
||||
|
||||
theorem IsLongestRevMatchAt.ne {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
(h : IsLongestRevMatchAt pat startPos endPos) : startPos ≠ endPos :=
|
||||
Std.ne_of_lt h.lt
|
||||
|
||||
@[simp]
|
||||
theorem not_isLongestRevMatchAt_self {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} {endPos : s.Pos} :
|
||||
¬IsLongestRevMatchAt pat endPos endPos :=
|
||||
fun h => h.ne rfl
|
||||
|
||||
theorem IsLongestRevMatchAt.eq {pat : ρ} [PatternModel pat] {s : Slice} {startPos startPos' endPos : s.Pos}
|
||||
(h : IsLongestRevMatchAt pat startPos endPos) (h' : IsLongestRevMatchAt pat startPos' endPos) :
|
||||
startPos = startPos' := by
|
||||
@@ -532,77 +335,6 @@ theorem isLongestRevMatchAt_endPos_iff {pat : ρ} [PatternModel pat] {s : Slice}
|
||||
⟨fun h => isLongestRevMatch_of_eq (by simp) (by simp) h,
|
||||
fun h => isLongestRevMatch_of_eq (by simp) (by simp) h⟩
|
||||
|
||||
theorem isLongestRevMatch_iff_isLongestRevMatchAt_ofSliceTo {pat : ρ} [PatternModel pat]
|
||||
{s : Slice} {base : s.Pos} (startPos : (s.sliceTo base).Pos) :
|
||||
IsLongestRevMatch pat startPos ↔ IsLongestRevMatchAt pat (Pos.ofSliceTo startPos) base := by
|
||||
simp [← isLongestRevMatchAt_endPos_iff, isLongestRevMatchAt_iff_isLongestRevMatchAt_ofSliceTo]
|
||||
|
||||
theorem IsLongestRevMatchAt.matches_slice {pat : ρ} [PatternModel pat] {s : Slice}
|
||||
{startPos endPos : s.Pos} (h : IsLongestRevMatchAt pat startPos endPos) :
|
||||
PatternModel.Matches pat (s.slice startPos endPos h.le).copy := by
|
||||
simpa using h.isLongestRevMatch_sliceTo.isRevMatch.matches_copy
|
||||
|
||||
@[simp]
|
||||
theorem isLongestRevMatchAt_cast_iff {pat : ρ} [PatternModel pat] {s t : Slice} (hst : s.copy = t.copy)
|
||||
{startPos endPos : s.Pos} :
|
||||
IsLongestRevMatchAt pat (startPos.cast hst) (endPos.cast hst) ↔ IsLongestRevMatchAt pat startPos endPos := by
|
||||
simp [isLongestRevMatchAt_iff, Pos.sliceTo_cast]
|
||||
|
||||
theorem IsLongestRevMatchAt.of_eq {pat : ρ} [PatternModel pat] {s t : Slice} {s₁ e₁ : s.Pos} {s₂ e₂ : t.Pos}
|
||||
(h : IsLongestRevMatchAt pat s₁ e₁) (h₁ : s.copy = t.copy) (h₂ : s₁.cast h₁ = s₂) (h₃ : e₁.cast h₁ = e₂) :
|
||||
IsLongestRevMatchAt pat s₂ e₂ := by
|
||||
subst h₂ h₃; simpa
|
||||
|
||||
theorem IsLongestRevMatchAt.sliceFrom {pat : ρ} [PatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
(h : IsLongestRevMatchAt pat startPos endPos) (p : s.Pos) (hp : p ≤ startPos) :
|
||||
IsLongestRevMatchAt pat (Pos.sliceFrom p startPos hp) (Pos.sliceFrom p endPos (by exact Std.le_trans hp h.le)) := by
|
||||
simp only [isLongestRevMatchAt_iff, Pos.sliceFrom_le_sliceFrom_iff] at ⊢ h
|
||||
obtain ⟨h, hp'⟩ := h
|
||||
exact ⟨h, (hp'.sliceFrom (Pos.sliceTo endPos p (Std.le_trans hp h)) (by simpa)).of_eq (by simp) (by ext; simp)⟩
|
||||
|
||||
theorem isLongestRevMatchAt_of_ofSliceFrom {pat : ρ} [PatternModel pat] {s : Slice} {p : s.Pos} {startPos endPos : (s.sliceFrom p).Pos}
|
||||
(h : IsLongestRevMatchAt pat (Pos.ofSliceFrom startPos) (Pos.ofSliceFrom endPos)) :
|
||||
IsLongestRevMatchAt pat startPos endPos := by
|
||||
simpa using h.sliceFrom p Pos.le_ofSliceFrom
|
||||
|
||||
/--
|
||||
Predicate stating that the range between two positions of {name}`s` can be covered by longest
|
||||
reverse matches of the pattern within {name}`s`.
|
||||
-/
|
||||
inductive IsLongestRevMatchAtChain (pat : ρ) [PatternModel pat] {s : Slice} : s.Pos → s.Pos → Prop where
|
||||
| nil (p : s.Pos) : IsLongestRevMatchAtChain pat p p
|
||||
| cons (startPos middlePos endPos : s.Pos) : IsLongestRevMatchAtChain pat startPos middlePos →
|
||||
IsLongestRevMatchAt pat middlePos endPos → IsLongestRevMatchAtChain pat startPos endPos
|
||||
|
||||
attribute [simp] IsLongestRevMatchAtChain.nil
|
||||
|
||||
theorem IsLongestRevMatchAtChain.eq_of_isLongestRevMatchAt_self {pat : ρ} [PatternModel pat] {s : Slice}
|
||||
{startPos endPos : s.Pos} (h : IsLongestRevMatchAtChain pat startPos endPos) (h' : IsLongestRevMatchAt pat endPos endPos) :
|
||||
startPos = endPos := by
|
||||
induction h with
|
||||
| nil => rfl
|
||||
| cons mid endP hchain hmatch ih =>
|
||||
obtain rfl := hmatch.eq h'
|
||||
exact ih hmatch
|
||||
|
||||
theorem IsLongestRevMatchAtChain.le {pat : ρ} [PatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
(h : IsLongestRevMatchAtChain pat startPos endPos) : startPos ≤ endPos := by
|
||||
induction h with
|
||||
| nil => exact Std.le_refl _
|
||||
| cons mid endP hchain hmatch ih => exact Std.le_trans ih hmatch.le
|
||||
|
||||
theorem IsLongestRevMatchAtChain.sliceFrom {pat : ρ} [PatternModel pat] {s : Slice} {startPos endPos : s.Pos}
|
||||
(h : IsLongestRevMatchAtChain pat startPos endPos) (p : s.Pos) (hp : p ≤ startPos) :
|
||||
IsLongestRevMatchAtChain pat (Pos.sliceFrom p startPos hp) (Pos.sliceFrom p endPos (by exact Std.le_trans hp h.le)) := by
|
||||
induction h with
|
||||
| nil => simp
|
||||
| cons mid endP hchain hmatch ih => exact .cons _ _ _ ih (hmatch.sliceFrom p (Std.le_trans hp hchain.le))
|
||||
|
||||
theorem isLongestRevMatchAtChain_of_ofSliceFrom {pat : ρ} [PatternModel pat] {s : Slice} {p : s.Pos}
|
||||
{startPos endPos : (s.sliceFrom p).Pos} (h : IsLongestRevMatchAtChain pat (Pos.ofSliceFrom startPos) (Pos.ofSliceFrom endPos)) :
|
||||
IsLongestRevMatchAtChain pat startPos endPos := by
|
||||
simpa using h.sliceFrom p Pos.le_ofSliceFrom
|
||||
|
||||
/--
|
||||
Predicate stating that there is a (longest) match starting at the given position.
|
||||
-/
|
||||
@@ -628,7 +360,7 @@ theorem matchesAt_iff_exists_isMatch {pat : ρ} [PatternModel pat] {s : Slice}
|
||||
by simpa using hq⟩⟩
|
||||
|
||||
@[simp]
|
||||
theorem not_matchesAt_endPos {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} :
|
||||
theorem not_matchesAt_endPos {pat : ρ} [PatternModel pat] {s : Slice} :
|
||||
¬ MatchesAt pat s.endPos := by
|
||||
simp only [matchesAt_iff_exists_isMatch, Pos.endPos_le, exists_prop_eq]
|
||||
intro h
|
||||
@@ -648,14 +380,6 @@ theorem IsLongestMatchAt.matchesAt {pat : ρ} [PatternModel pat] {s : Slice} {st
|
||||
(h : IsLongestMatchAt pat startPos endPos) : MatchesAt pat startPos where
|
||||
exists_isLongestMatchAt := ⟨_, h⟩
|
||||
|
||||
@[simp]
|
||||
theorem matchesAt_cast_iff {pat : ρ} [PatternModel pat] {s t : Slice} (hst : s.copy = t.copy)
|
||||
{pos : s.Pos} : MatchesAt pat (pos.cast hst) ↔ MatchesAt pat pos := by
|
||||
simp only [matchesAt_iff_exists_isLongestMatchAt]
|
||||
refine ⟨fun ⟨endPos, h⟩ => ?_, fun ⟨endPos, h⟩ => ?_⟩
|
||||
· exact ⟨endPos.cast hst.symm, by simpa [← isLongestMatchAt_cast_iff hst]⟩
|
||||
· exact ⟨endPos.cast hst, by simpa⟩
|
||||
|
||||
/--
|
||||
Predicate stating that there is a (longest) match ending at the given position.
|
||||
-/
|
||||
@@ -681,13 +405,13 @@ theorem revMatchesAt_iff_exists_isRevMatch {pat : ρ} [PatternModel pat] {s : Sl
|
||||
by simpa using hq⟩⟩
|
||||
|
||||
@[simp]
|
||||
theorem not_revMatchesAt_startPos {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice} :
|
||||
theorem not_revMatchesAt_startPos {pat : ρ} [PatternModel pat] {s : Slice} :
|
||||
¬ RevMatchesAt pat s.startPos := by
|
||||
simp only [revMatchesAt_iff_exists_isRevMatch, Pos.le_startPos, exists_prop_eq]
|
||||
intro h
|
||||
simpa [← Pos.ofSliceTo_inj] using h.ne_endPos
|
||||
|
||||
theorem revMatchesAt_iff_revMatchesAt_ofSliceTo {pat : ρ} [PatternModel pat] {s : Slice} {base : s.Pos}
|
||||
theorem revMatchesAt_iff_revMatchesAt_ofSliceto {pat : ρ} [PatternModel pat] {s : Slice} {base : s.Pos}
|
||||
{pos : (s.sliceTo base).Pos} : RevMatchesAt pat pos ↔ RevMatchesAt pat (Pos.ofSliceTo pos) := by
|
||||
simp only [revMatchesAt_iff_exists_isLongestRevMatchAt]
|
||||
constructor
|
||||
@@ -701,14 +425,6 @@ theorem IsLongestRevMatchAt.revMatchesAt {pat : ρ} [PatternModel pat] {s : Slic
|
||||
(h : IsLongestRevMatchAt pat startPos endPos) : RevMatchesAt pat endPos where
|
||||
exists_isLongestRevMatchAt := ⟨_, h⟩
|
||||
|
||||
@[simp]
|
||||
theorem revMatchesAt_cast_iff {pat : ρ} [PatternModel pat] {s t : Slice} (hst : s.copy = t.copy)
|
||||
{pos : s.Pos} : RevMatchesAt pat (pos.cast hst) ↔ RevMatchesAt pat pos := by
|
||||
simp only [revMatchesAt_iff_exists_isLongestRevMatchAt]
|
||||
refine ⟨fun ⟨endPos, h⟩ => ?_, fun ⟨endPos, h⟩ => ?_⟩
|
||||
· exact ⟨endPos.cast hst.symm, by simpa [← isLongestRevMatchAt_cast_iff hst]⟩
|
||||
· exact ⟨endPos.cast hst, by simpa⟩
|
||||
|
||||
open Classical in
|
||||
/--
|
||||
Noncomputable model function returning the end point of the longest match starting at the given
|
||||
@@ -734,21 +450,6 @@ theorem matchAt?_eq_none_iff {ρ : Type} {pat : ρ} [PatternModel pat]
|
||||
| case1 h => simpa using ⟨h⟩
|
||||
| case2 h => simpa using fun ⟨h'⟩ => h h'
|
||||
|
||||
theorem lt_of_matchAt?_eq_some {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat]
|
||||
{s : Slice} {startPos endPos : s.Pos} (h : matchAt? pat startPos = some endPos) :
|
||||
startPos < endPos :=
|
||||
(matchAt?_eq_some_iff.1 h).lt
|
||||
|
||||
@[simp]
|
||||
theorem matchAt?_cast {ρ : Type} (pat : ρ) [PatternModel pat] {s t : Slice} (hst : s.copy = t.copy)
|
||||
{startPos : s.Pos} :
|
||||
matchAt? pat (startPos.cast hst) = (matchAt? pat startPos).map (Slice.Pos.cast · hst) := by
|
||||
refine Option.ext (fun endPos => ?_)
|
||||
have : endPos = (endPos.cast hst.symm).cast hst := by simp
|
||||
conv => lhs; rw [this, matchAt?_eq_some_iff, isLongestMatchAt_cast_iff]
|
||||
simp only [Option.map_eq_some_iff, matchAt?_eq_some_iff]
|
||||
exact ⟨fun h => ⟨_, ⟨h, by simp⟩⟩, by rintro ⟨pos, h, rfl⟩; simpa⟩
|
||||
|
||||
open Classical in
|
||||
/--
|
||||
Noncomputable model function returning the start point of the longest match ending at the given
|
||||
@@ -774,21 +475,6 @@ theorem revMatchAt?_eq_none_iff {ρ : Type} {pat : ρ} [PatternModel pat]
|
||||
| case1 h => simpa using ⟨h⟩
|
||||
| case2 h => simpa using fun ⟨h'⟩ => h h'
|
||||
|
||||
theorem lt_of_revMatchAt?_eq_some {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat]
|
||||
{s : Slice} {startPos endPos : s.Pos} (h : revMatchAt? pat endPos = some startPos) :
|
||||
startPos < endPos :=
|
||||
(revMatchAt?_eq_some_iff.1 h).lt
|
||||
|
||||
@[simp]
|
||||
theorem revMatchAt?_cast {ρ : Type} (pat : ρ) [PatternModel pat] {s t : Slice} (hst : s.copy = t.copy)
|
||||
{startPos : s.Pos} :
|
||||
revMatchAt? pat (startPos.cast hst) = (revMatchAt? pat startPos).map (Slice.Pos.cast · hst) := by
|
||||
refine Option.ext (fun endPos => ?_)
|
||||
have : endPos = (endPos.cast hst.symm).cast hst := by simp
|
||||
conv => lhs; rw [this, revMatchAt?_eq_some_iff, isLongestRevMatchAt_cast_iff]
|
||||
simp only [Option.map_eq_some_iff, revMatchAt?_eq_some_iff]
|
||||
exact ⟨fun h => ⟨_, ⟨h, by simp⟩⟩, by rintro ⟨pos, h, rfl⟩; simpa⟩
|
||||
|
||||
/--
|
||||
Predicate stating compatibility between {name}`PatternModel` and {name}`ForwardPattern`.
|
||||
|
||||
@@ -819,8 +505,8 @@ theorem LawfulForwardPatternModel.skipPrefix?_eq_none_iff {ρ : Type} {pat : ρ}
|
||||
/--
|
||||
Predicate stating compatibility between {name}`PatternModel` and {name}`BackwardPattern`.
|
||||
|
||||
This extends {name}`LawfulBackwardPattern`, but it is much stronger because it forces the
|
||||
{name}`BackwardPattern` to match the longest prefix of the given slice that matches the property
|
||||
This extends {name}`LawfulForwardPattern`, but it is much stronger because it forces the
|
||||
{name}`ForwardPattern` to match the longest prefix of the given slice that matches the property
|
||||
supplied by the {name}`PatternModel` instance.
|
||||
-/
|
||||
class LawfulBackwardPatternModel {ρ : Type} (pat : ρ) [BackwardPattern pat]
|
||||
@@ -884,24 +570,6 @@ theorem IsValidSearchFrom.endPos_of_eq {pat : ρ} [PatternModel pat] {s : Slice}
|
||||
cases hl
|
||||
exact IsValidSearchFrom.endPos
|
||||
|
||||
theorem isValidSearchFrom_cast_iff {pat : ρ} [PatternModel pat] {s t : Slice} (hst : s.copy = t.copy)
|
||||
{pos : s.Pos} {l : List (SearchStep t)} :
|
||||
IsValidSearchFrom pat (pos.cast hst) l ↔ IsValidSearchFrom pat pos (l.map (·.cast hst.symm)) := by
|
||||
suffices ∀ (s t : Slice) (hst : s.copy = t.copy) (pos : s.Pos) (l : List (SearchStep s)),
|
||||
IsValidSearchFrom pat pos l → IsValidSearchFrom pat (pos.cast hst) (l.map (·.cast hst)) from
|
||||
⟨fun h => by simpa using this _ _ hst.symm _ _ h, fun h => by
|
||||
have hcomp : (SearchStep.cast hst) ∘ (SearchStep.cast hst.symm) = id := by ext; simp
|
||||
simpa [hcomp] using this _ _ hst _ _ h⟩
|
||||
intro s t hst pos l hl
|
||||
induction hl with
|
||||
| endPos => simpa using IsValidSearchFrom.endPos
|
||||
| matched h₁ h₂ ih =>
|
||||
simpa only [List.map_cons, SearchStep.cast_matched] using IsValidSearchFrom.matched (by simpa) ih
|
||||
| mismatched h₁ h₂ h₃ ih =>
|
||||
simp only [List.map_cons, SearchStep.cast_rejected]
|
||||
refine IsValidSearchFrom.mismatched (by simpa) (fun p hp₁ hp₂ hp₃ => ?_) ih
|
||||
exact h₂ (p.cast hst.symm) (by simpa [Pos.le_cast_iff]) (by simpa [Pos.cast_lt_iff]) (by simpa)
|
||||
|
||||
/--
|
||||
Predicate stating compatibility between {name}`PatternModel` and {name}`ToForwardSearcher`.
|
||||
|
||||
@@ -995,24 +663,6 @@ theorem IsValidRevSearchFrom.startPos_of_eq {pat : ρ} [PatternModel pat] {s : S
|
||||
cases hl
|
||||
exact IsValidRevSearchFrom.startPos
|
||||
|
||||
theorem isValidRevSearchFrom_cast_iff {pat : ρ} [PatternModel pat] {s t : Slice} (hst : s.copy = t.copy)
|
||||
{pos : s.Pos} {l : List (SearchStep t)} :
|
||||
IsValidRevSearchFrom pat (pos.cast hst) l ↔ IsValidRevSearchFrom pat pos (l.map (·.cast hst.symm)) := by
|
||||
suffices ∀ (s t : Slice) (hst : s.copy = t.copy) (pos : s.Pos) (l : List (SearchStep s)),
|
||||
IsValidRevSearchFrom pat pos l → IsValidRevSearchFrom pat (pos.cast hst) (l.map (·.cast hst)) from
|
||||
⟨fun h => by simpa using this _ _ hst.symm _ _ h, fun h => by
|
||||
have hcomp : (SearchStep.cast hst) ∘ (SearchStep.cast hst.symm) = id := by ext; simp
|
||||
simpa [hcomp] using this _ _ hst _ _ h⟩
|
||||
intro s t hst pos l hl
|
||||
induction hl with
|
||||
| startPos => simpa using IsValidRevSearchFrom.startPos
|
||||
| matched h₁ h₂ ih =>
|
||||
simpa only [List.map_cons, SearchStep.cast_matched] using IsValidRevSearchFrom.matched (by simpa) ih
|
||||
| mismatched h₁ h₂ h₃ ih =>
|
||||
simp only [List.map_cons, SearchStep.cast_rejected]
|
||||
refine IsValidRevSearchFrom.mismatched (by simpa) (fun p hp₁ hp₂ hp₃ => ?_) ih
|
||||
exact h₂ (p.cast hst.symm) (by simpa [Pos.lt_cast_iff]) (by simpa [Pos.cast_le_iff]) (by simpa)
|
||||
|
||||
/--
|
||||
Predicate stating compatibility between {name}`PatternModel` and {name}`ToBackwardSearcher`.
|
||||
|
||||
|
||||
@@ -28,9 +28,7 @@ namespace String.Slice.Pattern.Model.Char
|
||||
|
||||
instance {c : Char} : PatternModel c where
|
||||
Matches s := s = String.singleton c
|
||||
|
||||
instance {c : Char} : StrictPatternModel c where
|
||||
not_matches_empty := by simp [PatternModel.Matches]
|
||||
not_matches_empty := by simp
|
||||
|
||||
instance {c : Char} : NoPrefixPatternModel c :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
@@ -170,61 +168,11 @@ theorem isLongestMatchAt_iff_isLongestMatchAt_beq {c : Char} {s : Slice}
|
||||
IsLongestMatchAt c pos pos' ↔ IsLongestMatchAt (· == c) pos pos' := by
|
||||
simp [Model.isLongestMatchAt_iff, isLongestMatch_iff_isLongestMatch_beq]
|
||||
|
||||
theorem isLongestMatchAtChain_iff_isLongestMatchAtChain_beq {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain c pos pos' ↔ IsLongestMatchAtChain (· == c) pos pos' := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih => exact .cons _ _ _ (isLongestMatchAt_iff_isLongestMatchAt_beq.1 h₁) ih
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih => exact .cons _ _ _ (isLongestMatchAt_iff_isLongestMatchAt_beq.2 h₁) ih
|
||||
|
||||
theorem isLongestMatchAtChain_iff {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain c pos pos' ↔ pos ≤ pos' ∧ ∀ pos'', pos ≤ pos'' → (h : pos'' < pos') → pos''.get (Pos.ne_endPos_of_lt h) = c := by
|
||||
simp [isLongestMatchAtChain_iff_isLongestMatchAtChain_beq, CharPred.isLongestMatchAtChain_iff]
|
||||
|
||||
theorem isLongestMatchAtChain_iff_toList {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain c pos pos' ↔
|
||||
∃ (h : pos ≤ pos'), (s.slice pos pos' h).copy.toList = List.replicate (s.slice pos pos' h).copy.length c := by
|
||||
simp [isLongestMatchAtChain_iff_isLongestMatchAtChain_beq, CharPred.isLongestMatchAtChain_iff_toList,
|
||||
List.eq_replicate_iff]
|
||||
|
||||
theorem isLongestMatchAtChain_startPos_endPos_iff_toList {c : Char} {s : Slice} :
|
||||
IsLongestMatchAtChain c s.startPos s.endPos ↔ s.copy.toList = List.replicate s.copy.length c := by
|
||||
simp [isLongestMatchAtChain_iff_isLongestMatchAtChain_beq,
|
||||
CharPred.isLongestMatchAtChain_startPos_endPos_iff_toList, List.eq_replicate_iff]
|
||||
|
||||
theorem isLongestRevMatchAt_iff_isLongestRevMatchAt_beq {c : Char} {s : Slice}
|
||||
{pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAt c pos pos' ↔ IsLongestRevMatchAt (· == c) pos pos' := by
|
||||
simp [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff_isLongestRevMatch_beq]
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_beq {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain c pos pos' ↔ IsLongestRevMatchAtChain (· == c) pos pos' := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₂ p₃ _ hmatch ih => exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_beq.1 hmatch)
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₂ p₃ _ hmatch ih => exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_beq.2 hmatch)
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain c pos pos' ↔ pos ≤ pos' ∧ ∀ pos'', pos ≤ pos'' → (h : pos'' < pos') → pos''.get (Pos.ne_endPos_of_lt h) = c := by
|
||||
simp [isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_beq, CharPred.isLongestRevMatchAtChain_iff]
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff_toList {c : Char} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain c pos pos' ↔
|
||||
∃ (h : pos ≤ pos'), (s.slice pos pos' h).copy.toList = List.replicate (s.slice pos pos' h).copy.length c := by
|
||||
simp [isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_beq, CharPred.isLongestRevMatchAtChain_iff_toList,
|
||||
List.eq_replicate_iff]
|
||||
|
||||
theorem isLongestRevMatchAtChain_startPos_endPos_iff_toList {c : Char} {s : Slice} :
|
||||
IsLongestRevMatchAtChain c s.startPos s.endPos ↔ s.copy.toList = List.replicate s.copy.length c := by
|
||||
simp [isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_beq,
|
||||
CharPred.isLongestRevMatchAtChain_startPos_endPos_iff_toList, List.eq_replicate_iff]
|
||||
|
||||
theorem matchesAt_iff_matchesAt_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
MatchesAt c pos ↔ MatchesAt (· == c) pos := by
|
||||
simp [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff_isLongestMatchAt_beq]
|
||||
@@ -294,21 +242,18 @@ theorem skipPrefix?_char_eq_skipPrefix?_beq {c : Char} {s : Slice} :
|
||||
theorem Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq {c : Char} {s : Slice} :
|
||||
skipPrefix? c s = skipPrefix? (· == c) s := (rfl)
|
||||
|
||||
theorem Pos.skip?_char_eq_skip?_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? c = pos.skip? (· == c) := (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 [Pos.skipWhile]
|
||||
simp [← Pos.skip?_char_eq_skip?_beq, h₁, h₂, ih]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pos.skip?_char_eq_skip?_beq, h, ih]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pos.skip?_char_eq_skip?_beq, h]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_char_eq_skipPrefix?_beq]
|
||||
|
||||
theorem skipPrefixWhile_char_eq_skipPrefixWhile_beq {c : Char} {s : Slice} :
|
||||
s.skipPrefixWhile c = s.skipPrefixWhile (· == c) :=
|
||||
@@ -324,7 +269,7 @@ theorem takeWhile_char_eq_takeWhile_beq {c : Char} {s : Slice} :
|
||||
|
||||
theorem all_char_eq_all_beq {c : Char} {s : Slice} :
|
||||
s.all c = s.all (· == c) := by
|
||||
simp only [all, skipPrefixWhile_char_eq_skipPrefixWhile_beq]
|
||||
simp only [all, dropWhile_char_eq_dropWhile_beq]
|
||||
|
||||
theorem find?_char_eq_find?_beq {c : Char} {s : Slice} :
|
||||
s.find? c = s.find? (· == c) :=
|
||||
@@ -353,21 +298,18 @@ theorem dropSuffix_char_eq_dropSuffix_beq {c : Char} {s : Slice} :
|
||||
theorem Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq {c : Char} {s : Slice} :
|
||||
skipSuffix? c s = skipSuffix? (· == c) s := (rfl)
|
||||
|
||||
theorem Pos.revSkip?_char_eq_revSkip?_beq {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? c = pos.revSkip? (· == c) := (rfl)
|
||||
|
||||
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 [Pos.revSkipWhile]
|
||||
simp [← Pos.revSkip?_char_eq_revSkip?_beq, h₁, h₂, ih]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pos.revSkip?_char_eq_revSkip?_beq, h, ih]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pos.revSkip?_char_eq_revSkip?_beq, h]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_char_eq_skipSuffix?_beq]
|
||||
|
||||
theorem skipSuffixWhile_char_eq_skipSuffixWhile_beq {c : Char} {s : Slice} :
|
||||
s.skipSuffixWhile c = s.skipSuffixWhile (· == c) :=
|
||||
@@ -381,16 +323,4 @@ theorem takeEndWhile_char_eq_takeEndWhile_beq {c : Char} {s : Slice} :
|
||||
s.takeEndWhile c = s.takeEndWhile (· == c) := by
|
||||
simp only [takeEndWhile]; exact congrArg _ skipSuffixWhile_char_eq_skipSuffixWhile_beq
|
||||
|
||||
theorem revFind?_char_eq_revFind?_beq {c : Char} {s : Slice} :
|
||||
s.revFind? c = s.revFind? (· == c) :=
|
||||
(rfl)
|
||||
|
||||
theorem Pos.revFind?_char_eq_revFind?_beq {c : Char} {s : Slice} {p : s.Pos} :
|
||||
p.revFind? c = p.revFind? (· == c) :=
|
||||
(rfl)
|
||||
|
||||
theorem revAll_char_eq_revAll_beq {c : Char} {s : Slice} :
|
||||
s.revAll c = s.revAll (· == c) := by
|
||||
simp [revAll, skipSuffixWhile_char_eq_skipSuffixWhile_beq]
|
||||
|
||||
end String.Slice
|
||||
|
||||
@@ -23,8 +23,8 @@ open Std String.Slice Pattern Pattern.Model
|
||||
|
||||
namespace String.Slice
|
||||
|
||||
theorem Pattern.Model.find?_eq_some_iff {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
theorem Pattern.Model.find?_eq_some_iff {ρ : Type} (pat : ρ) [PatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} {pos : s.Pos} :
|
||||
s.find? pat = some pos ↔ MatchesAt pat pos ∧ (∀ pos', pos' < pos → ¬ MatchesAt pat pos') := by
|
||||
@@ -40,8 +40,8 @@ theorem Pattern.Model.find?_eq_some_iff {ρ : Type} (pat : ρ) [PatternModel pat
|
||||
| matched h₁ _ _ => have := h₁.matchesAt; grind
|
||||
| mismatched => grind
|
||||
|
||||
theorem Pattern.Model.find?_eq_none_iff {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
theorem Pattern.Model.find?_eq_none_iff {ρ : Type} (pat : ρ) [PatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} :
|
||||
s.find? pat = none ↔ ∀ (pos : s.Pos), ¬ MatchesAt pat pos := by
|
||||
@@ -65,15 +65,15 @@ theorem find?_eq_none_iff {ρ : Type} (pat : ρ) {σ : Slice → Type}
|
||||
[ToForwardSearcher pat σ] {s : Slice} : s.find? pat = none ↔ s.contains pat = false := by
|
||||
rw [← Option.isNone_iff_eq_none, ← Option.isSome_eq_false_iff, isSome_find?]
|
||||
|
||||
theorem Pattern.Model.contains_eq_false_iff {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
theorem Pattern.Model.contains_eq_false_iff {ρ : Type} (pat : ρ) [PatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} :
|
||||
s.contains pat = false ↔ ∀ (pos : s.Pos), ¬ MatchesAt pat pos := by
|
||||
rw [← find?_eq_none_iff, Slice.find?_eq_none_iff]
|
||||
|
||||
theorem Pattern.Model.contains_eq_true_iff {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
theorem Pattern.Model.contains_eq_true_iff {ρ : Type} (pat : ρ) [PatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} :
|
||||
s.contains pat ↔ ∃ (pos : s.Pos), MatchesAt pat pos := by
|
||||
@@ -85,7 +85,7 @@ theorem Pos.find?_eq_find?_sliceFrom {ρ : Type} {pat : ρ} {σ : Slice → Type
|
||||
p.find? pat = ((s.sliceFrom p).find? pat).map Pos.ofSliceFrom :=
|
||||
(rfl)
|
||||
|
||||
theorem Pattern.Model.posFind?_eq_some_iff {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {σ : Slice → Type}
|
||||
theorem Pattern.Model.posFind?_eq_some_iff {ρ : Type} {pat : ρ} [PatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} {pos pos' : s.Pos} :
|
||||
@@ -100,8 +100,8 @@ theorem Pattern.Model.posFind?_eq_some_iff {ρ : Type} {pat : ρ} [PatternModel
|
||||
refine ⟨Pos.sliceFrom _ _ h₁, ⟨by simpa using h₂, fun p hp₁ hp₂ => ?_⟩, by simp⟩
|
||||
exact h₃ (Pos.ofSliceFrom p) Slice.Pos.le_ofSliceFrom (Pos.lt_sliceFrom_iff.1 hp₁) hp₂
|
||||
|
||||
theorem Pattern.Model.posFind?_eq_none_iff {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat]
|
||||
{σ : Slice → Type} [∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
theorem Pattern.Model.posFind?_eq_none_iff {ρ : Type} {pat : ρ} [PatternModel pat] {σ : Slice → Type}
|
||||
[∀ s, Iterator (σ s) Id (SearchStep s)] [∀ s, Iterators.Finite (σ s) Id]
|
||||
[∀ s, IteratorLoop (σ s) Id Id] [∀ s, LawfulIteratorLoop (σ s) Id Id]
|
||||
[ToForwardSearcher pat σ] [LawfulToForwardSearcherModel pat] {s : Slice} {pos : s.Pos} :
|
||||
pos.find? pat = none ↔ ∀ pos', pos ≤ pos' → ¬ MatchesAt pat pos' := by
|
||||
|
||||
@@ -49,10 +49,9 @@ theorem contains_slice_iff {t s : Slice} :
|
||||
by_cases ht : t.isEmpty
|
||||
· simp [contains_eq_true_of_isEmpty ht s, copy_eq_empty_iff.mpr ht, String.toList_empty]
|
||||
· simp only [Bool.not_eq_true] at ht
|
||||
have := Pattern.Model.ForwardSliceSearcher.strictPatternModel ht
|
||||
have := Pattern.Model.ForwardSliceSearcher.lawfulToForwardSearcherModel ht
|
||||
simp only [Pattern.Model.contains_eq_true_iff,
|
||||
Pattern.Model.ForwardSliceSearcher.exists_matchesAt_iff_eq_append, isInfix_toList_iff]
|
||||
Pattern.Model.ForwardSliceSearcher.exists_matchesAt_iff_eq_append ht, isInfix_toList_iff]
|
||||
|
||||
@[simp]
|
||||
theorem contains_string_iff {t : String} {s : Slice} :
|
||||
|
||||
@@ -18,7 +18,6 @@ import Init.Data.String.Lemmas.Basic
|
||||
import Init.Data.String.Lemmas.Order
|
||||
import Init.Data.Order.Lemmas
|
||||
import Init.Data.String.OrderInstances
|
||||
import Init.Data.String.Lemmas.Iterate
|
||||
import Init.Omega
|
||||
import Init.Data.String.Lemmas.FindPos
|
||||
|
||||
@@ -28,9 +27,8 @@ namespace String.Slice.Pattern.Model.CharPred
|
||||
|
||||
instance {p : Char → Bool} : PatternModel p where
|
||||
Matches s := ∃ c, s = singleton c ∧ p c
|
||||
|
||||
instance {p : Char → Bool} : StrictPatternModel p where
|
||||
not_matches_empty := by simp [PatternModel.Matches]
|
||||
not_matches_empty := by
|
||||
simp
|
||||
|
||||
instance {p : Char → Bool} : NoPrefixPatternModel p :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
@@ -73,39 +71,6 @@ theorem isLongestMatchAt_iff {p : Char → Bool} {s : Slice} {pos pos' : s.Pos}
|
||||
simp +contextual [Model.isLongestMatchAt_iff, isLongestMatch_iff, ← Pos.ofSliceFrom_inj,
|
||||
Pos.get_eq_get_ofSliceFrom, Pos.ofSliceFrom_next]
|
||||
|
||||
theorem isLongestMatchAtChain_iff {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain p pos pos' ↔ pos ≤ pos' ∧ ∀ pos'', pos ≤ pos'' → (h : pos'' < pos') → p (pos''.get (Pos.ne_endPos_of_lt h)) := by
|
||||
induction pos using WellFounded.induction Pos.wellFounded_gt with | h pos ih
|
||||
obtain (h|rfl|h) := Std.lt_trichotomy pos pos'
|
||||
· refine ⟨fun h => ?_, fun ⟨h₁, h₂⟩ => ?_⟩
|
||||
· cases h with
|
||||
| nil => exact (Std.lt_irrefl h).elim
|
||||
| cons _ mid _ h₁ h₂ =>
|
||||
obtain ⟨h₀, rfl, h₁'⟩ := isLongestMatchAt_iff.1 h₁
|
||||
refine ⟨Std.le_of_lt h, fun pos'' hp₁ hp₂ => ?_⟩
|
||||
obtain (hh|rfl) := Std.le_iff_lt_or_eq.1 hp₁
|
||||
· exact ((ih (pos.next (Pos.ne_endPos_of_lt h)) Pos.lt_next).1 h₂).2 _ (by simpa) hp₂
|
||||
· exact h₁'
|
||||
· refine .cons _ (pos.next (Pos.ne_endPos_of_lt h)) _ ?_ ((ih _ Pos.lt_next).2 ?_)
|
||||
· exact isLongestMatchAt_iff.2 ⟨Pos.ne_endPos_of_lt h, rfl, h₂ _ (by simp) h⟩
|
||||
· exact ⟨by simpa, fun pos'' hp₁ hp₂ => h₂ _ (Std.le_trans Pos.le_next hp₁) hp₂⟩
|
||||
· simpa using fun _ h₁ h₂ => (Std.lt_irrefl (Std.lt_of_le_of_lt h₁ h₂)).elim
|
||||
· simpa [Std.not_le.2 h] using fun h' => (Std.not_le.2 h h'.le).elim
|
||||
|
||||
theorem isLongestMatchAtChain_iff_toList {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain p pos pos' ↔ ∃ (h : pos ≤ pos'), ∀ c, c ∈ (s.slice pos pos' h).copy.toList → p c := by
|
||||
simp only [isLongestMatchAtChain_iff, mem_toList_copy_iff_exists_get, Pos.get_eq_get_ofSlice,
|
||||
forall_exists_index]
|
||||
refine ⟨fun ⟨h₁, h₂⟩ => ⟨h₁, fun c p' hp => ?_⟩, fun ⟨h₁, h₂⟩ => ⟨h₁, fun p' hp₁ hp₂ => ?_⟩⟩
|
||||
· rintro rfl
|
||||
exact h₂ _ Pos.le_ofSlice (by simp [Pos.ofSlice_lt_iff, h₁, hp])
|
||||
· refine h₂ _ (Pos.slice p' _ _ hp₁ (Std.le_of_lt hp₂)) ?_ (by simp)
|
||||
rwa [← Pos.lt_endPos_iff, ← Pos.slice_eq_endPos (h := h₁), Pos.slice_lt_slice_iff]
|
||||
|
||||
theorem isLongestMatchAtChain_startPos_endPos_iff_toList {p : Char → Bool} {s : Slice} :
|
||||
IsLongestMatchAtChain p s.startPos s.endPos ↔ ∀ c, c ∈ s.copy.toList → p c := by
|
||||
simp [isLongestMatchAtChain_iff_toList]
|
||||
|
||||
theorem isLongestRevMatchAt_iff {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAt p pos pos' ↔ ∃ h, pos = pos'.prev h ∧ p ((pos'.prev h).get (by simp)) := by
|
||||
simp +contextual [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff, ← Pos.ofSliceTo_inj,
|
||||
@@ -119,35 +84,6 @@ theorem isLongestRevMatchAt_of_get {p : Char → Bool} {s : Slice} {pos : s.Pos}
|
||||
(hc : p ((pos.prev h).get (by simp))) : IsLongestRevMatchAt p (pos.prev h) pos :=
|
||||
isLongestRevMatchAt_iff.2 ⟨h, by simp [hc]⟩
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain p pos pos' ↔ pos ≤ pos' ∧ ∀ pos'', pos ≤ pos'' → (h : pos'' < pos') → p (pos''.get (Pos.ne_endPos_of_lt h)) := by
|
||||
induction pos' using WellFounded.induction Pos.wellFounded_lt with | h pos' ih
|
||||
obtain (h|rfl|h) := Std.lt_trichotomy pos pos'
|
||||
· refine ⟨fun h => ?_, fun ⟨h₁, h₂⟩ => ?_⟩
|
||||
· cases h with
|
||||
| nil => exact (Std.lt_irrefl h).elim
|
||||
| cons _ _ hchain hmatch =>
|
||||
obtain ⟨hne, hmid, hp⟩ := isLongestRevMatchAt_iff.1 hmatch
|
||||
refine ⟨Std.le_of_lt h, fun pos'' hp₁ hp₂ => ?_⟩
|
||||
rcases Std.le_iff_lt_or_eq.1 (Pos.le_prev_iff_lt.2 hp₂) with hh | heq
|
||||
· exact ((ih _ Pos.prev_lt).1 (hmid ▸ hchain)).2 _ hp₁ hh
|
||||
· exact heq ▸ hp
|
||||
· have hne : pos' ≠ s.startPos := Slice.Pos.ne_startPos_of_lt h
|
||||
refine .cons _ (pos'.prev hne) _ ((ih _ Pos.prev_lt).2 ?_)
|
||||
(isLongestRevMatchAt_of_get (h₂ _ (Pos.le_prev_iff_lt.2 h) Pos.prev_lt))
|
||||
exact ⟨Pos.le_prev_iff_lt.2 h, fun pos'' hp₁ hp₂ =>
|
||||
h₂ _ hp₁ (Std.lt_trans hp₂ Pos.prev_lt)⟩
|
||||
· simpa using fun _ h₁ h₂ => (Std.lt_irrefl (Std.lt_of_le_of_lt h₁ h₂)).elim
|
||||
· simpa [Std.not_le.2 h] using fun h' => (Std.not_le.2 h h'.le).elim
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff_toList {p : Char → Bool} {s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain p pos pos' ↔ ∃ (h : pos ≤ pos'), ∀ c, c ∈ (s.slice pos pos' h).copy.toList → p c :=
|
||||
isLongestRevMatchAtChain_iff.trans (isLongestMatchAtChain_iff.symm.trans isLongestMatchAtChain_iff_toList)
|
||||
|
||||
theorem isLongestRevMatchAtChain_startPos_endPos_iff_toList {p : Char → Bool} {s : Slice} :
|
||||
IsLongestRevMatchAtChain p s.startPos s.endPos ↔ ∀ c, c ∈ s.copy.toList → p c := by
|
||||
simp [isLongestRevMatchAtChain_iff_toList]
|
||||
|
||||
instance {p : Char → Bool} : LawfulForwardPatternModel p where
|
||||
skipPrefix?_eq_some_iff {s} pos := by
|
||||
simp [isLongestMatch_iff, ForwardPattern.skipPrefix?, and_comm, eq_comm (b := pos)]
|
||||
@@ -192,9 +128,7 @@ namespace Decidable
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : PatternModel p where
|
||||
Matches := PatternModel.Matches (decide <| p ·)
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : StrictPatternModel p where
|
||||
not_matches_empty := StrictPatternModel.not_matches_empty (pat := (decide <| p ·))
|
||||
not_matches_empty := PatternModel.not_matches_empty (pat := (decide <| p ·))
|
||||
|
||||
instance {p : Char → Prop} [DecidablePred p] : NoPrefixPatternModel p where
|
||||
eq_empty := NoPrefixPatternModel.eq_empty (pat := (decide <| p ·))
|
||||
@@ -248,32 +182,6 @@ theorem isLongestRevMatchAt_iff_isLongestRevMatchAt_decide {p : Char → Prop} [
|
||||
IsLongestRevMatchAt p pos pos' ↔ IsLongestRevMatchAt (decide <| p ·) pos pos' := by
|
||||
simp [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff_isLongestRevMatch_decide]
|
||||
|
||||
theorem isLongestMatchAtChain_iff_isLongestMatchAtChain_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestMatchAtChain p pos pos' ↔ IsLongestMatchAtChain (decide <| p ·) pos pos' := by
|
||||
constructor
|
||||
· intro h; induction h with
|
||||
| nil => exact .nil _
|
||||
| cons _ mid _ hmatch hchain ih =>
|
||||
exact .cons _ mid _ (isLongestMatchAt_iff_isLongestMatchAt_decide.1 hmatch) ih
|
||||
· intro h; induction h with
|
||||
| nil => exact .nil _
|
||||
| cons _ mid _ hmatch hchain ih =>
|
||||
exact .cons _ mid _ (isLongestMatchAt_iff_isLongestMatchAt_decide.2 hmatch) ih
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} {pos pos' : s.Pos} :
|
||||
IsLongestRevMatchAtChain p pos pos' ↔ IsLongestRevMatchAtChain (decide <| p ·) pos pos' := by
|
||||
constructor
|
||||
· intro h; induction h with
|
||||
| nil => exact .nil _
|
||||
| cons _ _ hchain hmatch ih =>
|
||||
exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_decide.1 hmatch)
|
||||
· intro h; induction h with
|
||||
| nil => exact .nil _
|
||||
| cons _ _ hchain hmatch ih =>
|
||||
exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_decide.2 hmatch)
|
||||
|
||||
theorem isLongestMatchAt_iff {p : Char → Prop} [DecidablePred p] {s : Slice}
|
||||
{pos pos' : s.Pos} :
|
||||
IsLongestMatchAt p pos pos' ↔ ∃ h, pos' = pos.next h ∧ p (pos.get h) := by
|
||||
@@ -411,9 +319,6 @@ theorem dropPrefix_prop_eq_dropPrefix_decide {p : Char → Prop} [DecidablePred
|
||||
theorem skipPrefix?_prop_eq_skipPrefix?_decide {p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
s.skipPrefix? p = s.skipPrefix? (decide <| p ·) := (rfl)
|
||||
|
||||
theorem Pos.skip?_prop_eq_skip?_decide {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? p = pos.skip? (decide <| p ·) := (rfl)
|
||||
|
||||
theorem Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide
|
||||
{p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
skipPrefix? p s = skipPrefix? (decide <| p ·) s := (rfl)
|
||||
@@ -424,13 +329,13 @@ theorem Pos.skipWhile_prop_eq_skipWhile_decide {p : Char → Prop} [DecidablePre
|
||||
fun_induction Pos.skipWhile curr p with
|
||||
| case1 pos nextCurr h₁ h₂ ih =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pos.skip?_prop_eq_skip?_decide, h₁, h₂, ih]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pos.skip?_prop_eq_skip?_decide, h, ih]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pos.skip?_prop_eq_skip?_decide, h]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_prop_eq_skipPrefix?_decide]
|
||||
|
||||
theorem skipPrefixWhile_prop_eq_skipPrefixWhile_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} :
|
||||
@@ -447,7 +352,7 @@ theorem takeWhile_prop_eq_takeWhile_decide {p : Char → Prop} [DecidablePred p]
|
||||
|
||||
theorem all_prop_eq_all_decide {p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
s.all p = s.all (decide <| p ·) := by
|
||||
simp only [all, skipPrefixWhile_prop_eq_skipPrefixWhile_decide]
|
||||
simp only [all, dropWhile_prop_eq_dropWhile_decide]
|
||||
|
||||
theorem find?_prop_eq_find?_decide {p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
s.find? p = s.find? (decide <| p ·) :=
|
||||
@@ -478,22 +383,19 @@ theorem Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide
|
||||
{p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
skipSuffix? p s = skipSuffix? (decide <| p ·) s := (rfl)
|
||||
|
||||
theorem Pos.revSkip?_prop_eq_revSkip?_decide {p : Char → Prop} [DecidablePred p] {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? p = pos.revSkip? (decide <| p ·) := (rfl)
|
||||
|
||||
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 [Pos.revSkipWhile]
|
||||
simp [← Pos.revSkip?_prop_eq_revSkip?_decide, h₁, h₂, ih]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pos.revSkip?_prop_eq_revSkip?_decide, h, ih]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pos.revSkip?_prop_eq_revSkip?_decide, h]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_prop_eq_skipSuffix?_decide]
|
||||
|
||||
theorem skipSuffixWhile_prop_eq_skipSuffixWhile_decide {p : Char → Prop} [DecidablePred p]
|
||||
{s : Slice} :
|
||||
@@ -510,8 +412,4 @@ theorem takeEndWhile_prop_eq_takeEndWhile_decide {p : Char → Prop} [DecidableP
|
||||
s.takeEndWhile p = s.takeEndWhile (decide <| p ·) := by
|
||||
simp only [takeEndWhile]; exact congrArg _ skipSuffixWhile_prop_eq_skipSuffixWhile_decide
|
||||
|
||||
theorem revAll_prop_eq_revAll_decide {p : Char → Prop} [DecidablePred p] {s : Slice} :
|
||||
s.revAll p = s.revAll (decide <| p ·) := by
|
||||
simp only [revAll, skipSuffixWhile_prop_eq_skipSuffixWhile_decide]
|
||||
|
||||
end String.Slice
|
||||
|
||||
@@ -36,7 +36,7 @@ This gives a low-level correctness proof from which higher-level API lemmas can
|
||||
namespace String.Slice.Pattern.Model
|
||||
|
||||
@[cbv_opaque]
|
||||
public protected noncomputable def split {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat] {s : Slice}
|
||||
public protected noncomputable def split {ρ : Type} (pat : ρ) [PatternModel pat] {s : Slice}
|
||||
(firstRejected curr : s.Pos) (hle : firstRejected ≤ curr) : List s.Subslice :=
|
||||
if h : curr = s.endPos then
|
||||
[s.subslice _ _ hle]
|
||||
@@ -49,12 +49,12 @@ public protected noncomputable def split {ρ : Type} (pat : ρ) [PatternModel pa
|
||||
termination_by curr
|
||||
|
||||
@[simp]
|
||||
public theorem split_endPos {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat] {s : Slice}
|
||||
public theorem split_endPos {ρ : Type} {pat : ρ} [PatternModel pat] {s : Slice}
|
||||
{firstRejected : s.Pos} :
|
||||
Model.split (s := s) pat firstRejected s.endPos (by simp) = [s.subslice firstRejected s.endPos (by simp)] := by
|
||||
simp [Model.split]
|
||||
|
||||
public theorem split_eq_of_isLongestMatchAt {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat]
|
||||
public theorem split_eq_of_isLongestMatchAt {ρ : Type} {pat : ρ} [PatternModel pat]
|
||||
{s : Slice} {firstRejected start stop : s.Pos} {hle} (h : IsLongestMatchAt pat start stop) :
|
||||
Model.split pat firstRejected start hle =
|
||||
s.subslice _ _ hle :: Model.split pat stop stop (by exact Std.le_refl _) := by
|
||||
@@ -63,7 +63,7 @@ public theorem split_eq_of_isLongestMatchAt {ρ : Type} {pat : ρ} [PatternModel
|
||||
· congr <;> exact (matchAt?_eq_some_iff.1 ‹_›).eq h
|
||||
· simp [matchAt?_eq_some_iff.2 ‹_›] at *
|
||||
|
||||
public theorem split_eq_of_not_matchesAt {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat]
|
||||
public theorem split_eq_of_not_matchesAt {ρ : Type} {pat : ρ} [PatternModel pat]
|
||||
{s : Slice} {firstRejected start} (stop : s.Pos) (h₀ : start ≤ stop) {hle}
|
||||
(h : ∀ p, start ≤ p → p < stop → ¬ MatchesAt pat p) :
|
||||
Model.split pat firstRejected start hle =
|
||||
@@ -80,7 +80,7 @@ public theorem split_eq_of_not_matchesAt {ρ : Type} {pat : ρ} [PatternModel pa
|
||||
· obtain rfl : start = stop := Std.le_antisymm h₀ (Std.not_lt.1 h')
|
||||
simp
|
||||
|
||||
public theorem split_eq_next_of_not_matchesAt {ρ : Type} {pat : ρ} [PatternModel pat] [StrictPatternModel pat]
|
||||
public theorem split_eq_next_of_not_matchesAt {ρ : Type} {pat : ρ} [PatternModel pat]
|
||||
{s : Slice} {firstRejected start} {hle} (hs : start ≠ s.endPos) (h : ¬ MatchesAt pat start) :
|
||||
Model.split pat firstRejected start hle =
|
||||
Model.split pat firstRejected (start.next hs) (by exact Std.le_trans hle (by simp)) := by
|
||||
@@ -103,7 +103,7 @@ def splitFromSteps {s : Slice} (currPos : s.Pos) (l : List (SearchStep s)) : Lis
|
||||
| .matched p q :: l => s.subslice! currPos p :: splitFromSteps q l
|
||||
|
||||
theorem IsValidSearchFrom.splitFromSteps_eq_extend_split {ρ : Type} (pat : ρ)
|
||||
[PatternModel pat] [StrictPatternModel pat] (l : List (SearchStep s)) (pos pos' : s.Pos) (h₀ : pos ≤ pos')
|
||||
[PatternModel pat] (l : List (SearchStep s)) (pos pos' : s.Pos) (h₀ : pos ≤ pos')
|
||||
(h' : ∀ p, pos ≤ p → p < pos' → ¬ MatchesAt pat p)
|
||||
(h : IsValidSearchFrom pat pos' l) :
|
||||
splitFromSteps pos l = Model.split pat pos pos' h₀ := by
|
||||
@@ -155,7 +155,7 @@ end Model
|
||||
open Model
|
||||
|
||||
@[cbv_eval]
|
||||
public theorem toList_splitToSubslice_eq_modelSplit {ρ : Type} (pat : ρ) [PatternModel pat] [StrictPatternModel pat]
|
||||
public theorem toList_splitToSubslice_eq_modelSplit {ρ : Type} (pat : ρ) [PatternModel pat]
|
||||
{σ : Slice → Type} [ToForwardSearcher pat σ] [∀ s, Std.Iterator (σ s) Id (SearchStep s)]
|
||||
[∀ s, Std.Iterators.Finite (σ s) Id] [LawfulToForwardSearcherModel pat] (s : Slice) :
|
||||
(s.splitToSubslice pat).toList = Model.split pat s.startPos s.startPos (by exact Std.le_refl _) := by
|
||||
@@ -168,7 +168,7 @@ end Pattern
|
||||
open Pattern
|
||||
|
||||
public theorem toList_splitToSubslice_of_isEmpty {ρ : Type} (pat : ρ)
|
||||
[Model.PatternModel pat] [Model.StrictPatternModel pat] {σ : Slice → Type}
|
||||
[Model.PatternModel pat] {σ : Slice → Type}
|
||||
[ToForwardSearcher pat σ] [∀ s, Std.Iterator (σ s) Id (SearchStep s)]
|
||||
[∀ s, Std.Iterators.Finite (σ s) Id] [Model.LawfulToForwardSearcherModel pat] {s : Slice}
|
||||
(h : s.isEmpty = true) :
|
||||
@@ -182,7 +182,7 @@ public theorem toList_split_eq_splitToSubslice {ρ : Type} (pat : ρ) {σ : Slic
|
||||
simp [split, Std.Iter.toList_map]
|
||||
|
||||
public theorem toList_split_of_isEmpty {ρ : Type} (pat : ρ)
|
||||
[Model.PatternModel pat] [Model.StrictPatternModel pat] {σ : Slice → Type}
|
||||
[Model.PatternModel pat] {σ : Slice → Type}
|
||||
[ToForwardSearcher pat σ] [∀ s, Std.Iterator (σ s) Id (SearchStep s)]
|
||||
[∀ s, Std.Iterators.Finite (σ s) Id] [Model.LawfulToForwardSearcherModel pat] {s : Slice}
|
||||
(h : s.isEmpty = true) :
|
||||
@@ -200,7 +200,7 @@ public theorem split_eq_split_toSlice {ρ : Type} {pat : ρ} {σ : Slice → Typ
|
||||
|
||||
@[simp]
|
||||
public theorem toList_split_empty {ρ : Type} (pat : ρ)
|
||||
[Model.PatternModel pat] [Model.StrictPatternModel pat] {σ : Slice → Type}
|
||||
[Model.PatternModel pat] {σ : Slice → Type}
|
||||
[ToForwardSearcher pat σ] [∀ s, Std.Iterator (σ s) Id (SearchStep s)]
|
||||
[∀ s, Std.Iterators.Finite (σ s) Id] [Model.LawfulToForwardSearcherModel pat] :
|
||||
("".split pat).toList.map Slice.copy = [""] := by
|
||||
|
||||
@@ -10,9 +10,6 @@ public import Init.Data.String.Pattern.String
|
||||
public import Init.Data.String.Lemmas.Pattern.Basic
|
||||
import Init.Data.String.Lemmas.IsEmpty
|
||||
import Init.Data.String.Lemmas.Basic
|
||||
import Init.Data.String.Lemmas.Intercalate
|
||||
import Init.Data.String.OrderInstances
|
||||
import Init.Data.String.Lemmas.Splits
|
||||
import Init.Data.ByteArray.Lemmas
|
||||
import Init.Omega
|
||||
|
||||
@@ -23,10 +20,17 @@ namespace String.Slice.Pattern.Model
|
||||
namespace ForwardSliceSearcher
|
||||
|
||||
instance {pat : Slice} : PatternModel pat where
|
||||
Matches s := s = pat.copy
|
||||
/-
|
||||
See the docstring of `PatternModel` for an explanation about why we disallow matching the
|
||||
empty string.
|
||||
|
||||
theorem strictPatternModel {pat : Slice} (hpat : pat.isEmpty = false) : StrictPatternModel pat where
|
||||
not_matches_empty := by simpa [PatternModel.Matches]
|
||||
Requiring `s ≠ ""` is a trick that allows us to give a `PatternModel` instance
|
||||
unconditionally, without forcing `pat.copy` to be non-empty (which would make it very awkward
|
||||
to state theorems about the instance). It does not change anything about the fact that all lemmas
|
||||
about this instance require `pat.isEmpty = false`.
|
||||
-/
|
||||
Matches s := s ≠ "" ∧ s = pat.copy
|
||||
not_matches_empty := by simp
|
||||
|
||||
instance {pat : Slice} : NoPrefixPatternModel pat :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
@@ -34,111 +38,59 @@ instance {pat : Slice} : NoPrefixPatternModel pat :=
|
||||
instance {pat : Slice} : NoSuffixPatternModel pat :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
|
||||
theorem isMatch_iff {pat s : Slice} {pos : s.Pos} :
|
||||
theorem isMatch_iff {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
IsMatch pat pos ↔ (s.sliceTo pos).copy = pat.copy := by
|
||||
simp [Model.isMatch_iff, PatternModel.Matches]
|
||||
simp only [Model.isMatch_iff, PatternModel.Matches, ne_eq, copy_eq_empty_iff,
|
||||
Bool.not_eq_true, and_iff_right_iff_imp]
|
||||
intro h'
|
||||
rw [← isEmpty_copy (s := s.sliceTo pos), h', isEmpty_copy, h]
|
||||
|
||||
theorem isRevMatch_iff {pat s : Slice} {pos : s.Pos} :
|
||||
theorem isRevMatch_iff {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
IsRevMatch pat pos ↔ (s.sliceFrom pos).copy = pat.copy := by
|
||||
simp [Model.isRevMatch_iff, PatternModel.Matches]
|
||||
simp only [Model.isRevMatch_iff, PatternModel.Matches, ne_eq, copy_eq_empty_iff,
|
||||
Bool.not_eq_true, and_iff_right_iff_imp]
|
||||
intro h'
|
||||
rw [← isEmpty_copy (s := s.sliceFrom pos), h', isEmpty_copy, h]
|
||||
|
||||
theorem isLongestMatch_iff {pat s : Slice} {pos : s.Pos} :
|
||||
theorem isLongestMatch_iff {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
IsLongestMatch pat pos ↔ (s.sliceTo pos).copy = pat.copy := by
|
||||
rw [isLongestMatch_iff_isMatch, isMatch_iff]
|
||||
rw [isLongestMatch_iff_isMatch, isMatch_iff h]
|
||||
|
||||
theorem isLongestRevMatch_iff {pat s : Slice} {pos : s.Pos} :
|
||||
theorem isLongestRevMatch_iff {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
IsLongestRevMatch pat pos ↔ (s.sliceFrom pos).copy = pat.copy := by
|
||||
rw [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff]
|
||||
rw [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff h]
|
||||
|
||||
theorem isLongestMatchAt_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
theorem isLongestMatchAt_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} (h : pat.isEmpty = false) :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔ ∃ h, (s.slice pos₁ pos₂ h).copy = pat.copy := by
|
||||
simp [Model.isLongestMatchAt_iff, isLongestMatch_iff]
|
||||
simp [Model.isLongestMatchAt_iff, isLongestMatch_iff h]
|
||||
|
||||
theorem isLongestMatchAtChain_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAtChain pat pos₁ pos₂ ↔
|
||||
∃ h n, (s.slice pos₁ pos₂ h).copy = String.join (List.replicate n pat.copy) := by
|
||||
refine ⟨fun h => ⟨h.le, ?_⟩, fun ⟨h, n, h'⟩ => ?_⟩
|
||||
· induction h with
|
||||
| nil => simpa using ⟨0, by simp⟩
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih =>
|
||||
rw [isLongestMatchAt_iff] at h₁
|
||||
obtain ⟨n, ih⟩ := ih
|
||||
obtain ⟨h₀, h₁⟩ := h₁
|
||||
have : (s.slice p₁ p₃ (Std.le_trans h₀ h₂.le)).copy = (s.slice p₁ p₂ h₀).copy ++ (s.slice p₂ p₃ h₂.le).copy := by
|
||||
simp [(Slice.Pos.slice p₂ _ _ h₀ h₂.le).splits.eq_append]
|
||||
refine ⟨n + 1, ?_⟩
|
||||
rw [this, h₁, ih]
|
||||
simp [← String.join_cons, ← List.replicate_succ]
|
||||
· induction n generalizing pos₁ pos₂ with
|
||||
| zero => simp_all
|
||||
| succ n ih =>
|
||||
rw [List.replicate_succ, String.join_cons] at h'
|
||||
refine .cons _ (Pos.ofSlice (Pos.ofEqAppend h')) _ ?_ (ih ?_ Pos.ofSlice_le ?_)
|
||||
· simpa [isLongestMatchAt_iff] using (Pos.splits_ofEqAppend h').copy_sliceTo_eq
|
||||
· simpa [sliceFrom_slice ▸ (Pos.splits_ofEqAppend h').copy_sliceFrom_eq] using ⟨n, rfl⟩
|
||||
· simpa using (Pos.splits_ofEqAppend h').copy_sliceFrom_eq
|
||||
|
||||
theorem isLongestMatchAtChain_startPos_endPos_iff {pat s : Slice} :
|
||||
IsLongestMatchAtChain pat s.startPos s.endPos ↔
|
||||
∃ n, s.copy = String.join (List.replicate n pat.copy) := by
|
||||
simp [isLongestMatchAtChain_iff]
|
||||
|
||||
theorem isLongestRevMatchAt_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
theorem isLongestRevMatchAt_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} (h : pat.isEmpty = false) :
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔ ∃ h, (s.slice pos₁ pos₂ h).copy = pat.copy := by
|
||||
simp [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff]
|
||||
simp [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff h]
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAtChain pat pos₁ pos₂ ↔
|
||||
∃ h n, (s.slice pos₁ pos₂ h).copy = String.join (List.replicate n pat.copy) := by
|
||||
refine ⟨fun h => ⟨h.le, ?_⟩, fun ⟨h, n, h'⟩ => ?_⟩
|
||||
· induction h with
|
||||
| nil => simpa using ⟨0, by simp⟩
|
||||
| cons p₂ p₃ h₁ h₂ ih =>
|
||||
rw [isLongestRevMatchAt_iff] at h₂
|
||||
obtain ⟨n, ih⟩ := ih
|
||||
obtain ⟨h₀, h₂⟩ := h₂
|
||||
have : (s.slice pos₁ p₃ (Std.le_trans h₁.le h₀)).copy = (s.slice pos₁ p₂ h₁.le).copy ++ (s.slice p₂ p₃ h₀).copy := by
|
||||
simp [(Slice.Pos.slice p₂ _ _ (IsLongestRevMatchAtChain.le ‹_›) h₀).splits.eq_append]
|
||||
refine ⟨n + 1, ?_⟩
|
||||
rw [this, h₂, ih]
|
||||
simp [← List.replicate_append_replicate]
|
||||
· induction n generalizing pos₁ pos₂ with
|
||||
| zero => simp_all
|
||||
| succ n ih =>
|
||||
have h'' : (s.slice pos₁ pos₂ h).copy = String.join (List.replicate n pat.copy) ++ pat.copy := by
|
||||
rw [h', List.replicate_succ', String.join_append]; simp
|
||||
refine .cons _ (Pos.ofSlice (Pos.ofEqAppend h'')) _ (ih ?_ Pos.le_ofSlice ?_) ?_
|
||||
· simpa [sliceTo_slice ▸ (Pos.splits_ofEqAppend h'').copy_sliceTo_eq] using ⟨n, rfl⟩
|
||||
· simpa using (Pos.splits_ofEqAppend h'').copy_sliceTo_eq
|
||||
· simpa [isLongestRevMatchAt_iff] using (Pos.splits_ofEqAppend h'').copy_sliceFrom_eq
|
||||
|
||||
theorem isLongestRevMatchAtChain_startPos_endPos_iff {pat s : Slice} :
|
||||
IsLongestRevMatchAtChain pat s.startPos s.endPos ↔
|
||||
∃ n, s.copy = String.join (List.replicate n pat.copy) := by
|
||||
simp [isLongestRevMatchAtChain_iff]
|
||||
|
||||
theorem isLongestMatchAt_iff_splits {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
theorem isLongestMatchAt_iff_splits {pat s : Slice} {pos₁ pos₂ : s.Pos} (h : pat.isEmpty = false) :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔ ∃ t₁ t₂, pos₁.Splits t₁ (pat.copy ++ t₂) ∧
|
||||
pos₂.Splits (t₁ ++ pat.copy) t₂ := by
|
||||
simp only [isLongestMatchAt_iff, copy_slice_eq_iff_splits]
|
||||
simp only [isLongestMatchAt_iff h, copy_slice_eq_iff_splits]
|
||||
|
||||
theorem isLongestRevMatchAt_iff_splits {pat s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
theorem isLongestRevMatchAt_iff_splits {pat s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat.isEmpty = false) :
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔ ∃ t₁ t₂, pos₁.Splits t₁ (pat.copy ++ t₂) ∧
|
||||
pos₂.Splits (t₁ ++ pat.copy) t₂ := by
|
||||
simp only [isLongestRevMatchAt_iff, copy_slice_eq_iff_splits]
|
||||
simp only [isLongestRevMatchAt_iff h, copy_slice_eq_iff_splits]
|
||||
|
||||
theorem isLongestMatch_iff_splits {pat s : Slice} {pos : s.Pos} :
|
||||
theorem isLongestMatch_iff_splits {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
IsLongestMatch pat pos ↔ ∃ t, pos.Splits pat.copy t := by
|
||||
rw [isLongestMatch_iff, copy_sliceTo_eq_iff_exists_splits]
|
||||
rw [isLongestMatch_iff h, copy_sliceTo_eq_iff_exists_splits]
|
||||
|
||||
theorem isLongestRevMatch_iff_splits {pat s : Slice} {pos : s.Pos} :
|
||||
theorem isLongestRevMatch_iff_splits {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
IsLongestRevMatch pat pos ↔ ∃ t, pos.Splits t pat.copy := by
|
||||
rw [isLongestRevMatch_iff, copy_sliceFrom_eq_iff_exists_splits]
|
||||
rw [isLongestRevMatch_iff h, copy_sliceFrom_eq_iff_exists_splits]
|
||||
|
||||
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
|
||||
rw [isLongestMatchAt_iff]
|
||||
rw [isLongestMatchAt_iff h]
|
||||
refine ⟨fun ⟨h, h'⟩ => ?_, fun h' => ?_⟩
|
||||
· simp [← h', toByteArray_copy_slice]
|
||||
· rw [← Slice.toByteArray_copy_ne_empty_iff, ← h', ne_eq, ByteArray.extract_eq_empty_iff] at h
|
||||
@@ -150,7 +102,7 @@ theorem isLongestRevMatchAt_iff_extract {pat s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔
|
||||
s.copy.toByteArray.extract pos₁.offset.byteIdx pos₂.offset.byteIdx =
|
||||
pat.copy.toByteArray := by
|
||||
rw [isLongestRevMatchAt_iff]
|
||||
rw [isLongestRevMatchAt_iff h]
|
||||
refine ⟨fun ⟨h, h'⟩ => ?_, fun h' => ?_⟩
|
||||
· simp [← h', toByteArray_copy_slice]
|
||||
· rw [← Slice.toByteArray_copy_ne_empty_iff, ← h', ne_eq, ByteArray.extract_eq_empty_iff] at h
|
||||
@@ -178,21 +130,21 @@ theorem offset_of_isLongestRevMatchAt {pat s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
suffices pos₂.offset.byteIdx ≤ s.utf8ByteSize by omega
|
||||
simpa [Pos.le_iff, Pos.Raw.le_iff] using pos₂.le_endPos
|
||||
|
||||
theorem matchesAt_iff_splits {pat s : Slice} {pos : s.Pos} :
|
||||
theorem matchesAt_iff_splits {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
MatchesAt pat pos ↔ ∃ t₁ t₂, pos.Splits t₁ (pat.copy ++ t₂) := by
|
||||
simp only [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff_splits]
|
||||
simp only [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff_splits h]
|
||||
exact ⟨fun ⟨e, t₁, t₂, ht₁, ht₂⟩ => ⟨t₁, t₂, ht₁⟩,
|
||||
fun ⟨t₁, t₂, ht⟩ => ⟨ht.rotateRight, t₁, t₂, ht, ht.splits_rotateRight⟩⟩
|
||||
|
||||
theorem revMatchesAt_iff_splits {pat s : Slice} {pos : s.Pos} :
|
||||
theorem revMatchesAt_iff_splits {pat s : Slice} {pos : s.Pos} (h : pat.isEmpty = false) :
|
||||
RevMatchesAt pat pos ↔ ∃ t₁ t₂, pos.Splits (t₁ ++ pat.copy) t₂ := by
|
||||
simp only [revMatchesAt_iff_exists_isLongestRevMatchAt, isLongestRevMatchAt_iff_splits]
|
||||
simp only [revMatchesAt_iff_exists_isLongestRevMatchAt, isLongestRevMatchAt_iff_splits h]
|
||||
exact ⟨fun ⟨e, t₁, t₂, ht₁, ht₂⟩ => ⟨t₁, t₂, ht₂⟩,
|
||||
fun ⟨t₁, t₂, ht⟩ => ⟨ht.rotateLeft, t₁, t₂, ht.splits_rotateLeft, ht⟩⟩
|
||||
|
||||
theorem exists_matchesAt_iff_eq_append {pat s : Slice} :
|
||||
theorem exists_matchesAt_iff_eq_append {pat s : Slice} (h : pat.isEmpty = false) :
|
||||
(∃ (pos : s.Pos), MatchesAt pat pos) ↔ ∃ t₁ t₂, s.copy = t₁ ++ pat.copy ++ t₂ := by
|
||||
simp only [matchesAt_iff_splits]
|
||||
simp only [matchesAt_iff_splits h]
|
||||
constructor
|
||||
· rintro ⟨pos, t₁, t₂, hsplit⟩
|
||||
exact ⟨t₁, t₂, by rw [hsplit.eq_append, append_assoc]⟩
|
||||
@@ -202,9 +154,9 @@ theorem exists_matchesAt_iff_eq_append {pat s : Slice} :
|
||||
⟨t₁, pat.copy ++ t₂, by rw [← append_assoc]; exact heq, rfl⟩
|
||||
exact ⟨s.pos _ hvalid, t₁, t₂, ⟨by rw [← append_assoc]; exact heq, by simp⟩⟩
|
||||
|
||||
theorem exists_revMatchesAt_iff_eq_append {pat s : Slice} :
|
||||
theorem exists_revMatchesAt_iff_eq_append {pat s : Slice} (h : pat.isEmpty = false) :
|
||||
(∃ (pos : s.Pos), RevMatchesAt pat pos) ↔ ∃ t₁ t₂, s.copy = t₁ ++ pat.copy ++ t₂ := by
|
||||
simp only [revMatchesAt_iff_splits]
|
||||
simp only [revMatchesAt_iff_splits h]
|
||||
constructor
|
||||
· rintro ⟨pos, t₁, t₂, hsplit⟩
|
||||
exact ⟨t₁, t₂, by rw [hsplit.eq_append, append_assoc]⟩
|
||||
@@ -281,10 +233,8 @@ end ForwardSliceSearcher
|
||||
namespace ForwardStringSearcher
|
||||
|
||||
instance {pat : String} : PatternModel pat where
|
||||
Matches s := s = pat
|
||||
|
||||
theorem strictPatternModel {pat : String} (h : pat ≠ "") : StrictPatternModel pat where
|
||||
not_matches_empty := by simpa [PatternModel.Matches]
|
||||
Matches s := s ≠ "" ∧ s = pat
|
||||
not_matches_empty := by simp
|
||||
|
||||
instance {pat : String} : NoPrefixPatternModel pat :=
|
||||
.of_length_eq (by simp +contextual [PatternModel.Matches])
|
||||
@@ -317,60 +267,12 @@ theorem isLongestMatchAt_iff_isLongestMatchAt_toSlice {pat : String} {s : Slice}
|
||||
IsLongestMatchAt (ρ := Slice) pat.toSlice pos₁ pos₂ := by
|
||||
simp [Model.isLongestMatchAt_iff, isLongestMatch_iff_isLongestMatch_toSlice]
|
||||
|
||||
theorem isLongestMatchAtChain_iff_isLongestMatchAtChain_toSlice {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAtChain pat pos₁ pos₂ ↔
|
||||
IsLongestMatchAtChain pat.toSlice pos₁ pos₂ := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih =>
|
||||
exact .cons _ _ _ (isLongestMatchAt_iff_isLongestMatchAt_toSlice.1 h₁) ih
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₁ p₂ p₃ h₁ h₂ ih =>
|
||||
exact .cons _ _ _ (isLongestMatchAt_iff_isLongestMatchAt_toSlice.2 h₁) ih
|
||||
|
||||
theorem isLongestMatchAtChain_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestMatchAtChain pat pos₁ pos₂ ↔
|
||||
∃ h n, (s.slice pos₁ pos₂ h).copy = String.join (List.replicate n pat) := by
|
||||
simp [isLongestMatchAtChain_iff_isLongestMatchAtChain_toSlice,
|
||||
ForwardSliceSearcher.isLongestMatchAtChain_iff]
|
||||
|
||||
theorem isLongestMatchAtChain_startPos_endPos_iff {pat : String} {s : Slice} :
|
||||
IsLongestMatchAtChain pat s.startPos s.endPos ↔
|
||||
∃ n, s.copy = String.join (List.replicate n pat) := by
|
||||
simp [isLongestMatchAtChain_iff]
|
||||
|
||||
theorem isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice {pat : String} {s : Slice}
|
||||
{pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAt (ρ := String) pat pos₁ pos₂ ↔
|
||||
IsLongestRevMatchAt (ρ := Slice) pat.toSlice pos₁ pos₂ := by
|
||||
simp [Model.isLongestRevMatchAt_iff, isLongestRevMatch_iff_isLongestRevMatch_toSlice]
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_toSlice {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAtChain pat pos₁ pos₂ ↔
|
||||
IsLongestRevMatchAtChain pat.toSlice pos₁ pos₂ := by
|
||||
refine ⟨fun h => ?_, fun h => ?_⟩
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₂ p₃ _ hmatch ih =>
|
||||
exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice.1 hmatch)
|
||||
· induction h with
|
||||
| nil => simp
|
||||
| cons p₂ p₃ _ hmatch ih =>
|
||||
exact .cons _ _ _ ih (isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice.2 hmatch)
|
||||
|
||||
theorem isLongestRevMatchAtChain_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
IsLongestRevMatchAtChain pat pos₁ pos₂ ↔
|
||||
∃ h n, (s.slice pos₁ pos₂ h).copy = String.join (List.replicate n pat) := by
|
||||
simp [isLongestRevMatchAtChain_iff_isLongestRevMatchAtChain_toSlice,
|
||||
ForwardSliceSearcher.isLongestRevMatchAtChain_iff]
|
||||
|
||||
theorem isLongestRevMatchAtChain_startPos_endPos_iff {pat : String} {s : Slice} :
|
||||
IsLongestRevMatchAtChain pat s.startPos s.endPos ↔
|
||||
∃ n, s.copy = String.join (List.replicate n pat) := by
|
||||
simp [isLongestRevMatchAtChain_iff]
|
||||
|
||||
theorem matchesAt_iff_toSlice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
MatchesAt (ρ := String) pat pos ↔ MatchesAt (ρ := Slice) pat.toSlice pos := by
|
||||
simp [matchesAt_iff_exists_isLongestMatchAt, isLongestMatchAt_iff_isLongestMatchAt_toSlice]
|
||||
@@ -380,55 +282,61 @@ theorem revMatchesAt_iff_toSlice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
simp [revMatchesAt_iff_exists_isLongestRevMatchAt,
|
||||
isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice]
|
||||
|
||||
theorem isMatch_iff {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
private theorem toSlice_isEmpty (h : pat ≠ "") : pat.toSlice.isEmpty = false := by
|
||||
rwa [isEmpty_toSlice, isEmpty_eq_false_iff]
|
||||
|
||||
theorem isMatch_iff {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "") :
|
||||
IsMatch pat pos ↔ (s.sliceTo pos).copy = pat := by
|
||||
rw [isMatch_iff_slice, ForwardSliceSearcher.isMatch_iff]
|
||||
rw [isMatch_iff_slice, ForwardSliceSearcher.isMatch_iff (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem isRevMatch_iff {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
theorem isRevMatch_iff {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "") :
|
||||
IsRevMatch pat pos ↔ (s.sliceFrom pos).copy = pat := by
|
||||
rw [isRevMatch_iff_slice, ForwardSliceSearcher.isRevMatch_iff]
|
||||
rw [isRevMatch_iff_slice, ForwardSliceSearcher.isRevMatch_iff (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem isLongestMatch_iff {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
theorem isLongestMatch_iff {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "") :
|
||||
IsLongestMatch pat pos ↔ (s.sliceTo pos).copy = pat := by
|
||||
rw [isLongestMatch_iff_isMatch, isMatch_iff]
|
||||
rw [isLongestMatch_iff_isMatch, isMatch_iff h]
|
||||
|
||||
theorem isLongestRevMatch_iff {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
theorem isLongestRevMatch_iff {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "") :
|
||||
IsLongestRevMatch pat pos ↔ (s.sliceFrom pos).copy = pat := by
|
||||
rw [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff]
|
||||
rw [isLongestRevMatch_iff_isRevMatch, isRevMatch_iff h]
|
||||
|
||||
theorem isLongestMatchAt_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
theorem isLongestMatchAt_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} (h : pat ≠ "") :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔ ∃ h, (s.slice pos₁ pos₂ h).copy = pat := by
|
||||
rw [isLongestMatchAt_iff_isLongestMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff]
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem isLongestRevMatchAt_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
theorem isLongestRevMatchAt_iff {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} (h : pat ≠ "") :
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔ ∃ h, (s.slice pos₁ pos₂ h).copy = pat := by
|
||||
rw [isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestRevMatchAt_iff]
|
||||
ForwardSliceSearcher.isLongestRevMatchAt_iff (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem isLongestMatchAt_iff_splits {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
theorem isLongestMatchAt_iff_splits {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat ≠ "") :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔
|
||||
∃ t₁ t₂, pos₁.Splits t₁ (pat ++ t₂) ∧ pos₂.Splits (t₁ ++ pat) t₂ := by
|
||||
rw [isLongestMatchAt_iff_isLongestMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff_splits]
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff_splits (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem isLongestRevMatchAt_iff_splits {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} :
|
||||
theorem isLongestRevMatchAt_iff_splits {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat ≠ "") :
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔
|
||||
∃ t₁ t₂, pos₁.Splits t₁ (pat ++ t₂) ∧ pos₂.Splits (t₁ ++ pat) t₂ := by
|
||||
rw [isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestRevMatchAt_iff_splits]
|
||||
ForwardSliceSearcher.isLongestRevMatchAt_iff_splits (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem isLongestMatchAt_iff_extract {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos} (h : pat ≠ "") :
|
||||
theorem isLongestMatchAt_iff_extract {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat ≠ "") :
|
||||
IsLongestMatchAt pat pos₁ pos₂ ↔
|
||||
s.copy.toByteArray.extract pos₁.offset.byteIdx pos₂.offset.byteIdx = pat.toByteArray := by
|
||||
rw [isLongestMatchAt_iff_isLongestMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff_extract (by simpa)]
|
||||
ForwardSliceSearcher.isLongestMatchAt_iff_extract (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem isLongestRevMatchAt_iff_extract {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
@@ -436,38 +344,38 @@ theorem isLongestRevMatchAt_iff_extract {pat : String} {s : Slice} {pos₁ pos
|
||||
IsLongestRevMatchAt pat pos₁ pos₂ ↔
|
||||
s.copy.toByteArray.extract pos₁.offset.byteIdx pos₂.offset.byteIdx = pat.toByteArray := by
|
||||
rw [isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice,
|
||||
ForwardSliceSearcher.isLongestRevMatchAt_iff_extract (by simpa)]
|
||||
ForwardSliceSearcher.isLongestRevMatchAt_iff_extract (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem offset_of_isLongestMatchAt {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat ≠ "") (h' : IsLongestMatchAt pat pos₁ pos₂) :
|
||||
pos₂.offset = pos₁.offset.increaseBy pat.utf8ByteSize := by
|
||||
rw [show pat.utf8ByteSize = pat.toSlice.utf8ByteSize from utf8ByteSize_toSlice.symm]
|
||||
exact ForwardSliceSearcher.offset_of_isLongestMatchAt (by simpa)
|
||||
exact ForwardSliceSearcher.offset_of_isLongestMatchAt (toSlice_isEmpty h)
|
||||
(isLongestMatchAt_iff_isLongestMatchAt_toSlice.1 h')
|
||||
|
||||
theorem offset_of_isLongestRevMatchAt {pat : String} {s : Slice} {pos₁ pos₂ : s.Pos}
|
||||
(h : pat ≠ "") (h' : IsLongestRevMatchAt pat pos₁ pos₂) :
|
||||
pos₂.offset = pos₁.offset.increaseBy pat.utf8ByteSize := by
|
||||
rw [show pat.utf8ByteSize = pat.toSlice.utf8ByteSize from utf8ByteSize_toSlice.symm]
|
||||
exact ForwardSliceSearcher.offset_of_isLongestRevMatchAt (by simpa)
|
||||
exact ForwardSliceSearcher.offset_of_isLongestRevMatchAt (toSlice_isEmpty h)
|
||||
(isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice.1 h')
|
||||
|
||||
theorem matchesAt_iff_splits {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
theorem matchesAt_iff_splits {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "") :
|
||||
MatchesAt pat pos ↔ ∃ t₁ t₂, pos.Splits t₁ (pat ++ t₂) := by
|
||||
rw [matchesAt_iff_toSlice,
|
||||
ForwardSliceSearcher.matchesAt_iff_splits]
|
||||
ForwardSliceSearcher.matchesAt_iff_splits (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem revMatchesAt_iff_splits {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
theorem revMatchesAt_iff_splits {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "") :
|
||||
RevMatchesAt pat pos ↔ ∃ t₁ t₂, pos.Splits (t₁ ++ pat) t₂ := by
|
||||
rw [revMatchesAt_iff_toSlice,
|
||||
ForwardSliceSearcher.revMatchesAt_iff_splits]
|
||||
ForwardSliceSearcher.revMatchesAt_iff_splits (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem exists_matchesAt_iff_eq_append {pat : String} {s : Slice} :
|
||||
theorem exists_matchesAt_iff_eq_append {pat : String} {s : Slice} (h : pat ≠ "") :
|
||||
(∃ (pos : s.Pos), MatchesAt pat pos) ↔ ∃ t₁ t₂, s.copy = t₁ ++ pat ++ t₂ := by
|
||||
simp only [matchesAt_iff_splits]
|
||||
simp only [matchesAt_iff_splits h]
|
||||
constructor
|
||||
· rintro ⟨pos, t₁, t₂, hsplit⟩
|
||||
exact ⟨t₁, t₂, by rw [hsplit.eq_append, append_assoc]⟩
|
||||
@@ -477,12 +385,12 @@ theorem exists_matchesAt_iff_eq_append {pat : String} {s : Slice} :
|
||||
⟨t₁, pat ++ t₂, by rw [← append_assoc]; exact heq, rfl⟩
|
||||
exact ⟨s.pos _ hvalid, t₁, t₂, ⟨by rw [← append_assoc]; exact heq, by simp⟩⟩
|
||||
|
||||
theorem exists_revMatchesAt_iff_eq_append {pat : String} {s : Slice} :
|
||||
theorem exists_revMatchesAt_iff_eq_append {pat : String} {s : Slice} (h : pat ≠ "") :
|
||||
(∃ (pos : s.Pos), RevMatchesAt pat pos) ↔ ∃ t₁ t₂, s.copy = t₁ ++ pat ++ t₂ := by
|
||||
rw [show (∃ (pos : s.Pos), RevMatchesAt (ρ := String) pat pos) ↔
|
||||
(∃ (pos : s.Pos), RevMatchesAt (ρ := Slice) pat.toSlice pos) from by
|
||||
simp [revMatchesAt_iff_toSlice],
|
||||
ForwardSliceSearcher.exists_revMatchesAt_iff_eq_append]
|
||||
ForwardSliceSearcher.exists_revMatchesAt_iff_eq_append (toSlice_isEmpty h)]
|
||||
simp
|
||||
|
||||
theorem matchesAt_iff_isLongestMatchAt {pat : String} {s : Slice} {pos : s.Pos}
|
||||
@@ -490,7 +398,7 @@ theorem matchesAt_iff_isLongestMatchAt {pat : String} {s : Slice} {pos : s.Pos}
|
||||
MatchesAt pat pos ↔ ∃ (h : (pos.offset.increaseBy pat.utf8ByteSize).IsValidForSlice s),
|
||||
IsLongestMatchAt pat pos (s.pos _ h) := by
|
||||
have key := ForwardSliceSearcher.matchesAt_iff_isLongestMatchAt (pat := pat.toSlice)
|
||||
(by simpa) (pos := pos)
|
||||
(toSlice_isEmpty h) (pos := pos)
|
||||
simp only [utf8ByteSize_toSlice, ← isLongestMatchAt_iff_isLongestMatchAt_toSlice] at key
|
||||
rwa [matchesAt_iff_toSlice]
|
||||
|
||||
@@ -500,7 +408,7 @@ theorem revMatchesAt_iff_isLongestRevMatchAt {pat : String} {s : Slice} {pos : s
|
||||
∃ (h : (pos.offset.decreaseBy pat.utf8ByteSize).IsValidForSlice s),
|
||||
IsLongestRevMatchAt pat (s.pos _ h) pos := by
|
||||
have key := ForwardSliceSearcher.revMatchesAt_iff_isLongestRevMatchAt (pat := pat.toSlice)
|
||||
(by simpa) (pos := pos)
|
||||
(toSlice_isEmpty h) (pos := pos)
|
||||
simp only [utf8ByteSize_toSlice, ← isLongestRevMatchAt_iff_isLongestRevMatchAt_toSlice] at key
|
||||
rwa [revMatchesAt_iff_toSlice]
|
||||
|
||||
@@ -510,14 +418,14 @@ theorem matchesAt_iff_getElem {pat : String} {s : Slice} {pos : s.Pos} (h : pat
|
||||
∀ (j), (hj : j < pat.toByteArray.size) →
|
||||
pat.toByteArray[j] = s.copy.toByteArray[pos.offset.byteIdx + j] := by
|
||||
have key := ForwardSliceSearcher.matchesAt_iff_getElem (pat := pat.toSlice)
|
||||
(by simpa) (pos := pos)
|
||||
(toSlice_isEmpty h) (pos := pos)
|
||||
simp only [copy_toSlice] at key
|
||||
rwa [matchesAt_iff_toSlice]
|
||||
|
||||
theorem le_of_matchesAt {pat : String} {s : Slice} {pos : s.Pos} (h : pat ≠ "")
|
||||
(h' : MatchesAt pat pos) : pos.offset.increaseBy pat.utf8ByteSize ≤ s.rawEndPos := by
|
||||
rw [show pat.utf8ByteSize = pat.toSlice.utf8ByteSize from utf8ByteSize_toSlice.symm]
|
||||
exact ForwardSliceSearcher.le_of_matchesAt (by simpa)
|
||||
exact ForwardSliceSearcher.le_of_matchesAt (toSlice_isEmpty h)
|
||||
(matchesAt_iff_toSlice.1 h')
|
||||
|
||||
theorem matchesAt_iff_matchesAt_toSlice {pat : String} {s : Slice}
|
||||
|
||||
@@ -56,7 +56,7 @@ theorem skipPrefix?_eq_some_iff {pat s : Slice} {pos : s.Pos} :
|
||||
simp only [reduceCtorEq, false_iff]
|
||||
intro heq
|
||||
have := h (s.sliceFrom pos).copy
|
||||
simp [← heq, -sliceTo_append_sliceFrom, pos.splits.eq_append] at this
|
||||
simp [← heq, pos.splits.eq_append] at this
|
||||
|
||||
theorem isSome_skipPrefix? {pat s : Slice} : (skipPrefix? pat s).isSome = startsWith pat s := by
|
||||
fun_cases skipPrefix? <;> simp_all
|
||||
@@ -76,11 +76,14 @@ namespace Model.ForwardSliceSearcher
|
||||
|
||||
open Pattern.ForwardSliceSearcher
|
||||
|
||||
public instance {pat : Slice} : LawfulForwardPatternModel pat where
|
||||
public instance {pat : Slice} : LawfulForwardPattern pat where
|
||||
skipPrefixOfNonempty?_eq _ := rfl
|
||||
startsWith_eq _ := isSome_skipPrefix?.symm
|
||||
|
||||
public theorem lawfulForwardPatternModel {pat : Slice} (hpat : pat.isEmpty = false) :
|
||||
LawfulForwardPatternModel pat where
|
||||
skipPrefix?_eq_some_iff pos := by
|
||||
simp [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff, isLongestMatch_iff]
|
||||
simp [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff, isLongestMatch_iff hpat]
|
||||
|
||||
end Model.ForwardSliceSearcher
|
||||
|
||||
@@ -88,11 +91,14 @@ namespace Model.ForwardStringSearcher
|
||||
|
||||
open Pattern.ForwardSliceSearcher
|
||||
|
||||
public instance {pat : String} : LawfulForwardPatternModel pat where
|
||||
public instance {pat : String} : LawfulForwardPattern pat where
|
||||
skipPrefixOfNonempty?_eq _ := rfl
|
||||
startsWith_eq _ := isSome_skipPrefix?.symm
|
||||
|
||||
public theorem lawfulForwardPatternModel {pat : String} (hpat : pat ≠ "") :
|
||||
LawfulForwardPatternModel pat where
|
||||
skipPrefix?_eq_some_iff pos := by
|
||||
simp [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff, isLongestMatch_iff]
|
||||
simp [ForwardPattern.skipPrefix?, skipPrefix?_eq_some_iff, isLongestMatch_iff hpat]
|
||||
|
||||
end Model.ForwardStringSearcher
|
||||
|
||||
@@ -147,7 +153,7 @@ theorem skipSuffix?_eq_some_iff {pat s : Slice} {pos : s.Pos} :
|
||||
simp only [reduceCtorEq, false_iff]
|
||||
intro heq
|
||||
have := h (s.sliceTo pos).copy
|
||||
simp [← heq, -sliceTo_append_sliceFrom, pos.splits.eq_append] at this
|
||||
simp [← heq, pos.splits.eq_append] at this
|
||||
|
||||
theorem isSome_skipSuffix? {pat s : Slice} : (skipSuffix? pat s).isSome = endsWith pat s := by
|
||||
fun_cases skipSuffix? <;> simp_all
|
||||
@@ -167,12 +173,15 @@ namespace Model.BackwardSliceSearcher
|
||||
|
||||
open Pattern.BackwardSliceSearcher
|
||||
|
||||
public instance {pat : Slice} : LawfulBackwardPatternModel pat where
|
||||
public instance {pat : Slice} : LawfulBackwardPattern pat where
|
||||
skipSuffixOfNonempty?_eq _ := rfl
|
||||
endsWith_eq _ := isSome_skipSuffix?.symm
|
||||
|
||||
public theorem lawfulBackwardPatternModel {pat : Slice} (hpat : pat.isEmpty = false) :
|
||||
LawfulBackwardPatternModel pat where
|
||||
skipSuffix?_eq_some_iff pos := by
|
||||
simp [BackwardPattern.skipSuffix?, skipSuffix?_eq_some_iff,
|
||||
ForwardSliceSearcher.isLongestRevMatch_iff]
|
||||
ForwardSliceSearcher.isLongestRevMatch_iff hpat]
|
||||
|
||||
end Model.BackwardSliceSearcher
|
||||
|
||||
@@ -180,12 +189,15 @@ namespace Model.BackwardStringSearcher
|
||||
|
||||
open Pattern.BackwardSliceSearcher
|
||||
|
||||
public instance {pat : String} : LawfulBackwardPatternModel pat where
|
||||
public instance {pat : String} : LawfulBackwardPattern pat where
|
||||
skipSuffixOfNonempty?_eq _ := rfl
|
||||
endsWith_eq _ := isSome_skipSuffix?.symm
|
||||
|
||||
public theorem lawfulBackwardPatternModel {pat : String} (hpat : pat ≠ "") :
|
||||
LawfulBackwardPatternModel pat where
|
||||
skipSuffix?_eq_some_iff pos := by
|
||||
simp [BackwardPattern.skipSuffix?, skipSuffix?_eq_some_iff,
|
||||
ForwardStringSearcher.isLongestRevMatch_iff]
|
||||
ForwardStringSearcher.isLongestRevMatch_iff hpat]
|
||||
|
||||
end Model.BackwardStringSearcher
|
||||
|
||||
@@ -207,22 +219,19 @@ public theorem Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice
|
||||
{pat : String} {s : Slice} :
|
||||
skipPrefix? pat s = skipPrefix? pat.toSlice s := (rfl)
|
||||
|
||||
public theorem Pos.skip?_string_eq_skip?_toSlice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? pat = pos.skip? pat.toSlice := (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 [Pos.skipWhile]
|
||||
simp [← Pos.skip?_string_eq_skip?_toSlice, h₁, h₂, ih]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pos.skip?_string_eq_skip?_toSlice, h, ih]
|
||||
simp [← Pattern.ForwardPattern.skipPrefix?_string_eq_skipPrefix?_toSlice, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.skipWhile]
|
||||
simp [← Pos.skip?_string_eq_skip?_toSlice, h]
|
||||
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 :=
|
||||
@@ -238,7 +247,7 @@ public theorem takeWhile_string_eq_takeWhile_toSlice {pat : String} {s : Slice}
|
||||
|
||||
public theorem all_string_eq_all_toSlice {pat : String} {s : Slice} :
|
||||
s.all pat = s.all pat.toSlice := by
|
||||
simp only [all, skipPrefixWhile_string_eq_skipPrefixWhile_toSlice]
|
||||
simp only [all, dropWhile_string_eq_dropWhile_toSlice]
|
||||
|
||||
public theorem endsWith_string_eq_endsWith_toSlice {pat : String} {s : Slice} :
|
||||
s.endsWith pat = s.endsWith pat.toSlice := (rfl)
|
||||
@@ -256,22 +265,19 @@ public theorem Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice
|
||||
{pat : String} {s : Slice} :
|
||||
skipSuffix? pat s = skipSuffix? pat.toSlice s := (rfl)
|
||||
|
||||
public theorem Pos.revSkip?_string_eq_revSkip?_toSlice {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? pat = pos.revSkip? pat.toSlice := (rfl)
|
||||
|
||||
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 [Pos.revSkipWhile]
|
||||
simp [← Pos.revSkip?_string_eq_revSkip?_toSlice, h₁, h₂, ih]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice, h₁, h₂, ih]
|
||||
| case2 pos nextCurr h ih =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pos.revSkip?_string_eq_revSkip?_toSlice, h, ih]
|
||||
simp [← Pattern.BackwardPattern.skipSuffix?_string_eq_skipSuffix?_toSlice, h, ih]
|
||||
| case3 pos h =>
|
||||
conv => rhs; rw [Pos.revSkipWhile]
|
||||
simp [← Pos.revSkip?_string_eq_revSkip?_toSlice, h]
|
||||
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 :=
|
||||
@@ -285,8 +291,4 @@ public theorem takeEndWhile_string_eq_takeEndWhile_toSlice {pat : String} {s : S
|
||||
s.takeEndWhile pat = s.takeEndWhile pat.toSlice := by
|
||||
simp only [takeEndWhile]; exact congrArg _ skipSuffixWhile_string_eq_skipSuffixWhile_toSlice
|
||||
|
||||
public theorem revAll_string_eq_revAll_toSlice {pat : String} {s : Slice} :
|
||||
s.revAll pat = s.revAll pat.toSlice := by
|
||||
simp [revAll, skipSuffixWhile_string_eq_skipSuffixWhile_toSlice]
|
||||
|
||||
end String.Slice
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -56,77 +56,6 @@ theorem eq_append_of_dropPrefix?_char_eq_some {c : Char} {s res : Slice} (h : s.
|
||||
s.copy = singleton c ++ res.copy := by
|
||||
simpa [PatternModel.Matches] using Pattern.Model.eq_append_of_dropPrefix?_eq_some h
|
||||
|
||||
theorem Pos.skip?_char_eq_some_iff {c : Char} {s : Slice} {pos res : s.Pos} :
|
||||
pos.skip? c = some res ↔ ∃ h, res = pos.next h ∧ pos.get h = c := by
|
||||
simp [Pattern.Model.Pos.skip?_eq_some_iff, Char.isLongestMatchAt_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_char_eq_none_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? c = none ↔ ∀ h, pos.get h ≠ c := by
|
||||
simp [Pattern.Model.Pos.skip?_eq_none_iff, Char.matchesAt_iff]
|
||||
|
||||
theorem Pos.get_skipWhile_char_ne {c : Char} {s : Slice} {pos : s.Pos} {h} :
|
||||
(pos.skipWhile c).get h ≠ c := by
|
||||
have := Pattern.Model.Pos.not_matchesAt_skipWhile c pos
|
||||
simp_all [Char.matchesAt_iff]
|
||||
|
||||
theorem Pos.skipWhile_char_eq_self_iff_get {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.skipWhile c = pos ↔ ∀ h, pos.get h ≠ c := by
|
||||
simp [Pattern.Model.Pos.skipWhile_eq_self_iff, Char.matchesAt_iff]
|
||||
|
||||
theorem Pos.get_eq_of_lt_skipWhile_char {c : Char} {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos ≤ pos') (h₂ : pos' < pos.skipWhile c) : pos'.get (ne_endPos_of_lt h₂) = c :=
|
||||
(Char.isLongestMatchAtChain_iff.1 (Pattern.Model.Pos.isLongestMatchAtChain_skipWhile c pos)).2 _ h₁ h₂
|
||||
|
||||
theorem get_skipPrefixWhile_char_ne {c : Char} {s : Slice} {h} :
|
||||
(s.skipPrefixWhile c).get h ≠ c := by
|
||||
simp [skipPrefixWhile_eq_skipWhile_startPos, Pos.get_skipWhile_char_ne]
|
||||
|
||||
theorem get_eq_of_lt_skipPrefixWhile_char {c : Char} {s : Slice} {pos : s.Pos} (h : pos < s.skipPrefixWhile c) :
|
||||
pos.get (Pos.ne_endPos_of_lt h) = c :=
|
||||
Pos.get_eq_of_lt_skipWhile_char (Pos.startPos_le _) (by rwa [skipPrefixWhile_eq_skipWhile_startPos] at h)
|
||||
|
||||
@[simp]
|
||||
theorem all_char_iff {c : Char} {s : Slice} : s.all c ↔ s.copy.toList = List.replicate s.copy.length c := by
|
||||
rw [Bool.eq_iff_iff]
|
||||
simp [Pattern.Model.all_eq_true_iff, Char.isLongestMatchAtChain_startPos_endPos_iff_toList]
|
||||
|
||||
theorem Pos.revSkip?_char_eq_some_iff {c : Char} {s : Slice} {pos res : s.Pos} :
|
||||
pos.revSkip? c = some res ↔ ∃ h, res = pos.prev h ∧ (pos.prev h).get (by simp) = c := by
|
||||
simp [Pattern.Model.Pos.revSkip?_eq_some_iff, Char.isLongestRevMatchAt_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_char_eq_none_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? c = none ↔ ∀ h, (pos.prev h).get (by simp) ≠ c := by
|
||||
simp [Pattern.Model.Pos.revSkip?_eq_none_iff, Char.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.get_revSkipWhile_char_ne {c : Char} {s : Slice} {pos : s.Pos} {h} :
|
||||
((pos.revSkipWhile c).prev h).get (by simp) ≠ c := by
|
||||
have := Pattern.Model.Pos.not_revMatchesAt_revSkipWhile c pos
|
||||
simp_all [Char.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.revSkipWhile_char_eq_self_iff_get {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkipWhile c = pos ↔ ∀ h, (pos.prev h).get (by simp) ≠ c := by
|
||||
simp [Pattern.Model.Pos.revSkipWhile_eq_self_iff, Char.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.get_eq_of_revSkipWhile_le_char {c : Char} {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos' < pos) (h₂ : pos.revSkipWhile c ≤ pos') : pos'.get (Pos.ne_endPos_of_lt h₁) = c :=
|
||||
(Char.isLongestRevMatchAtChain_iff.1 (Pattern.Model.Pos.isLongestRevMatchAtChain_revSkipWhile c pos)).2 _ h₂ h₁
|
||||
|
||||
theorem get_skipSuffixWhile_char_ne {c : Char} {s : Slice} {h} :
|
||||
((s.skipSuffixWhile c).prev h).get (by simp) ≠ c := by
|
||||
simp [skipSuffixWhile_eq_revSkipWhile_endPos, Pos.get_revSkipWhile_char_ne]
|
||||
|
||||
theorem get_eq_of_skipSuffixWhile_le_char {c : Char} {s : Slice} {pos : s.Pos}
|
||||
(h : s.skipSuffixWhile c ≤ pos) (h' : pos < s.endPos) :
|
||||
pos.get (Pos.ne_endPos_of_lt h') = c :=
|
||||
Pos.get_eq_of_revSkipWhile_le_char h' (by rwa [skipSuffixWhile_eq_revSkipWhile_endPos] at h)
|
||||
|
||||
@[simp]
|
||||
theorem revAll_char_iff {c : Char} {s : Slice} : s.revAll c ↔ s.copy.toList = List.replicate s.copy.length c := by
|
||||
rw [Bool.eq_iff_iff]
|
||||
simp [Pattern.Model.revAll_eq_true_iff, Char.isLongestRevMatchAtChain_startPos_endPos_iff_toList]
|
||||
|
||||
theorem skipSuffix?_char_eq_some_iff {c : Char} {s : Slice} {pos : s.Pos} :
|
||||
s.skipSuffix? c = some pos ↔ ∃ h, pos = s.endPos.prev h ∧ (s.endPos.prev h).get (by simp) = c := by
|
||||
rw [Pattern.Model.skipSuffix?_eq_some_iff, Char.isLongestRevMatch_iff]
|
||||
@@ -171,19 +100,19 @@ theorem skipPrefix?_char_eq_some_iff {c : Char} {s : String} {pos : s.Pos} :
|
||||
|
||||
theorem startsWith_char_iff_get {c : Char} {s : String} :
|
||||
s.startsWith c ↔ ∃ h, s.startPos.get h = c := by
|
||||
simp [← startsWith_toSlice, Slice.startsWith_char_iff_get]
|
||||
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_toSlice, Slice.startsWith_char_eq_false_iff_get]
|
||||
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_toSlice, Slice.startsWith_char_eq_head?]
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_char_eq_head?]
|
||||
|
||||
theorem startsWith_char_iff_exists_append {c : Char} {s : String} :
|
||||
s.startsWith c ↔ ∃ t, s = singleton c ++ t := by
|
||||
simp [← startsWith_toSlice, Slice.startsWith_char_iff_exists_append]
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_char_iff_exists_append]
|
||||
|
||||
theorem startsWith_char_eq_false_iff_forall_append {c : Char} {s : String} :
|
||||
s.startsWith c = false ↔ ∀ t, s ≠ singleton c ++ t := by
|
||||
@@ -201,19 +130,19 @@ theorem skipSuffix?_char_eq_some_iff {c : Char} {s : String} {pos : s.Pos} :
|
||||
|
||||
theorem endsWith_char_iff_get {c : Char} {s : String} :
|
||||
s.endsWith c ↔ ∃ h, (s.endPos.prev h).get (by simp) = c := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_char_iff_get, Pos.prev_toSlice]
|
||||
simp [endsWith_eq_endsWith_toSlice, Slice.endsWith_char_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_char_eq_false_iff_get {c : Char} {s : String} :
|
||||
s.endsWith c = false ↔ ∀ h, (s.endPos.prev h).get (by simp) ≠ c := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_char_eq_false_iff_get, Pos.prev_toSlice]
|
||||
simp [endsWith_eq_endsWith_toSlice, Slice.endsWith_char_eq_false_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_char_eq_getLast? {c : Char} {s : String} :
|
||||
s.endsWith c = (s.toList.getLast? == some c) := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_char_eq_getLast?]
|
||||
simp [endsWith_eq_endsWith_toSlice, Slice.endsWith_char_eq_getLast?]
|
||||
|
||||
theorem endsWith_char_iff_exists_append {c : Char} {s : String} :
|
||||
s.endsWith c ↔ ∃ t, s = t ++ singleton c := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_char_iff_exists_append]
|
||||
simp [endsWith_eq_endsWith_toSlice, Slice.endsWith_char_iff_exists_append]
|
||||
|
||||
theorem endsWith_char_eq_false_iff_forall_append {c : Char} {s : String} :
|
||||
s.endsWith c = false ↔ ∀ t, s ≠ t ++ singleton c := by
|
||||
|
||||
@@ -8,16 +8,11 @@ module
|
||||
prelude
|
||||
public import Init.Data.String.Slice
|
||||
public import Init.Data.String.TakeDrop
|
||||
public import Init.Data.String.Lemmas.Order
|
||||
import Init.Data.String.Lemmas.Pattern.TakeDrop.Basic
|
||||
import Init.Data.String.Lemmas.Pattern.Pred
|
||||
import Init.Data.Option.Lemmas
|
||||
import Init.Data.String.Lemmas.FindPos
|
||||
import Init.Data.String.Lemmas.Intercalate
|
||||
import Init.ByCases
|
||||
import Init.Data.Order.Lemmas
|
||||
import Init.Data.String.OrderInstances
|
||||
import Init.Data.String.Lemmas.Basic
|
||||
|
||||
public section
|
||||
|
||||
@@ -54,80 +49,6 @@ theorem eq_append_of_dropPrefix?_bool_eq_some {p : Char → Bool} {s res : Slice
|
||||
obtain ⟨_, ⟨c, ⟨rfl, h₁⟩⟩, h₂⟩ := by simpa [PatternModel.Matches] using Pattern.Model.eq_append_of_dropPrefix?_eq_some h
|
||||
exact ⟨_, h₂, h₁⟩
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_bool_eq_some_iff {p : Char → Bool} {s : Slice} {pos res : s.Pos} :
|
||||
pos.skip? p = some res ↔ ∃ h, res = pos.next h ∧ p (pos.get h) := by
|
||||
simp [Pattern.Model.Pos.skip?_eq_some_iff, CharPred.isLongestMatchAt_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_bool_eq_none_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? p = none ↔ ∀ h, p (pos.get h) = false := by
|
||||
simp [Pattern.Model.Pos.skip?_eq_none_iff, CharPred.matchesAt_iff]
|
||||
|
||||
theorem Pos.apply_skipWhile_bool_eq_false {p : Char → Bool} {s : Slice} {pos : s.Pos} {h} :
|
||||
p ((pos.skipWhile p).get h) = false := by
|
||||
have := Pattern.Model.Pos.not_matchesAt_skipWhile p pos
|
||||
simp_all [CharPred.matchesAt_iff]
|
||||
|
||||
theorem Pos.skipWhile_bool_eq_self_iff_get {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
pos.skipWhile p = pos ↔ ∀ h, p (pos.get h) = false := by
|
||||
simp [Pattern.Model.Pos.skipWhile_eq_self_iff, CharPred.matchesAt_iff]
|
||||
|
||||
theorem Pos.apply_eq_true_of_lt_skipWhile_bool {p : Char → Bool} {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos ≤ pos') (h₂ : pos' < pos.skipWhile p) : p (pos'.get (ne_endPos_of_lt h₂)) = true :=
|
||||
(CharPred.isLongestMatchAtChain_iff.1 (Pattern.Model.Pos.isLongestMatchAtChain_skipWhile p pos)).2 _ h₁ h₂
|
||||
|
||||
theorem apply_skipPrefixWhile_bool_eq_false {p : Char → Bool} {s : Slice} {h} :
|
||||
p ((s.skipPrefixWhile p).get h) = false := by
|
||||
simp [skipPrefixWhile_eq_skipWhile_startPos, Pos.apply_skipWhile_bool_eq_false]
|
||||
|
||||
theorem apply_eq_true_of_lt_skipPrefixWhile_bool {p : Char → Bool} {s : Slice} {pos : s.Pos} (h : pos < s.skipPrefixWhile p) :
|
||||
p (pos.get (Pos.ne_endPos_of_lt h)) = true :=
|
||||
Pos.apply_eq_true_of_lt_skipWhile_bool (Pos.startPos_le _) (skipPrefixWhile_eq_skipWhile_startPos ▸ h)
|
||||
|
||||
@[simp]
|
||||
theorem all_bool_eq {p : Char → Bool} {s : Slice} : s.all p = s.copy.toList.all p := by
|
||||
rw [Bool.eq_iff_iff, Pattern.Model.all_eq_true_iff,
|
||||
CharPred.isLongestMatchAtChain_startPos_endPos_iff_toList, List.all_eq_true]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : Slice} {pos res : s.Pos} :
|
||||
pos.skip? P = some res ↔ ∃ h, res = pos.next h ∧ P (pos.get h) := by
|
||||
simp [Pos.skip?_prop_eq_skip?_decide, skip?_bool_eq_some_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_prop_eq_none_iff {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
|
||||
pos.skip? P = none ↔ ∀ h, ¬ P (pos.get h) := by
|
||||
simp [Pos.skip?_prop_eq_skip?_decide, skip?_bool_eq_none_iff]
|
||||
|
||||
theorem Pos.apply_skipWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} {h} :
|
||||
¬ P ((pos.skipWhile P).get h) := by
|
||||
have := Pattern.Model.Pos.not_matchesAt_skipWhile P pos
|
||||
simp_all [CharPred.Decidable.matchesAt_iff]
|
||||
|
||||
theorem Pos.skipWhile_prop_eq_self_iff_get {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
|
||||
pos.skipWhile P = pos ↔ ∀ h, ¬ P (pos.get h) := by
|
||||
simp [Pos.skipWhile_prop_eq_skipWhile_decide, skipWhile_bool_eq_self_iff_get]
|
||||
|
||||
theorem Pos.apply_of_lt_skipWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos ≤ pos') (h₂ : pos' < pos.skipWhile P) : P (pos'.get (ne_endPos_of_lt h₂)) := by
|
||||
simp [Pos.skipWhile_prop_eq_skipWhile_decide] at h₂
|
||||
simpa using apply_eq_true_of_lt_skipWhile_bool h₁ h₂
|
||||
|
||||
theorem apply_skipPrefixWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {h} :
|
||||
¬ P ((s.skipPrefixWhile P).get h) := by
|
||||
simp [skipPrefixWhile_eq_skipWhile_startPos, Pos.apply_skipWhile_prop]
|
||||
|
||||
theorem apply_of_lt_skipPrefixWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos}
|
||||
(h : pos < s.skipPrefixWhile P) : P (pos.get (Pos.ne_endPos_of_lt h)) := by
|
||||
simp [skipPrefixWhile_prop_eq_skipPrefixWhile_decide] at h
|
||||
simpa using apply_eq_true_of_lt_skipPrefixWhile_bool h
|
||||
|
||||
@[simp]
|
||||
theorem all_prop_eq {P : Char → Prop} [DecidablePred P] {s : Slice} :
|
||||
s.all P = s.copy.toList.all (decide <| P ·) := by
|
||||
simp [all_prop_eq_all_decide]
|
||||
|
||||
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]
|
||||
@@ -144,7 +65,7 @@ theorem startsWith_prop_eq_head? {P : Char → Prop} [DecidablePred P] {s : Slic
|
||||
s.startsWith P = s.copy.toList.head?.any (decide <| P ·) := by
|
||||
simp [startsWith_prop_eq_startsWith_decide, startsWith_bool_eq_head?]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_prop_eq_some {P : Char → Prop} [DecidablePred P] {s res : Slice} (h : s.dropPrefix? P = some res) :
|
||||
theorem eq_append_of_dropPrefix_prop_eq_some {P : Char → Prop} [DecidablePred P] {s res : Slice} (h : s.dropPrefix? P = some res) :
|
||||
∃ c, s.copy = singleton c ++ res.copy ∧ P c := by
|
||||
rw [dropPrefix?_prop_eq_dropPrefix?_decide] at h
|
||||
simpa using eq_append_of_dropPrefix?_bool_eq_some h
|
||||
@@ -197,83 +118,6 @@ theorem eq_append_of_dropSuffix?_prop_eq_some {P : Char → Prop} [DecidablePred
|
||||
rw [dropSuffix?_prop_eq_dropSuffix?_decide] at h
|
||||
simpa using eq_append_of_dropSuffix?_bool_eq_some h
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_bool_eq_some_iff {p : Char → Bool} {s : Slice} {pos res : s.Pos} :
|
||||
pos.revSkip? p = some res ↔ ∃ h, res = pos.prev h ∧ p ((pos.prev h).get (by simp)) := by
|
||||
simp [Pattern.Model.Pos.revSkip?_eq_some_iff, CharPred.isLongestRevMatchAt_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_bool_eq_none_iff {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? p = none ↔ ∀ h, p ((pos.prev h).get (by simp)) = false := by
|
||||
simp [Pattern.Model.Pos.revSkip?_eq_none_iff, CharPred.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.apply_revSkipWhile_bool_eq_false {p : Char → Bool} {s : Slice} {pos : s.Pos} {h} :
|
||||
p (((pos.revSkipWhile p).prev h).get (by simp)) = false := by
|
||||
have := Pattern.Model.Pos.not_revMatchesAt_revSkipWhile p pos
|
||||
simp_all [CharPred.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.revSkipWhile_bool_eq_self_iff_get {p : Char → Bool} {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkipWhile p = pos ↔ ∀ h, p ((pos.prev h).get (by simp)) = false := by
|
||||
simp [Pattern.Model.Pos.revSkipWhile_eq_self_iff, CharPred.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.apply_eq_true_of_revSkipWhile_le_bool {p : Char → Bool} {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos' < pos) (h₂ : pos.revSkipWhile p ≤ pos') : p (pos'.get (Pos.ne_endPos_of_lt h₁)) = true :=
|
||||
(CharPred.isLongestRevMatchAtChain_iff.1 (Pattern.Model.Pos.isLongestRevMatchAtChain_revSkipWhile p pos)).2 _ h₂ h₁
|
||||
|
||||
theorem apply_skipSuffixWhile_bool_eq_false {p : Char → Bool} {s : Slice} {h} :
|
||||
p (((s.skipSuffixWhile p).prev h).get (by simp)) = false := by
|
||||
simp [skipSuffixWhile_eq_revSkipWhile_endPos, Pos.apply_revSkipWhile_bool_eq_false]
|
||||
|
||||
theorem apply_eq_true_of_skipSuffixWhile_le_bool {p : Char → Bool} {s : Slice} {pos : s.Pos}
|
||||
(h : s.skipSuffixWhile p ≤ pos) (h' : pos < s.endPos) :
|
||||
p (pos.get (Pos.ne_endPos_of_lt h')) = true :=
|
||||
Pos.apply_eq_true_of_revSkipWhile_le_bool h' (skipSuffixWhile_eq_revSkipWhile_endPos ▸ h)
|
||||
|
||||
@[simp]
|
||||
theorem revAll_bool_eq {p : Char → Bool} {s : Slice} : s.revAll p = s.copy.toList.all p := by
|
||||
rw [Bool.eq_iff_iff, Pattern.Model.revAll_eq_true_iff,
|
||||
CharPred.isLongestRevMatchAtChain_startPos_endPos_iff_toList, List.all_eq_true]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : Slice} {pos res : s.Pos} :
|
||||
pos.revSkip? P = some res ↔ ∃ h, res = pos.prev h ∧ P ((pos.prev h).get (by simp)) := by
|
||||
simp [Pos.revSkip?_prop_eq_revSkip?_decide, revSkip?_bool_eq_some_iff]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_prop_eq_none_iff {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkip? P = none ↔ ∀ h, ¬ P ((pos.prev h).get (by simp)) := by
|
||||
simp [Pos.revSkip?_prop_eq_revSkip?_decide, revSkip?_bool_eq_none_iff]
|
||||
|
||||
theorem Pos.apply_revSkipWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} {h} :
|
||||
¬ P (((pos.revSkipWhile P).prev h).get (by simp)) := by
|
||||
have := Pattern.Model.Pos.not_revMatchesAt_revSkipWhile P pos
|
||||
simp_all [CharPred.Decidable.revMatchesAt_iff]
|
||||
|
||||
theorem Pos.revSkipWhile_prop_eq_self_iff_get {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos} :
|
||||
pos.revSkipWhile P = pos ↔ ∀ h, ¬ P ((pos.prev h).get (by simp)) := by
|
||||
simp [Pos.revSkipWhile_prop_eq_revSkipWhile_decide, revSkipWhile_bool_eq_self_iff_get]
|
||||
|
||||
theorem Pos.apply_of_revSkipWhile_le_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos pos' : s.Pos}
|
||||
(h₁ : pos' < pos) (h₂ : pos.revSkipWhile P ≤ pos') : P (pos'.get (Pos.ne_endPos_of_lt h₁)) := by
|
||||
have h₂' : pos.revSkipWhile (decide <| P ·) ≤ pos' :=
|
||||
Pos.revSkipWhile_prop_eq_revSkipWhile_decide (p := P) pos ▸ h₂
|
||||
simpa using Pos.apply_eq_true_of_revSkipWhile_le_bool h₁ h₂'
|
||||
|
||||
theorem apply_skipSuffixWhile_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {h} :
|
||||
¬ P (((s.skipSuffixWhile P).prev h).get (by simp)) := by
|
||||
have := Pattern.Model.Pos.not_revMatchesAt_revSkipWhile P s.endPos
|
||||
simp_all [CharPred.Decidable.revMatchesAt_iff, skipSuffixWhile_eq_revSkipWhile_endPos]
|
||||
|
||||
theorem apply_of_skipSuffixWhile_le_prop {P : Char → Prop} [DecidablePred P] {s : Slice} {pos : s.Pos}
|
||||
(h : s.skipSuffixWhile P ≤ pos) (h' : pos < s.endPos) :
|
||||
P (pos.get (Pos.ne_endPos_of_lt h')) :=
|
||||
Pos.apply_of_revSkipWhile_le_prop h' (skipSuffixWhile_eq_revSkipWhile_endPos (pat := P) ▸ h)
|
||||
|
||||
@[simp]
|
||||
theorem revAll_prop_eq {P : Char → Prop} [DecidablePred P] {s : Slice} :
|
||||
s.revAll P = s.copy.toList.all (decide <| P ·) := by
|
||||
simp [revAll_prop_eq_revAll_decide, revAll_bool_eq]
|
||||
|
||||
end Slice
|
||||
|
||||
theorem skipPrefix?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
@@ -283,58 +127,21 @@ theorem skipPrefix?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos : s.P
|
||||
|
||||
theorem startsWith_bool_iff_get {p : Char → Bool} {s : String} :
|
||||
s.startsWith p ↔ ∃ h, p (s.startPos.get h) = true := by
|
||||
simp [← startsWith_toSlice, Slice.startsWith_bool_iff_get]
|
||||
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_toSlice, Slice.startsWith_bool_eq_false_iff_get]
|
||||
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_toSlice, Slice.startsWith_bool_eq_head?]
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_bool_eq_head?]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_bool_eq_some {p : Char → Bool} {s : String} {res : Slice} (h : s.dropPrefix? p = some res) :
|
||||
∃ c, s = singleton c ++ res.copy ∧ p c = true := by
|
||||
rw [dropPrefix?_eq_dropPrefix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropPrefix?_bool_eq_some h
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos res : s.Pos} :
|
||||
pos.skip? p = some res ↔ ∃ h, res = pos.next h ∧ p (pos.get h) := by
|
||||
simp [skip?_eq_skip?_toSlice, ← toSlice_inj, toSlice_next]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_bool_eq_none_iff {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
pos.skip? p = none ↔ ∀ h, p (pos.get h) = false := by
|
||||
simp [skip?_eq_skip?_toSlice]
|
||||
|
||||
theorem Pos.apply_skipWhile_bool_eq_false {p : Char → Bool} {s : String} {pos : s.Pos} {h} :
|
||||
p ((pos.skipWhile p).get h) = false := by
|
||||
simp [skipWhile_eq_skipWhile_toSlice, Slice.Pos.apply_skipWhile_bool_eq_false]
|
||||
|
||||
theorem Pos.skipWhile_bool_eq_self_iff_get {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
pos.skipWhile p = pos ↔ ∀ h, p (pos.get h) = false := by
|
||||
simp [skipWhile_eq_skipWhile_toSlice, ← toSlice_inj, Slice.Pos.skipWhile_bool_eq_self_iff_get]
|
||||
|
||||
theorem Pos.apply_eq_true_of_lt_skipWhile_bool {p : Char → Bool} {s : String} {pos pos' : s.Pos}
|
||||
(h₁ : pos ≤ pos') (h₂ : pos' < pos.skipWhile p) : p (pos'.get (ne_endPos_of_lt h₂)) = true := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.Pos.apply_eq_true_of_lt_skipWhile_bool (toSlice_le_toSlice_iff.2 h₁)
|
||||
(by simpa [skipWhile_eq_skipWhile_toSlice] using h₂)
|
||||
|
||||
theorem apply_skipPrefixWhile_bool_eq_false {p : Char → Bool} {s : String} {h} :
|
||||
p ((s.skipPrefixWhile p).get h) = false := by
|
||||
simp [skipPrefixWhile_eq_skipPrefixWhile_toSlice, Slice.apply_skipPrefixWhile_bool_eq_false]
|
||||
|
||||
theorem apply_eq_true_of_lt_skipPrefixWhile_bool {p : Char → Bool} {s : String} {pos : s.Pos} (h : pos < s.skipPrefixWhile p) :
|
||||
p (pos.get (Pos.ne_endPos_of_lt h)) = true := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.apply_eq_true_of_lt_skipPrefixWhile_bool (by simpa [skipPrefixWhile_eq_skipPrefixWhile_toSlice] using h)
|
||||
|
||||
@[simp]
|
||||
theorem all_bool_eq {p : Char → Bool} {s : String} : s.all p = s.toList.all p := by
|
||||
simp [← all_toSlice]
|
||||
|
||||
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,
|
||||
@@ -342,20 +149,20 @@ theorem skipPrefix?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s :
|
||||
|
||||
theorem startsWith_prop_iff_get {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.startsWith P ↔ ∃ h, P (s.startPos.get h) := by
|
||||
simp [← startsWith_toSlice, Slice.startsWith_prop_iff_get]
|
||||
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_toSlice, Slice.startsWith_prop_eq_false_iff_get]
|
||||
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_toSlice, Slice.startsWith_prop_eq_head?]
|
||||
simp [startsWith_eq_startsWith_toSlice, Slice.startsWith_prop_eq_head?]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_prop_eq_some {P : Char → Prop} [DecidablePred P] {s : String} {res : Slice}
|
||||
(h : s.dropPrefix? P = some res) : ∃ c, s = singleton c ++ res.copy ∧ P c := by
|
||||
rw [dropPrefix?_eq_dropPrefix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropPrefix?_prop_eq_some h
|
||||
simpa using Slice.eq_append_of_dropPrefix_prop_eq_some h
|
||||
|
||||
theorem skipSuffix?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
s.skipSuffix? p = some pos ↔ ∃ h, pos = s.endPos.prev h ∧ p ((s.endPos.prev h).get (by simp)) = true := by
|
||||
@@ -364,15 +171,15 @@ theorem skipSuffix?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos : s.P
|
||||
|
||||
theorem endsWith_bool_iff_get {p : Char → Bool} {s : String} :
|
||||
s.endsWith p ↔ ∃ h, p ((s.endPos.prev h).get (by simp)) = true := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_bool_iff_get, Pos.prev_toSlice]
|
||||
simp [endsWith_eq_endsWith_toSlice, Slice.endsWith_bool_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_bool_eq_false_iff_get {p : Char → Bool} {s : String} :
|
||||
s.endsWith p = false ↔ ∀ h, p ((s.endPos.prev h).get (by simp)) = false := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_bool_eq_false_iff_get, Pos.prev_toSlice]
|
||||
simp [endsWith_eq_endsWith_toSlice, Slice.endsWith_bool_eq_false_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_bool_eq_getLast? {p : Char → Bool} {s : String} :
|
||||
s.endsWith p = s.toList.getLast?.any p := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_bool_eq_getLast?]
|
||||
simp [endsWith_eq_endsWith_toSlice, Slice.endsWith_bool_eq_getLast?]
|
||||
|
||||
theorem eq_append_of_dropSuffix?_bool_eq_some {p : Char → Bool} {s : String} {res : Slice} (h : s.dropSuffix? p = some res) :
|
||||
∃ c, s = res.copy ++ singleton c ∧ p c = true := by
|
||||
@@ -386,154 +193,19 @@ theorem skipSuffix?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s :
|
||||
|
||||
theorem endsWith_prop_iff_get {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.endsWith P ↔ ∃ h, P ((s.endPos.prev h).get (by simp)) := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_prop_iff_get, Pos.prev_toSlice]
|
||||
simp [endsWith_eq_endsWith_toSlice, Slice.endsWith_prop_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_prop_eq_false_iff_get {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.endsWith P = false ↔ ∀ h, ¬ P ((s.endPos.prev h).get (by simp)) := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_prop_eq_false_iff_get, Pos.prev_toSlice]
|
||||
simp [endsWith_eq_endsWith_toSlice, Slice.endsWith_prop_eq_false_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem endsWith_prop_eq_getLast? {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.endsWith P = s.toList.getLast?.any (decide <| P ·) := by
|
||||
simp [← endsWith_toSlice, Slice.endsWith_prop_eq_getLast?]
|
||||
simp [endsWith_eq_endsWith_toSlice, Slice.endsWith_prop_eq_getLast?]
|
||||
|
||||
theorem eq_append_of_dropSuffix?_prop_eq_some {P : Char → Prop} [DecidablePred P] {s : String} {res : Slice}
|
||||
(h : s.dropSuffix? P = some res) : ∃ c, s = res.copy ++ singleton c ∧ P c := by
|
||||
rw [dropSuffix?_eq_dropSuffix?_toSlice] at h
|
||||
simpa using Slice.eq_append_of_dropSuffix?_prop_eq_some h
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : String} {pos res : s.Pos} :
|
||||
pos.skip? P = some res ↔ ∃ h, res = pos.next h ∧ P (pos.get h) := by
|
||||
simp [skip?_eq_skip?_toSlice, ← toSlice_inj, toSlice_next]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.skip?_prop_eq_none_iff {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
|
||||
pos.skip? P = none ↔ ∀ h, ¬ P (pos.get h) := by
|
||||
simp [skip?_eq_skip?_toSlice]
|
||||
|
||||
theorem Pos.apply_skipWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} {h} :
|
||||
¬ P ((pos.skipWhile P).get h) := by
|
||||
simp [skipWhile_eq_skipWhile_toSlice, Slice.Pos.apply_skipWhile_prop]
|
||||
|
||||
theorem Pos.skipWhile_prop_eq_self_iff_get {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
|
||||
pos.skipWhile P = pos ↔ ∀ h, ¬ P (pos.get h) := by
|
||||
simp [skipWhile_eq_skipWhile_toSlice, ← toSlice_inj, Slice.Pos.skipWhile_prop_eq_self_iff_get]
|
||||
|
||||
theorem Pos.apply_of_lt_skipWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos pos' : s.Pos}
|
||||
(h₁ : pos ≤ pos') (h₂ : pos' < pos.skipWhile P) : P (pos'.get (ne_endPos_of_lt h₂)) := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.Pos.apply_of_lt_skipWhile_prop (toSlice_le_toSlice_iff.2 h₁)
|
||||
(by simpa [skipWhile_eq_skipWhile_toSlice] using h₂)
|
||||
|
||||
theorem apply_skipPrefixWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {h} :
|
||||
¬ P ((s.skipPrefixWhile P).get h) := by
|
||||
simp [skipPrefixWhile_eq_skipPrefixWhile_toSlice, Slice.apply_skipPrefixWhile_prop]
|
||||
|
||||
theorem apply_of_lt_skipPrefixWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos}
|
||||
(h : pos < s.skipPrefixWhile P) : P (pos.get (Pos.ne_endPos_of_lt h)) := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.apply_of_lt_skipPrefixWhile_prop (by simpa [skipPrefixWhile_eq_skipPrefixWhile_toSlice] using h)
|
||||
|
||||
@[simp]
|
||||
theorem all_prop_eq {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.all P = s.toList.all (decide <| P ·) := by
|
||||
simp [← all_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_bool_eq_some_iff {p : Char → Bool} {s : String} {pos res : s.Pos} :
|
||||
pos.revSkip? p = some res ↔ ∃ h, res = pos.prev h ∧ p ((pos.prev h).get (by simp)) := by
|
||||
simp [revSkip?_eq_revSkip?_toSlice, ← toSlice_inj, toSlice_prev, get_eq_get_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_bool_eq_none_iff {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
pos.revSkip? p = none ↔ ∀ h, p ((pos.prev h).get (by simp)) = false := by
|
||||
simp [revSkip?_eq_revSkip?_toSlice, Pos.prev_toSlice]
|
||||
|
||||
theorem Pos.apply_revSkipWhile_bool_eq_false {p : Char → Bool} {s : String} {pos : s.Pos} {h} :
|
||||
p (((pos.revSkipWhile p).prev h).get (by simp)) = false := by
|
||||
have h' : pos.toSlice.revSkipWhile p ≠ s.toSlice.startPos := by
|
||||
simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, ← toSlice_inj] using h
|
||||
have := Slice.Pos.apply_revSkipWhile_bool_eq_false (pos := pos.toSlice) (h := h')
|
||||
simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, Pos.prev_ofToSlice]
|
||||
|
||||
theorem Pos.revSkipWhile_bool_eq_self_iff_get {p : Char → Bool} {s : String} {pos : s.Pos} :
|
||||
pos.revSkipWhile p = pos ↔ ∀ h, p ((pos.prev h).get (by simp)) = false := by
|
||||
simp [Pos.revSkipWhile_eq_revSkipWhile_toSlice, ← toSlice_inj, Slice.Pos.revSkipWhile_bool_eq_self_iff_get,
|
||||
Pos.prev_toSlice]
|
||||
|
||||
theorem Pos.apply_eq_true_of_revSkipWhile_le_bool {p : Char → Bool} {s : String} {pos pos' : s.Pos}
|
||||
(h₁ : pos' < pos) (h₂ : pos.revSkipWhile p ≤ pos') : p (pos'.get (ne_endPos_of_lt h₁)) = true := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.Pos.apply_eq_true_of_revSkipWhile_le_bool
|
||||
(Pos.toSlice_lt_toSlice_iff.2 h₁)
|
||||
(by simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, Pos.ofToSlice_le_iff] using h₂)
|
||||
|
||||
theorem apply_skipSuffixWhile_bool_eq_false {p : Char → Bool} {s : String} {h} :
|
||||
p (((s.skipSuffixWhile p).prev h).get (by simp)) = false := by
|
||||
have h' : s.toSlice.skipSuffixWhile p ≠ s.toSlice.startPos := by
|
||||
simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, ← Pos.toSlice_inj] using h
|
||||
have := Slice.apply_skipSuffixWhile_bool_eq_false (s := s.toSlice) (h := h')
|
||||
simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, Pos.prev_ofToSlice]
|
||||
|
||||
theorem apply_eq_true_of_skipSuffixWhile_le_bool {p : Char → Bool} {s : String} {pos : s.Pos}
|
||||
(h : s.skipSuffixWhile p ≤ pos) (h' : pos < s.endPos) :
|
||||
p (pos.get (Pos.ne_endPos_of_lt h')) = true := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.apply_eq_true_of_skipSuffixWhile_le_bool
|
||||
(by simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, Pos.ofToSlice_le_iff] using h)
|
||||
(by simpa [Pos.toSlice_lt_toSlice_iff] using h')
|
||||
|
||||
@[simp]
|
||||
theorem revAll_bool_eq {p : Char → Bool} {s : String} : s.revAll p = s.toList.all p := by
|
||||
simp [← revAll_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_prop_eq_some_iff {P : Char → Prop} [DecidablePred P] {s : String} {pos res : s.Pos} :
|
||||
pos.revSkip? P = some res ↔ ∃ h, res = pos.prev h ∧ P ((pos.prev h).get (by simp)) := by
|
||||
simp [revSkip?_eq_revSkip?_toSlice, ← toSlice_inj, toSlice_prev, get_eq_get_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem Pos.revSkip?_prop_eq_none_iff {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
|
||||
pos.revSkip? P = none ↔ ∀ h, ¬ P ((pos.prev h).get (by simp)) := by
|
||||
simp [revSkip?_eq_revSkip?_toSlice, Pos.prev_toSlice]
|
||||
|
||||
theorem Pos.apply_revSkipWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} {h} :
|
||||
¬ P (((pos.revSkipWhile P).prev h).get (by simp)) := by
|
||||
have h' : pos.toSlice.revSkipWhile P ≠ s.toSlice.startPos := by
|
||||
simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, ← toSlice_inj] using h
|
||||
have := Slice.Pos.apply_revSkipWhile_prop (pos := pos.toSlice) (h := h')
|
||||
simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, Pos.prev_ofToSlice]
|
||||
|
||||
theorem Pos.revSkipWhile_prop_eq_self_iff_get {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos} :
|
||||
pos.revSkipWhile P = pos ↔ ∀ h, ¬ P ((pos.prev h).get (by simp)) := by
|
||||
simp [Pos.revSkipWhile_eq_revSkipWhile_toSlice, ← toSlice_inj,
|
||||
Slice.Pos.revSkipWhile_prop_eq_self_iff_get, Pos.prev_toSlice]
|
||||
|
||||
theorem Pos.apply_of_revSkipWhile_le_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos pos' : s.Pos}
|
||||
(h₁ : pos' < pos) (h₂ : pos.revSkipWhile P ≤ pos') : P (pos'.get (ne_endPos_of_lt h₁)) := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.Pos.apply_of_revSkipWhile_le_prop
|
||||
(Pos.toSlice_lt_toSlice_iff.2 h₁)
|
||||
(by simpa [Pos.revSkipWhile_eq_revSkipWhile_toSlice, Pos.ofToSlice_le_iff] using h₂)
|
||||
|
||||
theorem apply_skipSuffixWhile_prop {P : Char → Prop} [DecidablePred P] {s : String} {h} :
|
||||
¬ P (((s.skipSuffixWhile P).prev h).get (by simp)) := by
|
||||
have h' : s.toSlice.skipSuffixWhile P ≠ s.toSlice.startPos := by
|
||||
simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, ← Pos.toSlice_inj] using h
|
||||
have := Slice.apply_skipSuffixWhile_prop (s := s.toSlice) (h := h')
|
||||
simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, Pos.prev_ofToSlice]
|
||||
|
||||
theorem apply_of_skipSuffixWhile_le_prop {P : Char → Prop} [DecidablePred P] {s : String} {pos : s.Pos}
|
||||
(h : s.skipSuffixWhile P ≤ pos) (h' : pos < s.endPos) :
|
||||
P (pos.get (Pos.ne_endPos_of_lt h')) := by
|
||||
rw [Pos.get_eq_get_toSlice]
|
||||
exact Slice.apply_of_skipSuffixWhile_le_prop
|
||||
(by simpa [skipSuffixWhile_eq_skipSuffixWhile_toSlice, Pos.ofToSlice_le_iff] using h)
|
||||
(by simpa [Pos.toSlice_lt_toSlice_iff] using h')
|
||||
|
||||
@[simp]
|
||||
theorem revAll_prop_eq {P : Char → Prop} [DecidablePred P] {s : String} :
|
||||
s.revAll P = s.toList.all (decide <| P ·) := by
|
||||
simp [← revAll_toSlice]
|
||||
|
||||
end String
|
||||
|
||||
@@ -30,7 +30,11 @@ theorem skipPrefix?_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true)
|
||||
@[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
|
||||
rw [Pattern.Model.skipPrefix?_eq_some_iff, ForwardSliceSearcher.isLongestMatch_iff_splits]
|
||||
match h : pat.isEmpty with
|
||||
| false =>
|
||||
have := ForwardSliceSearcher.lawfulForwardPatternModel h
|
||||
rw [Pattern.Model.skipPrefix?_eq_some_iff, ForwardSliceSearcher.isLongestMatch_iff_splits h]
|
||||
| true => simp [skipPrefix?_slice_of_isEmpty h, (show pat.copy = "" by simpa), eq_comm]
|
||||
|
||||
theorem startsWith_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
s.startsWith pat = true := by
|
||||
@@ -39,10 +43,14 @@ theorem startsWith_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true)
|
||||
@[simp]
|
||||
theorem startsWith_slice_iff {pat s : Slice} :
|
||||
s.startsWith pat ↔ pat.copy.toList <+: s.copy.toList := by
|
||||
simp only [Model.startsWith_iff, ForwardSliceSearcher.matchesAt_iff_splits,
|
||||
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]⟩⟩
|
||||
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 => simp [startsWith_slice_of_isEmpty h, (show pat.copy = "" by simpa)]
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_slice_eq_false_iff {pat s : Slice} :
|
||||
@@ -55,18 +63,14 @@ theorem dropPrefix?_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true)
|
||||
|
||||
theorem eq_append_of_dropPrefix?_slice_eq_some {pat s res : Slice} (h : s.dropPrefix? pat = some res) :
|
||||
s.copy = pat.copy ++ res.copy := by
|
||||
have := Pattern.Model.eq_append_of_dropPrefix?_eq_some h
|
||||
simp only [PatternModel.Matches] at this
|
||||
obtain ⟨_, ⟨-, rfl⟩, h⟩ := this
|
||||
exact h
|
||||
|
||||
@[simp]
|
||||
theorem all_slice_iff {pat s : Slice} : s.all pat ↔ ∃ n, s.copy = String.join (List.replicate n pat.copy) := by
|
||||
simp [Pattern.Model.all_eq_true_iff, ForwardSliceSearcher.isLongestMatchAtChain_startPos_endPos_iff]
|
||||
|
||||
@[simp]
|
||||
theorem revAll_slice_iff {pat s : Slice} : s.revAll pat ↔ ∃ n, s.copy = String.join (List.replicate n pat.copy) := by
|
||||
simp [Pattern.Model.revAll_eq_true_iff, ForwardSliceSearcher.isLongestRevMatchAtChain_startPos_endPos_iff]
|
||||
match hpat : pat.isEmpty with
|
||||
| false =>
|
||||
have := ForwardSliceSearcher.lawfulForwardPatternModel hpat
|
||||
have := Pattern.Model.eq_append_of_dropPrefix?_eq_some h
|
||||
simp only [PatternModel.Matches] at this
|
||||
obtain ⟨_, ⟨-, rfl⟩, h⟩ := this
|
||||
exact h
|
||||
| true => simp [Option.some.inj (h ▸ dropPrefix?_slice_of_isEmpty hpat), (show pat.copy = "" by simpa)]
|
||||
|
||||
@[simp]
|
||||
theorem skipPrefix?_string_eq_some_iff {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
@@ -100,7 +104,6 @@ theorem eq_append_of_dropPrefix?_string_eq_some {pat : String} {s res : Slice} (
|
||||
rw [dropPrefix?_string_eq_dropPrefix?_toSlice] at h
|
||||
simpa using eq_append_of_dropPrefix?_slice_eq_some h
|
||||
|
||||
|
||||
theorem skipSuffix?_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
s.skipSuffix? pat = some s.endPos := by
|
||||
rw [skipSuffix?_eq_backwardPatternSkipSuffix?, BackwardSliceSearcher.skipSuffix?_of_isEmpty hpat]
|
||||
@@ -108,7 +111,11 @@ theorem skipSuffix?_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true)
|
||||
@[simp]
|
||||
theorem skipSuffix?_slice_eq_some_iff {pat s : Slice} {pos : s.Pos} :
|
||||
s.skipSuffix? pat = some pos ↔ ∃ t, pos.Splits t pat.copy := by
|
||||
rw [Pattern.Model.skipSuffix?_eq_some_iff, ForwardSliceSearcher.isLongestRevMatch_iff_splits]
|
||||
match h : pat.isEmpty with
|
||||
| false =>
|
||||
have := BackwardSliceSearcher.lawfulBackwardPatternModel h
|
||||
rw [Pattern.Model.skipSuffix?_eq_some_iff, ForwardSliceSearcher.isLongestRevMatch_iff_splits h]
|
||||
| true => simp [skipSuffix?_slice_of_isEmpty h, (show pat.copy = "" by simpa), eq_comm]
|
||||
|
||||
theorem endsWith_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
s.endsWith pat = true := by
|
||||
@@ -117,10 +124,14 @@ theorem endsWith_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true) :
|
||||
@[simp]
|
||||
theorem endsWith_slice_iff {pat s : Slice} :
|
||||
s.endsWith pat ↔ pat.copy.toList <:+ s.copy.toList := by
|
||||
simp only [Model.endsWith_iff, ForwardSliceSearcher.revMatchesAt_iff_splits,
|
||||
splits_endPos_iff, exists_eq_right]
|
||||
simp only [← toList_inj, toList_append, List.suffix_iff_exists_append_eq]
|
||||
exact ⟨fun ⟨t, ht⟩ => ⟨t.toList, by simp [ht]⟩, fun ⟨t, ht⟩ => ⟨String.ofList t, by simp [← ht]⟩⟩
|
||||
match h : pat.isEmpty with
|
||||
| false =>
|
||||
have := BackwardSliceSearcher.lawfulBackwardPatternModel h
|
||||
simp only [Model.endsWith_iff, ForwardSliceSearcher.revMatchesAt_iff_splits h,
|
||||
splits_endPos_iff, exists_eq_right]
|
||||
simp only [← toList_inj, toList_append, List.suffix_iff_exists_append_eq]
|
||||
exact ⟨fun ⟨t, ht⟩ => ⟨t.toList, by simp [ht]⟩, fun ⟨t, ht⟩ => ⟨String.ofList t, by simp [← ht]⟩⟩
|
||||
| true => simp [endsWith_slice_of_isEmpty h, (show pat.copy = "" by simpa)]
|
||||
|
||||
@[simp]
|
||||
theorem endsWith_slice_eq_false_iff {pat s : Slice} :
|
||||
@@ -133,10 +144,14 @@ theorem dropSuffix?_slice_of_isEmpty {pat s : Slice} (hpat : pat.isEmpty = true)
|
||||
|
||||
theorem eq_append_of_dropSuffix?_slice_eq_some {pat s res : Slice} (h : s.dropSuffix? pat = some res) :
|
||||
s.copy = res.copy ++ pat.copy := by
|
||||
have := Pattern.Model.eq_append_of_dropSuffix?_eq_some h
|
||||
simp only [PatternModel.Matches] at this
|
||||
obtain ⟨_, ⟨-, rfl⟩, h⟩ := this
|
||||
exact h
|
||||
match hpat : pat.isEmpty with
|
||||
| false =>
|
||||
have := BackwardSliceSearcher.lawfulBackwardPatternModel hpat
|
||||
have := Pattern.Model.eq_append_of_dropSuffix?_eq_some h
|
||||
simp only [PatternModel.Matches] at this
|
||||
obtain ⟨_, ⟨-, rfl⟩, h⟩ := this
|
||||
exact h
|
||||
| true => simp [Option.some.inj (h ▸ dropSuffix?_slice_of_isEmpty hpat), (show pat.copy = "" by simpa)]
|
||||
|
||||
@[simp]
|
||||
theorem skipSuffix?_string_eq_some_iff' {pat : String} {s : Slice} {pos : s.Pos} :
|
||||
@@ -193,12 +208,12 @@ theorem startsWith_slice_of_isEmpty {pat : Slice} {s : String} (hpat : pat.isEmp
|
||||
@[simp]
|
||||
theorem startsWith_slice_iff {pat : Slice} {s : String} :
|
||||
s.startsWith pat ↔ pat.copy.toList <+: s.toList := by
|
||||
simp [← startsWith_toSlice]
|
||||
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_toSlice]
|
||||
simp [startsWith_eq_startsWith_toSlice]
|
||||
|
||||
theorem dropPrefix?_slice_of_isEmpty {pat : Slice} {s : String} (hpat : pat.isEmpty = true) :
|
||||
s.dropPrefix? pat = some s.toSlice := by
|
||||
@@ -224,21 +239,21 @@ theorem skipPrefix?_string_eq_some_iff {pat s : String} {pos : s.Pos} :
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_string_empty {s : String} : s.startsWith "" = true := by
|
||||
simp [← startsWith_toSlice]
|
||||
simp [startsWith_eq_startsWith_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem startsWith_string_iff {pat s : String} :
|
||||
s.startsWith pat ↔ pat.toList <+: s.toList := by
|
||||
simp [← startsWith_toSlice]
|
||||
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_toSlice]
|
||||
simp [startsWith_eq_startsWith_toSlice]
|
||||
|
||||
@[simp]
|
||||
theorem dropPrefix?_string_empty {s : String} : s.dropPrefix? "" = some s.toSlice := by
|
||||
simp [← dropPrefix?_toSlice]
|
||||
simp [dropPrefix?_eq_dropPrefix?_toSlice]
|
||||
|
||||
theorem eq_append_of_dropPrefix?_string_eq_some {s pat : String} {res : Slice} (h : s.dropPrefix? pat = some res) :
|
||||
s = pat ++ res.copy := by
|
||||
|
||||
@@ -99,11 +99,6 @@ theorem Pos.splits {s : String} (p : s.Pos) :
|
||||
eq_append := by simp [← toByteArray_inj, Slice.toByteArray_copy, ← size_toByteArray]
|
||||
offset_eq_rawEndPos := by simp
|
||||
|
||||
@[simp]
|
||||
theorem sliceTo_append_sliceFrom {s : String} {pos : s.Pos} :
|
||||
(s.sliceTo pos).copy ++ (s.sliceFrom pos).copy = s :=
|
||||
pos.splits.eq_append.symm
|
||||
|
||||
theorem Slice.Pos.splits {s : Slice} (p : s.Pos) :
|
||||
p.Splits (s.sliceTo p).copy (s.sliceFrom p).copy where
|
||||
eq_append := copy_eq_copy_sliceTo
|
||||
@@ -380,10 +375,6 @@ theorem Slice.copy_sliceTo_eq_iff_exists_splits {s : Slice} {p : s.Pos} {t₁ :
|
||||
· rintro ⟨t₂, h⟩
|
||||
exact p.splits.eq_left h
|
||||
|
||||
theorem Slice.copy_sliceTo_eq_iff_splits {s : Slice} {p : s.Pos} {t₁ : String} :
|
||||
(s.sliceTo p).copy = t₁ ↔ p.Splits t₁ (s.sliceFrom p).copy :=
|
||||
⟨fun h => h ▸ p.splits, p.splits.eq_left⟩
|
||||
|
||||
theorem Slice.copy_sliceFrom_eq_iff_exists_splits {s : Slice} {p : s.Pos} {t₂ : String} :
|
||||
(s.sliceFrom p).copy = t₂ ↔ ∃ t₁, p.Splits t₁ t₂ := by
|
||||
refine ⟨?_, ?_⟩
|
||||
@@ -392,26 +383,14 @@ theorem Slice.copy_sliceFrom_eq_iff_exists_splits {s : Slice} {p : s.Pos} {t₂
|
||||
· rintro ⟨t₂, h⟩
|
||||
exact p.splits.eq_right h
|
||||
|
||||
theorem Slice.copy_sliceFrom_eq_iff_splits {s : Slice} {p : s.Pos} {t₂ : String} :
|
||||
(s.sliceFrom p).copy = t₂ ↔ p.Splits (s.sliceTo p).copy t₂ :=
|
||||
⟨fun h => h ▸ p.splits, p.splits.eq_right⟩
|
||||
|
||||
theorem copy_sliceTo_eq_iff_exists_splits {s : String} {p : s.Pos} {t₁ : String} :
|
||||
(s.sliceTo p).copy = t₁ ↔ ∃ t₂, p.Splits t₁ t₂ := by
|
||||
simp [← Pos.splits_toSlice_iff, ← Slice.copy_sliceTo_eq_iff_exists_splits]
|
||||
|
||||
theorem copy_sliceTo_eq_iff_splits {s : String} {p : s.Pos} {t₁ : String} :
|
||||
(s.sliceTo p).copy = t₁ ↔ p.Splits t₁ (s.sliceFrom p).copy :=
|
||||
⟨fun h => h ▸ p.splits, p.splits.eq_left⟩
|
||||
|
||||
theorem copy_sliceFrom_eq_iff_exists_splits {s : String} {p : s.Pos} {t₂ : String} :
|
||||
(s.sliceFrom p).copy = t₂ ↔ ∃ t₁, p.Splits t₁ t₂ := by
|
||||
simp [← Pos.splits_toSlice_iff, ← Slice.copy_sliceFrom_eq_iff_exists_splits]
|
||||
|
||||
theorem copy_sliceFrom_eq_iff_splits {s : String} {p : s.Pos} {t₂ : String} :
|
||||
(s.sliceFrom p).copy = t₂ ↔ p.Splits (s.sliceTo p).copy t₂ :=
|
||||
⟨fun h => h ▸ p.splits, p.splits.eq_right⟩
|
||||
|
||||
theorem Pos.Splits.offset_eq_decreaseBy {s : String} {p : s.Pos} (h : p.Splits t₁ t₂) :
|
||||
p.offset = s.rawEndPos.decreaseBy t₂.utf8ByteSize := by
|
||||
simp [h.offset_eq_rawEndPos, h.eq_append, Pos.Raw.ext_iff]
|
||||
@@ -662,28 +641,6 @@ theorem Pos.splits_append_rawEndPos {s t : String} :
|
||||
eq_append := rfl
|
||||
offset_eq_rawEndPos := rfl
|
||||
|
||||
/--
|
||||
Given a slice `s` such that `s.copy = t₁ ++ t₂`, obtain the position sitting between `t₁` and `t₂`.
|
||||
-/
|
||||
def Slice.Pos.ofEqAppend {s : Slice} {t₁ t₂ : String} (h : s.copy = t₁ ++ t₂) : s.Pos :=
|
||||
s.pos t₁.rawEndPos
|
||||
(by simpa [← Pos.Raw.isValid_copy_iff, h] using ((Pos.Raw.isValid_rawEndPos).append_right t₂))
|
||||
|
||||
theorem Slice.Pos.splits_ofEqAppend {s : Slice} {t₁ t₂ : String} (h : s.copy = t₁ ++ t₂) :
|
||||
(ofEqAppend h).Splits t₁ t₂ where
|
||||
eq_append := h
|
||||
offset_eq_rawEndPos := by simp [ofEqAppend]
|
||||
|
||||
/--
|
||||
Given a string `s` such that `s = t₁ ++ t₂`, obtain the position sitting between `t₁` and `t₂`.
|
||||
-/
|
||||
def Pos.ofEqAppend {s t₁ t₂ : String} (h : s = t₁ ++ t₂) : s.Pos :=
|
||||
((t₁ ++ t₂).pos t₁.rawEndPos ((Pos.Raw.isValid_rawEndPos).append_right t₂)).cast h.symm
|
||||
|
||||
theorem Pos.splits_ofEqAppend {s t₁ t₂ : String} (h : s = t₁ ++ t₂) : (ofEqAppend h).Splits t₁ t₂ where
|
||||
eq_append := h
|
||||
offset_eq_rawEndPos := by simp [ofEqAppend]
|
||||
|
||||
theorem Pos.Splits.copy_sliceTo_eq {s : String} {p : s.Pos} (h : p.Splits t₁ t₂) :
|
||||
(s.sliceTo p).copy = t₁ :=
|
||||
p.splits.eq_left h
|
||||
@@ -783,44 +740,4 @@ theorem splits_prevn_endPos (s : String) (n : Nat) :
|
||||
(s.endPos.prevn n).Splits (String.ofList (s.toList.take (s.length - n))) (String.ofList (s.toList.drop (s.length - n))) := by
|
||||
simpa using s.splits_endPos.prevn n
|
||||
|
||||
@[simp]
|
||||
theorem Slice.copy_sliceFrom_cast {s t : Slice} (hst : s.copy = t.copy) {pos : s.Pos} :
|
||||
(t.sliceFrom (pos.cast hst)).copy = (s.sliceFrom pos).copy := by
|
||||
simpa [copy_sliceFrom_eq_iff_exists_splits] using ⟨_, pos.splits⟩
|
||||
|
||||
@[simp]
|
||||
theorem Slice.copy_sliceTo_cast {s t : Slice} (hst : s.copy = t.copy) {pos : s.Pos} :
|
||||
(t.sliceTo (pos.cast hst)).copy = (s.sliceTo pos).copy := by
|
||||
simpa [copy_sliceTo_eq_iff_exists_splits] using ⟨_, pos.splits⟩
|
||||
|
||||
@[simp]
|
||||
theorem copy_sliceFrom_cast {s t : String} (hst : s = t) {pos : s.Pos} :
|
||||
(t.sliceFrom (pos.cast hst)).copy = (s.sliceFrom pos).copy := by
|
||||
simpa [copy_sliceFrom_eq_iff_exists_splits] using ⟨_, pos.splits⟩
|
||||
|
||||
@[simp]
|
||||
theorem copy_sliceTo_cast {s t : String} (hst : s = t) {pos : s.Pos} :
|
||||
(t.sliceTo (pos.cast hst)).copy = (s.sliceTo pos).copy := by
|
||||
simpa [copy_sliceTo_eq_iff_exists_splits] using ⟨_, pos.splits⟩
|
||||
|
||||
theorem Slice.Pos.sliceFrom_cast {s t : Slice} {hst : s.copy = t.copy} (p q : s.Pos) {h} :
|
||||
Slice.Pos.sliceFrom (p.cast hst) (q.cast hst) h =
|
||||
(Slice.Pos.sliceFrom p q (by simpa using h)).cast (by simp) := by
|
||||
ext1; simp
|
||||
|
||||
theorem Slice.Pos.sliceTo_cast {s t : Slice} {hst : s.copy = t.copy} (p q : s.Pos) {h} :
|
||||
Slice.Pos.sliceTo (p.cast hst) (q.cast hst) h =
|
||||
(Slice.Pos.sliceTo p q (by simpa using h)).cast (by simp) := by
|
||||
ext1; simp
|
||||
|
||||
theorem Pos.sliceFrom_cast {s t : String} {hst : s = t} (p q : s.Pos) {h} :
|
||||
Pos.sliceFrom (p.cast hst) (q.cast hst) h =
|
||||
(Pos.sliceFrom p q (by simpa using h)).cast (by simp) := by
|
||||
ext1; simp
|
||||
|
||||
theorem Pos.sliceTo_cast {s t : String} {hst : s = t} (p q : s.Pos) {h} :
|
||||
Pos.sliceTo (p.cast hst) (q.cast hst) h =
|
||||
(Pos.sliceTo p q (by simpa using h)).cast (by simp) := by
|
||||
ext1; simp
|
||||
|
||||
end String
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Leonardo de Moura
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Init.Data.String.Basic
|
||||
public import Init.Data.Order.Classes
|
||||
import Init.Data.List.Lex
|
||||
import Init.Data.Char.Lemmas
|
||||
import Init.Data.Char.Order
|
||||
import Init.Data.Order.Factories
|
||||
import Init.Data.Order.Lemmas
|
||||
|
||||
public section
|
||||
|
||||
open Std
|
||||
|
||||
namespace String
|
||||
|
||||
@[simp] protected theorem not_le {a b : String} : ¬ a ≤ b ↔ b < a := Decidable.not_not
|
||||
@[simp] protected theorem not_lt {a b : String} : ¬ a < b ↔ b ≤ a := Iff.rfl
|
||||
@[simp] protected theorem le_refl (a : String) : a ≤ a := List.le_refl _
|
||||
@[simp] protected theorem lt_irrefl (a : String) : ¬ a < a := List.lt_irrefl _
|
||||
|
||||
attribute [local instance] Char.notLTTrans Char.ltTrichotomous Char.ltAsymm
|
||||
|
||||
protected theorem le_trans {a b c : String} : a ≤ b → b ≤ c → a ≤ c := List.le_trans
|
||||
protected theorem lt_trans {a b c : String} : a < b → b < c → a < c := List.lt_trans
|
||||
protected theorem le_total (a b : String) : a ≤ b ∨ b ≤ a := List.le_total _ _
|
||||
protected theorem le_antisymm {a b : String} : a ≤ b → b ≤ a → a = b := fun h₁ h₂ => String.ext (List.le_antisymm (as := a.toList) (bs := b.toList) h₁ h₂)
|
||||
protected theorem lt_asymm {a b : String} (h : a < b) : ¬ b < a := List.lt_asymm h
|
||||
protected theorem ne_of_lt {a b : String} (h : a < b) : a ≠ b := by
|
||||
have := String.lt_irrefl a
|
||||
intro h; subst h; contradiction
|
||||
|
||||
instance instIsLinearOrder : IsLinearOrder String := by
|
||||
apply IsLinearOrder.of_le
|
||||
case le_antisymm => constructor; apply String.le_antisymm
|
||||
case le_trans => constructor; apply String.le_trans
|
||||
case le_total => constructor; apply String.le_total
|
||||
|
||||
instance : LawfulOrderLT String where
|
||||
lt_iff a b := by
|
||||
simp [← String.not_le, Decidable.imp_iff_not_or, Std.Total.total]
|
||||
|
||||
end String
|
||||
@@ -96,44 +96,6 @@ theorem endPos_ofSliceFrom {s : Slice} {p : s.Pos} {st : SearchStep (s.sliceFrom
|
||||
st.ofSliceFrom.endPos = Slice.Pos.ofSliceFrom st.endPos := by
|
||||
cases st <;> simp [ofSliceFrom]
|
||||
|
||||
/--
|
||||
Converts a {lean}`SearchStep s` into a {lean}`SearchStep t` by applying {name}`Slice.Pos.cast` to the
|
||||
start and end position.
|
||||
-/
|
||||
@[inline]
|
||||
def cast {s t : Slice} (hst : s.copy = t.copy) : SearchStep s → SearchStep t
|
||||
| .rejected startPos endPos => .rejected (startPos.cast hst) (endPos.cast hst)
|
||||
| .matched startPos endPos => .matched (startPos.cast hst) (endPos.cast hst)
|
||||
|
||||
@[simp]
|
||||
theorem cast_rejected {s t : Slice} {hst : s.copy = t.copy} {startPos endPos : s.Pos} :
|
||||
(SearchStep.rejected startPos endPos).cast hst = .rejected (startPos.cast hst) (endPos.cast hst) :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem cast_matched {s t : Slice} {hst : s.copy = t.copy} {startPos endPos : s.Pos} :
|
||||
(SearchStep.matched startPos endPos).cast hst = .matched (startPos.cast hst) (endPos.cast hst) :=
|
||||
(rfl)
|
||||
|
||||
@[simp]
|
||||
theorem startPos_cast {s t : Slice} (hst : s.copy = t.copy) {st : SearchStep s} :
|
||||
(st.cast hst).startPos = st.startPos.cast hst := by
|
||||
cases st <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem endPos_cast {s t : Slice} (hst : s.copy = t.copy) {st : SearchStep s} :
|
||||
(st.cast hst).endPos = st.endPos.cast hst := by
|
||||
cases st <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem cast_rfl {s : Slice} {st : SearchStep s} : st.cast rfl = st := by
|
||||
cases st <;> simp
|
||||
|
||||
@[simp]
|
||||
theorem cast_cast {s t u : Slice} {hst : s.copy = t.copy} {htu : t.copy = u.copy} {st : SearchStep s} :
|
||||
(st.cast hst).cast htu = st.cast (hst.trans htu) := by
|
||||
cases st <;> simp
|
||||
|
||||
end SearchStep
|
||||
|
||||
/--
|
||||
|
||||
@@ -311,6 +311,23 @@ def Internal.containsImpl (s : String) (c : Char) : Bool :=
|
||||
def Internal.anyImpl (s : String) (p : Char → Bool) :=
|
||||
String.any s p
|
||||
|
||||
/--
|
||||
Checks whether a slice only consists of matches of the pattern {name}`pat`.
|
||||
|
||||
Short-circuits at the first pattern mis-match.
|
||||
|
||||
This function is generic over all currently supported patterns.
|
||||
|
||||
Examples:
|
||||
* {lean}`"brown".all Char.isLower = true`
|
||||
* {lean}`"brown and orange".all Char.isLower = false`
|
||||
* {lean}`"aaaaaa".all 'a' = true`
|
||||
* {lean}`"aaaaaa".all "aa" = true`
|
||||
* {lean}`"aaaaaaa".all "aa" = false`
|
||||
-/
|
||||
@[inline, suggest_for String.every] def all (s : String) (pat : ρ) [ForwardPattern pat] : Bool :=
|
||||
s.toSlice.all pat
|
||||
|
||||
/--
|
||||
Checks whether the string can be interpreted as the decimal representation of a natural number.
|
||||
|
||||
|
||||
@@ -426,13 +426,13 @@ Advances {name}`pos` as long as {name}`pat` matches.
|
||||
-/
|
||||
@[specialize pat]
|
||||
def Pos.skipWhile {s : Slice} (pos : s.Pos) (pat : ρ) [ForwardPattern pat] : s.Pos :=
|
||||
match pos.skip? pat with
|
||||
| some nextCurr =>
|
||||
if pos < nextCurr then
|
||||
skipWhile nextCurr pat
|
||||
if let some nextCurr := ForwardPattern.skipPrefix? pat (s.sliceFrom pos) then
|
||||
if pos < Pos.ofSliceFrom nextCurr then
|
||||
skipWhile (Pos.ofSliceFrom nextCurr) pat
|
||||
else
|
||||
pos
|
||||
| none => pos
|
||||
else
|
||||
pos
|
||||
termination_by pos
|
||||
|
||||
/--
|
||||
@@ -572,7 +572,7 @@ Examples:
|
||||
-/
|
||||
@[inline]
|
||||
def all (s : Slice) (pat : ρ) [ForwardPattern pat] : Bool :=
|
||||
s.skipPrefixWhile pat == s.endPos
|
||||
s.dropWhile pat |>.isEmpty
|
||||
|
||||
end ForwardPatternUsers
|
||||
|
||||
@@ -706,14 +706,14 @@ Returns {name}`none` otherwise.
|
||||
This function is generic over all currently supported patterns.
|
||||
-/
|
||||
@[inline]
|
||||
def Pos.revSkip? {s : Slice} (pos : s.Pos) (pat : ρ) [BackwardPattern pat] : Option s.Pos :=
|
||||
((s.sliceTo pos).skipSuffix? pat).map Pos.ofSliceTo
|
||||
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.
|
||||
|
||||
Use {name (scope := "Init.Data.String.Slice")}`String.Slice.dropSuffix` to return the slice
|
||||
unchanged when {name}`pat` does not match a suffix.
|
||||
unchanged when {name}`pat` does not match a prefix.
|
||||
|
||||
This function is generic over all currently supported patterns.
|
||||
|
||||
@@ -765,53 +765,23 @@ Rewinds {name}`pos` as long as {name}`pat` matches.
|
||||
-/
|
||||
@[specialize pat]
|
||||
def Pos.revSkipWhile {s : Slice} (pos : s.Pos) (pat : ρ) [BackwardPattern pat] : s.Pos :=
|
||||
match pos.revSkip? pat with
|
||||
| some nextCurr =>
|
||||
if nextCurr < pos then
|
||||
revSkipWhile nextCurr pat
|
||||
if let some nextCurr := BackwardPattern.skipSuffix? pat (s.sliceTo pos) then
|
||||
if Pos.ofSliceTo nextCurr < pos then
|
||||
revSkipWhile (Pos.ofSliceTo nextCurr) pat
|
||||
else
|
||||
pos
|
||||
| none => pos
|
||||
else
|
||||
pos
|
||||
termination_by pos.down
|
||||
|
||||
/--
|
||||
Returns the position at the start of the longest suffix of {name}`s` for which {name}`pat` matches
|
||||
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
|
||||
|
||||
/--
|
||||
Checks whether a slice only consists of matches of the pattern {name}`pat`, starting from the back
|
||||
of the string.
|
||||
|
||||
Short-circuits at the first pattern mis-match.
|
||||
|
||||
This function is generic over all currently supported patterns.
|
||||
|
||||
For many types of patterns, this function can be expected to return the same result as
|
||||
{name}`Slice.all`. If mismatches are expected to occur close to the end of the string, this function
|
||||
might be more efficient.
|
||||
|
||||
For some types of patterns, this function will return a different result than {name}`Slice.all`.
|
||||
Consider, for example, a pattern that matches the longest string at the given position that matches
|
||||
the regular expression {lean}`"a|aa|ab"`. Then, given the input string {lean}`"aab"`, performing
|
||||
{name}`Slice.all` will greedily match the prefix {lean}`"aa"` and then get stuck on the remainder
|
||||
{lean}`"b"`, causing it to return {lean}`false`. On the other hand, {name}`Slice.revAll` will match
|
||||
the suffix {lean}`"ab"` and then match the remainder {lean}`"a"`, so it will return {lean}`true`.
|
||||
|
||||
Examples:
|
||||
* {lean}`"brown".toSlice.revAll Char.isLower = true`
|
||||
* {lean}`"brown and orange".toSlice.revAll Char.isLower = false`
|
||||
* {lean}`"aaaaaa".toSlice.revAll 'a' = true`
|
||||
* {lean}`"aaaaaa".toSlice.revAll "aa" = true`
|
||||
* {lean}`"aaaaaaa".toSlice.revAll "aa" = false`
|
||||
-/
|
||||
@[inline]
|
||||
def revAll (s : Slice) (pat : ρ) [BackwardPattern pat] : Bool :=
|
||||
s.skipSuffixWhile pat == s.startPos
|
||||
|
||||
/--
|
||||
Creates a new slice that contains the longest suffix of {name}`s` for which {name}`pat` matched
|
||||
(potentially repeatedly).
|
||||
|
||||
@@ -23,7 +23,7 @@ Given a {name}`Slice` {name}`s`, the type {lean}`s.Subslice` is the type of half
|
||||
in {name}`s` delineated by a valid position on both sides.
|
||||
|
||||
This type is useful to track regions of interest within some larger slice that is also of interest.
|
||||
In contrast, {name}`Slice` is used to track regions of interest within some larger string that is
|
||||
In contrast, {name}`Slice` is used to track regions of interest whithin some larger string that is
|
||||
not or no longer relevant.
|
||||
|
||||
Equality on {name}`Subslice` is somewhat better behaved than on {name}`Slice`, but note that there
|
||||
|
||||
@@ -224,53 +224,6 @@ Returns the position after the longest prefix of {name}`s` for which {name}`pat`
|
||||
@[inline] def skipPrefixWhile (s : String) (pat : ρ) [ForwardPattern pat] : s.Pos :=
|
||||
Pos.ofToSlice (s.toSlice.skipPrefixWhile pat)
|
||||
|
||||
/--
|
||||
Checks whether a string only consists of matches of the pattern {name}`pat`.
|
||||
|
||||
Short-circuits at the first pattern mis-match.
|
||||
|
||||
This function is generic over all currently supported patterns.
|
||||
|
||||
Examples:
|
||||
* {lean}`"brown".all Char.isLower = true`
|
||||
* {lean}`"brown and orange".all Char.isLower = false`
|
||||
* {lean}`"aaaaaa".all 'a' = true`
|
||||
* {lean}`"aaaaaa".all "aa" = true`
|
||||
* {lean}`"aaaaaaa".all "aa" = false`
|
||||
-/
|
||||
@[inline, suggest_for String.every] def all (s : String) (pat : ρ) [ForwardPattern pat] : Bool :=
|
||||
s.toSlice.all pat
|
||||
|
||||
/--
|
||||
Checks whether a string only consists of matches of the pattern {name}`pat`, starting from the back
|
||||
of the string.
|
||||
|
||||
Short-circuits at the first pattern mis-match.
|
||||
|
||||
This function is generic over all currently supported patterns.
|
||||
|
||||
For many types of patterns, this function can be expected to return the same result as
|
||||
{name}`String.all`. If mismatches are expected to occur close to the end of the string, this function
|
||||
might be more efficient.
|
||||
|
||||
For some types of patterns, this function will return a different result than {name}`String.all`.
|
||||
Consider, for example, a pattern that matches the longest string at the given position that matches
|
||||
the regular expression {lean}`"a|aa|ab"`. Then, given the input string {lean}`"aab"`, performing
|
||||
{name}`String.all` will greedily match the prefix {lean}`"aa"` and then get stuck on the remainder
|
||||
{lean}`"b"`, causing it to return {lean}`false`. On the other hand, {name}`String.revAll` will match
|
||||
the suffix {lean}`"ab"` and then match the remainder {lean}`"a"`, so it will return {lean}`true`.
|
||||
|
||||
Examples:
|
||||
* {lean}`"brown".revAll Char.isLower = true`
|
||||
* {lean}`"brown and orange".revAll Char.isLower = false`
|
||||
* {lean}`"aaaaaa".revAll 'a' = true`
|
||||
* {lean}`"aaaaaa".revAll "aa" = true`
|
||||
* {lean}`"aaaaaaa".revAll "aa" = false`
|
||||
-/
|
||||
@[inline]
|
||||
def revAll (s : String) (pat : ρ) [BackwardPattern pat] : Bool :=
|
||||
s.toSlice.revAll pat
|
||||
|
||||
/--
|
||||
If {name}`pat` matches at {name}`pos`, returns the position after the end of the match.
|
||||
Returns {name}`none` otherwise.
|
||||
@@ -361,7 +314,7 @@ Returns {name}`none` otherwise.
|
||||
This function is generic over all currently supported patterns.
|
||||
-/
|
||||
@[inline]
|
||||
def Pos.revSkip? {s : String} (pos : s.Pos) (pat : ρ) [BackwardPattern pat] : Option s.Pos :=
|
||||
def Pos.revSkip? {s : String} (pos : s.Pos) (pat : ρ) [ForwardPattern pat] : Option s.Pos :=
|
||||
(pos.toSlice.revSkip? pat).map Pos.ofToSlice
|
||||
|
||||
/--
|
||||
@@ -508,7 +461,7 @@ def dropPrefix? (s : String) (pat : ρ) [ForwardPattern pat] : Option String.Sli
|
||||
If {name}`pat` matches a suffix of {name}`s`, returns the remainder. Returns {name}`none` otherwise.
|
||||
|
||||
Use {name (scope := "Init.Data.String.TakeDrop")}`String.dropSuffix` to return the slice
|
||||
unchanged when {name}`pat` does not match a suffix.
|
||||
unchanged when {name}`pat` does not match a prefix.
|
||||
|
||||
This is a cheap operation because it does not allocate a new string to hold the result.
|
||||
To convert the result into a string, use {name}`String.Slice.copy`.
|
||||
|
||||
@@ -506,16 +506,6 @@ Examples:
|
||||
@[inline, expose] def sum [Add α] [Zero α] (xs : Vector α n) : α :=
|
||||
xs.toArray.sum
|
||||
|
||||
/--
|
||||
Computes the product of the elements of a vector.
|
||||
|
||||
Examples:
|
||||
* `#v[a, b, c].prod = a * (b * (c * 1))`
|
||||
* `#v[1, 2, 5].prod = 10`
|
||||
-/
|
||||
@[inline, expose] def prod [Mul α] [One α] (xs : Vector α n) : α :=
|
||||
xs.toArray.prod
|
||||
|
||||
/--
|
||||
Pad a vector on the left with a given element.
|
||||
|
||||
|
||||
@@ -30,16 +30,4 @@ theorem sum_reverse_int (xs : Vector Int n) : xs.reverse.sum = xs.sum := by
|
||||
theorem sum_eq_foldl_int {xs : Vector Int n} : xs.sum = xs.foldl (b := 0) (· + ·) := by
|
||||
simp only [foldl_eq_foldr_reverse, Int.add_comm, ← sum_eq_foldr, sum_reverse_int]
|
||||
|
||||
@[simp] theorem prod_replicate_int {n : Nat} {a : Int} : (replicate n a).prod = a ^ n := by
|
||||
simp [← prod_toArray, Array.prod_replicate_int]
|
||||
|
||||
theorem prod_append_int {as₁ as₂ : Vector Int n} : (as₁ ++ as₂).prod = as₁.prod * as₂.prod := by
|
||||
simp [← prod_toArray]
|
||||
|
||||
theorem prod_reverse_int (xs : Vector Int n) : xs.reverse.prod = xs.prod := by
|
||||
simp [prod_reverse]
|
||||
|
||||
theorem prod_eq_foldl_int {xs : Vector Int n} : xs.prod = xs.foldl (b := 1) (· * ·) := by
|
||||
simp only [foldl_eq_foldr_reverse, Int.mul_comm, ← prod_eq_foldr, prod_reverse_int]
|
||||
|
||||
end Vector
|
||||
|
||||
@@ -278,12 +278,6 @@ theorem toArray_mk {xs : Array α} (h : xs.size = n) : (Vector.mk xs h).toArray
|
||||
@[simp, grind =] theorem sum_toArray [Add α] [Zero α] {xs : Vector α n} :
|
||||
xs.toArray.sum = xs.sum := rfl
|
||||
|
||||
@[simp] theorem prod_mk [Mul α] [One α] {xs : Array α} (h : xs.size = n) :
|
||||
(Vector.mk xs h).prod = xs.prod := rfl
|
||||
|
||||
@[simp, grind =] theorem prod_toArray [Mul α] [One α] {xs : Vector α n} :
|
||||
xs.toArray.prod = xs.prod := rfl
|
||||
|
||||
@[simp] theorem eq_mk : xs = Vector.mk as h ↔ xs.toArray = as := by
|
||||
cases xs
|
||||
simp
|
||||
@@ -557,10 +551,6 @@ theorem toArray_toList {xs : Vector α n} : xs.toList.toArray = xs.toArray := rf
|
||||
xs.toList.sum = xs.sum := by
|
||||
rw [← toList_toArray, Array.sum_toList, sum_toArray]
|
||||
|
||||
@[simp, grind =] theorem prod_toList [Mul α] [One α] {xs : Vector α n} :
|
||||
xs.toList.prod = xs.prod := by
|
||||
rw [← toList_toArray, Array.prod_toList, prod_toArray]
|
||||
|
||||
@[simp] theorem getElem_toList {xs : Vector α n} {i : Nat} (h : i < xs.toList.length) :
|
||||
xs.toList[i] = xs[i]'(by simpa using h) := by
|
||||
cases xs
|
||||
@@ -3144,39 +3134,3 @@ theorem sum_eq_foldl [Zero α] [Add α]
|
||||
{xs : Vector α n} :
|
||||
xs.sum = xs.foldl (b := 0) (· + ·) := by
|
||||
simp [← sum_toList, List.sum_eq_foldl]
|
||||
|
||||
/-! ### prod -/
|
||||
|
||||
@[simp, grind =] theorem prod_empty [Mul α] [One α] : (#v[] : Vector α 0).prod = 1 := rfl
|
||||
theorem prod_eq_foldr [Mul α] [One α] {xs : Vector α n} :
|
||||
xs.prod = xs.foldr (b := 1) (· * ·) :=
|
||||
rfl
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_append [One α] [Mul α] [Std.Associative (α := α) (· * ·)]
|
||||
[Std.LeftIdentity (α := α) (· * ·) 1] [Std.LawfulLeftIdentity (α := α) (· * ·) 1]
|
||||
{as₁ as₂ : Vector α n} : (as₁ ++ as₂).prod = as₁.prod * as₂.prod := by
|
||||
simp [← prod_toList, List.prod_append]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_singleton [Mul α] [One α] [Std.LawfulRightIdentity (· * ·) (1 : α)] {x : α} :
|
||||
#v[x].prod = x := by
|
||||
simp [← prod_toList, Std.LawfulRightIdentity.right_id x]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_push [Mul α] [One α] [Std.Associative (α := α) (· * ·)]
|
||||
[Std.LawfulIdentity (· * ·) (1 : α)] {xs : Vector α n} {x : α} :
|
||||
(xs.push x).prod = xs.prod * x := by
|
||||
simp [← prod_toArray]
|
||||
|
||||
@[simp, grind =]
|
||||
theorem prod_reverse [One α] [Mul α] [Std.Associative (α := α) (· * ·)]
|
||||
[Std.Commutative (α := α) (· * ·)]
|
||||
[Std.LawfulLeftIdentity (α := α) (· * ·) 1] (xs : Vector α n) : xs.reverse.prod = xs.prod := by
|
||||
simp [← prod_toList, List.prod_reverse]
|
||||
|
||||
theorem prod_eq_foldl [One α] [Mul α]
|
||||
[Std.Associative (α := α) (· * ·)] [Std.LawfulIdentity (· * ·) (1 : α)]
|
||||
{xs : Vector α n} :
|
||||
xs.prod = xs.foldl (b := 1) (· * ·) := by
|
||||
simp [← prod_toList, List.prod_eq_foldl]
|
||||
|
||||
@@ -37,23 +37,4 @@ theorem sum_reverse_nat (xs : Vector Nat n) : xs.reverse.sum = xs.sum := by
|
||||
theorem sum_eq_foldl_nat {xs : Vector Nat n} : xs.sum = xs.foldl (b := 0) (· + ·) := by
|
||||
simp only [foldl_eq_foldr_reverse, Nat.add_comm, ← sum_eq_foldr, sum_reverse_nat]
|
||||
|
||||
protected theorem prod_pos_iff_forall_pos_nat {xs : Vector Nat n} : 0 < xs.prod ↔ ∀ x ∈ xs, 0 < x := by
|
||||
simp [← prod_toArray, Array.prod_pos_iff_forall_pos_nat]
|
||||
|
||||
protected theorem prod_eq_zero_iff_exists_zero_nat {xs : Vector Nat n} :
|
||||
xs.prod = 0 ↔ ∃ x ∈ xs, x = 0 := by
|
||||
simp [← prod_toArray, Array.prod_eq_zero_iff_exists_zero_nat]
|
||||
|
||||
@[simp] theorem prod_replicate_nat {n : Nat} {a : Nat} : (replicate n a).prod = a ^ n := by
|
||||
simp [← prod_toArray, Array.prod_replicate_nat]
|
||||
|
||||
theorem prod_append_nat {as₁ as₂ : Vector Nat n} : (as₁ ++ as₂).prod = as₁.prod * as₂.prod := by
|
||||
simp [← prod_toArray]
|
||||
|
||||
theorem prod_reverse_nat (xs : Vector Nat n) : xs.reverse.prod = xs.prod := by
|
||||
simp [prod_reverse]
|
||||
|
||||
theorem prod_eq_foldl_nat {xs : Vector Nat n} : xs.prod = xs.foldl (b := 1) (· * ·) := by
|
||||
simp only [foldl_eq_foldr_reverse, Nat.mul_comm, ← prod_eq_foldr, prod_reverse_nat]
|
||||
|
||||
end Vector
|
||||
|
||||
@@ -564,28 +564,6 @@ end Ring
|
||||
|
||||
end IsCharP
|
||||
|
||||
/--
|
||||
`PowIdentity α p` states that `x ^ p = x` holds for all elements of `α`.
|
||||
|
||||
The primary source of instances is Fermat's little theorem: for a finite field with `q` elements,
|
||||
`x ^ q = x` for every `x`. For `Fin p` or `ZMod p` with prime `p`, this gives `x ^ p = x`.
|
||||
|
||||
The `grind` ring solver uses this typeclass to add the relation `x ^ p - x = 0` to the
|
||||
Groebner basis, which allows it to reduce high-degree polynomials. Mathlib can provide
|
||||
instances for general finite fields via `FiniteField.pow_card`.
|
||||
-/
|
||||
class PowIdentity (α : Type u) [CommSemiring α] (p : outParam Nat) : Prop where
|
||||
/-- Every element satisfies `x ^ p = x`. -/
|
||||
pow_eq (x : α) : x ^ p = x
|
||||
|
||||
namespace PowIdentity
|
||||
|
||||
variable [CommSemiring α] [PowIdentity α p]
|
||||
|
||||
theorem pow (x : α) : x ^ p = x := pow_eq x
|
||||
|
||||
end PowIdentity
|
||||
|
||||
open AddCommGroup
|
||||
|
||||
theorem no_int_zero_divisors {α : Type u} [IntModule α] [NoNatZeroDivisors α] {k : Int} {a : α}
|
||||
|
||||
@@ -193,7 +193,7 @@ theorem mul_assoc (a b c : Q α) : mul (mul a b) c = mul a (mul b c) := by
|
||||
simp [Semiring.left_distrib, Semiring.right_distrib]; refine ⟨0, ?_⟩; ac_rfl
|
||||
|
||||
theorem mul_one (a : Q α) : mul a (natCast 1) = a := by
|
||||
obtain ⟨⟨_, _⟩⟩ := a; simp
|
||||
obtain ⟨⟨_, _⟩⟩ := a; simp
|
||||
|
||||
theorem one_mul (a : Q α) : mul (natCast 1) a = a := by
|
||||
obtain ⟨⟨_, _⟩⟩ := a; simp
|
||||
|
||||
@@ -156,12 +156,6 @@ instance [i : NeZero n] : ToInt.Pow (Fin n) (.co 0 n) where
|
||||
rw [pow_succ, ToInt.Mul.toInt_mul, ih, ← ToInt.wrap_toInt,
|
||||
← IntInterval.wrap_mul (by simp), Int.pow_succ, ToInt.wrap_toInt]
|
||||
|
||||
instance : PowIdentity (Fin 2) 2 where
|
||||
pow_eq x := by
|
||||
match x with
|
||||
| ⟨0, _⟩ => rfl
|
||||
| ⟨1, _⟩ => rfl
|
||||
|
||||
end Fin
|
||||
|
||||
end Lean.Grind
|
||||
|
||||
@@ -36,6 +36,9 @@ private local instance : ToString Int where
|
||||
private local instance : Repr Int where
|
||||
reprPrec i prec := if i < 0 then Repr.addAppParen (toString i) prec else toString i
|
||||
|
||||
private local instance : Append String where
|
||||
append := String.Internal.append
|
||||
|
||||
/-- Internal representation of a linear combination of atoms, and a constant term. -/
|
||||
structure LinearCombo where
|
||||
/-- Constant term. -/
|
||||
|
||||
@@ -145,7 +145,7 @@ Examples:
|
||||
The constant function that ignores its argument.
|
||||
|
||||
If `a : α`, then `Function.const β a : β → α` is the “constant function with value `a`”. For all
|
||||
arguments `b : β`, `Function.const β a b = a`. It is often written directly as `fun _ => a`.
|
||||
arguments `b : β`, `Function.const β a b = a`.
|
||||
|
||||
Examples:
|
||||
* `Function.const Bool 10 true = 10`
|
||||
@@ -3754,7 +3754,7 @@ class Functor (f : Type u → Type v) : Type (max (u+1) v) where
|
||||
/--
|
||||
Mapping a constant function.
|
||||
|
||||
Given `a : α` and `v : f β`, `mapConst a v` is equivalent to `(fun _ => a) <$> v`. For some
|
||||
Given `a : α` and `v : f α`, `mapConst a v` is equivalent to `Function.const _ a <$> v`. For some
|
||||
functors, this can be implemented more efficiently; for all other functors, the default
|
||||
implementation may be used.
|
||||
-/
|
||||
|
||||
@@ -1880,12 +1880,3 @@ lead to undefined behavior.
|
||||
-/
|
||||
@[extern "lean_runtime_forget"]
|
||||
def Runtime.forget (a : α) : BaseIO Unit := return
|
||||
|
||||
set_option linter.unusedVariables false in
|
||||
/--
|
||||
Ensures `a` remains at least alive until the call site by holding a reference to `a`. This can be useful
|
||||
for unsafe code (such as an FFI) that relies on a Lean object not being freed until after some point
|
||||
in the program. At runtime, this will be a no-op as the C compiler will optimize away this call.
|
||||
-/
|
||||
@[extern "lean_runtime_hold"]
|
||||
def Runtime.hold (a : @& α) : BaseIO Unit := return
|
||||
|
||||
@@ -9,7 +9,6 @@ prelude
|
||||
public import Lean.Meta.Sorry
|
||||
public import Lean.Util.CollectAxioms
|
||||
public import Lean.OriginalConstKind
|
||||
import Lean.Compiler.MetaAttr
|
||||
import all Lean.OriginalConstKind -- for accessing `privateConstKindsExt`
|
||||
|
||||
public section
|
||||
@@ -209,12 +208,8 @@ where
|
||||
catch _ => pure ()
|
||||
|
||||
|
||||
def addAndCompile (decl : Declaration) (logCompileErrors : Bool := true)
|
||||
(markMeta : Bool := false) : CoreM Unit := do
|
||||
def addAndCompile (decl : Declaration) (logCompileErrors : Bool := true) : CoreM Unit := do
|
||||
addDecl decl
|
||||
if markMeta then
|
||||
for n in decl.getNames do
|
||||
modifyEnv (Lean.markMeta · n)
|
||||
compileDecl decl (logErrors := logCompileErrors)
|
||||
|
||||
end Lean
|
||||
|
||||
@@ -54,7 +54,7 @@ unsafe def registerInitAttrUnsafe (attrName : Name) (runAfterImport : Bool) (ref
|
||||
descr := "initialization procedure for global references"
|
||||
-- We want to run `[init]` in declaration order
|
||||
preserveOrder := true
|
||||
getParam := fun declName stx => withoutExporting do
|
||||
getParam := fun declName stx => do
|
||||
let decl ← getConstInfo declName
|
||||
match (← Attribute.Builtin.getIdent? stx) with
|
||||
| some initFnName =>
|
||||
@@ -149,6 +149,8 @@ def setBuiltinInitAttr (env : Environment) (declName : Name) (initFnName : Name
|
||||
def declareBuiltin (forDecl : Name) (value : Expr) : CoreM Unit :=
|
||||
-- can always be private, not referenced directly except through emitted C code
|
||||
withoutExporting do
|
||||
-- TODO: needs an update-stage0 + prefer_native=true for breaking symbol name
|
||||
withExporting do
|
||||
let name ← mkAuxDeclName (kind := `_regBuiltin ++ forDecl)
|
||||
let type := mkApp (mkConst `IO) (mkConst `Unit)
|
||||
let decl := Declaration.defnDecl { name, levelParams := [], type, value, hints := ReducibilityHints.opaque,
|
||||
|
||||
@@ -1230,14 +1230,7 @@ def instantiateRevRangeArgs (e : Expr) (beginIdx endIdx : Nat) (args : Array (Ar
|
||||
else
|
||||
e.instantiateRevRange beginIdx endIdx (args.map (·.toExpr))
|
||||
|
||||
/--
|
||||
Lookup function for compiler extensions with sorted persisted state that works in both `lean` and
|
||||
`leanir`.
|
||||
|
||||
`preferImported` defaults to false because in `leanir`, we do not want to mix information from
|
||||
`meta` compilation in `lean` with our own state. But in `lean`, setting `preferImported` can help
|
||||
with avoiding unnecessary task blocks.
|
||||
-/
|
||||
/-- Lookup function for compiler extensions with sorted persisted state that works in both `lean` and `leanir`. -/
|
||||
@[inline] def findExtEntry? [Inhabited σ] (env : Environment) (ext : PersistentEnvExtension α β σ) (declName : Name)
|
||||
(findAtSorted? : Array α → Name → Option α')
|
||||
(findInState? : σ → Name → Option α') : Option α' :=
|
||||
|
||||
@@ -232,7 +232,6 @@ partial def checkCases (c : Cases .pure) : CheckM Unit := do
|
||||
withParams params do check k
|
||||
|
||||
partial def check (code : Code .pure) : CheckM Unit := do
|
||||
checkSystem "LCNF check"
|
||||
match code with
|
||||
| .let decl k => checkLetDecl decl; withFVarId decl.fvarId do check k
|
||||
| .fun decl k =>
|
||||
|
||||
@@ -67,7 +67,7 @@ structure ParamMap where
|
||||
The set of fvars that were already annotated as borrowed before arriving at this pass. We try to
|
||||
preserve the annotations here if possible.
|
||||
-/
|
||||
annotatedBorrows : Std.HashSet FVarId := {}
|
||||
annoatedBorrows : Std.HashSet FVarId := {}
|
||||
|
||||
namespace ParamMap
|
||||
|
||||
@@ -95,7 +95,7 @@ where
|
||||
modify fun m =>
|
||||
{ m with
|
||||
map := m.map.insert (.decl decl.name) (initParamsIfNotExported exported decl.params),
|
||||
annotatedBorrows := decl.params.foldl (init := m.annotatedBorrows) fun acc p =>
|
||||
annoatedBorrows := decl.params.foldl (init := m.annoatedBorrows) fun acc p =>
|
||||
if p.borrow then acc.insert p.fvarId else acc
|
||||
}
|
||||
goCode decl.name code
|
||||
@@ -116,7 +116,7 @@ where
|
||||
modify fun m =>
|
||||
{ m with
|
||||
map := m.map.insert (.jp declName decl.fvarId) (initParams decl.params),
|
||||
annotatedBorrows := decl.params.foldl (init := m.annotatedBorrows) fun acc p =>
|
||||
annoatedBorrows := decl.params.foldl (init := m.annoatedBorrows) fun acc p =>
|
||||
if p.borrow then acc.insert p.fvarId else acc
|
||||
}
|
||||
goCode declName decl.value
|
||||
@@ -286,7 +286,7 @@ where
|
||||
|
||||
ownFVar (fvarId : FVarId) (reason : OwnReason) : InferM Unit := do
|
||||
unless (← get).owned.contains fvarId do
|
||||
if !reason.isForced && (← get).paramMap.annotatedBorrows.contains fvarId then
|
||||
if !reason.isForced && (← get).paramMap.annoatedBorrows.contains fvarId then
|
||||
trace[Compiler.inferBorrow] "user annotation blocked owning {← PP.run <| PP.ppFVar fvarId}: {← reason.toString}"
|
||||
else
|
||||
trace[Compiler.inferBorrow] "own {← PP.run <| PP.ppFVar fvarId}: {← reason.toString}"
|
||||
|
||||
@@ -78,13 +78,9 @@ def isValidMainType (type : Expr) : Bool :=
|
||||
isValidResultName resultName
|
||||
| _ => false
|
||||
|
||||
/-- A postponed call of `compileDecls`. -/
|
||||
structure PostponedCompileDecls where
|
||||
/-- Declaration names of this mutual group. -/
|
||||
declNames : Array Name
|
||||
/-- Options at time of original call, to be restored for tracing etc. -/
|
||||
options : Options
|
||||
deriving BEq
|
||||
deriving BEq, Hashable
|
||||
|
||||
/--
|
||||
Saves postponed `compileDecls` calls.
|
||||
@@ -105,20 +101,16 @@ builtin_initialize postponedCompileDeclsExt : SimplePersistentEnvExtension Postp
|
||||
{ exported := #[], server := #[], «private» := es.toArray }
|
||||
}
|
||||
|
||||
def resumeCompilation (declName : Name) (baseOpts : Options) : CoreM Unit := do
|
||||
def resumeCompilation (declName : Name) : CoreM Unit := do
|
||||
let some decls := postponedCompileDeclsExt.getState (← getEnv) |>.find? declName | return
|
||||
let opts := baseOpts.mergeBy (fun _ base _ => base) decls.options
|
||||
let opts := compiler.postponeCompile.set opts false
|
||||
modifyEnv (postponedCompileDeclsExt.modifyState · fun s => decls.declNames.foldl (·.erase) s)
|
||||
-- NOTE: we *must* throw away the current options as they could depend on the specific recursion
|
||||
-- we did to get here.
|
||||
withOptions (fun _ => opts) do
|
||||
withOptions (compiler.postponeCompile.set · false) do
|
||||
Core.prependError m!"Failed to compile `{declName}`" do
|
||||
(← compileDeclsRef.get) decls.declNames baseOpts
|
||||
(← compileDeclsRef.get) decls.declNames
|
||||
|
||||
namespace PassManager
|
||||
|
||||
partial def run (declNames : Array Name) (baseOpts : Options) : CompilerM Unit := withAtLeastMaxRecDepth 8192 do
|
||||
partial def run (declNames : Array Name) : CompilerM Unit := withAtLeastMaxRecDepth 8192 do
|
||||
/-
|
||||
Note: we need to increase the recursion depth because we currently do to save phase1
|
||||
declarations in .olean files. Then, we have to recursively compile all dependencies,
|
||||
@@ -149,14 +141,11 @@ partial def run (declNames : Array Name) (baseOpts : Options) : CompilerM Unit :
|
||||
|
||||
-- Now that we have done all input checks, check for postponement
|
||||
if (← getEnv).header.isModule && (← compiler.postponeCompile.getM) then
|
||||
modifyEnv (postponedCompileDeclsExt.addEntry · { declNames := decls.map (·.name), options := ← getOptions })
|
||||
modifyEnv (postponedCompileDeclsExt.addEntry · { declNames := decls.map (·.name) })
|
||||
-- meta defs are compiled locally so they are available for execution/compilation without
|
||||
-- importing `.ir` but still marked for `leanir` compilation so that we do not have to persist
|
||||
-- module-local compilation information between the two processes
|
||||
if decls.any (isMarkedMeta (← getEnv) ·.name) then
|
||||
-- avoid re-compiling the meta defs in this process; the entry for `leanir` is not affected
|
||||
modifyEnv (postponedCompileDeclsExt.modifyState · fun s => decls.foldl (·.erase ·.name) s)
|
||||
else
|
||||
if !decls.any (isMarkedMeta (← getEnv) ·.name) then
|
||||
trace[Compiler] "postponing compilation of {decls.map (·.name)}"
|
||||
return
|
||||
|
||||
@@ -168,7 +157,7 @@ partial def run (declNames : Array Name) (baseOpts : Options) : CompilerM Unit :
|
||||
let .let { value := .const c .., .. } .. := c | return
|
||||
-- Need to do some lookups to get the actual name passed to `compileDecls`
|
||||
let c := Compiler.getImplementedBy? (← getEnv) c |>.getD c
|
||||
resumeCompilation c baseOpts
|
||||
resumeCompilation c
|
||||
|
||||
let decls := markRecDecls decls
|
||||
let manager ← getPassManager
|
||||
@@ -199,7 +188,6 @@ where
|
||||
profileitM Exception profilerName (← getOptions) do
|
||||
let mut state : (pu : Purity) × Array (Decl pu) := ⟨inPhase, decls⟩
|
||||
for pass in passes do
|
||||
checkSystem "LCNF compiler"
|
||||
state ← withTraceNode `Compiler (fun _ => return m!"compiler phase: {pass.phase}, pass: {pass.name}") do
|
||||
let decls ← withPhase pass.phase do
|
||||
state.fst.withAssertPurity pass.phase.toPurity fun h => do
|
||||
@@ -211,9 +199,9 @@ where
|
||||
|
||||
end PassManager
|
||||
|
||||
def main (declNames : Array Name) (baseOpts : Options) : CoreM Unit := do
|
||||
def main (declNames : Array Name) : CoreM Unit := do
|
||||
withTraceNode `Compiler (fun _ => return m!"compiling: {declNames}") do
|
||||
CompilerM.run <| PassManager.run declNames baseOpts
|
||||
CompilerM.run <| PassManager.run declNames
|
||||
|
||||
builtin_initialize
|
||||
compileDeclsRef.set main
|
||||
|
||||
@@ -121,7 +121,7 @@ def mkPerDeclaration (name : Name) (phase : Phase)
|
||||
occurrence := occurrence
|
||||
phase := phase
|
||||
name := name
|
||||
run := fun xs => xs.mapM fun decl => do checkSystem "LCNF compiler"; run decl
|
||||
run := fun xs => xs.mapM run
|
||||
|
||||
end Pass
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ inserts addition instructions to attempt to reuse the memory right away instead
|
||||
allocator.
|
||||
|
||||
For this the paper defines three functions:
|
||||
- `R` (called `Decl.insertResetReuse` here) which looks for candidates that might be eligible for
|
||||
- `R` (called `Decl.insertResetReuse` here) which looks for candidates that might be elligible for
|
||||
reuse. For these variables it invokes `D`.
|
||||
- `D` which looks for code regions in which the target variable is dead (i.e. no longer read from),
|
||||
it then invokes `S`. If `S` succeeds it inserts a `reset` instruction to match the `reuse`
|
||||
|
||||
@@ -217,8 +217,6 @@ Simplify `code`
|
||||
-/
|
||||
partial def simp (code : Code .pure) : SimpM (Code .pure) := withIncRecDepth do
|
||||
incVisited
|
||||
if (← get).visited % 128 == 0 then
|
||||
checkSystem "LCNF simp"
|
||||
match code with
|
||||
| .let decl k =>
|
||||
let baseDecl := decl
|
||||
|
||||
@@ -146,7 +146,7 @@ Similar to the default `Lean.withIncRecDepth`, but include the `inlineStack` in
|
||||
@[inline] def withIncRecDepth (x : SimpM α) : SimpM α := do
|
||||
let curr ← MonadRecDepth.getRecDepth
|
||||
let max ← MonadRecDepth.getMaxRecDepth
|
||||
if max != 0 && curr == max then
|
||||
if curr == max then
|
||||
throwMaxRecDepth
|
||||
else
|
||||
MonadRecDepth.withRecDepth (curr+1) x
|
||||
|
||||
@@ -24,7 +24,7 @@ In particular we perform:
|
||||
- folding of the most common cases arm into the default arm if possible
|
||||
|
||||
Note: Currently the simplifier still contains almost equivalent simplifications to the ones shown
|
||||
here. We know that this causes unforeseen behavior and do plan on changing it eventually.
|
||||
here. We know that this causes unforseen behavior and do plan on changing it eventually.
|
||||
-/
|
||||
|
||||
-- TODO: the following functions are duplicated from simp and should be deleted in simp once we
|
||||
|
||||
@@ -171,7 +171,7 @@ def toDecl (declName : Name) : CompilerM (Decl .pure) := do
|
||||
if compiler.ignoreBorrowAnnotation.get (← getOptions) then
|
||||
decl := { decl with params := ← decl.params.mapM (·.updateBorrow false) }
|
||||
if isExport env decl.name && decl.params.any (·.borrow) then
|
||||
throwError m!" Declaration {decl.name} is marked as `export` but some of its parameters have borrow annotations.\n Consider using `set_option compiler.ignoreBorrowAnnotation true in` to suppress the borrow annotations in its type.\n If the declaration is part of an `export`/`extern` pair make sure to also suppress the annotations at the `extern` declaration."
|
||||
throwError m!" Declaration {decl.name} is marked as `export` but some of its parameters have borrow annotations.\n Consider using `set_option compiler.ignoreBorrowAnnotation true in` to supress the borrow annotations in its type.\n If the declaration is part of an `export`/`extern` pair make sure to also supress the annotations at the `extern` declaration."
|
||||
return decl
|
||||
|
||||
end Lean.Compiler.LCNF
|
||||
|
||||
@@ -279,13 +279,13 @@ partial def casesFloatArrayToMono (c : Cases .pure) (_ : c.typeName == ``FloatAr
|
||||
let k ← k.toMono
|
||||
return .let decl k
|
||||
|
||||
/-- Eliminate `cases` for `String`. -/
|
||||
/-- Eliminate `cases` for `String. -/
|
||||
partial def casesStringToMono (c : Cases .pure) (_ : c.typeName == ``String) : ToMonoM (Code .pure) := do
|
||||
assert! c.alts.size == 1
|
||||
let .alt _ ps k := c.alts[0]! | unreachable!
|
||||
eraseParams ps
|
||||
let p := ps[0]!
|
||||
let decl := { fvarId := p.fvarId, binderName := p.binderName, type := anyExpr, value := .const ``String.toByteArray [] #[.fvar c.discr] }
|
||||
let decl := { fvarId := p.fvarId, binderName := p.binderName, type := anyExpr, value := .const ``String.toList [] #[.fvar c.discr] }
|
||||
modifyLCtx fun lctx => lctx.addLetDecl decl
|
||||
let k ← k.toMono
|
||||
return .let decl k
|
||||
|
||||
@@ -19,7 +19,7 @@ that fulfill the requirements of `shouldGenerateCode`.
|
||||
def compile (declNames : Array Name) : CoreM Unit := do profileitM Exception "compiler new" (← getOptions) do
|
||||
withOptions (compiler.postponeCompile.set · false) do
|
||||
withTraceNode `Compiler (fun _ => return m!"compiling: {declNames}") do
|
||||
LCNF.main declNames {}
|
||||
LCNF.main declNames
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Compiler
|
||||
|
||||
@@ -20,8 +20,6 @@ register_builtin_option diagnostics : Bool := {
|
||||
descr := "collect diagnostic information"
|
||||
}
|
||||
|
||||
builtin_initialize registerTraceClass `diagnostics
|
||||
|
||||
register_builtin_option diagnostics.threshold : Nat := {
|
||||
defValue := 20
|
||||
descr := "only diagnostic counters above this threshold are reported by the definitional equality"
|
||||
@@ -446,26 +444,21 @@ Note that the value of `ctx.initHeartbeats` is ignored and replaced with `IO.get
|
||||
@[inline] def CoreM.toIO' (x : CoreM α) (ctx : Context) (s : State) : IO α :=
|
||||
(·.1) <$> x.toIO ctx s
|
||||
|
||||
/-- withIncRecDepth for a monad `m` such that `[MonadControlT CoreM n]`. -/
|
||||
protected def withIncRecDepth [Monad m] [MonadControlT CoreM m] (x : m α) : m α :=
|
||||
controlAt CoreM fun runInBase => withIncRecDepth (runInBase x)
|
||||
|
||||
/--
|
||||
Throws an internal interrupt exception if cancellation has been requested. The exception is not
|
||||
caught by `try catch` but is intended to be caught by `Command.withLoggingExceptions` at the top
|
||||
level of elaboration. In particular, we want to skip producing further incremental snapshots after
|
||||
the exception has been thrown.
|
||||
|
||||
Like `checkSystem` but without the global heartbeat check, for callers that have their own
|
||||
heartbeat tracking (e.g. `SynthInstance`).
|
||||
-/
|
||||
@[inline] def checkInterrupted : CoreM Unit := do
|
||||
if let some tk := (← read).cancelTk? then
|
||||
if (← tk.isSet) then
|
||||
throwInterruptException
|
||||
|
||||
/-- withIncRecDepth for a monad `m` such that `[MonadControlT CoreM n]`.
|
||||
Also checks for cancellation, so that recursive elaboration functions
|
||||
(inferType, whnf, isDefEq, …) respond promptly to interrupt requests. -/
|
||||
protected def withIncRecDepth [Monad m] [MonadControlT CoreM m] (x : m α) : m α :=
|
||||
controlAt CoreM fun runInBase => do checkInterrupted; withIncRecDepth (runInBase x)
|
||||
|
||||
register_builtin_option debug.moduleNameAtTimeout : Bool := {
|
||||
defValue := true
|
||||
descr := "include module name in deterministic timeout error messages.\nRemark: we set this option to false to increase the stability of our test suite"
|
||||
@@ -715,11 +708,11 @@ breaks the cycle by making `compileDeclsImpl` a "dynamic" call through the ref t
|
||||
to the linker. In the compiler there is a matching `builtin_initialize` to set this ref to the
|
||||
actual implementation of compileDeclsRef.
|
||||
-/
|
||||
builtin_initialize compileDeclsRef : IO.Ref (Array Name → Options → CoreM Unit) ←
|
||||
IO.mkRef (fun _ _ => throwError m!"call to compileDecls with uninitialized compileDeclsRef")
|
||||
builtin_initialize compileDeclsRef : IO.Ref (Array Name → CoreM Unit) ←
|
||||
IO.mkRef (fun _ => throwError m!"call to compileDecls with uninitialized compileDeclsRef")
|
||||
|
||||
private def compileDeclsImpl (declNames : Array Name) : CoreM Unit := do
|
||||
(← compileDeclsRef.get) declNames {}
|
||||
(← compileDeclsRef.get) declNames
|
||||
|
||||
-- `ref?` is used for error reporting if available
|
||||
def compileDecls (decls : Array Name) (logErrors := true) : CoreM Unit := do
|
||||
|
||||
@@ -82,17 +82,11 @@ def mergeBy (f : Name → DataValue → DataValue → DataValue) (o1 o2 : Option
|
||||
|
||||
end Options
|
||||
|
||||
structure OptionDeprecation where
|
||||
since : String
|
||||
text? : Option String := none
|
||||
deriving Inhabited
|
||||
|
||||
structure OptionDecl where
|
||||
name : Name
|
||||
declName : Name := by exact decl_name%
|
||||
defValue : DataValue
|
||||
descr : String := ""
|
||||
deprecation? : Option OptionDeprecation := none
|
||||
deriving Inhabited
|
||||
|
||||
def OptionDecl.fullDescr (self : OptionDecl) : String := Id.run do
|
||||
@@ -189,7 +183,6 @@ namespace Option
|
||||
protected structure Decl (α : Type) where
|
||||
defValue : α
|
||||
descr : String := ""
|
||||
deprecation? : Option OptionDeprecation := none
|
||||
|
||||
protected def get? [KVMap.Value α] (opts : Options) (opt : Lean.Option α) : Option α :=
|
||||
opts.get? opt.name
|
||||
@@ -221,7 +214,6 @@ protected def register [KVMap.Value α] (name : Name) (decl : Lean.Option.Decl
|
||||
declName := ref
|
||||
defValue := KVMap.Value.toDataValue decl.defValue
|
||||
descr := decl.descr
|
||||
deprecation? := decl.deprecation?
|
||||
}
|
||||
return { name := name, defValue := decl.defValue }
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ variable {β : Type v}
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
@[specialize]
|
||||
partial def forInAux {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] [inh : Inhabited β]
|
||||
(f : α → β → m (ForInStep β)) (n : @&PersistentArrayNode α) (b : β) : m (ForInStep β) := do
|
||||
(f : α → β → m (ForInStep β)) (n : PersistentArrayNode α) (b : β) : m (ForInStep β) := do
|
||||
let mut b := b
|
||||
match n with
|
||||
| leaf vs =>
|
||||
@@ -243,7 +243,7 @@ partial def forInAux {α : Type u} {β : Type v} {m : Type v → Type w} [Monad
|
||||
| ForInStep.yield bNew => b := bNew
|
||||
return ForInStep.yield b
|
||||
|
||||
@[specialize] protected def forIn (t : @&PersistentArray α) (init : β) (f : α → β → m (ForInStep β)) : m β := do
|
||||
@[specialize] protected def forIn (t : PersistentArray α) (init : β) (f : α → β → m (ForInStep β)) : m β := do
|
||||
match (← forInAux (inh := ⟨init⟩) f t.root init) with
|
||||
| ForInStep.done b => pure b
|
||||
| ForInStep.yield b =>
|
||||
|
||||
@@ -153,7 +153,7 @@ partial def findAtAux [BEq α] (keys : Array α) (vals : Array β) (heq : keys.s
|
||||
else findAtAux keys vals heq (i+1) k
|
||||
else none
|
||||
|
||||
partial def findAux [BEq α] : @&Node α β → USize → α → Option β
|
||||
partial def findAux [BEq α] : Node α β → USize → α → Option β
|
||||
| Node.entries entries, h, k =>
|
||||
let j := (mod2Shift h shift).toNat
|
||||
match entries[j]! with
|
||||
@@ -162,7 +162,7 @@ partial def findAux [BEq α] : @&Node α β → USize → α → Option β
|
||||
| Entry.entry k' v => if k == k' then some v else none
|
||||
| Node.collision keys vals heq, _, k => findAtAux keys vals heq 0 k
|
||||
|
||||
def find? {_ : BEq α} {_ : Hashable α} : @&PersistentHashMap α β → α → Option β
|
||||
def find? {_ : BEq α} {_ : Hashable α} : PersistentHashMap α β → α → Option β
|
||||
| { root }, k => findAux root (hash k |>.toUSize) k
|
||||
|
||||
instance {_ : BEq α} {_ : Hashable α} : GetElem (PersistentHashMap α β) α (Option β) fun _ _ => True where
|
||||
@@ -184,7 +184,7 @@ partial def findEntryAtAux [BEq α] (keys : Array α) (vals : Array β) (heq : k
|
||||
else findEntryAtAux keys vals heq (i+1) k
|
||||
else none
|
||||
|
||||
partial def findEntryAux [BEq α] : @&Node α β → USize → α → Option (α × β)
|
||||
partial def findEntryAux [BEq α] : Node α β → USize → α → Option (α × β)
|
||||
| Node.entries entries, h, k =>
|
||||
let j := (mod2Shift h shift).toNat
|
||||
match entries[j]! with
|
||||
@@ -193,7 +193,7 @@ partial def findEntryAux [BEq α] : @&Node α β → USize → α → Option (α
|
||||
| Entry.entry k' v => if k == k' then some (k', v) else none
|
||||
| Node.collision keys vals heq, _, k => findEntryAtAux keys vals heq 0 k
|
||||
|
||||
def findEntry? {_ : BEq α} {_ : Hashable α} : @&PersistentHashMap α β → α → Option (α × β)
|
||||
def findEntry? {_ : BEq α} {_ : Hashable α} : PersistentHashMap α β → α → Option (α × β)
|
||||
| { root }, k => findEntryAux root (hash k |>.toUSize) k
|
||||
|
||||
partial def findKeyDAtAux [BEq α] (keys : Array α) (vals : Array β) (heq : keys.size = vals.size) (i : Nat) (k : α) (k₀ : α) : α :=
|
||||
@@ -320,7 +320,7 @@ def foldl {_ : BEq α} {_ : Hashable α} (map : PersistentHashMap α β) (f : σ
|
||||
Id.run <| map.foldlM (pure <| f · · ·) init
|
||||
|
||||
protected def forIn {_ : BEq α} {_ : Hashable α} [Monad m]
|
||||
(map : @&PersistentHashMap α β) (init : σ) (f : α × β → σ → m (ForInStep σ)) : m σ := do
|
||||
(map : PersistentHashMap α β) (init : σ) (f : α × β → σ → m (ForInStep σ)) : m σ := do
|
||||
let intoError : ForInStep σ → Except σ σ
|
||||
| .done s => .error s
|
||||
| .yield s => .ok s
|
||||
|
||||
@@ -60,7 +60,7 @@ instance : EmptyCollection (Trie α) :=
|
||||
instance : Inhabited (Trie α) where
|
||||
default := empty
|
||||
|
||||
/-- Insert or update the value at the given key `s`. -/
|
||||
/-- Insert or update the value at a the given key `s`. -/
|
||||
partial def upsert (t : Trie α) (s : String) (f : Option α → α) : Trie α :=
|
||||
let rec insertEmpty (i : Nat) : Trie α :=
|
||||
if h : i < s.utf8ByteSize then
|
||||
@@ -100,7 +100,7 @@ partial def upsert (t : Trie α) (s : String) (f : Option α → α) : Trie α :
|
||||
node (f v) cs ts
|
||||
loop 0 t
|
||||
|
||||
/-- Inserts a value at the given key `s`, overriding an existing value if present. -/
|
||||
/-- Inserts a value at a the given key `s`, overriding an existing value if present. -/
|
||||
partial def insert (t : Trie α) (s : String) (val : α) : Trie α :=
|
||||
upsert t s (fun _ => val)
|
||||
|
||||
@@ -131,9 +131,9 @@ partial def find? (t : Trie α) (s : String) : Option α :=
|
||||
loop 0 t
|
||||
|
||||
/-- Returns an `Array` of all values in the trie, in no particular order. -/
|
||||
partial def values (t : @&Trie α) : Array α := go t |>.run #[] |>.2
|
||||
partial def values (t : Trie α) : Array α := go t |>.run #[] |>.2
|
||||
where
|
||||
go : @&Trie α → StateM (Array α) Unit
|
||||
go : Trie α → StateM (Array α) Unit
|
||||
| leaf a? => do
|
||||
if let some a := a? then
|
||||
modify (·.push a)
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Wojciech Różowski
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Lean.Compiler.ModPkgExt
|
||||
|
||||
public section
|
||||
|
||||
namespace Lean
|
||||
|
||||
structure DeprecatedModuleEntry where
|
||||
message? : Option String := none
|
||||
since? : Option String := none
|
||||
deriving Inhabited
|
||||
|
||||
register_builtin_option linter.deprecated.module : Bool := {
|
||||
defValue := true
|
||||
descr := "if true, generate warnings when importing deprecated modules"
|
||||
}
|
||||
|
||||
builtin_initialize deprecatedModuleExt : ModuleEnvExtension <| Option DeprecatedModuleEntry ←
|
||||
registerModuleEnvExtension <| pure none
|
||||
|
||||
def Environment.getDeprecatedModuleByIdx? (env : Environment) (idx : ModuleIdx) : Option DeprecatedModuleEntry :=
|
||||
deprecatedModuleExt.getStateByIdx? env idx |>.join
|
||||
|
||||
def Environment.setDeprecatedModule (entry : Option DeprecatedModuleEntry) (env : Environment) : Environment :=
|
||||
deprecatedModuleExt.setState env entry
|
||||
|
||||
def formatDeprecatedModuleWarning (env : Environment) (idx : ModuleIdx) (modName : Name)
|
||||
(entry : DeprecatedModuleEntry) : String :=
|
||||
let msg := entry.message?.getD ""
|
||||
let replacements := env.header.moduleData[idx.toNat]!.imports.filter fun imp =>
|
||||
imp.module != `Init
|
||||
let lines := replacements.foldl (init := "") fun acc imp =>
|
||||
acc ++ s!"import {imp.module}\n"
|
||||
s!"{msg}\n\
|
||||
'{modName}' has been deprecated: please replace this import by\n\n\
|
||||
{lines}"
|
||||
|
||||
end Lean
|
||||
@@ -13,7 +13,6 @@ public import Lean.IdentifierSuggestion
|
||||
import all Lean.Elab.ErrorUtils
|
||||
import Lean.Elab.DeprecatedArg
|
||||
import Init.Omega
|
||||
import Init.Data.List.MapIdx
|
||||
|
||||
public section
|
||||
|
||||
@@ -1300,13 +1299,13 @@ where
|
||||
inductive LValResolution where
|
||||
/-- When applied to `f`, effectively expands to `BaseStruct.fieldName (self := Struct.toBase f)`.
|
||||
This is a special named argument where it suppresses any explicit arguments depending on it so that type parameters don't need to be supplied. -/
|
||||
| projFn (baseStructName : Name) (structName : Name) (fieldName : Name) (levels : List Level)
|
||||
| projFn (baseStructName : Name) (structName : Name) (fieldName : Name)
|
||||
/-- Similar to `projFn`, but for extracting field indexed by `idx`. Works for one-constructor inductive types in general. -/
|
||||
| projIdx (structName : Name) (idx : Nat)
|
||||
/-- When applied to `f`, effectively expands to `constName ... (Struct.toBase f)`, with the argument placed in the correct
|
||||
positional argument if possible, or otherwise as a named argument. The `Struct.toBase` is not present if `baseStructName == structName`,
|
||||
in which case these do not need to be structures. Supports generalized field notation. -/
|
||||
| const (baseStructName : Name) (structName : Name) (constName : Name) (levels : List Level)
|
||||
| const (baseStructName : Name) (structName : Name) (constName : Name)
|
||||
/-- Like `const`, but with `fvar` instead of `constName`.
|
||||
The `baseName` is the base name of the type to search for in the parameter list. -/
|
||||
| localRec (baseName : Name) (fvar : Expr)
|
||||
@@ -1381,7 +1380,7 @@ private def reverseFieldLookup (env : Environment) (fieldName : String) :=
|
||||
|
||||
private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM LValResolution := do
|
||||
match eType.getAppFn, lval with
|
||||
| .const structName _, LVal.fieldIdx ref idx levels =>
|
||||
| .const structName _, LVal.fieldIdx ref idx =>
|
||||
if idx == 0 then
|
||||
throwError "Invalid projection: Index must be greater than 0"
|
||||
let env ← getEnv
|
||||
@@ -1394,14 +1393,10 @@ private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM L
|
||||
if idx - 1 < numFields then
|
||||
if isStructure env structName then
|
||||
let fieldNames := getStructureFields env structName
|
||||
return LValResolution.projFn structName structName fieldNames[idx - 1]! levels
|
||||
return LValResolution.projFn structName structName fieldNames[idx - 1]!
|
||||
else
|
||||
/- `structName` was declared using `inductive` command.
|
||||
So, we don't projection functions for it. Thus, we use `Expr.proj` -/
|
||||
unless levels.isEmpty do
|
||||
throwError "Invalid projection: Explicit universe levels are only supported for inductive types \
|
||||
defined using the `structure` command. \
|
||||
The expression{indentExpr e}\nhas type{inlineExpr eType}which is not a `structure`."
|
||||
return LValResolution.projIdx structName (idx - 1)
|
||||
else
|
||||
if numFields == 0 then
|
||||
@@ -1414,33 +1409,31 @@ private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM L
|
||||
++ MessageData.note m!"The expression{indentExpr e}\nhas type{inlineExpr eType}which has only \
|
||||
{numFields} field{numFields.plural}"
|
||||
++ tupleHint
|
||||
| .const structName _, LVal.fieldName ref fieldName levels _ _ => withRef ref do
|
||||
| .const structName _, LVal.fieldName ref fieldName _ _ => withRef ref do
|
||||
let env ← getEnv
|
||||
if isStructure env structName then
|
||||
if let some baseStructName := findField? env structName (Name.mkSimple fieldName) then
|
||||
return LValResolution.projFn baseStructName structName (Name.mkSimple fieldName) levels
|
||||
return LValResolution.projFn baseStructName structName (Name.mkSimple fieldName)
|
||||
-- Search the local context first
|
||||
let fullName := Name.mkStr (privateToUserName structName) fieldName
|
||||
for localDecl in (← getLCtx) do
|
||||
if localDecl.isAuxDecl then
|
||||
if let some localDeclFullName := (← getLCtx).auxDeclToFullName.get? localDecl.fvarId then
|
||||
if fullName == privateToUserName localDeclFullName then
|
||||
unless levels.isEmpty do
|
||||
throwInvalidExplicitUniversesForLocal localDecl.toExpr
|
||||
/- LVal notation is being used to make a "local" recursive call. -/
|
||||
return LValResolution.localRec structName localDecl.toExpr
|
||||
-- Then search the environment
|
||||
if let some (baseStructName, fullName) ← findMethod? structName (.mkSimple fieldName) then
|
||||
return LValResolution.const baseStructName structName fullName levels
|
||||
return LValResolution.const baseStructName structName fullName
|
||||
throwInvalidFieldAt ref fieldName fullName
|
||||
-- Suggest a potential unreachable private name as hint. This does not cover structure
|
||||
-- inheritance, nor `import all`.
|
||||
(declHint := (mkPrivateName env structName).mkStr fieldName)
|
||||
|
||||
| .forallE .., LVal.fieldName ref fieldName levels suffix? fullRef =>
|
||||
| .forallE .., LVal.fieldName ref fieldName suffix? fullRef =>
|
||||
let fullName := Name.str `Function fieldName
|
||||
if (← getEnv).contains fullName then
|
||||
return LValResolution.const `Function `Function fullName levels
|
||||
return LValResolution.const `Function `Function fullName
|
||||
match e.getAppFn, suffix? with
|
||||
| Expr.const c _, some suffix =>
|
||||
throwUnknownNameWithSuggestions (idOrConst := "constant") (ref? := fullRef) (c ++ suffix)
|
||||
@@ -1450,7 +1443,7 @@ private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM L
|
||||
throwError "Invalid projection: Projections cannot be used on functions, and{indentExpr e}\n\
|
||||
has function type{inlineExprTrailing eType}"
|
||||
|
||||
| .mvar .., .fieldName _ fieldName levels _ _ =>
|
||||
| .mvar .., .fieldName _ fieldName _ _ =>
|
||||
let hint := match reverseFieldLookup (← getEnv) fieldName with
|
||||
| #[] => MessageData.nil
|
||||
| #[opt] => .hint' m!"Consider replacing the field projection `.{fieldName}` with a call to the function `{.ofConstName opt}`."
|
||||
@@ -1458,13 +1451,13 @@ private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM L
|
||||
{MessageData.joinSep (opts.toList.map (indentD m!"• `{.ofConstName ·}`")) .nil}"
|
||||
throwNamedError lean.invalidField (m!"Invalid field notation: Type of{indentExpr e}\nis not \
|
||||
known; cannot resolve field `{fieldName}`" ++ hint)
|
||||
| .mvar .., .fieldIdx _ i _ =>
|
||||
| .mvar .., .fieldIdx _ i =>
|
||||
throwError m!"Invalid projection: Type of{indentExpr e}\nis not known; cannot resolve \
|
||||
projection `{i}`"
|
||||
|
||||
| _, _ =>
|
||||
match e.getAppFn, lval with
|
||||
| Expr.const c _, .fieldName _ref _fieldName _levels (some suffix) fullRef =>
|
||||
| Expr.const c _, .fieldName _ref _fieldName (some suffix) fullRef =>
|
||||
throwUnknownNameWithSuggestions (idOrConst := "constant") (ref? := fullRef) (c ++ suffix)
|
||||
| _, .fieldName .. =>
|
||||
throwNamedError lean.invalidField m!"Invalid field notation: Field projection operates on \
|
||||
@@ -1713,12 +1706,12 @@ private def elabAppLValsAux (namedArgs : Array NamedArg) (args : Array Arg) (exp
|
||||
let f ← mkProjAndCheck structName idx f
|
||||
let f ← addTermInfo lval.getRef f
|
||||
loop f lvals
|
||||
| LValResolution.projFn baseStructName structName fieldName levels =>
|
||||
| LValResolution.projFn baseStructName structName fieldName =>
|
||||
let f ← mkBaseProjections baseStructName structName f
|
||||
let some info := getFieldInfo? (← getEnv) baseStructName fieldName | unreachable!
|
||||
if (← isInaccessiblePrivateName info.projFn) then
|
||||
throwError "Field `{fieldName}` from structure `{structName}` is private"
|
||||
let projFn ← withRef lval.getRef <| mkConst info.projFn levels
|
||||
let projFn ← withRef lval.getRef <| mkConst info.projFn
|
||||
let projFn ← addProjTermInfo lval.getRef projFn
|
||||
if lvals.isEmpty then
|
||||
let namedArgs ← addNamedArg namedArgs { name := `self, val := Arg.expr f, suppressDeps := true }
|
||||
@@ -1726,9 +1719,9 @@ private def elabAppLValsAux (namedArgs : Array NamedArg) (args : Array Arg) (exp
|
||||
else
|
||||
let f ← elabAppArgs projFn #[{ name := `self, val := Arg.expr f, suppressDeps := true }] #[] (expectedType? := none) (explicit := false) (ellipsis := false)
|
||||
loop f lvals
|
||||
| LValResolution.const baseStructName structName constName levels =>
|
||||
| LValResolution.const baseStructName structName constName =>
|
||||
let f ← if baseStructName != structName then mkBaseProjections baseStructName structName f else pure f
|
||||
let projFn ← withRef lval.getRef <| mkConst constName levels
|
||||
let projFn ← withRef lval.getRef <| mkConst constName
|
||||
let projFn ← addProjTermInfo lval.getRef projFn
|
||||
if lvals.isEmpty then
|
||||
let (args, namedArgs) ← addLValArg baseStructName f args namedArgs projFn explicit
|
||||
@@ -1779,19 +1772,15 @@ false, no elaboration function executed by `x` will reset it to
|
||||
/--
|
||||
Elaborates the resolutions of a function. The `fns` array is the output of `resolveName'`.
|
||||
-/
|
||||
private def elabAppFnResolutions (fRef : Syntax) (fns : List (Expr × Syntax × List Syntax × List Level)) (lvals : List LVal)
|
||||
private def elabAppFnResolutions (fRef : Syntax) (fns : List (Expr × Syntax × List Syntax)) (lvals : List LVal)
|
||||
(namedArgs : Array NamedArg) (args : Array Arg) (expectedType? : Option Expr) (explicit ellipsis overloaded : Bool)
|
||||
(acc : Array (TermElabResult Expr)) (forceTermInfo : Bool := false) :
|
||||
TermElabM (Array (TermElabResult Expr)) := do
|
||||
let overloaded := overloaded || fns.length > 1
|
||||
-- Set `errToSorry` to `false` if `fns` > 1. See comment above about the interaction between `errToSorry` and `observing`.
|
||||
withReader (fun ctx => { ctx with errToSorry := fns.length == 1 && ctx.errToSorry }) do
|
||||
fns.foldlM (init := acc) fun acc (f, fIdent, fields, projLevels) => do
|
||||
let lastIdx := fields.length - 1
|
||||
let lvals' := fields.mapIdx fun idx field =>
|
||||
let suffix? := if idx == 0 then some <| toName fields else none
|
||||
let levels := if idx == lastIdx then projLevels else []
|
||||
LVal.fieldName field field.getId.getString! levels suffix? fRef
|
||||
fns.foldlM (init := acc) fun acc (f, fIdent, fields) => do
|
||||
let lvals' := toLVals fields (first := true)
|
||||
let s ← observing do
|
||||
checkDeprecated fIdent f
|
||||
let f ← addTermInfo fIdent f expectedType? (force := forceTermInfo)
|
||||
@@ -1805,6 +1794,11 @@ where
|
||||
| field :: fields => .mkStr (go fields) field.getId.toString
|
||||
go fields.reverse
|
||||
|
||||
toLVals : List Syntax → (first : Bool) → List LVal
|
||||
| [], _ => []
|
||||
| field::fields, true => .fieldName field field.getId.getString! (toName (field::fields)) fRef :: toLVals fields false
|
||||
| field::fields, false => .fieldName field field.getId.getString! none fRef :: toLVals fields false
|
||||
|
||||
private def elabAppFnId (fIdent : Syntax) (fExplicitUnivs : List Level) (lvals : List LVal)
|
||||
(namedArgs : Array NamedArg) (args : Array Arg) (expectedType? : Option Expr) (explicit ellipsis overloaded : Bool)
|
||||
(acc : Array (TermElabResult Expr)) :
|
||||
@@ -1838,19 +1832,17 @@ To infer a namespace from the expected type, we do the following operations:
|
||||
- if the type is of the form `c x₁ ... xₙ` with `c` a constant, then try using `c` as the namespace,
|
||||
and if that doesn't work, try unfolding the expression and continuing.
|
||||
-/
|
||||
private partial def resolveDottedIdentFn (idRef : Syntax) (id : Name) (explicitUnivs : List Level) (expectedType? : Option Expr) : TermElabM (List (Expr × Syntax × List Syntax × List Level)) := do
|
||||
private partial def resolveDottedIdentFn (idRef : Syntax) (id : Name) (expectedType? : Option Expr) : TermElabM (List (Expr × Syntax × List Syntax)) := do
|
||||
unless id.isAtomic do
|
||||
throwError "Invalid dotted identifier notation: The name `{id}` must be atomic"
|
||||
tryPostponeIfNoneOrMVar expectedType?
|
||||
let some expectedType := expectedType?
|
||||
| throwNoExpectedType
|
||||
addCompletionInfo <| CompletionInfo.dotId idRef id (← getLCtx) expectedType?
|
||||
-- We will check deprecations in `elabAppFnResolutions`.
|
||||
withoutCheckDeprecated do
|
||||
withForallBody expectedType fun resultType => do
|
||||
go resultType expectedType #[]
|
||||
where
|
||||
throwNoExpectedType {α} : TermElabM α := do
|
||||
throwNoExpectedType := do
|
||||
let hint ← match reverseFieldLookup (← getEnv) (id.getString!) with
|
||||
| #[] => pure MessageData.nil
|
||||
| suggestions =>
|
||||
@@ -1869,7 +1861,7 @@ where
|
||||
withForallBody body k
|
||||
else
|
||||
k type
|
||||
go (resultType : Expr) (expectedType : Expr) (previousExceptions : Array Exception) : TermElabM (List (Expr × Syntax × List Syntax × List Level)) := do
|
||||
go (resultType : Expr) (expectedType : Expr) (previousExceptions : Array Exception) : TermElabM (List (Expr × Syntax × List Syntax)) := do
|
||||
let resultType ← instantiateMVars resultType
|
||||
let resultTypeFn := resultType.getAppFn
|
||||
try
|
||||
@@ -1886,11 +1878,9 @@ where
|
||||
|>.filter (fun (_, fieldList) => fieldList.isEmpty)
|
||||
|>.map Prod.fst
|
||||
if !candidates.isEmpty then
|
||||
candidates.mapM fun resolvedName => return (← mkConst resolvedName explicitUnivs, ← getRef, [], [])
|
||||
candidates.mapM fun resolvedName => return (← mkConst resolvedName, ← getRef, [])
|
||||
else if let some (fvar, []) ← resolveLocalName fullName then
|
||||
unless explicitUnivs.isEmpty do
|
||||
throwInvalidExplicitUniversesForLocal fvar
|
||||
return [(fvar, ← getRef, [], [])]
|
||||
return [(fvar, ← getRef, [])]
|
||||
else
|
||||
throwUnknownIdentifierAt (← getRef) (declHint := fullName) <| m!"Unknown constant `{.ofConstName fullName}`"
|
||||
++ .note m!"Inferred this name from the expected resulting type of `.{id}`:{indentExpr expectedType}"
|
||||
@@ -1920,37 +1910,22 @@ private partial def elabAppFn (f : Syntax) (lvals : List LVal) (namedArgs : Arra
|
||||
withReader (fun ctx => { ctx with errToSorry := false }) do
|
||||
f.getArgs.foldlM (init := acc) fun acc f => elabAppFn f lvals namedArgs args expectedType? explicit ellipsis true acc
|
||||
else
|
||||
let elabFieldName (e field : Syntax) (explicitUnivs : List Level) := do
|
||||
let comps := field.identComponents
|
||||
let lastIdx := comps.length - 1
|
||||
let newLVals := comps.mapIdx fun idx comp =>
|
||||
let levels := if idx = lastIdx then explicitUnivs else []
|
||||
let suffix? := none -- We use `none` since the field can't be part of a composite name
|
||||
LVal.fieldName comp comp.getId.getString! levels suffix? f
|
||||
let elabFieldName (e field : Syntax) (explicit : Bool) := do
|
||||
let newLVals := field.identComponents.map fun comp =>
|
||||
-- We use `none` in `suffix?` since `field` can't be part of a composite name
|
||||
LVal.fieldName comp comp.getId.getString! none f
|
||||
elabAppFn e (newLVals ++ lvals) namedArgs args expectedType? explicit ellipsis overloaded acc
|
||||
let elabFieldIdx (e idxStx : Syntax) (explicitUnivs : List Level) := do
|
||||
let elabFieldIdx (e idxStx : Syntax) (explicit : Bool) := do
|
||||
let some idx := idxStx.isFieldIdx?
|
||||
| throwError "Internal error: Unexpected field index syntax `{idxStx}`"
|
||||
elabAppFn e (LVal.fieldIdx idxStx idx explicitUnivs :: lvals) namedArgs args expectedType? explicit ellipsis overloaded acc
|
||||
let elabDottedIdent (id : Syntax) (explicitUnivs : List Level) : TermElabM (Array (TermElabResult Expr)) := do
|
||||
let res ← withRef f <| resolveDottedIdentFn id id.getId.eraseMacroScopes explicitUnivs expectedType?
|
||||
-- Use (forceTermInfo := true) because we want to record the result of .ident resolution even in patterns
|
||||
elabAppFnResolutions f res lvals namedArgs args expectedType? explicit ellipsis overloaded acc (forceTermInfo := true)
|
||||
elabAppFn e (LVal.fieldIdx idxStx idx :: lvals) namedArgs args expectedType? explicit ellipsis overloaded acc
|
||||
match f with
|
||||
| `($(e).$idx:fieldIdx)
|
||||
| `($e |>.$idx:fieldIdx) =>
|
||||
elabFieldIdx e idx []
|
||||
| `($(e).$idx:fieldIdx.{$us,*})
|
||||
| `($e |>.$idx:fieldIdx.{$us,*}) =>
|
||||
let us ← elabExplicitUnivs us
|
||||
elabFieldIdx e idx us
|
||||
| `($(e).$field:ident)
|
||||
| `($e |>.$field:ident) =>
|
||||
elabFieldName e field []
|
||||
| `($(e).$field:ident.{$us,*})
|
||||
| `($e |>.$field:ident.{$us,*}) =>
|
||||
let us ← elabExplicitUnivs us
|
||||
elabFieldName e field us
|
||||
| `($(e).$idx:fieldIdx) => elabFieldIdx e idx explicit
|
||||
| `($e |>.$idx:fieldIdx) => elabFieldIdx e idx explicit
|
||||
| `($(e).$field:ident) => elabFieldName e field explicit
|
||||
| `($e |>.$field:ident) => elabFieldName e field explicit
|
||||
| `(@$(e).$idx:fieldIdx) => elabFieldIdx e idx (explicit := true)
|
||||
| `(@$(e).$field:ident) => elabFieldName e field (explicit := true)
|
||||
| `($_:ident@$_:term) =>
|
||||
throwError m!"Expected a function, but found the named pattern{indentD f}"
|
||||
++ .note m!"Named patterns `<identifier>@<term>` can only be used when pattern-matching"
|
||||
@@ -1959,20 +1934,16 @@ private partial def elabAppFn (f : Syntax) (lvals : List LVal) (namedArgs : Arra
|
||||
| `($id:ident.{$us,*}) => do
|
||||
let us ← elabExplicitUnivs us
|
||||
elabAppFnId id us lvals namedArgs args expectedType? explicit ellipsis overloaded acc
|
||||
| `(.$id:ident) => elabDottedIdent id []
|
||||
| `(.$id:ident.{$us,*}) =>
|
||||
let us ← elabExplicitUnivs us
|
||||
elabDottedIdent id us
|
||||
| `(@$_:ident)
|
||||
| `(@$_:ident.{$_us,*})
|
||||
| `(@$(_).$_:fieldIdx)
|
||||
| `(@$(_).$_:ident)
|
||||
| `(@$(_).$_:ident.{$_us,*})
|
||||
| `(@.$_:ident)
|
||||
| `(@.$_:ident.{$_us,*}) =>
|
||||
| `(@$id:ident) =>
|
||||
elabAppFn id lvals namedArgs args expectedType? (explicit := true) ellipsis overloaded acc
|
||||
| `(@$_:ident.{$_us,*}) =>
|
||||
elabAppFn (f.getArg 1) lvals namedArgs args expectedType? (explicit := true) ellipsis overloaded acc
|
||||
| `(@$_) => throwUnsupportedSyntax -- invalid occurrence of `@`
|
||||
| `(_) => throwError "A placeholder `_` cannot be used where a function is expected"
|
||||
| `(.$id:ident) =>
|
||||
let res ← withRef f <| resolveDottedIdentFn id id.getId.eraseMacroScopes expectedType?
|
||||
-- Use (forceTermInfo := true) because we want to record the result of .ident resolution even in patterns
|
||||
elabAppFnResolutions f res lvals namedArgs args expectedType? explicit ellipsis overloaded acc (forceTermInfo := true)
|
||||
| _ => do
|
||||
let catchPostpone := !overloaded
|
||||
/- If we are processing a choice node, then we should use `catchPostpone == false` when elaborating terms.
|
||||
@@ -2104,10 +2075,10 @@ private def elabAtom : TermElab := fun stx expectedType? => do
|
||||
@[builtin_term_elab dotIdent] def elabDotIdent : TermElab := elabAtom
|
||||
@[builtin_term_elab explicitUniv] def elabExplicitUniv : TermElab := elabAtom
|
||||
@[builtin_term_elab pipeProj] def elabPipeProj : TermElab
|
||||
| `($e |>.%$tk$f$[.{$us?,*}]? $args*), expectedType? =>
|
||||
| `($e |>.%$tk$f $args*), expectedType? =>
|
||||
universeConstraintsCheckpoint do
|
||||
let (namedArgs, args, ellipsis) ← expandArgs args
|
||||
let mut stx ← `($e |>.%$tk$f$[.{$us?,*}]?)
|
||||
let mut stx ← `($e |>.%$tk$f)
|
||||
if let (some startPos, some stopPos) := (e.raw.getPos?, f.raw.getTailPos?) then
|
||||
stx := ⟨stx.raw.setInfo <| .synthetic (canonical := true) startPos stopPos⟩
|
||||
elabAppAux stx namedArgs args (ellipsis := ellipsis) expectedType?
|
||||
@@ -2115,16 +2086,13 @@ private def elabAtom : TermElab := fun stx expectedType? => do
|
||||
|
||||
@[builtin_term_elab explicit] def elabExplicit : TermElab := fun stx expectedType? =>
|
||||
match stx with
|
||||
| `(@$_:ident) => elabAtom stx expectedType? -- Recall that `elabApp` also has support for `@`
|
||||
| `(@$_:ident.{$_us,*}) => elabAtom stx expectedType?
|
||||
| `(@$(_).$_:fieldIdx) => elabAtom stx expectedType?
|
||||
| `(@$(_).$_:ident) => elabAtom stx expectedType?
|
||||
| `(@$(_).$_:ident.{$_us,*}) => elabAtom stx expectedType?
|
||||
| `(@.$_:ident) => elabAtom stx expectedType?
|
||||
| `(@.$_:ident.{$_us,*}) => elabAtom stx expectedType?
|
||||
| `(@($t)) => elabTerm t expectedType? (implicitLambda := false) -- `@` is being used just to disable implicit lambdas
|
||||
| `(@$t) => elabTerm t expectedType? (implicitLambda := false) -- `@` is being used just to disable implicit lambdas
|
||||
| _ => throwUnsupportedSyntax
|
||||
| `(@$_:ident) => elabAtom stx expectedType? -- Recall that `elabApp` also has support for `@`
|
||||
| `(@$_:ident.{$_us,*}) => elabAtom stx expectedType?
|
||||
| `(@$(_).$_:fieldIdx) => elabAtom stx expectedType?
|
||||
| `(@$(_).$_:ident) => elabAtom stx expectedType?
|
||||
| `(@($t)) => elabTerm t expectedType? (implicitLambda := false) -- `@` is being used just to disable implicit lambdas
|
||||
| `(@$t) => elabTerm t expectedType? (implicitLambda := false) -- `@` is being used just to disable implicit lambdas
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
@[builtin_term_elab choice] def elabChoice : TermElab := elabAtom
|
||||
@[builtin_term_elab proj] def elabProj : TermElab := elabAtom
|
||||
|
||||
@@ -9,12 +9,10 @@ prelude
|
||||
public import Lean.Meta.Reduce
|
||||
public import Lean.Elab.Eval
|
||||
public import Lean.Elab.Command
|
||||
import Lean.Elab.DeprecatedSyntax
|
||||
public import Lean.Elab.Open
|
||||
import Init.Data.Nat.Order
|
||||
import Init.Data.Order.Lemmas
|
||||
import Init.System.Platform
|
||||
import Lean.DeprecatedModule
|
||||
|
||||
public section
|
||||
|
||||
@@ -510,20 +508,10 @@ def failIfSucceeds (x : CommandElabM Unit) : CommandElabM Unit := do
|
||||
pure ()
|
||||
|
||||
@[builtin_command_elab «set_option»] def elabSetOption : CommandElab := fun stx => do
|
||||
let (options, decl) ← Elab.elabSetOption stx[1] stx[3]
|
||||
withRef stx[1] <| Elab.checkDeprecatedOption (stx[1].getId.eraseMacroScopes) decl
|
||||
let options ← Elab.elabSetOption stx[1] stx[3]
|
||||
modify fun s => { s with maxRecDepth := maxRecDepth.get options }
|
||||
modifyScope fun scope => { scope with opts := options }
|
||||
|
||||
@[builtin_command_elab «unlock_limits»] def elabUnlockLimits : CommandElab := fun _ => do
|
||||
let opts ← getOptions
|
||||
let opts := maxHeartbeats.set opts 0
|
||||
let opts := maxRecDepth.set opts 0
|
||||
let opts := synthInstance.maxHeartbeats.set opts 0
|
||||
modifyScope ({ · with opts })
|
||||
-- update cached value as well
|
||||
modify ({ · with maxRecDepth := 0 })
|
||||
|
||||
open Lean.Parser.Command.InternalSyntax in
|
||||
@[builtin_macro Lean.Parser.Command.«in»] def expandInCmd : Macro
|
||||
| `($cmd₁ in%$tk $cmd₂) =>
|
||||
@@ -718,54 +706,4 @@ where
|
||||
let env ← getEnv
|
||||
IO.eprintln (← env.dbgFormatAsyncState)
|
||||
|
||||
/-- Elaborate `deprecated_module`, marking the current module as deprecated. -/
|
||||
@[builtin_command_elab Parser.Command.deprecated_module]
|
||||
def elabDeprecatedModule : CommandElab
|
||||
| `(Parser.Command.deprecated_module| deprecated_module $[$msg?]? $[(since := $since?)]?) => do
|
||||
let message? := msg?.map TSyntax.getString
|
||||
let since? := since?.map TSyntax.getString
|
||||
if (deprecatedModuleExt.getState (← getEnv)).isSome then
|
||||
logWarning "module is already marked as deprecated"
|
||||
if since?.isNone then
|
||||
logWarning "`deprecated_module` should specify the date or library version \
|
||||
at which the deprecation was introduced, using `(since := \"...\")`"
|
||||
modifyEnv fun env => env.setDeprecatedModule (some { message?, since? })
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
/-- Elaborate `#show_deprecated_modules`, displaying all deprecated modules. -/
|
||||
@[builtin_command_elab Parser.Command.showDeprecatedModules]
|
||||
def elabShowDeprecatedModules : CommandElab := fun _ => do
|
||||
let env ← getEnv
|
||||
let mut parts : Array String := #["Deprecated modules\n"]
|
||||
for h : idx in [:env.header.moduleNames.size] do
|
||||
if let some entry := env.getDeprecatedModuleByIdx? idx then
|
||||
let modName := env.header.moduleNames[idx]
|
||||
let msg := match entry.message? with
|
||||
| some str => s!"message '{str}'"
|
||||
| none => "no message"
|
||||
let replacements := env.header.moduleData[idx]!.imports.filter fun imp =>
|
||||
imp.module != `Init
|
||||
parts := parts.push s!"'{modName}' deprecates to\n{replacements.map (·.module)}\nwith {msg}\n"
|
||||
-- Also show the current module's deprecation if set.
|
||||
if let some entry := deprecatedModuleExt.getState env then
|
||||
let modName := env.mainModule
|
||||
let msg := match entry.message? with
|
||||
| some str => s!"message '{str}'"
|
||||
| none => "no message"
|
||||
let replacements := env.imports.filter fun imp =>
|
||||
imp.module != `Init
|
||||
parts := parts.push s!"'{modName}' deprecates to\n{replacements.map (·.module)}\nwith {msg}\n"
|
||||
logInfo (String.intercalate "\n" parts.toList)
|
||||
|
||||
@[builtin_command_elab Parser.Command.deprecatedSyntax] def elabDeprecatedSyntax : CommandElab := fun stx => do
|
||||
let id := stx[1]
|
||||
let kind ← liftCoreM <| checkSyntaxNodeKindAtNamespaces id.getId (← getCurrNamespace)
|
||||
let text? := if stx[2].isNone then none else stx[2][0].isStrLit?
|
||||
let since? := if stx[3].isNone then none else stx[3][3].isStrLit?
|
||||
if since?.isNone then
|
||||
logWarning "`deprecated_syntax` should specify the date or library version at which the \
|
||||
deprecation was introduced, using `(since := \"...\")`"
|
||||
modifyEnv fun env =>
|
||||
deprecatedSyntaxExt.addEntry env { kind, text?, since? }
|
||||
|
||||
end Lean.Elab.Command
|
||||
|
||||
@@ -111,14 +111,8 @@ open Lean.Meta
|
||||
for x in loopMutVars do
|
||||
let defn ← getLocalDeclFromUserName x.getId
|
||||
Term.addTermInfo' x defn.toExpr
|
||||
-- ForIn forces the mut tuple into the universe mi.u: that of the do block result type.
|
||||
-- If we don't do this, then we are stuck on solving constraints such as
|
||||
-- `max ?u.46 ?u.47 =?= max (max ?u.22 ?u.46) ?u.47`
|
||||
-- It's important we do this as a separate isLevelDefEq check on the decremented level because
|
||||
-- otherwise (`ensureHasType (mkSort mi.u.succ)`) we are stuck on constraints like
|
||||
-- `max (?u+1) (?v+1) =?= ?u+1`
|
||||
let u ← getDecLevel defn.type
|
||||
discard <| isLevelDefEq u mi.u
|
||||
-- ForIn forces all mut vars into the same universe: that of the do block result type.
|
||||
discard <| Term.ensureHasType (mkSort (mi.u.succ)) defn.type
|
||||
defs := defs.push defn.toExpr
|
||||
if info.returnsEarly && loopMutVars.isEmpty then
|
||||
defs := defs.push (mkConst ``Unit.unit)
|
||||
|
||||
@@ -81,15 +81,8 @@ private def pushTypeIntoReassignment (letOrReassign : LetOrReassign) (decl : TSy
|
||||
else
|
||||
pure decl
|
||||
|
||||
private def checkLetConfigInDo (config : Term.LetConfig) : DoElabM Unit := do
|
||||
if config.postponeValue then
|
||||
throwError "`+postponeValue` is not supported in `do` blocks"
|
||||
if config.generalize then
|
||||
throwError "`+generalize` is not supported in `do` blocks"
|
||||
|
||||
partial def elabDoLetOrReassign (config : Term.LetConfig) (letOrReassign : LetOrReassign) (decl : TSyntax ``letDecl)
|
||||
partial def elabDoLetOrReassign (letOrReassign : LetOrReassign) (decl : TSyntax ``letDecl)
|
||||
(dec : DoElemCont) : DoElabM Expr := do
|
||||
checkLetConfigInDo config
|
||||
let vars ← getLetDeclVars decl
|
||||
letOrReassign.checkMutVars vars
|
||||
-- Some decl preprocessing on the patterns and expected types:
|
||||
@@ -98,7 +91,7 @@ partial def elabDoLetOrReassign (config : Term.LetConfig) (letOrReassign : LetOr
|
||||
match decl with
|
||||
| `(letDecl| $decl:letEqnsDecl) =>
|
||||
let declNew ← `(letDecl| $(⟨← liftMacroM <| Term.expandLetEqnsDecl decl⟩):letIdDecl)
|
||||
return ← Term.withMacroExpansion decl declNew <| elabDoLetOrReassign config letOrReassign declNew dec
|
||||
return ← Term.withMacroExpansion decl declNew <| elabDoLetOrReassign letOrReassign declNew dec
|
||||
| `(letDecl| $pattern:term $[: $xType?]? := $rhs) =>
|
||||
let rhs ← match xType? with | some xType => `(($rhs : $xType)) | none => pure rhs
|
||||
let contElab : DoElabM Expr := elabWithReassignments letOrReassign vars dec.continueWithUnit
|
||||
@@ -106,21 +99,15 @@ partial def elabDoLetOrReassign (config : Term.LetConfig) (letOrReassign : LetOr
|
||||
-- The infamous MVar postponement trick below popularized by `if` is necessary in Lake.CLI.Main.
|
||||
-- We need it because we specify a constant motive, otherwise the `match` elaborator would have postponed.
|
||||
let mvar ← Lean.withRef rhs `(?m)
|
||||
let term ← if let some h := config.eq? then
|
||||
`(let_mvar% ?m := $rhs;
|
||||
wait_if_type_mvar% ?m;
|
||||
match $h:ident : $mvar:term with
|
||||
| $pattern:term => $body)
|
||||
else
|
||||
`(let_mvar% ?m := $rhs;
|
||||
wait_if_type_mvar% ?m;
|
||||
match (motive := ∀_, $(← Term.exprToSyntax mγ)) $mvar:term with
|
||||
| $pattern:term => $body)
|
||||
let term ← `(let_mvar% ?m := $rhs;
|
||||
wait_if_type_mvar% ?m;
|
||||
match (motive := ∀_, $(← Term.exprToSyntax mγ)) $mvar:term with
|
||||
| $pattern:term => $body)
|
||||
Term.withMacroExpansion (← getRef) term do Term.elabTermEnsuringType term (some mγ)
|
||||
| `(letDecl| $decl:letIdDecl) =>
|
||||
let { id, binders, type, value } := Term.mkLetIdDeclView decl
|
||||
let id ← if id.isIdent then pure id else Term.mkFreshIdent id (canonical := true)
|
||||
let nondep := config.nondep || letOrReassign matches .have
|
||||
let nondep := letOrReassign matches .have
|
||||
-- Only non-`mut` lets will be elaborated as `let`s; `let mut` and reassigns behave as `have`s.
|
||||
-- See `elabLetDeclAux` for rationale.
|
||||
let (type, val) ← Term.elabBindersEx binders fun xs => do
|
||||
@@ -141,25 +128,8 @@ partial def elabDoLetOrReassign (config : Term.LetConfig) (letOrReassign : LetOr
|
||||
withLetDecl id.getId (kind := kind) type val (nondep := nondep) fun x => do
|
||||
Term.addLocalVarInfo id x
|
||||
elabWithReassignments letOrReassign vars do
|
||||
match config.eq? with
|
||||
| none =>
|
||||
let body ← dec.continueWithUnit
|
||||
if config.zeta then
|
||||
pure <| (← body.abstractM #[x]).instantiate1 val
|
||||
else
|
||||
mkLetFVars #[x] body (usedLetOnly := config.usedOnly) (generalizeNondepLet := false)
|
||||
| some h =>
|
||||
let hTy ← mkEq x val
|
||||
withLetDecl h.getId hTy (← mkEqRefl x) (nondep := true) fun h' => do
|
||||
Term.addLocalVarInfo h h'
|
||||
let body ← dec.continueWithUnit
|
||||
if config.zeta then
|
||||
pure <| (← body.abstractM #[x, h']).instantiateRev #[val, ← mkEqRefl val]
|
||||
else if nondep then
|
||||
let f ← mkLambdaFVars #[x, h'] body
|
||||
return mkApp2 f val (← mkEqRefl val)
|
||||
else
|
||||
mkLetFVars #[x, h'] body (usedLetOnly := config.usedOnly) (generalizeNondepLet := false)
|
||||
let body ← dec.continueWithUnit
|
||||
mkLetFVars #[x] body (usedLetOnly := false) (generalizeNondepLet := false)
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
def elabDoArrow (letOrReassign : LetOrReassign) (stx : TSyntax [``doIdDecl, ``doPatDecl]) (dec : DoElemCont) : DoElabM Expr := do
|
||||
@@ -198,21 +168,13 @@ def elabDoArrow (letOrReassign : LetOrReassign) (stx : TSyntax [``doIdDecl, ``do
|
||||
elabDoElem (← `(doElem| $pattern:term := $x)) dec
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
private def getLetConfigAndCheckMut (letConfigStx : TSyntax ``Parser.Term.letConfig)
|
||||
(mutTk? : Option Syntax) (initConfig : Term.LetConfig := {}) : DoElabM Term.LetConfig := do
|
||||
if mutTk?.isSome && !letConfigStx.raw[0].getArgs.isEmpty then
|
||||
throwErrorAt letConfigStx "configuration options are not allowed with `let mut`"
|
||||
Term.mkLetConfig letConfigStx initConfig
|
||||
|
||||
@[builtin_doElem_elab Lean.Parser.Term.doLet] def elabDoLet : DoElab := fun stx dec => do
|
||||
let `(doLet| let $[mut%$mutTk?]? $config:letConfig $decl:letDecl) := stx | throwUnsupportedSyntax
|
||||
let config ← getLetConfigAndCheckMut config mutTk?
|
||||
elabDoLetOrReassign config (.let mutTk?) decl dec
|
||||
let `(doLet| let $[mut%$mutTk?]? $decl:letDecl) := stx | throwUnsupportedSyntax
|
||||
elabDoLetOrReassign (.let mutTk?) decl dec
|
||||
|
||||
@[builtin_doElem_elab Lean.Parser.Term.doHave] def elabDoHave : DoElab := fun stx dec => do
|
||||
let `(doHave| have $config:letConfig $decl:letDecl) := stx | throwUnsupportedSyntax
|
||||
let config ← Term.mkLetConfig config { nondep := true }
|
||||
elabDoLetOrReassign config .have decl dec
|
||||
let `(doHave| have $decl:letDecl) := stx | throwUnsupportedSyntax
|
||||
elabDoLetOrReassign .have decl dec
|
||||
|
||||
@[builtin_doElem_elab Lean.Parser.Term.doLetRec] def elabDoLetRec : DoElab := fun stx dec => do
|
||||
let `(doLetRec| let rec $decls:letRecDecls) := stx | throwUnsupportedSyntax
|
||||
@@ -230,17 +192,14 @@ private def getLetConfigAndCheckMut (letConfigStx : TSyntax ``Parser.Term.letCon
|
||||
| `(doReassign| $x:ident $[: $xType?]? := $rhs) =>
|
||||
let decl : TSyntax ``letIdDecl ← `(letIdDecl| $x:ident $[: $xType?]? := $rhs)
|
||||
let decl : TSyntax ``letDecl := ⟨mkNode ``letDecl #[decl]⟩
|
||||
elabDoLetOrReassign {} .reassign decl dec
|
||||
elabDoLetOrReassign .reassign decl dec
|
||||
| `(doReassign| $decl:letPatDecl) =>
|
||||
let decl : TSyntax ``letDecl := ⟨mkNode ``letDecl #[decl]⟩
|
||||
elabDoLetOrReassign {} .reassign decl dec
|
||||
elabDoLetOrReassign .reassign decl dec
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
@[builtin_doElem_elab Lean.Parser.Term.doLetElse] def elabDoLetElse : DoElab := fun stx dec => do
|
||||
let `(doLetElse| let $[mut%$mutTk?]? $cfg:letConfig $pattern := $rhs | $otherwise $(body?)?) := stx
|
||||
| throwUnsupportedSyntax
|
||||
let config ← getLetConfigAndCheckMut cfg mutTk?
|
||||
checkLetConfigInDo config
|
||||
let `(doLetElse| let $[mut%$mutTk?]? $pattern := $rhs | $otherwise $(body?)?) := stx | throwUnsupportedSyntax
|
||||
let letOrReassign := LetOrReassign.let mutTk?
|
||||
let vars ← getPatternVarsEx pattern
|
||||
letOrReassign.checkMutVars vars
|
||||
@@ -249,17 +208,10 @@ private def getLetConfigAndCheckMut (letConfigStx : TSyntax ``Parser.Term.letCon
|
||||
if mutTk?.isSome then
|
||||
for var in vars do
|
||||
body ← `(doSeqIndent| let mut $var := $var; do $body:doSeqIndent)
|
||||
if let some h := config.eq? then
|
||||
elabDoElem (← `(doElem| match $h:ident : $rhs:term with | $pattern => $body:doSeqIndent | _ => $otherwise:doSeqIndent)) dec
|
||||
else
|
||||
elabDoElem (← `(doElem| match $rhs:term with | $pattern => $body:doSeqIndent | _ => $otherwise:doSeqIndent)) dec
|
||||
elabDoElem (← `(doElem| match $rhs:term with | $pattern => $body:doSeqIndent | _ => $otherwise:doSeqIndent)) dec
|
||||
|
||||
@[builtin_doElem_elab Lean.Parser.Term.doLetArrow] def elabDoLetArrow : DoElab := fun stx dec => do
|
||||
let `(doLetArrow| let $[mut%$mutTk?]? $cfg:letConfig $decl) := stx | throwUnsupportedSyntax
|
||||
let config ← getLetConfigAndCheckMut cfg mutTk?
|
||||
checkLetConfigInDo config
|
||||
if config.nondep || config.usedOnly || config.zeta || config.eq?.isSome then
|
||||
throwErrorAt cfg "configuration options are not supported with `←`"
|
||||
let `(doLetArrow| let $[mut%$mutTk?]? $decl) := stx | throwUnsupportedSyntax
|
||||
elabDoArrow (.let mutTk?) decl dec
|
||||
|
||||
@[builtin_doElem_elab Lean.Parser.Term.doReassignArrow] def elabDoReassignArrow : DoElab := fun stx dec => do
|
||||
|
||||
@@ -371,8 +371,7 @@ private def mkSilentAnnotationIfHole (e : Expr) : TermElabM Expr := do
|
||||
popScope
|
||||
|
||||
@[builtin_term_elab «set_option»] def elabSetOption : TermElab := fun stx expectedType? => do
|
||||
let (options, decl) ← Elab.elabSetOption stx[1] stx[3]
|
||||
withRef stx[1] <| Elab.checkDeprecatedOption (stx[1].getId.eraseMacroScopes) decl
|
||||
let options ← Elab.elabSetOption stx[1] stx[3]
|
||||
withOptions (fun _ => options) do
|
||||
try
|
||||
elabTerm stx[5] expectedType?
|
||||
|
||||
@@ -10,7 +10,6 @@ public import Lean.Meta.Diagnostics
|
||||
public import Lean.Elab.Binders
|
||||
public import Lean.Elab.Command.Scope
|
||||
public import Lean.Elab.SetOption
|
||||
import Lean.Elab.DeprecatedSyntax
|
||||
public meta import Lean.Parser.Command
|
||||
|
||||
public section
|
||||
@@ -469,7 +468,6 @@ where go := do
|
||||
else withTraceNode `Elab.command (fun _ => return stx) (tag :=
|
||||
-- special case: show actual declaration kind for `declaration` commands
|
||||
(if stx.isOfKind ``Parser.Command.declaration then stx[1] else stx).getKind.toString) do
|
||||
checkDeprecatedSyntax stx (← read).macroStack
|
||||
let s ← get
|
||||
match (← liftMacroM <| expandMacroImpl? s.env stx) with
|
||||
| some (decl, stxNew?) =>
|
||||
@@ -875,7 +873,7 @@ first evaluates any local `set_option ... in ...` clauses and then invokes `cmd`
|
||||
partial def withSetOptionIn (cmd : CommandElab) : CommandElab := fun stx => do
|
||||
if stx.getKind == ``Lean.Parser.Command.in &&
|
||||
stx[0].getKind == ``Lean.Parser.Command.set_option then
|
||||
let (opts, _) ← Elab.elabSetOption stx[0][1] stx[0][3]
|
||||
let opts ← Elab.elabSetOption stx[0][1] stx[0][3]
|
||||
Command.withScope (fun scope => { scope with opts }) do
|
||||
withSetOptionIn cmd stx[2]
|
||||
else
|
||||
|
||||
@@ -65,28 +65,9 @@ def Visibility.isPublic : Visibility → Bool
|
||||
| .public => true
|
||||
| _ => false
|
||||
|
||||
/--
|
||||
Returns whether the given visibility modifier should be interpreted as `public` in the current
|
||||
environment.
|
||||
|
||||
NOTE: `Environment.isExporting` defaults to `false` when command elaborators are invoked for
|
||||
backward compatibility. It needs to be initialized apropriately first before calling this function
|
||||
as e.g. done in `elabDeclaration`.
|
||||
-/
|
||||
def Visibility.isInferredPublic (env : Environment) (v : Visibility) : Bool :=
|
||||
if env.isExporting || !env.header.isModule then !v.isPrivate else v.isPublic
|
||||
|
||||
/-- Converts optional visibility syntax to a `Visibility` value. -/
|
||||
def elabVisibility [Monad m] [MonadError m] (vis? : Option (TSyntax ``Parser.Command.visibility)) :
|
||||
m Visibility :=
|
||||
match vis? with
|
||||
| none => pure .regular
|
||||
| some v =>
|
||||
match v with
|
||||
| `(Parser.Command.visibility| private) => pure .private
|
||||
| `(Parser.Command.visibility| public) => pure .public
|
||||
| _ => throwErrorAt v "unexpected visibility modifier"
|
||||
|
||||
/-- Whether a declaration is default, partial or nonrec. -/
|
||||
inductive RecKind where
|
||||
| «partial» | «nonrec» | default
|
||||
@@ -202,7 +183,13 @@ def elabModifiers (stx : TSyntax ``Parser.Command.declModifiers) : m Modifiers :
|
||||
else
|
||||
RecKind.nonrec
|
||||
let docString? := docCommentStx.getOptional?.map (TSyntax.mk ·, doc.verso.get (← getOptions))
|
||||
let visibility ← elabVisibility (visibilityStx.getOptional?.map (⟨·⟩))
|
||||
let visibility ← match visibilityStx.getOptional? with
|
||||
| none => pure .regular
|
||||
| some v =>
|
||||
match v with
|
||||
| `(Parser.Command.visibility| private) => pure .private
|
||||
| `(Parser.Command.visibility| public) => pure .public
|
||||
| _ => throwErrorAt v "unexpected visibility modifier"
|
||||
let isProtected := !protectedStx.isNone
|
||||
let attrs ← match attrsStx.getOptional? with
|
||||
| none => pure #[]
|
||||
|
||||
@@ -340,29 +340,31 @@ def elabMutual : CommandElab := fun stx => do
|
||||
|
||||
@[builtin_command_elab Lean.Parser.Command.«initialize»] def elabInitialize : CommandElab
|
||||
| stx@`($declModifiers:declModifiers $kw:initializeKeyword $[$id? : $type? ←]? $doSeq) => do
|
||||
withExporting (isExporting := (← getScope).isPublic) do
|
||||
let attrId := mkIdentFrom stx <| if kw.raw[0].isToken "initialize" then `init else `builtin_init
|
||||
if let (some id, some type) := (id?, type?) then
|
||||
let `(Parser.Command.declModifiersT| $[$doc?:docComment]? $[@[$attrs?,*]]? $(vis?)? $[meta%$meta?]? $[unsafe%$unsafe?]?) := stx[0]
|
||||
| throwErrorAt declModifiers "invalid initialization command, unexpected modifiers"
|
||||
let defStx ← `($[$doc?:docComment]? @[$attrId:ident initFn, $(attrs?.getD ∅),*] $(vis?)? $[meta%$meta?]? opaque $id : $type)
|
||||
let mut fullId := (← getCurrNamespace) ++ id.getId
|
||||
let visibility ← elabVisibility vis?
|
||||
if !visibility.isInferredPublic (← getEnv) then
|
||||
if vis?.any (·.raw.isOfKind ``Parser.Command.private) then
|
||||
fullId := mkPrivateName (← getEnv) fullId
|
||||
-- We need to add `id`'s ranges *before* elaborating `initFn` (and then `id` itself) as
|
||||
-- otherwise the info context created by `with_decl_name` will be incomplete and break the
|
||||
-- call hierarchy
|
||||
addDeclarationRangesForBuiltin fullId ⟨defStx.raw[0]⟩ defStx.raw[1]
|
||||
let vis := Parser.Command.visibility.ofBool (!isPrivateName fullId)
|
||||
elabCommand (← `(
|
||||
@[no_expose] private $[meta%$meta?]? $[unsafe%$unsafe?]? def initFn : IO $type := with_decl_name% $(mkIdent fullId) do $doSeq
|
||||
$vis:visibility $[meta%$meta?]? $[unsafe%$unsafe?]? def initFn : IO $type := with_decl_name% $(mkIdent fullId) do $doSeq
|
||||
$defStx:command))
|
||||
else
|
||||
let `(Parser.Command.declModifiersT| $[$doc?:docComment]? $[@[$attrs?,*]]? $(_)? $[meta%$meta?]? $[unsafe%$unsafe?]?) := declModifiers
|
||||
| throwErrorAt declModifiers "invalid initialization command, unexpected modifiers"
|
||||
let attrs := (attrs?.map (·.getElems)).getD #[]
|
||||
let attrs := attrs.push (← `(Lean.Parser.Term.attrInstance| $attrId:ident))
|
||||
elabCommand (← `($[$doc?:docComment]? @[no_expose, $[$attrs],*] private $[meta%$meta?]? $[unsafe%$unsafe?]? def initFn : IO Unit := do $doSeq))
|
||||
-- `[builtin_init]` can be private as it is used for local codegen only but `[init]` must be
|
||||
-- available for the interpreter.
|
||||
let vis := Parser.Command.visibility.ofBool (attrId.getId == `init)
|
||||
elabCommand (← `($[$doc?:docComment]? @[$[$attrs],*] $vis:visibility $[meta%$meta?]? $[unsafe%$unsafe?]? def initFn : IO Unit := do $doSeq))
|
||||
| _ => throwUnsupportedSyntax
|
||||
|
||||
builtin_initialize
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
/-
|
||||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
Authors: Wojciech Różowski
|
||||
-/
|
||||
module
|
||||
|
||||
prelude
|
||||
public import Lean.MonadEnv
|
||||
public import Lean.Linter.Basic
|
||||
public import Lean.Elab.Util
|
||||
|
||||
public section
|
||||
|
||||
namespace Lean.Linter
|
||||
|
||||
register_builtin_option linter.deprecated.syntax : Bool := {
|
||||
defValue := true
|
||||
descr := "if true, generate warnings when deprecated syntax is used"
|
||||
}
|
||||
|
||||
end Lean.Linter
|
||||
|
||||
namespace Lean.Elab
|
||||
|
||||
/-- Entry recording that a syntax kind has been deprecated. -/
|
||||
structure SyntaxDeprecationEntry where
|
||||
/-- The syntax node kind that is deprecated. -/
|
||||
kind : SyntaxNodeKind
|
||||
/-- Optional deprecation message. -/
|
||||
text? : Option String := none
|
||||
/-- Optional version or date at which the syntax was deprecated. -/
|
||||
since? : Option String := none
|
||||
|
||||
builtin_initialize deprecatedSyntaxExt :
|
||||
SimplePersistentEnvExtension SyntaxDeprecationEntry (NameMap SyntaxDeprecationEntry) ←
|
||||
registerSimplePersistentEnvExtension {
|
||||
addImportedFn := mkStateFromImportedEntries (fun m e => m.insert e.kind e) {}
|
||||
addEntryFn := fun m e => m.insert e.kind e
|
||||
}
|
||||
|
||||
/--
|
||||
Check whether `stx` is a deprecated syntax kind, and if so, emit a warning.
|
||||
|
||||
If `macroStack` is non-empty, the warning is attributed to the macro call site rather than the
|
||||
syntax itself.
|
||||
-/
|
||||
def checkDeprecatedSyntax [Monad m] [MonadEnv m] [MonadLog m] [MonadOptions m]
|
||||
[AddMessageContext m] [MonadRef m] (stx : Syntax) (macroStack : MacroStack) : m Unit := do
|
||||
let env ← getEnv
|
||||
let kind := stx.getKind
|
||||
if let some entry := (deprecatedSyntaxExt.getState env).find? kind then
|
||||
let extraMsg := match entry.text? with
|
||||
| some text => m!": {text}"
|
||||
| none => m!""
|
||||
match macroStack with
|
||||
| { before := macroStx, .. } :: { before := callerStx, .. } :: _ =>
|
||||
let expandedFrom :=
|
||||
if callerStx.getKind != macroStx.getKind then
|
||||
m!" (expanded from '{callerStx.getKind}')"
|
||||
else m!""
|
||||
Linter.logLintIf Linter.linter.deprecated.syntax macroStx
|
||||
m!"macro '{macroStx.getKind}'{expandedFrom} produces deprecated syntax '{kind}'{extraMsg}"
|
||||
| { before := macroStx, .. } :: [] =>
|
||||
Linter.logLintIf Linter.linter.deprecated.syntax macroStx
|
||||
m!"macro '{macroStx.getKind}' produces deprecated syntax '{kind}'{extraMsg}"
|
||||
| [] =>
|
||||
Linter.logLintIf Linter.linter.deprecated.syntax stx
|
||||
m!"syntax '{kind}' has been deprecated{extraMsg}"
|
||||
|
||||
end Lean.Elab
|
||||
@@ -233,41 +233,27 @@ def processDefDeriving (view : DerivingClassView) (decl : Expr) (isNoncomputable
|
||||
finally
|
||||
Core.setMessageLog (msgLog ++ (← Core.getMessageLog))
|
||||
let env ← getEnv
|
||||
let isPropType ← isProp result.type
|
||||
if isPropType then
|
||||
let decl ← mkThmOrUnsafeDef {
|
||||
name := instName, levelParams := result.levelParams.toList,
|
||||
type := result.type, value := result.value
|
||||
}
|
||||
addDecl decl
|
||||
let hints := ReducibilityHints.regular (getMaxHeight env result.value + 1)
|
||||
let decl ← mkDefinitionValInferringUnsafe instName result.levelParams.toList result.type result.value hints
|
||||
-- Pre-check: if the instance value depends on noncomputable definitions and the user didn't write
|
||||
-- `noncomputable`, give an actionable error with a `Try this:` suggestion.
|
||||
unless isNoncomputable || (← read).isNoncomputableSection || (← isProp result.type) do
|
||||
let noncompRef? := preNormValue.foldConsts none fun n acc =>
|
||||
acc <|> if Lean.isNoncomputable (asyncMode := .local) env n then some n else none
|
||||
if let some noncompRef := noncompRef? then
|
||||
if let some cmdRef := cmdRef? then
|
||||
if let some origText := cmdRef.reprint then
|
||||
let newText := (origText.replace "deriving instance " "deriving noncomputable instance ").trimAscii
|
||||
logInfoAt cmdRef m!"Try this: {newText}"
|
||||
throwError "failed to derive instance because it depends on \
|
||||
`{.ofConstName noncompRef}`, which is noncomputable"
|
||||
if isNoncomputable || (← read).isNoncomputableSection then
|
||||
addDecl <| Declaration.defnDecl decl
|
||||
modifyEnv (addNoncomputable · instName)
|
||||
else
|
||||
let hints := ReducibilityHints.regular (getMaxHeight env result.value + 1)
|
||||
let decl ← mkDefinitionValInferringUnsafe instName result.levelParams.toList result.type result.value hints
|
||||
-- Pre-check: if the instance value depends on noncomputable definitions and the user didn't write
|
||||
-- `noncomputable`, give an actionable error with a `Try this:` suggestion.
|
||||
unless isNoncomputable || (← read).isNoncomputableSection do
|
||||
let noncompRef? := preNormValue.foldConsts none fun n acc =>
|
||||
acc <|> if Lean.isNoncomputable (asyncMode := .local) env n then some n else none
|
||||
if let some noncompRef := noncompRef? then
|
||||
if let some cmdRef := cmdRef? then
|
||||
if let some origText := cmdRef.reprint then
|
||||
let newText := (origText.replace "deriving instance " "deriving noncomputable instance ").trimAscii
|
||||
logInfoAt cmdRef m!"Try this: {newText}"
|
||||
throwError "failed to derive instance because it depends on \
|
||||
`{.ofConstName noncompRef}`, which is noncomputable"
|
||||
if isNoncomputable || (← read).isNoncomputableSection then
|
||||
addDecl <| Declaration.defnDecl decl
|
||||
modifyEnv (addNoncomputable · instName)
|
||||
else
|
||||
addAndCompile <| Declaration.defnDecl decl
|
||||
addAndCompile <| Declaration.defnDecl decl
|
||||
trace[Elab.Deriving] "Derived instance `{.ofConstName instName}`"
|
||||
-- For Prop-typed instances (theorems), skip `implicit_reducible` since reducibility hints are
|
||||
-- irrelevant for theorems. This matches the behavior of the handwritten `instance` command
|
||||
-- (see `MutualDef.lean`).
|
||||
if isPropType then
|
||||
addInstance instName AttributeKind.global (eval_prio default)
|
||||
else
|
||||
registerInstance instName AttributeKind.global (eval_prio default)
|
||||
registerInstance instName AttributeKind.global (eval_prio default)
|
||||
addDeclarationRangesFromSyntax instName (← getRef)
|
||||
|
||||
end Term
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user