mirror of
https://github.com/enso-org/enso.git
synced 2024-10-04 00:33:36 +03:00
Refactoring: merge utils into prelude; merge workspaces. (#3151)
This commit is contained in:
parent
ea1a411f9a
commit
942464cbaf
6
.github/settings.yml
vendored
6
.github/settings.yml
vendored
@ -214,12 +214,6 @@ branches:
|
||||
- "Build and Test (ubuntu-18.04)"
|
||||
- "Build and Test (windows-latest)"
|
||||
- "Docs Check"
|
||||
- "Rust Check"
|
||||
- "Rust Lint"
|
||||
- "Rust Test Native (macOS-latest)"
|
||||
- "Rust Test Native (ubuntu-latest)"
|
||||
- "Rust Test Native (windows-latest)"
|
||||
- "Rust Test WASM"
|
||||
- "Verify Notice Package"
|
||||
- "license/cla"
|
||||
# GUI jobs
|
||||
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@ -23,7 +23,7 @@ env:
|
||||
# Please ensure that this is in sync with project/build.properties
|
||||
sbtVersion: 1.5.2
|
||||
# Please ensure that this is in sync with rustVersion in build.sbt
|
||||
rustToolchain: nightly-2021-05-12
|
||||
rustToolchain: nightly-2021-11-08
|
||||
|
||||
jobs:
|
||||
vuln-scan:
|
||||
|
16
.github/workflows/gui.yml
vendored
16
.github/workflows/gui.yml
vendored
@ -50,7 +50,6 @@ jobs:
|
||||
node ./run ci-gen --skip-version-validation
|
||||
content=`cat CURRENT_RELEASE_CHANGELOG.json`
|
||||
echo "::set-output name=content::$content"
|
||||
|
||||
shell: bash
|
||||
- name: Assert Version Unstable
|
||||
run: node ./run assert-version-unstable --skip-version-validation
|
||||
@ -76,7 +75,6 @@ jobs:
|
||||
list=`git diff --name-only origin/${{github.base_ref}} HEAD | tr '\n' ' '`
|
||||
echo $list
|
||||
echo "::set-output name=list::'$list'"
|
||||
|
||||
shell: bash
|
||||
if: >-
|
||||
github.base_ref == 'develop' || github.base_ref == 'unstable' ||
|
||||
@ -116,7 +114,7 @@ jobs:
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2021-10-29
|
||||
toolchain: nightly-2021-11-08
|
||||
override: true
|
||||
- name: Install Clippy
|
||||
run: rustup component add clippy
|
||||
@ -146,7 +144,7 @@ jobs:
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2021-10-29
|
||||
toolchain: nightly-2021-11-08
|
||||
override: true
|
||||
- name: Run tests (no WASM)
|
||||
run: node ./run test --no-wasm --skip-version-validation
|
||||
@ -172,7 +170,7 @@ jobs:
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2021-10-29
|
||||
toolchain: nightly-2021-11-08
|
||||
override: true
|
||||
- name: Install wasm-pack (macOS)
|
||||
env:
|
||||
@ -231,7 +229,7 @@ jobs:
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2021-10-29
|
||||
toolchain: nightly-2021-11-08
|
||||
override: true
|
||||
- name: Install wasm-pack (macOS)
|
||||
env:
|
||||
@ -299,7 +297,6 @@ jobs:
|
||||
node ./run ci-gen --skip-version-validation
|
||||
content=`cat CURRENT_RELEASE_CHANGELOG.json`
|
||||
echo "::set-output name=content::$content"
|
||||
|
||||
shell: bash
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v1
|
||||
@ -310,7 +307,7 @@ jobs:
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2021-10-29
|
||||
toolchain: nightly-2021-11-08
|
||||
override: true
|
||||
- name: Install wasm-pack (macOS)
|
||||
env:
|
||||
@ -443,7 +440,6 @@ jobs:
|
||||
node ./run ci-gen --skip-version-validation
|
||||
content=`cat CURRENT_RELEASE_CHANGELOG.json`
|
||||
echo "::set-output name=content::$content"
|
||||
|
||||
shell: bash
|
||||
- id: checkCurrentReleaseTag
|
||||
uses: mukunku/tag-exists-action@v1.0.0
|
||||
@ -496,7 +492,6 @@ jobs:
|
||||
node ./run ci-gen --skip-version-validation
|
||||
content=`cat CURRENT_RELEASE_CHANGELOG.json`
|
||||
echo "::set-output name=content::$content"
|
||||
|
||||
shell: bash
|
||||
- shell: bash
|
||||
run: |2-
|
||||
@ -507,7 +502,6 @@ jobs:
|
||||
us-west-1
|
||||
text
|
||||
EOF
|
||||
|
||||
- name: Upload 'index.js.gz' to CDN
|
||||
shell: bash
|
||||
run: >-
|
||||
|
2
.github/workflows/legal-review.yml
vendored
2
.github/workflows/legal-review.yml
vendored
@ -14,7 +14,7 @@ env:
|
||||
# Please ensure that this is in sync with project/build.properties
|
||||
sbtVersion: 1.5.2
|
||||
# Please ensure that this is in sync with rustVersion in build.sbt
|
||||
rustToolchain: nightly-2021-05-12
|
||||
rustToolchain: nightly-2021-11-08
|
||||
excludedPaths: |
|
||||
.github/PULL_REQUEST_TEMPLATE.md
|
||||
.github/CODEOWNERS
|
||||
|
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@ -16,7 +16,7 @@ env:
|
||||
# Please ensure that this is in sync with project/build.properties
|
||||
sbtVersion: 1.5.2
|
||||
# Please ensure that this is in sync with rustVersion in build.sbt
|
||||
rustToolchain: nightly-2021-05-12
|
||||
rustToolchain: nightly-2021-11-08
|
||||
# Please ensure that this is in sync with nodeVersion in scala.yml
|
||||
nodeVersion: 14.17.2
|
||||
# Specifies how many nightly releases should be kept. Any older releases are removed.
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -13,7 +13,7 @@ env:
|
||||
# Please ensure that this is in sync with project/build.properties
|
||||
sbtVersion: 1.5.2
|
||||
# Please ensure that this is in sync with rustVersion in build.sbt
|
||||
rustToolchain: nightly-2021-05-12
|
||||
rustToolchain: nightly-2021-11-08
|
||||
# Please ensure that this is in sync with nodeVersion in scala.yml
|
||||
nodeVersion: 14.17.2
|
||||
|
||||
|
186
.github/workflows/rust.yml
vendored
186
.github/workflows/rust.yml
vendored
@ -1,186 +0,0 @@
|
||||
name: Rust CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [develop, "release/*"]
|
||||
pull_request:
|
||||
branches: ["*"]
|
||||
|
||||
env:
|
||||
wasmpackVersion: 0.8.1
|
||||
nodeVersion: 14.16.1
|
||||
rustToolchain: nightly-2021-10-29
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Rust Check
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Checkout Library Sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Install Tooling
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: ${{ env.rustToolchain }}
|
||||
override: true
|
||||
|
||||
# TODO [AA] at some point when the caching bug is fixed we will want to
|
||||
# re-enable the caches
|
||||
# # Caches
|
||||
# - name: Cache Cargo Registry
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ~/.cargo/registry
|
||||
# key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**Cargo.toml') }}
|
||||
# restore-keys: ${{ runner.os }}-cargo-registry
|
||||
# - name: Cache Cargo Test
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ./target/rust
|
||||
# key: ${{ runner.os }}-cargo-build-${{ hashFiles('**Cargo.toml') }}
|
||||
# restore-keys: ${{ runner.os }}-cargo-build
|
||||
|
||||
# Lint
|
||||
- name: Check Code
|
||||
uses: actions-rs/cargo@v1.0.1
|
||||
with:
|
||||
command: check
|
||||
args: --all-targets
|
||||
|
||||
lint:
|
||||
name: Rust Lint
|
||||
runs-on: ubuntu-latest
|
||||
needs: check
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Checkout Library Sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Install Tooling
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: ${{ env.rustToolchain }}
|
||||
override: true
|
||||
- name: Install Clippy
|
||||
run: rustup component add clippy
|
||||
|
||||
# # Caches
|
||||
# - name: Cache Cargo Registry
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ~/.cargo/registry
|
||||
# key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**Cargo.toml') }}
|
||||
# restore-keys: ${{ runner.os }}-cargo-registry
|
||||
# - name: Cache Cargo Test
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ./target/rust
|
||||
# key: ${{ runner.os }}-cargo-build-${{ hashFiles('**Cargo.toml') }}
|
||||
# restore-keys: ${{ runner.os }}-cargo-build
|
||||
|
||||
# Lint
|
||||
- name: Lint Code
|
||||
uses: actions-rs/cargo@v1.0.1
|
||||
with:
|
||||
command: clippy
|
||||
|
||||
test-native:
|
||||
name: Rust Test Native
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: check
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macOS-latest, ubuntu-latest, windows-latest]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Checkout Library Sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Install Tooling
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: ${{ env.rustToolchain }}
|
||||
override: true
|
||||
|
||||
# # Caches
|
||||
# - name: Cache Cargo Registry
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ~/.cargo/registry
|
||||
# key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**Cargo.toml') }}
|
||||
# restore-keys: ${{ runner.os }}-cargo-registry
|
||||
# - name: Cache Cargo Test
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ./target/rust
|
||||
# key: ${{ runner.os }}-cargo-build-${{ hashFiles('**Cargo.toml') }}
|
||||
# restore-keys: ${{ runner.os }}-cargo-build
|
||||
|
||||
# Tests
|
||||
- name: Test Native
|
||||
uses: actions-rs/cargo@v1.0.1
|
||||
with:
|
||||
command: test
|
||||
|
||||
test-wasm:
|
||||
name: Rust Test WASM
|
||||
runs-on: ubuntu-latest
|
||||
needs: check
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Checkout Library Sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Install Tooling
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1.0.6
|
||||
with:
|
||||
toolchain: ${{ env.rustToolchain }}
|
||||
override: true
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.nodeVersion }}
|
||||
- name: Install wasm-pack
|
||||
# We could use cargo install wasm-pack, but that takes 3.5 minutes
|
||||
# compared to a few seconds.
|
||||
env:
|
||||
WASMPACKURL:
|
||||
https://github.com/rustwasm/wasm-pack/releases/download/v${{
|
||||
env.wasmpackVersion }}
|
||||
WASMPACKDIR: wasm-pack-v${{ env.wasmpackVersion }}-x86_64-unknown-linux-musl
|
||||
shell: bash
|
||||
run: |
|
||||
curl -L "$WASMPACKURL/$WASMPACKDIR.tar.gz" | tar -xz -C .
|
||||
mv $WASMPACKDIR/wasm-pack ~/.cargo/bin
|
||||
rm -r $WASMPACKDIR
|
||||
|
||||
# # Caches
|
||||
# - name: Cache Cargo Registry
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ~/.cargo/registry
|
||||
# key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**Cargo.toml') }}
|
||||
# restore-keys: ${{ runner.os }}-cargo-registry
|
||||
# - name: Cache Cargo Test
|
||||
# uses: actions/cache@v2
|
||||
# with:
|
||||
# path: ./target/rust
|
||||
# key: ${{ runner.os }}-cargo-build-${{ hashFiles('**Cargo.toml') }}
|
||||
# restore-keys: ${{ runner.os }}-cargo-build
|
||||
|
||||
# Tests
|
||||
- name: Test WASM
|
||||
run: ./tools/run --test-wasm
|
2
.github/workflows/scala.yml
vendored
2
.github/workflows/scala.yml
vendored
@ -14,7 +14,7 @@ env:
|
||||
# Please ensure that this is in sync with project/build.properties
|
||||
sbtVersion: 1.5.2
|
||||
# Please ensure that this is in sync with rustVersion in build.sbt
|
||||
rustToolchain: nightly-2021-05-12
|
||||
rustToolchain: nightly-2021-11-08
|
||||
# Some moderately recent version of Node.JS is needed to run the library download tests.
|
||||
nodeVersion: 14.17.2
|
||||
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -22,6 +22,8 @@ target/
|
||||
**/*.rs.bk
|
||||
wasm-pack.log
|
||||
generated/
|
||||
/target
|
||||
/build/rust/target/
|
||||
|
||||
#############
|
||||
## Haskell ##
|
||||
@ -135,4 +137,3 @@ test/Database_Tests/data/redshift_credentials.json
|
||||
|
||||
*.ir
|
||||
*.meta
|
||||
|
||||
|
2
.ignore
Normal file
2
.ignore
Normal file
@ -0,0 +1,2 @@
|
||||
# This file should be ignored by cargo watch, but not by git
|
||||
.github
|
3087
Cargo.lock
generated
3087
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
58
Cargo.toml
58
Cargo.toml
@ -1,6 +1,40 @@
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"gui/src/rust/build",
|
||||
"gui/src/rust/ensogl",
|
||||
"gui/src/rust/ensogl/example",
|
||||
"gui/src/rust/ensogl/lib/core",
|
||||
"gui/src/rust/ensogl/lib/components",
|
||||
"gui/src/rust/ensogl/lib/theme",
|
||||
"gui/src/rust/ensogl/lib/text/embedded-fonts",
|
||||
"gui/src/rust/ensogl/lib/text/msdf-sys",
|
||||
"gui/src/rust/ensogl/lib/text",
|
||||
"gui/src/rust/ensogl/lib/web",
|
||||
"gui/src/rust/ide",
|
||||
"gui/src/rust/ide/controller",
|
||||
"gui/src/rust/ide/controller/double-representation",
|
||||
"gui/src/rust/ide/controller/engine-model",
|
||||
"gui/src/rust/ide/controller/engine-protocol",
|
||||
"gui/src/rust/ide/lib/args",
|
||||
"gui/src/rust/ide/lib/ast/impl",
|
||||
"gui/src/rust/ide/lib/ast/macros",
|
||||
"gui/src/rust/ide/lib/json-rpc",
|
||||
"gui/src/rust/ide/lib/parser",
|
||||
"gui/src/rust/ide/lib/span-tree",
|
||||
"gui/src/rust/ide/lib/span-tree/example",
|
||||
"gui/src/rust/ide/view",
|
||||
"gui/src/rust/ide/view/graph-editor",
|
||||
"gui/src/rust/lib/callback",
|
||||
"gui/src/rust/lib/code-builder",
|
||||
"gui/src/rust/lib/config",
|
||||
"gui/src/rust/lib/eval-tt",
|
||||
"gui/src/rust/lib/frp",
|
||||
"gui/src/rust/lib/fuzzly",
|
||||
"gui/src/rust/lib/shortcuts",
|
||||
"gui/src/rust/lib/shortcuts/example",
|
||||
"gui/src/rust/lib/system/web",
|
||||
"gui/src/rust/lib/types",
|
||||
"lib/rust/ast",
|
||||
"lib/rust/launcher-shims",
|
||||
"lib/rust/lexer/definition",
|
||||
@ -23,25 +57,25 @@ members = [
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
lto = false
|
||||
debug = true
|
||||
opt-level = 0
|
||||
lto = false
|
||||
debug = true
|
||||
debug-assertions = true
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
debug = false
|
||||
opt-level = 3
|
||||
lto = true
|
||||
debug = false
|
||||
debug-assertions = false
|
||||
|
||||
[profile.bench]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
debug = false
|
||||
opt-level = 3
|
||||
lto = true
|
||||
debug = false
|
||||
debug-assertions = false
|
||||
|
||||
[profile.test]
|
||||
opt-level = 0
|
||||
lto = false
|
||||
debug = true
|
||||
opt-level = 0
|
||||
lto = false
|
||||
debug = true
|
||||
debug-assertions = true
|
||||
|
@ -15,7 +15,7 @@ import java.io.File
|
||||
// ============================================================================
|
||||
|
||||
val scalacVersion = "2.13.6"
|
||||
val rustVersion = "1.54.0-nightly"
|
||||
val rustVersion = "1.58.0-nightly"
|
||||
val graalVersion = "21.1.0"
|
||||
val javaVersion = "11"
|
||||
val ensoVersion = "0.2.32-SNAPSHOT" // Note [Engine And Launcher Version]
|
||||
|
@ -234,10 +234,12 @@ commands.test.rust = async function (argv) {
|
||||
}
|
||||
|
||||
if (argv.wasm) {
|
||||
// test-all script requires to be run in the same directory as workspace's Cargo.toml
|
||||
process.chdir(paths.repo)
|
||||
console.log(`Running Rust WASM test suite.`)
|
||||
let args = [
|
||||
'run',
|
||||
'--manifest-path=test/Cargo.toml',
|
||||
'--manifest-path=gui/src/rust/test/Cargo.toml',
|
||||
'--bin',
|
||||
'test_all',
|
||||
'--',
|
||||
@ -252,6 +254,8 @@ commands.test.rust = async function (argv) {
|
||||
|
||||
commands.lint = command(`Lint the codebase`)
|
||||
commands.lint.rust = async function () {
|
||||
// Cargo fmt must be in the same directory as Cargo.toml
|
||||
process.chdir(paths.repo)
|
||||
await run_cargo('cargo', ['clippy', '--', '-D', 'warnings'])
|
||||
await run_cargo('cargo', ['fmt', '--', '--check'])
|
||||
}
|
||||
|
@ -278,8 +278,7 @@ let getListOfChangedFiles = {
|
||||
run: `
|
||||
list=\`git diff --name-only origin/\${{github.base_ref}} HEAD | tr '\\n' ' '\`
|
||||
echo $list
|
||||
echo "::set-output name=list::'$list'"
|
||||
`,
|
||||
echo "::set-output name=list::'$list'"`,
|
||||
shell: 'bash',
|
||||
if: `github.base_ref == 'develop' || github.base_ref == 'unstable' || github.base_ref == 'stable'`,
|
||||
}
|
||||
@ -294,8 +293,7 @@ let getCurrentReleaseChangelogInfo = {
|
||||
run: `
|
||||
node ./run ci-gen --skip-version-validation
|
||||
content=\`cat CURRENT_RELEASE_CHANGELOG.json\`
|
||||
echo "::set-output name=content::$content"
|
||||
`,
|
||||
echo "::set-output name=content::$content"`,
|
||||
shell: 'bash',
|
||||
}
|
||||
|
||||
@ -342,8 +340,7 @@ prepareAwsSessionCDN = {
|
||||
\${{ secrets.ARTEFACT_S3_SECRET_ACCESS_KEY }}
|
||||
us-west-1
|
||||
text
|
||||
EOF
|
||||
`,
|
||||
EOF`,
|
||||
}
|
||||
|
||||
function uploadToCDN(...names) {
|
||||
@ -523,15 +520,14 @@ let workflow = {
|
||||
},
|
||||
}
|
||||
|
||||
let header = `
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
let header = `# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
# DO NOT CHANGE THIS FILE. IT WAS GENERATED FROM 'build/workflow.js'. READ DOCS THERE TO LEARN MORE.
|
||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
`
|
||||
|
||||
/// Generates a new GitHub workflow file (in .github/workflow/...).
|
||||
function generate() {
|
||||
let workflow_script = header + '\n' + yaml.dump(workflow, { noRefs: true })
|
||||
let workflow_script = header + '\n' + yaml.dump(workflow, { noRefs: true, quotingType: '"' })
|
||||
fss.writeFileSync(path.join(paths.github.workflows, 'gui.yml'), workflow_script)
|
||||
}
|
||||
|
||||
|
3800
gui/src/rust/Cargo.lock
generated
3800
gui/src/rust/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,72 +0,0 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"build",
|
||||
"ensogl",
|
||||
"ensogl/example",
|
||||
"ensogl/lib/core",
|
||||
"ensogl/lib/components",
|
||||
"ensogl/lib/theme",
|
||||
"ensogl/lib/text/embedded-fonts",
|
||||
"ensogl/lib/text/msdf-sys",
|
||||
"ensogl/lib/text",
|
||||
"ensogl/lib/web",
|
||||
"ide",
|
||||
"ide/controller",
|
||||
"ide/controller/double-representation",
|
||||
"ide/controller/engine-model",
|
||||
"ide/controller/engine-protocol",
|
||||
"ide/lib/args",
|
||||
"ide/lib/ast/impl",
|
||||
"ide/lib/ast/macros",
|
||||
"ide/lib/json-rpc",
|
||||
"ide/lib/parser",
|
||||
"ide/lib/span-tree",
|
||||
"ide/lib/span-tree/example",
|
||||
"ide/lib/utils",
|
||||
"ide/view",
|
||||
"ide/view/graph-editor",
|
||||
"lib/callback",
|
||||
"lib/code-builder",
|
||||
"lib/config",
|
||||
"lib/eval-tt",
|
||||
"lib/frp",
|
||||
"lib/fuzzly",
|
||||
"lib/shortcuts",
|
||||
"lib/shortcuts/example",
|
||||
"lib/system/web",
|
||||
"lib/types",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
lto = false
|
||||
debug = true
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
debug = false
|
||||
|
||||
[profile.bench]
|
||||
opt-level = 3
|
||||
lto = true
|
||||
debug = false
|
||||
|
||||
[profile.test]
|
||||
opt-level = 0
|
||||
lto = false
|
||||
debug = true
|
||||
|
||||
## These patch versions exist to allow local development of these libraries alongside the IDE. It
|
||||
## assumes you have `rust-lib` in the same directory as `ide`. See:
|
||||
## https://github.com/enso-org/rust-lib/blob/main/docs/CONTRIBUTING.md#developing-in-conjunction-with-enso--ide
|
||||
#[patch.crates-io]
|
||||
#enso-automata = { path = '../../../rust-lib/src/automata' }
|
||||
#enso-data = { path = '../../../rust-lib/src/data' }
|
||||
#enso-generics = { path = '../../../rust-lib/src/generics' }
|
||||
#enso-logger = { path = '../../../rust-lib/src/logger' }
|
||||
#enso-macro-utils = { path = '../../../rust-lib/src/macro-utils' }
|
||||
#enso-optics = { path = '../../../rust-lib/src/optics' }
|
||||
#enso-prelude = { path = '../../../rust-lib/src/prelude' }
|
||||
#enso-shapely = { path = '../../../rust-lib/src/shapely/impl' }
|
||||
#enso-shapely-macros = { path = '../../../rust-lib/src/shapely/macros' }
|
@ -31,7 +31,6 @@ ide-view = { path = "view" }
|
||||
engine-protocol = { path = "controller/engine-protocol" }
|
||||
json-rpc = { path = "lib/json-rpc" }
|
||||
parser = { path = "lib/parser" }
|
||||
utils = { path = "lib/utils" }
|
||||
span-tree = { path = "lib/span-tree" }
|
||||
bimap = { version = "0.4.0" }
|
||||
console_error_panic_hook = { version = "0.1.6" }
|
||||
|
@ -11,7 +11,6 @@ crate-type = ["cdylib", "rlib"]
|
||||
ast = { version = "0.1.0", path = "../../lib/ast/impl" }
|
||||
engine-protocol = { version = "0.1.0", path = "../engine-protocol" }
|
||||
parser = { version = "0.1.0", path = "../../lib/parser" }
|
||||
utils = { version = "0.1.0", path = "../../lib/utils" }
|
||||
enso-data = { path = "../../../../../../lib/rust/data"}
|
||||
enso-logger = { path = "../../../../../../lib/rust/logger"}
|
||||
enso-prelude = { path = "../../../../../../lib/rust/prelude"}
|
||||
|
@ -138,7 +138,7 @@ impl AliasAnalyzer {
|
||||
|
||||
/// Temporarily sets contest and invokes `f` within it.
|
||||
fn in_context(&mut self, context: Context, f: impl FnOnce(&mut Self)) {
|
||||
self.with_items_added(|this| &mut this.context, std::iter::once(context), f);
|
||||
self.with_items_added(|this| &mut this.context, iter::once(context), f);
|
||||
}
|
||||
|
||||
/// Enters a new location (relative to the current one), invokes `f`, leaves the location.
|
||||
|
@ -546,7 +546,6 @@ mod tests {
|
||||
use crate::module;
|
||||
use crate::INDENT;
|
||||
|
||||
use utils::test::ExpectTuple;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
@ -14,7 +14,7 @@ use crate::node::NodeInfo;
|
||||
use ast::known;
|
||||
use ast::Ast;
|
||||
use ast::BlockLine;
|
||||
use utils::fail::FallibleResult;
|
||||
|
||||
|
||||
|
||||
/// Graph uses the same `Id` as the definition which introduces the graph.
|
||||
@ -208,7 +208,6 @@ mod tests {
|
||||
use ast::macros::DocumentationCommentInfo;
|
||||
use ast::test_utils::expect_single_line;
|
||||
use ast::HasRepr;
|
||||
use utils::test::ExpectTuple;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
@ -52,11 +52,8 @@ pub mod prelude {
|
||||
pub use ast::prelude::*;
|
||||
pub use enso_logger::*;
|
||||
pub use enso_prelude::*;
|
||||
pub use utils::prelude::*;
|
||||
|
||||
pub use enso_logger::DefaultTraceLogger as Logger;
|
||||
pub use utils::fail::FallibleResult;
|
||||
pub use utils::vec::VecExt;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
@ -13,7 +13,6 @@ enso-logger = { path = "../../../../../../lib/rust/logger"}
|
||||
enso-prelude = { path = "../../../../../../lib/rust/prelude"}
|
||||
enso-shapely = { path = "../../../../../../lib/rust/shapely/impl"}
|
||||
json-rpc = { path = "../../lib/json-rpc" }
|
||||
utils = { path = "../../lib/utils" }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
failure = { version = "0.1.8" }
|
||||
flatbuffers = { version = "0.5" }
|
||||
|
@ -11,7 +11,6 @@ use futures::channel::mpsc::UnboundedSender;
|
||||
use json_rpc::Transport;
|
||||
use json_rpc::TransportEvent;
|
||||
use std::future::Future;
|
||||
use utils::fail::FallibleResult;
|
||||
|
||||
|
||||
|
||||
@ -295,8 +294,6 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
use json_rpc::test_util::transport::mock::MockTransport;
|
||||
use utils::test::future::FutureTestExt;
|
||||
use utils::test::stream::StreamTestExt;
|
||||
|
||||
#[test]
|
||||
fn test_closed_socked_event_passing() {
|
||||
|
@ -6,7 +6,6 @@ use crate::language_server::types::ContentRoot;
|
||||
use crate::language_server::MockClient;
|
||||
use crate::language_server::API;
|
||||
|
||||
use utils::fail::FallibleResult;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
|
@ -7,7 +7,6 @@ use json_rpc::test_util::transport::mock::MockTransport;
|
||||
use serde_json::json;
|
||||
use serde_json::Value;
|
||||
use std::future::Future;
|
||||
use utils::test::traits::*;
|
||||
|
||||
|
||||
|
||||
@ -314,7 +313,6 @@ fn test_acquire_capability() {
|
||||
fn test_computed_value_update() {
|
||||
use crate::language_server::Notification;
|
||||
use json_rpc::Event;
|
||||
use utils::test::traits::*;
|
||||
|
||||
let context_id = Uuid::parse_str("b36dea0b-b75a-40cf-aaad-5fcdf29a0573").unwrap();
|
||||
let id = Uuid::parse_str("d4b540c0-3ef5-487c-9453-df9d3efd351c").unwrap();
|
||||
|
@ -553,12 +553,12 @@ impl TextEdit {
|
||||
let source_length = source.chars().count();
|
||||
let target_length = target.chars().count();
|
||||
|
||||
let common_prefix_length = utils::string::common_prefix_length(source, target);
|
||||
let common_postfix_length = utils::string::common_postfix_length(source, target);
|
||||
let common_parts_length = common_prefix_length + common_postfix_length;
|
||||
let overlaping_chars = common_parts_length.saturating_sub(source_length.min(target_length));
|
||||
let prefix_length = common_prefix_length;
|
||||
let postfix_length = common_postfix_length - overlaping_chars;
|
||||
let common_prefix_len = common_prefix_length(source, target);
|
||||
let common_postfix_len = common_postfix_length(source, target);
|
||||
let common_parts_len = common_prefix_len + common_postfix_len;
|
||||
let overlaping_chars = common_parts_len.saturating_sub(source_length.min(target_length));
|
||||
let prefix_length = common_prefix_len;
|
||||
let postfix_length = common_postfix_len - overlaping_chars;
|
||||
|
||||
let source_start_index = Index::new(prefix_length);
|
||||
let source_end_index = Index::new(source_length - postfix_length);
|
||||
|
@ -38,7 +38,6 @@ pub mod prelude {
|
||||
pub use futures::Stream;
|
||||
pub use futures::StreamExt;
|
||||
pub use std::future::Future;
|
||||
pub use utils::fail::FallibleResult;
|
||||
pub use uuid::Uuid;
|
||||
|
||||
/// We want most our futures to be static. Otherwise, they would automatically inherit
|
||||
@ -48,9 +47,6 @@ pub mod prelude {
|
||||
/// We want all our streams to be static. Otherwise, the would automatically inherit
|
||||
/// lifetime of the client, which is not the desired behavior.
|
||||
pub type StaticBoxStream<T> = futures::stream::LocalBoxStream<'static, T>;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use utils::test::traits::*;
|
||||
}
|
||||
|
||||
/// Module gathering all traits which may be used by crate's users.
|
||||
|
@ -8,7 +8,7 @@ edition = "2018"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
derive_more = { version = "0.15.0" }
|
||||
derive_more = { version = "0.99.16" }
|
||||
failure = { version = "0.1.5" }
|
||||
lazy_static = { version = "1.4.0" }
|
||||
regex = { version = "1" }
|
||||
@ -20,4 +20,3 @@ ast-macros = { path = "../macros" }
|
||||
enso-data = { path = "../../../../../../../lib/rust/data"}
|
||||
enso-prelude = { path = "../../../../../../../lib/rust/prelude"}
|
||||
enso-shapely = { path = "../../../../../../../lib/rust/shapely/impl"}
|
||||
utils = { path = "../../utils" }
|
||||
|
@ -15,7 +15,7 @@ use crate::TokenConsumer;
|
||||
use enso_data::text::Index;
|
||||
use enso_data::text::Size;
|
||||
use enso_data::text::Span;
|
||||
use utils::fail::FallibleResult;
|
||||
|
||||
|
||||
|
||||
// ==============
|
||||
@ -1612,8 +1612,6 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::*;
|
||||
|
||||
use utils::test::ExpectTuple;
|
||||
|
||||
/// Gets item under given crumb and checks if its representation is as expected.
|
||||
fn expect_repr<C: Crumbable>(item: &C, crumb: &C::Crumb, expected_repr: impl Str) {
|
||||
assert_eq!(item.get(crumb).unwrap().repr(), expected_repr.as_ref());
|
||||
|
@ -33,7 +33,6 @@ pub mod prelude {
|
||||
|
||||
pub use crate::traits::*;
|
||||
pub use crate::Ast;
|
||||
pub use utils::fail::FallibleResult;
|
||||
}
|
||||
|
||||
/// The module containing constants related to the language AST describes.
|
||||
@ -1719,8 +1718,6 @@ mod tests {
|
||||
use enso_data::text::Size;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
use utils::test::ExpectTuple;
|
||||
|
||||
/// Assert that given value round trips JSON serialization.
|
||||
fn round_trips<T>(input_val: &T)
|
||||
where T: Serialize + DeserializeOwned + PartialEq + Debug {
|
||||
|
@ -19,8 +19,6 @@ use crate::SectionRight;
|
||||
use crate::SectionSides;
|
||||
use crate::Shape;
|
||||
|
||||
use utils::vec::VecExt;
|
||||
|
||||
|
||||
|
||||
// =================
|
||||
|
@ -15,8 +15,6 @@ use crate::Shifted;
|
||||
use crate::Token;
|
||||
use crate::TokenConsumer;
|
||||
|
||||
use utils::vec::VecExt;
|
||||
|
||||
|
||||
|
||||
// ================
|
||||
@ -219,7 +217,6 @@ impl HasTokens for Chain {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use utils::test::ExpectTuple;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[test]
|
||||
|
@ -8,7 +8,7 @@ use crate::HasRepr;
|
||||
use crate::Module;
|
||||
use crate::Shape;
|
||||
|
||||
use utils::test::ExpectTuple;
|
||||
|
||||
|
||||
/// "Downcasts" given AST's Shape to `T`. Panics if the shape doesn't match.
|
||||
pub fn expect_shape<'t, T>(ast: &'t Ast) -> &'t T
|
||||
|
@ -8,7 +8,7 @@ edition = "2018"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
enso-prelude = { path = "../../../../../../lib/rust/prelude"}
|
||||
enso-prelude = { path = "../../../../../../lib/rust/prelude", features = ["futures"]}
|
||||
enso-shapely = { path = "../../../../../../lib/rust/shapely/impl"}
|
||||
ensogl-system-web = { path = "../../../lib/system/web" }
|
||||
futures = { version = "0.3.1" }
|
||||
@ -16,4 +16,3 @@ failure = { version = "0.1.6" }
|
||||
serde = { version = "1.0.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0.0" }
|
||||
shrinkwraprs = { version = "0.3.0" }
|
||||
utils = { path = "../utils" }
|
||||
|
@ -22,7 +22,6 @@ use futures::Stream;
|
||||
use futures::StreamExt;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::future::Future;
|
||||
use utils::channel;
|
||||
|
||||
|
||||
|
||||
|
@ -30,9 +30,6 @@ pub use handler::Handler;
|
||||
pub use transport::Transport;
|
||||
pub use transport::TransportEvent;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use utils::test::traits::*;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub mod constants {
|
||||
use std::time::Duration;
|
||||
|
@ -12,7 +12,6 @@ use futures::channel::mpsc::UnboundedSender;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::collections::VecDeque;
|
||||
use utils::channel;
|
||||
|
||||
|
||||
|
||||
|
@ -18,7 +18,6 @@ use serde::Serialize;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::thread::sleep;
|
||||
use utils::test::traits::*;
|
||||
|
||||
type MockEvent = json_rpc::handler::Event<MockNotification>;
|
||||
|
||||
|
@ -11,8 +11,7 @@ crate-type = ["cdylib", "rlib"]
|
||||
[dependencies]
|
||||
ast = { path = "../ast/impl" }
|
||||
enso-data = { path = "../../../../../../lib/rust/data"}
|
||||
enso-prelude = { path = "../../../../../../lib/rust/prelude"}
|
||||
utils = { path = "../utils" }
|
||||
enso-prelude = { path = "../../../../../../lib/rust/prelude", features = ["serde", "serde_json"] }
|
||||
console_error_panic_hook = { version = "0.1.6" }
|
||||
failure = { version = "0.1" }
|
||||
js-sys = { version = "0.3.28" }
|
||||
|
@ -141,7 +141,7 @@ pub struct ParsedSourceFile<Metadata> {
|
||||
pub ast: ast::known::Module,
|
||||
/// Raw metadata in json.
|
||||
#[serde(bound(deserialize = "Metadata:Default+DeserializeOwned"))]
|
||||
#[serde(deserialize_with = "utils::serde::deserialize_or_default")]
|
||||
#[serde(deserialize_with = "enso_prelude::deserialize_or_default")]
|
||||
pub metadata: Metadata,
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,8 @@ use ast::Ast;
|
||||
use ast::BlockLine;
|
||||
use ast::IdMap;
|
||||
use std::panic;
|
||||
use utils::fail::FallibleResult;
|
||||
|
||||
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub mod prelude {
|
||||
|
@ -140,8 +140,8 @@ impl Config {
|
||||
/// using environment variables or, if they are not set, hardcoded
|
||||
/// defaults.
|
||||
pub fn from_env() -> Config {
|
||||
let host = utils::env::env_var_or(HOSTNAME_VAR, DEFAULT_HOSTNAME);
|
||||
let port = utils::env::parse_var_or(PORT_VAR, DEFAULT_PORT);
|
||||
let host = env::env_var_or(HOSTNAME_VAR, DEFAULT_HOSTNAME);
|
||||
let port = env::parse_var_or(PORT_VAR, DEFAULT_PORT);
|
||||
Config { host, port }
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ use serde::de::DeserializeOwned;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::str::FromStr;
|
||||
use utils::test::ExpectTuple;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
||||
|
||||
|
@ -9,7 +9,6 @@ ast = { path = "../ast/impl" }
|
||||
enso-data = { path = "../../../../../../lib/rust/data"}
|
||||
enso-prelude = { path = "../../../../../../lib/rust/prelude"}
|
||||
failure = { version = "0.1.6" }
|
||||
utils = { path = "../utils" }
|
||||
|
||||
[dev-dependencies]
|
||||
parser = { path = "../parser" }
|
||||
|
@ -43,7 +43,6 @@ pub mod prelude {
|
||||
pub use crate::traits::*;
|
||||
pub use ast::traits::*;
|
||||
pub use enso_prelude::*;
|
||||
pub use utils::fail::FallibleResult;
|
||||
}
|
||||
|
||||
use prelude::*;
|
||||
|
@ -1,19 +0,0 @@
|
||||
[package]
|
||||
name = "utils"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <contact@enso.org>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
enso-prelude = { path = "../../../../../../lib/rust/prelude"}
|
||||
enso-shapely = { path = "../../../../../../lib/rust/shapely/impl"}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0" }
|
||||
|
||||
failure = "0.1.5"
|
||||
futures = "0.3.1"
|
||||
itertools = "0.10.0"
|
||||
pin-utils = "0.1.0-alpha.4"
|
@ -1,28 +0,0 @@
|
||||
//! General purpose code for dealing with iterators.
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::prelude::*;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
/// Applies predicate to all items and returns two vectors:
|
||||
/// first with items not matching, second with items matching the predicate.
|
||||
///
|
||||
/// ```
|
||||
/// use utils::iter::split_by_predicate;
|
||||
///
|
||||
/// let (odd, even) = split_by_predicate(1..=5, |i| i % 2 == 0);
|
||||
/// assert_eq!(even, vec![2, 4]);
|
||||
/// assert_eq!(odd, vec![1, 3, 5]);
|
||||
/// ```
|
||||
pub fn split_by_predicate<Iter, Item, Predicate>(
|
||||
input: Iter,
|
||||
predicate: Predicate,
|
||||
) -> (Vec<Item>, Vec<Item>)
|
||||
where
|
||||
Iter: IntoIterator<Item = Item> + Sized,
|
||||
Predicate: Fn(&Item) -> bool,
|
||||
{
|
||||
let mut grouped = input.into_iter().into_group_map_by(predicate);
|
||||
(grouped.remove(&false).unwrap_or_default(), grouped.remove(&true).unwrap_or_default())
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
//! General purpose functions to be reused between components, not belonging to
|
||||
//! any other crate and yet not worth of being split into their own creates.
|
||||
|
||||
#![feature(associated_type_bounds)]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts)]
|
||||
#![warn(trivial_numeric_casts)]
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(unused_qualifications)]
|
||||
#![warn(unsafe_code)]
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
|
||||
pub use enso_prelude as prelude;
|
||||
|
||||
pub mod channel;
|
||||
pub mod env;
|
||||
pub mod fail;
|
||||
pub mod future;
|
||||
pub mod iter;
|
||||
pub mod serde;
|
||||
pub mod string;
|
||||
pub mod test;
|
||||
pub mod vec;
|
@ -1,55 +0,0 @@
|
||||
//! This module provides utils for strings.
|
||||
|
||||
use enso_prelude::*;
|
||||
|
||||
|
||||
|
||||
// ===============================
|
||||
// === Common Pre- and Postfix ===
|
||||
// ===============================
|
||||
|
||||
/// Return the length of the longest common prefix of the two strings. If they are completely
|
||||
/// different this will be zero.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// # use utils::string::common_prefix_length;
|
||||
/// let a = "🐁hospital";
|
||||
/// let b = "🐁host";
|
||||
/// let c = "🐇bunny🐇";
|
||||
///
|
||||
/// assert_eq!(common_prefix_length(a, b), 4);
|
||||
/// assert_eq!(common_prefix_length(a, c), 0);
|
||||
/// assert_eq!(common_prefix_length(a, a), 9);
|
||||
/// ```
|
||||
pub fn common_prefix_length(source_a: &str, source_b: &str) -> usize {
|
||||
let shortest = source_a.chars().count().min(source_b.chars().count());
|
||||
let chars_a = source_a.chars();
|
||||
let chars_b = source_b.chars();
|
||||
let mut zipped = chars_a.zip(chars_b);
|
||||
let mismatch = zipped.find_position(|(a, b)| *a != *b);
|
||||
mismatch.map(|(ix, _)| ix).unwrap_or(shortest)
|
||||
}
|
||||
|
||||
/// Return the length of the longest common postfix of the two strings. If they are completely
|
||||
/// different this will be zero.
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// # use utils::string::common_postfix_length;
|
||||
/// let a = "sunny🐇yard";
|
||||
/// let b = "🐇yard";
|
||||
/// let c = "🐇";
|
||||
///
|
||||
/// assert_eq!(common_postfix_length(a, b), 5);
|
||||
/// assert_eq!(common_postfix_length(a, c), 0);
|
||||
/// assert_eq!(common_postfix_length(a, a), 10);
|
||||
/// ```
|
||||
pub fn common_postfix_length(source_a: &str, source_b: &str) -> usize {
|
||||
let shortest = source_a.chars().count().min(source_b.chars().count());
|
||||
let chars_a = source_a.chars().rev();
|
||||
let chars_b = source_b.chars().rev();
|
||||
let mut zipped = chars_a.zip(chars_b);
|
||||
let mismatch = zipped.find_position(|(a, b)| *a != *b);
|
||||
mismatch.map(|(ix, _)| ix).unwrap_or(shortest)
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
//! This module provides utils for the standard Vec<T>.
|
||||
|
||||
/// Extension trait for `Vec<T>` with general-purpose utility functions.
|
||||
pub trait VecExt<T>: AsMut<Vec<T>> {
|
||||
/// Attempts to remove `T` if its `index` is valid. If not, it returns `None`.
|
||||
fn try_remove(&mut self, index: usize) -> Option<T> {
|
||||
let vec = self.as_mut();
|
||||
if index < vec.len() {
|
||||
Some(vec.remove(index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to remove the first element of `Vec<T>`, returns `None` if its length is zero.
|
||||
fn pop_front(&mut self) -> Option<T> {
|
||||
self.try_remove(0)
|
||||
}
|
||||
|
||||
/// Removes the last `n` elements from the vector. Returns true if the elements were removed.
|
||||
fn remove_last_n(&mut self, n: usize) -> bool {
|
||||
let vec = self.as_mut();
|
||||
let new_size = vec.len().checked_sub(n);
|
||||
new_size.map(|new_size| vec.truncate(new_size)).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> VecExt<T> for Vec<T> {}
|
@ -972,7 +972,6 @@ pub mod tests {
|
||||
use enso_data::text::Index;
|
||||
use enso_data::text::TextChange;
|
||||
use parser::Parser;
|
||||
use utils::test::ExpectTuple;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
|
||||
|
@ -344,7 +344,6 @@ pub mod tests {
|
||||
|
||||
use engine_protocol::language_server::types::test::value_update_with_method_ptr;
|
||||
use engine_protocol::language_server::types::test::value_update_with_type;
|
||||
use utils::test::traits::*;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
||||
|
||||
|
@ -1176,7 +1176,6 @@ pub mod test {
|
||||
use engine_protocol::language_server::types::test::value_update_with_type;
|
||||
use engine_protocol::language_server::SuggestionId;
|
||||
use json_rpc::expect_call;
|
||||
use utils::test::traits::*;
|
||||
|
||||
|
||||
|
||||
|
@ -395,7 +395,6 @@ mod test {
|
||||
use futures::future;
|
||||
use futures::SinkExt;
|
||||
use mockall::Sequence;
|
||||
use utils::test::traits::*;
|
||||
|
||||
|
||||
// === Test Providers ===
|
||||
|
@ -73,7 +73,7 @@ pub fn spawn_stream_handler<Weak, Stream, Function, Ret>(
|
||||
Function: FnMut(Stream::Item, Weak::Strong) -> Ret + 'static,
|
||||
Ret: Future<Output = ()> + 'static,
|
||||
{
|
||||
let handler = utils::channel::process_stream_with_handle(stream, weak, handler);
|
||||
let handler = channel::process_stream_with_handle(stream, weak, handler);
|
||||
spawn(handler);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ use crate::executor::global::set_spawner;
|
||||
use crate::executor::global::spawn;
|
||||
|
||||
use futures::executor;
|
||||
use utils::test::traits::*;
|
||||
|
||||
|
||||
|
||||
|
@ -48,7 +48,6 @@ use ide_view::open_dialog;
|
||||
use ide_view::searcher::entry::AnyModelProvider;
|
||||
use ide_view::searcher::entry::GlyphHighlightedLabel;
|
||||
use ide_view::searcher::new::Icon;
|
||||
use utils::iter::split_by_predicate;
|
||||
|
||||
|
||||
|
||||
@ -787,7 +786,8 @@ impl Model {
|
||||
let base_default_position = default_node_position();
|
||||
let mut trees = connections_info.trees.clone();
|
||||
let nodes = self.graph.graph().nodes()?;
|
||||
let (without_pos, with_pos) = split_by_predicate(&nodes, |n| n.has_position());
|
||||
let (without_pos, with_pos): (Vec<_>, Vec<_>) =
|
||||
nodes.iter().partition(|n| n.has_position());
|
||||
let bottommost_node_pos = with_pos
|
||||
.iter()
|
||||
.filter_map(|node| node.metadata.as_ref()?.position)
|
||||
|
@ -518,7 +518,6 @@ impl Manager {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use utils::test::traits::*;
|
||||
|
||||
use futures::future::ready;
|
||||
use ide_view::graph_editor::component::visualization::instance::ContextModule;
|
||||
|
@ -19,7 +19,6 @@ use mockall::automock;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use utils::future::ready_boxed;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
||||
@ -131,7 +130,7 @@ impl ComputedValueInfoRegistry {
|
||||
{
|
||||
let weak = Rc::downgrade(self);
|
||||
if let Some(ret) = self.get(&id).and_then(&mut f) {
|
||||
ready_boxed(Some(ret))
|
||||
future::ready_boxed(Some(ret))
|
||||
} else {
|
||||
let info_updates = self.subscribe().filter_map(move |update| {
|
||||
let result = update.contains(&id).and_option_from(|| {
|
||||
@ -396,7 +395,6 @@ mod tests {
|
||||
fn getting_future_type_from_registry() {
|
||||
let mut fixture = TestWithLocalPoolExecutor::set_up();
|
||||
|
||||
use utils::test::traits::*;
|
||||
let registry = Rc::new(ComputedValueInfoRegistry::default());
|
||||
let weak = Rc::downgrade(®istry);
|
||||
let id1 = Id::new_v4();
|
||||
|
@ -286,8 +286,6 @@ pub mod test {
|
||||
use engine_protocol::language_server::CapabilityRegistration;
|
||||
use engine_protocol::language_server::ExpressionUpdates;
|
||||
use json_rpc::expect_call;
|
||||
use utils::test::stream::StreamTestExt;
|
||||
use utils::test::ExpectTuple;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Fixture {
|
||||
|
@ -301,7 +301,7 @@ pub struct Notification {
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct Metadata {
|
||||
/// Metadata used within ide.
|
||||
#[serde(default, deserialize_with = "utils::serde::deserialize_or_default")]
|
||||
#[serde(default, deserialize_with = "enso_prelude::deserialize_or_default")]
|
||||
pub ide: IdeMetadata,
|
||||
#[serde(flatten)]
|
||||
/// Metadata of other users of ParsedSourceFile<Metadata> API.
|
||||
@ -326,7 +326,7 @@ impl Default for Metadata {
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
pub struct ProjectMetadata {
|
||||
/// The execution context of the displayed graph editor.
|
||||
#[serde(default, deserialize_with = "utils::serde::deserialize_or_default")]
|
||||
#[serde(default, deserialize_with = "enso_prelude::deserialize_or_default")]
|
||||
pub call_stack: Vec<model::execution_context::LocalCall>,
|
||||
}
|
||||
|
||||
@ -334,10 +334,10 @@ pub struct ProjectMetadata {
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
pub struct IdeMetadata {
|
||||
/// Metadata that belongs to nodes.
|
||||
#[serde(deserialize_with = "utils::serde::deserialize_or_default")]
|
||||
#[serde(deserialize_with = "enso_prelude::deserialize_or_default")]
|
||||
node: HashMap<ast::Id, NodeMetadata>,
|
||||
/// The project metadata. This is stored only in the main module's metadata.
|
||||
#[serde(default, deserialize_with = "utils::serde::deserialize_or_default")]
|
||||
#[serde(default, deserialize_with = "enso_prelude::deserialize_or_default")]
|
||||
project: Option<ProjectMetadata>,
|
||||
}
|
||||
|
||||
@ -345,19 +345,19 @@ pub struct IdeMetadata {
|
||||
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
pub struct NodeMetadata {
|
||||
/// Position in x,y coordinates.
|
||||
#[serde(default, deserialize_with = "utils::serde::deserialize_or_default")]
|
||||
#[serde(default, deserialize_with = "enso_prelude::deserialize_or_default")]
|
||||
pub position: Option<Position>,
|
||||
/// A method which user intends this node to be, e.g. by picking specific suggestion in
|
||||
/// Searcher Panel.
|
||||
///
|
||||
/// The methods may be defined for different types, so the name alone don't specify them.
|
||||
#[serde(default, deserialize_with = "utils::serde::deserialize_or_default")]
|
||||
#[serde(default, deserialize_with = "enso_prelude::deserialize_or_default")]
|
||||
pub intended_method: Option<MethodId>,
|
||||
/// Information about uploading file.
|
||||
///
|
||||
/// Designed to be present in nodes created by dragging and dropping files in IDE. Contains
|
||||
/// information about file and upload progress.
|
||||
#[serde(default, deserialize_with = "utils::serde::deserialize_or_default")]
|
||||
#[serde(default, deserialize_with = "enso_prelude::deserialize_or_default")]
|
||||
pub uploading_file: Option<UploadingFile>,
|
||||
/// Was node selected in the view.
|
||||
#[serde(default)]
|
||||
|
@ -235,7 +235,6 @@ mod test {
|
||||
use crate::model::module::Position;
|
||||
|
||||
use data::text;
|
||||
use utils::test::traits::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn applying_code_change() {
|
||||
|
@ -482,7 +482,6 @@ pub mod test {
|
||||
use engine_protocol::language_server::Position;
|
||||
use engine_protocol::language_server::TextRange;
|
||||
use json_rpc::error::RpcError;
|
||||
use utils::test::ExpectTuple;
|
||||
use wasm_bindgen_test::wasm_bindgen_test;
|
||||
|
||||
|
||||
|
@ -640,7 +640,6 @@ mod test {
|
||||
use engine_protocol::types::Sha3_224;
|
||||
use futures::SinkExt;
|
||||
use json_rpc::expect_call;
|
||||
use utils::test::traits::*;
|
||||
|
||||
|
||||
#[allow(unused)]
|
||||
|
@ -281,7 +281,6 @@ mod test {
|
||||
use engine_protocol::language_server::SuggestionsDatabaseEntry;
|
||||
use engine_protocol::language_server::SuggestionsDatabaseModification;
|
||||
use enso_data::text::TextLocation;
|
||||
use utils::test::stream::StreamTestExt;
|
||||
use wasm_bindgen_test::wasm_bindgen_test_configure;
|
||||
|
||||
|
||||
|
@ -108,7 +108,6 @@ impl<T> Synchronized<T> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::executor::test_utils::TestWithLocalPoolExecutor;
|
||||
use utils::test::traits::*;
|
||||
|
||||
#[test]
|
||||
fn synchronized() {
|
||||
|
@ -16,7 +16,6 @@ use engine_protocol::types::Sha3_224;
|
||||
use enso_frp::data::bitfield::BitField;
|
||||
use enso_frp::data::bitfield::BitField32;
|
||||
use json_rpc::expect_call;
|
||||
use utils::test::traits::*;
|
||||
|
||||
/// Utilities for mocking IDE components.
|
||||
pub mod mock {
|
||||
|
@ -8,7 +8,6 @@ use failure::Error;
|
||||
use futures::channel::mpsc;
|
||||
use json_rpc::Transport;
|
||||
use json_rpc::TransportEvent;
|
||||
use utils::channel;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::BinaryType;
|
||||
|
||||
@ -322,10 +321,10 @@ impl WebSocket {
|
||||
// Note [mwu] Ignore argument, `CloseEvent` here contains rubbish
|
||||
// anyway, nothing useful to pass to caller. Error code or reason
|
||||
// string should not be relied upon.
|
||||
utils::channel::emit(&transmitter_clone, Err(()));
|
||||
channel::emit(&transmitter_clone, Err(()));
|
||||
});
|
||||
self.set_on_open(move |_| {
|
||||
utils::channel::emit(&transmitter, Ok(()));
|
||||
channel::emit(&transmitter, Ok(()));
|
||||
});
|
||||
|
||||
match receiver.next().await {
|
||||
|
@ -12,7 +12,7 @@ default = ["console_error_panic_hook"]
|
||||
[dependencies]
|
||||
enso-data = { path = "../../../../../../lib/rust/data" }
|
||||
enso-logger = { path = "../../../../../../lib/rust/logger" }
|
||||
enso-prelude = { path = "../../../../../../lib/rust/prelude" }
|
||||
enso-prelude = { path = "../../../../../../lib/rust/prelude", features = ["wasm-bindgen"] }
|
||||
console_error_panic_hook = { version = "0.1.1", optional = true }
|
||||
failure = { version = "0.1.5" }
|
||||
gloo-timers = { version = "0.2.1", features = ["futures"] }
|
||||
|
@ -1,57 +1,56 @@
|
||||
#![feature(option_result_contains)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// List of workspace members that should not be tested by wasm-pack test.
|
||||
/// (e.g. because they do not target wasm at all)
|
||||
const PACKAGE_BLACKLIST:[&str;2] = [
|
||||
"build",
|
||||
"ide/file-manager/mock-server"
|
||||
];
|
||||
const PACKAGE_BLACKLIST: [&str; 2] =
|
||||
["gui/src/rust/test/build", "gui/src/rust/ide/file-manager/mock-server"];
|
||||
|
||||
/// Attributes that denote WASM tests.
|
||||
const WASM_TEST_ATTRIBUTES:[&str;2] = ["#[wasm_bindgen_test]", "#[wasm_bindgen_test(async)]"];
|
||||
const WASM_TEST_ATTRIBUTES: [&str; 2] = ["#[wasm_bindgen_test]", "#[wasm_bindgen_test(async)]"];
|
||||
|
||||
/// Subdirectories in the crate directory that contain sources for the crate.
|
||||
const SOURCE_SUBDIRECTORIES:[&str;4] = ["src", "benches", "examples", "tests"];
|
||||
const SOURCE_SUBDIRECTORIES: [&str; 4] = ["src", "benches", "examples", "tests"];
|
||||
|
||||
/// Lists members of given Cargo.toml workspace.
|
||||
fn get_workspace_members(cargo_toml_root:toml::Value) -> Vec<String> {
|
||||
fn get_workspace_members(cargo_toml_root: toml::Value) -> Vec<String> {
|
||||
let workspace = cargo_toml_root.get("workspace").expect("not a workspace");
|
||||
match &workspace["members"] {
|
||||
toml::Value::Array(list) => list.iter().map(|val| {
|
||||
match val {
|
||||
toml::Value::Array(list) => list
|
||||
.iter()
|
||||
.map(|val| match val {
|
||||
toml::Value::String(s) => s.clone(),
|
||||
_ => panic!("Workspace member is not a string"),
|
||||
}
|
||||
}).collect(),
|
||||
_ => panic!("Invalid workspace element")
|
||||
})
|
||||
.collect(),
|
||||
_ => panic!("Invalid workspace element"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses file under given path as TOML value.
|
||||
fn parse_toml(path:impl AsRef<Path>) -> toml::Value {
|
||||
fn parse_toml(path: impl AsRef<Path>) -> toml::Value {
|
||||
let path = path.as_ref();
|
||||
let data = std::fs::read_to_string(path).unwrap();
|
||||
data.parse().unwrap()
|
||||
}
|
||||
|
||||
/// Check if the given line of source code is an attribute denoting wasm test.
|
||||
fn is_wasm_test_attribute(line:&str) -> bool {
|
||||
fn is_wasm_test_attribute(line: &str) -> bool {
|
||||
WASM_TEST_ATTRIBUTES.contains(&line.trim())
|
||||
}
|
||||
|
||||
/// Check if the given workspace member contains any wasm tests in the sources.
|
||||
fn has_wasm_tests(member:&str) -> bool {
|
||||
fn has_wasm_tests(member: &str) -> bool {
|
||||
// We go over selected subdirectories only to avoid entering into sources of other crates that
|
||||
// are nested within this crate subtree.
|
||||
for subdir in SOURCE_SUBDIRECTORIES {
|
||||
let pattern = format!("{}/{}/**/*.rs", member,subdir);
|
||||
let pattern = format!("{}/{}/**/*.rs", member, subdir);
|
||||
for entry in glob::glob(&pattern).unwrap() {
|
||||
let contents = std::fs::read_to_string(entry.unwrap()).unwrap();
|
||||
if contents.lines().any(is_wasm_test_attribute) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,24 +58,24 @@ fn has_wasm_tests(member:&str) -> bool {
|
||||
}
|
||||
|
||||
/// Checks if the given member is blacklisted from running the tests.
|
||||
fn blacklisted(memeber:&str) -> bool {
|
||||
fn blacklisted(memeber: &str) -> bool {
|
||||
PACKAGE_BLACKLIST.contains(&memeber)
|
||||
}
|
||||
|
||||
/// Checks if for the given workspace member wasm-pack test should be run.
|
||||
fn to_be_tested(member:&str) -> bool {
|
||||
fn to_be_tested(member: &str) -> bool {
|
||||
has_wasm_tests(member) && !blacklisted(member) && !is_proc_macro_crate(member)
|
||||
}
|
||||
|
||||
/// Checks if given workspace member is a proc-macro crate.
|
||||
fn is_proc_macro_crate(member:&str) -> bool {
|
||||
fn is_proc_macro_crate(member: &str) -> bool {
|
||||
let cargo_toml_path = PathBuf::from(member).join("Cargo.toml");
|
||||
let cargo_toml_root = parse_toml(cargo_toml_path);
|
||||
get_proc_macro(cargo_toml_root).contains(&true)
|
||||
}
|
||||
|
||||
/// Retrieve a `lib.proc-macro` field from Cargo.toml
|
||||
fn get_proc_macro(cargo_toml:toml::Value) -> Option<bool> {
|
||||
fn get_proc_macro(cargo_toml: toml::Value) -> Option<bool> {
|
||||
cargo_toml.get("lib")?.get("proc-macro")?.as_bool()
|
||||
}
|
||||
|
||||
@ -85,23 +84,21 @@ fn get_proc_macro(cargo_toml:toml::Value) -> Option<bool> {
|
||||
/// This function reads workspace members list from `Cargo.toml` in current directory, and call
|
||||
/// `wasm-pack test` each member. All script arguments are passed to `wasm-pack` process.
|
||||
fn main() {
|
||||
let wasm_pack_args = std::env::args().skip(1).collect::<Vec<_>>();
|
||||
let wasm_pack_args = std::env::args().skip(1).collect::<Vec<_>>();
|
||||
let cargo_toml_root = parse_toml("Cargo.toml");
|
||||
let all_members = get_workspace_members(cargo_toml_root);
|
||||
let tested_members = all_members.iter().filter(|p| to_be_tested(&p));
|
||||
let all_members = get_workspace_members(cargo_toml_root);
|
||||
let tested_members = all_members.iter().filter(|p| to_be_tested(&p));
|
||||
|
||||
for member in tested_members {
|
||||
println!("Running tests for {}", member);
|
||||
let mut command = std::process::Command::new("wasm-pack");
|
||||
command.arg("test")
|
||||
.args(&wasm_pack_args)
|
||||
.arg(&member);
|
||||
println!("{:?}",command);
|
||||
command.arg("test").args(&wasm_pack_args).arg(&member);
|
||||
println!("{:?}", command);
|
||||
let status = command.status().unwrap();
|
||||
if !status.success() {
|
||||
panic!("Process for {} failed!{}", member, match status.code() {
|
||||
Some(code) => format!(" Code: {}", code),
|
||||
None => String::new()
|
||||
None => String::new(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "ast"
|
||||
name = "ast-new"
|
||||
version = "0.1.0"
|
||||
authors = ["Enso Team <enso-dev@enso.org>"]
|
||||
edition = "2018"
|
||||
@ -16,7 +16,6 @@ categories = ["parsing"]
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
name = "ast"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
test = true
|
||||
bench = true
|
||||
|
@ -1,10 +1,10 @@
|
||||
//! This module exports the implementation of the enso abstract syntax tree.
|
||||
|
||||
use app::*;
|
||||
use lines::*;
|
||||
use def::*;
|
||||
use name::*;
|
||||
use invalid::*;
|
||||
use lines::*;
|
||||
use name::*;
|
||||
use num::*;
|
||||
use txt::*;
|
||||
|
||||
@ -20,21 +20,21 @@ use uuid::Uuid;
|
||||
pub type AnyAst = Ast<Shape>;
|
||||
|
||||
/// An ast node with an unique id and length.
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Ast<T> {
|
||||
/// A unique identifier.
|
||||
uid : Option<Uuid>,
|
||||
pub uid: Option<Uuid>,
|
||||
/// Length in number of chars of this ast node.
|
||||
len : usize,
|
||||
pub len: usize,
|
||||
/// The number of trailing spaces.
|
||||
off : usize,
|
||||
pub off: usize,
|
||||
/// The ast node itself.
|
||||
ast : T,
|
||||
pub ast: T,
|
||||
}
|
||||
|
||||
// The set of all ast nodes.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Shape {
|
||||
Unrecognized(invalid::Unrecognized),
|
||||
Blank(name::Blank),
|
||||
@ -66,18 +66,18 @@ pub mod app {
|
||||
|
||||
/// The ast node for application.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Prefix {
|
||||
pub func : Box<AnyAst>,
|
||||
pub arg : Box<AnyAst>,
|
||||
pub func: Box<AnyAst>,
|
||||
pub arg: Box<AnyAst>,
|
||||
}
|
||||
|
||||
/// The ast node for an infix operator application.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Infix {
|
||||
pub larg: Box<AnyAst>,
|
||||
pub opr: Box<Ast<name::Opr>>,
|
||||
pub opr: Box<Ast<name::Opr>>,
|
||||
pub rarg: Box<AnyAst>,
|
||||
}
|
||||
}
|
||||
@ -98,24 +98,26 @@ pub mod lines {
|
||||
///
|
||||
/// The module consists of a sequence of possibly empty lines with no leading indentation.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Module { pub lines: Vec<Option<AnyAst>> }
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Module {
|
||||
pub lines: Vec<Option<AnyAst>>,
|
||||
}
|
||||
|
||||
/// The ast node for a block that represents a sequence of equally indented lines.
|
||||
///
|
||||
/// Lines may contain some child ast or be empty. Block is used for all code blocks except
|
||||
/// for the root one, which uses `Module`.
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Block {
|
||||
/// Absolute's block indent, counting from the module's root.
|
||||
pub indent: usize,
|
||||
pub indent: usize,
|
||||
/// Leading empty lines. Each line is represented by absolute count of spaces
|
||||
/// it contains, counting from the root.
|
||||
pub empty_lines: Vec<usize>,
|
||||
/// First line with non-empty item.
|
||||
pub first_line: Box<AnyAst>,
|
||||
pub first_line: Box<AnyAst>,
|
||||
/// Rest of lines, each of them optionally having contents.
|
||||
pub lines: Vec<Option<AnyAst>>,
|
||||
pub lines: Vec<Option<AnyAst>>,
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +135,7 @@ pub mod def {
|
||||
|
||||
/// The ast node for a method definition.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FunDef {
|
||||
pub name: Box<Ast<name::Var>>,
|
||||
pub args: Vec<AnyAst>,
|
||||
@ -142,7 +144,7 @@ pub mod def {
|
||||
|
||||
/// The ast node for an operator definition.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OprDef {
|
||||
pub name: Box<Ast<name::Opr>>,
|
||||
pub args: Vec<AnyAst>,
|
||||
@ -151,9 +153,9 @@ pub mod def {
|
||||
|
||||
/// The ast node for a variable definition.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VarDef {
|
||||
pub name: Box<Ast<name::Var>>,
|
||||
pub name: Box<Ast<name::Var>>,
|
||||
pub value: Box<AnyAst>,
|
||||
}
|
||||
}
|
||||
@ -168,23 +170,29 @@ pub mod def {
|
||||
pub mod name {
|
||||
/// The ast node for the underscore `_`.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone,Copy)]
|
||||
pub struct Blank { }
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Blank {}
|
||||
|
||||
/// The ast node for a variable.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Var { pub name: String }
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Var {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// The ast node for a constructor.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Cons { pub name: String }
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cons {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// The ast node for an operator.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Opr { pub name: String }
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Opr {
|
||||
pub name: String,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -197,8 +205,10 @@ pub mod name {
|
||||
pub mod invalid {
|
||||
/// Unrecognized token.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Unrecognized { pub str: String }
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Unrecognized {
|
||||
pub str: String,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -211,8 +221,10 @@ pub mod invalid {
|
||||
pub mod num {
|
||||
/// The ast node for a number.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Number { pub number: String }
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Number {
|
||||
pub number: String,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -226,26 +238,84 @@ pub mod num {
|
||||
pub mod txt {
|
||||
/// The ast node for a string of text.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Text { pub text: String }
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Text {
|
||||
pub text: String,
|
||||
}
|
||||
}
|
||||
|
||||
// === Into<Shape> ===
|
||||
|
||||
impl From<Unrecognized> for Shape { fn from(val:Unrecognized) -> Self { Self::Unrecognized(val) } }
|
||||
impl From<Blank> for Shape { fn from(val:Blank) -> Self { Self::Blank (val) } }
|
||||
impl From<Var> for Shape { fn from(val:Var) -> Self { Self::Var (val) } }
|
||||
impl From<Cons> for Shape { fn from(val:Cons) -> Self { Self::Cons (val) } }
|
||||
impl From<Opr> for Shape { fn from(val:Opr) -> Self { Self::Opr (val) } }
|
||||
impl From<Number> for Shape { fn from(val:Number) -> Self { Self::Number (val) } }
|
||||
impl From<Text> for Shape { fn from(val: Text) -> Self { Self::Text (val) } }
|
||||
impl From<Prefix> for Shape { fn from(val:Prefix) -> Self { Self::Prefix (val) } }
|
||||
impl From<Infix> for Shape { fn from(val:Infix) -> Self { Self::Infix (val) } }
|
||||
impl From<Module> for Shape { fn from(val:Module) -> Self { Self::Module (val) } }
|
||||
impl From<Block> for Shape { fn from(val:Block) -> Self { Self::Block (val) } }
|
||||
impl From<FunDef> for Shape { fn from(val:FunDef) -> Self { Self::FunDef (val) } }
|
||||
impl From<OprDef> for Shape { fn from(val:OprDef) -> Self { Self::OprDef (val) } }
|
||||
impl From<VarDef> for Shape { fn from(val:VarDef) -> Self { Self::VarDef (val) } }
|
||||
impl From<Unrecognized> for Shape {
|
||||
fn from(val: Unrecognized) -> Self {
|
||||
Self::Unrecognized(val)
|
||||
}
|
||||
}
|
||||
impl From<Blank> for Shape {
|
||||
fn from(val: Blank) -> Self {
|
||||
Self::Blank(val)
|
||||
}
|
||||
}
|
||||
impl From<Var> for Shape {
|
||||
fn from(val: Var) -> Self {
|
||||
Self::Var(val)
|
||||
}
|
||||
}
|
||||
impl From<Cons> for Shape {
|
||||
fn from(val: Cons) -> Self {
|
||||
Self::Cons(val)
|
||||
}
|
||||
}
|
||||
impl From<Opr> for Shape {
|
||||
fn from(val: Opr) -> Self {
|
||||
Self::Opr(val)
|
||||
}
|
||||
}
|
||||
impl From<Number> for Shape {
|
||||
fn from(val: Number) -> Self {
|
||||
Self::Number(val)
|
||||
}
|
||||
}
|
||||
impl From<Text> for Shape {
|
||||
fn from(val: Text) -> Self {
|
||||
Self::Text(val)
|
||||
}
|
||||
}
|
||||
impl From<Prefix> for Shape {
|
||||
fn from(val: Prefix) -> Self {
|
||||
Self::Prefix(val)
|
||||
}
|
||||
}
|
||||
impl From<Infix> for Shape {
|
||||
fn from(val: Infix) -> Self {
|
||||
Self::Infix(val)
|
||||
}
|
||||
}
|
||||
impl From<Module> for Shape {
|
||||
fn from(val: Module) -> Self {
|
||||
Self::Module(val)
|
||||
}
|
||||
}
|
||||
impl From<Block> for Shape {
|
||||
fn from(val: Block) -> Self {
|
||||
Self::Block(val)
|
||||
}
|
||||
}
|
||||
impl From<FunDef> for Shape {
|
||||
fn from(val: FunDef) -> Self {
|
||||
Self::FunDef(val)
|
||||
}
|
||||
}
|
||||
impl From<OprDef> for Shape {
|
||||
fn from(val: OprDef) -> Self {
|
||||
Self::OprDef(val)
|
||||
}
|
||||
}
|
||||
impl From<VarDef> for Shape {
|
||||
fn from(val: VarDef) -> Self {
|
||||
Self::VarDef(val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -255,42 +325,42 @@ impl From<VarDef> for Shape { fn from(val:VarDef) -> Self { Self::Va
|
||||
|
||||
impl AnyAst {
|
||||
/// Creates a new ast node with random `Uuid` from `Shape`.
|
||||
pub fn new(ast:impl Into<Shape>) -> Self {
|
||||
Self {ast:ast.into(), uid: Some(Uuid::new_v4()), len:0, off:0 }
|
||||
pub fn new(ast: impl Into<Shape>) -> Self {
|
||||
Self { ast: ast.into(), uid: Some(Uuid::new_v4()), len: 0, off: 0 }
|
||||
}
|
||||
|
||||
/// Creates a new ast node with `Shape::Unrecognized`.
|
||||
pub fn unrecognized(str:String) -> Self {
|
||||
Self::new(Unrecognized{str})
|
||||
pub fn unrecognized(str: String) -> Self {
|
||||
Self::new(Unrecognized { str })
|
||||
}
|
||||
|
||||
/// Creates a new ast node with `Shape::Blank`.
|
||||
pub fn blank() -> Self {
|
||||
Self::new(Blank{})
|
||||
Self::new(Blank {})
|
||||
}
|
||||
|
||||
/// Creates a new ast node with `Shape::Var`.
|
||||
pub fn var(name:String) -> Self {
|
||||
Self::new(Var{name})
|
||||
pub fn var(name: String) -> Self {
|
||||
Self::new(Var { name })
|
||||
}
|
||||
|
||||
/// Creates a new ast node with `Shape::Cons`.
|
||||
pub fn cons(name:String) -> Self {
|
||||
Self::new(Cons{name})
|
||||
pub fn cons(name: String) -> Self {
|
||||
Self::new(Cons { name })
|
||||
}
|
||||
|
||||
/// Creates a new ast node with `Shape::Opr`.
|
||||
pub fn opr(name:String) -> Self {
|
||||
Self::new(Opr{name})
|
||||
pub fn opr(name: String) -> Self {
|
||||
Self::new(Opr { name })
|
||||
}
|
||||
|
||||
/// Creates a new ast node with `Shape::Number`.
|
||||
pub fn num(number:i64) -> Self {
|
||||
Self::new(Number{number:number.to_string()})
|
||||
pub fn num(number: i64) -> Self {
|
||||
Self::new(Number { number: number.to_string() })
|
||||
}
|
||||
|
||||
/// Creates a new ast node with `Shape::Text`.
|
||||
pub fn text(text:String) -> Self {
|
||||
Self::new(Text{text})
|
||||
pub fn text(text: String) -> Self {
|
||||
Self::new(Text { text })
|
||||
}
|
||||
}
|
||||
|
@ -18,28 +18,28 @@ use syn::Ident;
|
||||
// =======================
|
||||
|
||||
/// A Scala ast generator.
|
||||
#[derive(Debug,Clone,Default)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ScalaGenerator {
|
||||
/// The content of the file.
|
||||
code: String,
|
||||
code: String,
|
||||
/// Current indentation.
|
||||
indent: usize,
|
||||
indent: usize,
|
||||
/// Inheritance hierarchy.
|
||||
extends: HashMap<Ident,Ident>
|
||||
extends: HashMap<Ident, Ident>,
|
||||
}
|
||||
|
||||
impl ScalaGenerator {
|
||||
/// Generates a Scala ast from `lib/rust/ast/src/lib.rs`.
|
||||
pub fn ast() -> std::io::Result<String> {
|
||||
let mut content = String::new();
|
||||
let mut file = File::open("lib/rust/ast/src/ast.rs")?;
|
||||
let mut file = File::open("lib/rust/ast/src/ast.rs")?;
|
||||
file.read_to_string(&mut content);
|
||||
|
||||
Ok(Self::file("ast", syn::parse_file(content.as_str()).unwrap()))
|
||||
}
|
||||
|
||||
/// Generates a Scala ast definition from a parsed Rust ast definition.
|
||||
pub fn file(name:&str, file:syn::File) -> String {
|
||||
pub fn file(name: &str, file: syn::File) -> String {
|
||||
let mut this = Self::default();
|
||||
writeln!(this.code, "package org.enso.ast\n");
|
||||
writeln!(this.code, "import java.util.UUID\n\n");
|
||||
@ -48,35 +48,34 @@ impl ScalaGenerator {
|
||||
}
|
||||
|
||||
/// Generates a block of Scala code.
|
||||
fn block(&mut self, ident:&Ident, lines:&[syn::Item]) {
|
||||
write!(self.code, "\n{:i$}object " , "", i=self.indent);
|
||||
fn block(&mut self, ident: &Ident, lines: &[syn::Item]) {
|
||||
write!(self.code, "\n{:i$}object ", "", i = self.indent);
|
||||
self.typ_name(ident);
|
||||
writeln!(self.code, " {{");
|
||||
self.indent += 2;
|
||||
if self.extends.contains_key(ident) {
|
||||
write!(self.code, "{:i$}sealed trait ", "", i=self.indent);
|
||||
write!(self.code, "{:i$}sealed trait ", "", i = self.indent);
|
||||
self.typ_name(ident);
|
||||
self.extends(ident);
|
||||
}
|
||||
|
||||
for item in lines {
|
||||
match item {
|
||||
syn::Item::Enum (val) => self.adt(val),
|
||||
syn::Item::Type (val) => {
|
||||
write!(self.code, "\n{:i$}type ", "", i=self.indent);
|
||||
syn::Item::Enum(val) => self.adt(val),
|
||||
syn::Item::Type(val) => {
|
||||
write!(self.code, "\n{:i$}type ", "", i = self.indent);
|
||||
self.typ_name(&val.ident);
|
||||
self.generics(&val.generics);
|
||||
write!(self.code, " = ");
|
||||
self.typ(val.ty.as_ref());
|
||||
writeln!(self.code);
|
||||
}
|
||||
syn::Item::Struct(val) => {
|
||||
syn::Item::Struct(val) =>
|
||||
if let syn::Fields::Named(fields) = &val.fields {
|
||||
self.class(&val.ident, &val.generics, fields);
|
||||
} else {
|
||||
panic!("All struct fields must be named!");
|
||||
}
|
||||
}
|
||||
},
|
||||
syn::Item::Mod(val) => {
|
||||
if let Some(content) = &val.content {
|
||||
self.block(&val.ident, &content.1[..]);
|
||||
@ -87,19 +86,21 @@ impl ScalaGenerator {
|
||||
}
|
||||
|
||||
self.indent -= 2;
|
||||
writeln!(self.code, "{:i$}}}", "", i=self.indent);
|
||||
writeln!(self.code, "{:i$}}}", "", i = self.indent);
|
||||
}
|
||||
|
||||
/// Generates a Scala case class.
|
||||
///
|
||||
/// `struct Foo { bar:Bar, baz:Baz }` => `case class Foo(bar:Bar, baz:Baz)`
|
||||
fn class(&mut self, ident:&Ident, generics:&syn::Generics, fields:&syn::FieldsNamed) {
|
||||
write!(self.code, "{:i$}case class ", "", i=self.indent);
|
||||
fn class(&mut self, ident: &Ident, generics: &syn::Generics, fields: &syn::FieldsNamed) {
|
||||
write!(self.code, "{:i$}case class ", "", i = self.indent);
|
||||
self.typ_name(ident);
|
||||
self.generics(generics);
|
||||
write!(self.code, "(");
|
||||
for (i, field) in fields.named.iter().enumerate() {
|
||||
if i != 0 { write!(self.code, ", "); }
|
||||
for (i, field) in fields.named.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(self.code, ", ");
|
||||
}
|
||||
if let Some(ident) = &field.ident {
|
||||
self.var_name(ident);
|
||||
}
|
||||
@ -116,7 +117,10 @@ impl ScalaGenerator {
|
||||
///
|
||||
/// 1) When the Rust enum variant has named fields:
|
||||
/// ```
|
||||
/// enum Foo { Bar{x:isize}, Baz{y:isize} }
|
||||
/// enum Foo {
|
||||
/// Bar { x: isize },
|
||||
/// Baz { y: isize },
|
||||
/// }
|
||||
/// ```
|
||||
/// ===>
|
||||
/// ```scala
|
||||
@ -127,10 +131,15 @@ impl ScalaGenerator {
|
||||
///
|
||||
/// 2) When the Rust enum variant has one unnamed field with qualified type:
|
||||
/// ```
|
||||
/// enum Foo { Bar(barz::Bar), Baz(barz::Baz) }
|
||||
/// enum Foo {
|
||||
/// Bar(barz::Bar),
|
||||
/// Baz(barz::Baz),
|
||||
/// }
|
||||
/// mod barz {
|
||||
/// pub struct Bar { }
|
||||
/// pub struct Baz {y:isize}
|
||||
/// pub struct Bar {}
|
||||
/// pub struct Baz {
|
||||
/// y: isize,
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// ===>
|
||||
@ -142,16 +151,16 @@ impl ScalaGenerator {
|
||||
/// case class Baz(y:size) extends Barz
|
||||
/// }
|
||||
/// ```
|
||||
fn adt(&mut self, adt:&syn::ItemEnum) {
|
||||
write!(self.code, "\n{:i$}sealed trait {}", "", adt.ident, i=self.indent);
|
||||
fn adt(&mut self, adt: &syn::ItemEnum) {
|
||||
write!(self.code, "\n{:i$}sealed trait {}", "", adt.ident, i = self.indent);
|
||||
self.generics(&adt.generics);
|
||||
self.extends(&adt.ident);
|
||||
for variant in &adt.variants {
|
||||
match &variant.fields {
|
||||
syn::Fields::Named (fields) => {
|
||||
syn::Fields::Named(fields) => {
|
||||
self.extends.insert(variant.ident.clone(), adt.ident.clone());
|
||||
self.class(&variant.ident, &adt.generics, fields);
|
||||
},
|
||||
}
|
||||
syn::Fields::Unnamed(fields) => {
|
||||
if let Some(syn::Type::Path(path)) = fields.unnamed.first().map(|f| &f.ty) {
|
||||
let path = path.path.segments.iter().rev().take(2).collect_tuple();
|
||||
@ -169,7 +178,7 @@ impl ScalaGenerator {
|
||||
/// Generates Scala class extension.
|
||||
///
|
||||
/// `foo` => `extends Foo`
|
||||
fn extends(&mut self, ident:&Ident) {
|
||||
fn extends(&mut self, ident: &Ident) {
|
||||
if let Some(name) = self.extends.get(ident).cloned() {
|
||||
write!(self.code, " extends ");
|
||||
self.typ_name(&name);
|
||||
@ -180,11 +189,15 @@ impl ScalaGenerator {
|
||||
/// Generates Scala type parameters.
|
||||
///
|
||||
/// `<Foo, Bar>` = `[Foo, Bar]`
|
||||
fn generics(&mut self, generics:&syn::Generics) {
|
||||
if generics.params.is_empty() {return}
|
||||
fn generics(&mut self, generics: &syn::Generics) {
|
||||
if generics.params.is_empty() {
|
||||
return;
|
||||
}
|
||||
write!(self.code, "[");
|
||||
for (i, param) in generics.params.iter().enumerate() {
|
||||
if i != 0 { write!(self.code, ", "); }
|
||||
if i != 0 {
|
||||
write!(self.code, ", ");
|
||||
}
|
||||
if let syn::GenericParam::Type(typ) = param {
|
||||
self.typ_name(&typ.ident)
|
||||
}
|
||||
@ -195,10 +208,12 @@ impl ScalaGenerator {
|
||||
/// Generates a qualified scala type with type arguments.
|
||||
///
|
||||
/// `foo::Bar<Baz>` => `Foo.Bar[Baz]`
|
||||
fn typ(&mut self, typ:&syn::Type) {
|
||||
fn typ(&mut self, typ: &syn::Type) {
|
||||
if let syn::Type::Path(path) = typ {
|
||||
for (i, typ) in path.path.segments.iter().enumerate() {
|
||||
if i != 0 { write!(self.code, "."); }
|
||||
if i != 0 {
|
||||
write!(self.code, ".");
|
||||
}
|
||||
self.typ_segment(typ);
|
||||
}
|
||||
}
|
||||
@ -207,29 +222,37 @@ impl ScalaGenerator {
|
||||
/// Generates a Scala type with type arguments.
|
||||
///
|
||||
/// `Foo<Bar<Baz>>` => `Foo[Bar[Baz]]`
|
||||
fn typ_segment(&mut self, typ:&syn::PathSegment) {
|
||||
fn typ_segment(&mut self, typ: &syn::PathSegment) {
|
||||
let boxed = typ.ident.to_string().as_str() == "Box";
|
||||
if !boxed { self.typ_name(&typ.ident); }
|
||||
if !boxed {
|
||||
self.typ_name(&typ.ident);
|
||||
}
|
||||
if let syn::PathArguments::AngleBracketed(typ) = &typ.arguments {
|
||||
if !boxed { write!(self.code, "["); }
|
||||
if !boxed {
|
||||
write!(self.code, "[");
|
||||
}
|
||||
for (i, typ) in typ.args.iter().enumerate() {
|
||||
if i != 0 { write!(self.code, ", "); }
|
||||
if let syn::GenericArgument::Type(typ) = typ{
|
||||
if i != 0 {
|
||||
write!(self.code, ", ");
|
||||
}
|
||||
if let syn::GenericArgument::Type(typ) = typ {
|
||||
self.typ(typ);
|
||||
}
|
||||
}
|
||||
if !boxed { write!(self.code, "]"); }
|
||||
if !boxed {
|
||||
write!(self.code, "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a Scala variable name (camel case).
|
||||
///
|
||||
/// `foo_bar` => `fooBar`
|
||||
fn var_name(&mut self, ident:&Ident) {
|
||||
fn var_name(&mut self, ident: &Ident) {
|
||||
let mut underscore = false;
|
||||
for char in ident.to_string().chars() {
|
||||
if char == '_' {
|
||||
underscore = true ;
|
||||
underscore = true;
|
||||
} else if underscore {
|
||||
underscore = false;
|
||||
for char in char.to_uppercase() {
|
||||
@ -252,18 +275,18 @@ impl ScalaGenerator {
|
||||
/// Vec => Vector,
|
||||
/// Uuid => UUID,
|
||||
/// ```
|
||||
fn typ_name(&mut self, ident:&Ident) {
|
||||
fn typ_name(&mut self, ident: &Ident) {
|
||||
let name = match ident.to_string().as_str() {
|
||||
"u32" | "i32" | "u16" | "i16" | "i8" => "Int",
|
||||
"usize" | "isize" | "u64" | "i64" => "Long",
|
||||
"u8" => "Byte",
|
||||
"char" => "Char",
|
||||
"Vec" => "Vector",
|
||||
"Uuid" => "UUID",
|
||||
"u32" | "i32" | "u16" | "i16" | "i8" => "Int",
|
||||
"usize" | "isize" | "u64" | "i64" => "Long",
|
||||
"u8" => "Byte",
|
||||
"char" => "Char",
|
||||
"Vec" => "Vector",
|
||||
"Uuid" => "UUID",
|
||||
name => {
|
||||
let mut chars = name.chars();
|
||||
if let Some(char) = chars.next() {
|
||||
write!(self.code, "{}", char.to_uppercase().to_string() + chars.as_str());
|
||||
write!(self.code, "{}", char.to_uppercase().to_string() + chars.as_str());
|
||||
}
|
||||
""
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
//! This module exports the implementation of the enso abstract syntax tree.
|
||||
|
||||
pub mod generation;
|
||||
mod ast;
|
||||
pub mod generation;
|
||||
|
||||
pub use crate::ast::*;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use ast::generation::ScalaGenerator;
|
||||
use ast_new::generation::ScalaGenerator;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
@ -46,22 +46,22 @@ use std::ops::RangeInclusive;
|
||||
///
|
||||
/// This type tracks these divisions explicitly for an input alphabet defined for all automata in
|
||||
/// this library as `0u64..=u64::max_value()`.
|
||||
#[derive(Clone,Debug,PartialEq,Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Segmentation {
|
||||
pub divisions : BTreeSet<Symbol>
|
||||
pub divisions: BTreeSet<Symbol>,
|
||||
}
|
||||
|
||||
impl Segmentation {
|
||||
/// Inserts a range of symbols into the alphabet.
|
||||
pub fn insert(&mut self,range:RangeInclusive<Symbol>) {
|
||||
pub fn insert(&mut self, range: RangeInclusive<Symbol>) {
|
||||
self.divisions.insert(range.start().clone());
|
||||
let end = range.end().clone();
|
||||
end.next().for_each(|t| self.divisions.insert(t));
|
||||
}
|
||||
|
||||
/// Creates a [`Segmentation`] from an input set of divisions.
|
||||
pub fn from_divisions(divisions:&[u64]) -> Self {
|
||||
pub fn from_divisions(divisions: &[u64]) -> Self {
|
||||
let mut dict = Self::default();
|
||||
for val in divisions {
|
||||
dict.divisions.insert(Symbol::from(*val));
|
||||
@ -106,33 +106,34 @@ impl Default for Segmentation {
|
||||
|
||||
/// An immutable version of `Segmentation` which consists cached information allowing for fast
|
||||
/// segmentation analysis.
|
||||
#[derive(Clone,Debug,Default,Eq,PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct SealedSegmentation {
|
||||
pub division_map : BTreeMap<Symbol,usize>
|
||||
pub division_map: BTreeMap<Symbol, usize>,
|
||||
}
|
||||
|
||||
impl SealedSegmentation {
|
||||
/// The index of the provided symbol. Please note that the index always exists, as the alphabet
|
||||
/// spans across all possible symbols.
|
||||
pub fn index_of_symbol(&self, symbol:&Symbol) -> usize {
|
||||
self.range(symbol..).next().map(|(k,v)|{
|
||||
if k == symbol { *v } else { v - 1 }
|
||||
}).unwrap_or_else(|| self.len() - 1)
|
||||
pub fn index_of_symbol(&self, symbol: &Symbol) -> usize {
|
||||
self.range(symbol..)
|
||||
.next()
|
||||
.map(|(k, v)| if k == symbol { *v } else { v - 1 })
|
||||
.unwrap_or_else(|| self.len() - 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SealedSegmentation {
|
||||
type Target = BTreeMap<Symbol,usize>;
|
||||
type Target = BTreeMap<Symbol, usize>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.division_map
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Segmentation> for SealedSegmentation {
|
||||
fn from(s:&Segmentation) -> Self {
|
||||
let division_map = s.divisions.iter().cloned().enumerate().map(|(ix,s)|(s,ix)).collect();
|
||||
Self {division_map}
|
||||
fn from(s: &Segmentation) -> Self {
|
||||
let division_map = s.divisions.iter().cloned().enumerate().map(|(ix, s)| (s, ix)).collect();
|
||||
Self { division_map }
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,14 +166,14 @@ mod tests {
|
||||
let num_to_insert = 10;
|
||||
let mut segmentation = Segmentation::default();
|
||||
for ix in 0u64..num_to_insert {
|
||||
segmentation.insert(Symbol::from(100+ix)..=Symbol::from(100+ix))
|
||||
segmentation.insert(Symbol::from(100 + ix)..=Symbol::from(100 + ix))
|
||||
}
|
||||
assert_eq!(segmentation.num_divisions(), (num_to_insert+2) as usize);
|
||||
assert_eq!(segmentation.num_divisions(), (num_to_insert + 2) as usize);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_divisions_construction() {
|
||||
let segmentation = Segmentation::from_divisions(&[0,5,10,15,20]);
|
||||
let segmentation = Segmentation::from_divisions(&[0, 5, 10, 15, 20]);
|
||||
assert_eq!(segmentation.num_divisions(), 5);
|
||||
assert!(segmentation.divisions.contains(&Symbol::from(15u64)));
|
||||
}
|
||||
|
@ -12,15 +12,15 @@ use std::ops::IndexMut;
|
||||
// ============
|
||||
|
||||
/// An efficient 2D matrix implemented on top of [`std::vec::Vec`].
|
||||
#[derive(Clone,Debug,Default,PartialEq,Eq)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Matrix<T> {
|
||||
pub rows : usize,
|
||||
pub columns : usize,
|
||||
pub matrix : Vec<T>,
|
||||
pub rows: usize,
|
||||
pub columns: usize,
|
||||
pub matrix: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T:Copy> Matrix<T> {
|
||||
impl<T: Copy> Matrix<T> {
|
||||
/// Get the number of rows in the matrix.
|
||||
pub fn rows(&self) -> usize {
|
||||
self.rows
|
||||
@ -37,21 +37,20 @@ impl<T:Copy> Matrix<T> {
|
||||
}
|
||||
|
||||
/// Indexing with bounds checking.
|
||||
pub fn safe_index(&self, row:usize, column:usize) -> Option<T> {
|
||||
(row < self.rows && column < self.columns).as_some_from(|| {
|
||||
self.matrix[row*self.columns+column]
|
||||
})
|
||||
pub fn safe_index(&self, row: usize, column: usize) -> Option<T> {
|
||||
(row < self.rows && column < self.columns)
|
||||
.as_some_from(|| self.matrix[row * self.columns + column])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Default> Matrix<T> {
|
||||
impl<T: Default> Matrix<T> {
|
||||
/// Construct a matrix with the dimensions given by `rows` and `columns`.
|
||||
pub fn new(rows:usize, columns:usize) -> Self {
|
||||
let mut matrix = Vec::with_capacity(rows*columns);
|
||||
pub fn new(rows: usize, columns: usize) -> Self {
|
||||
let mut matrix = Vec::with_capacity(rows * columns);
|
||||
for _ in 0..matrix.capacity() {
|
||||
matrix.push(default())
|
||||
}
|
||||
Self{rows,columns,matrix}
|
||||
Self { rows, columns, matrix }
|
||||
}
|
||||
|
||||
/// Add a new row to the matrix `self`, filled with default values.
|
||||
@ -68,8 +67,8 @@ impl<T:Default> Matrix<T> {
|
||||
/// allocations around.
|
||||
pub fn new_column(&mut self) {
|
||||
for n in 0..self.rows {
|
||||
let index = self.columns*n + self.columns+n;
|
||||
self.matrix.insert(index,default());
|
||||
let index = self.columns * n + self.columns + n;
|
||||
self.matrix.insert(index, default());
|
||||
}
|
||||
self.columns += 1;
|
||||
}
|
||||
@ -78,16 +77,16 @@ impl<T:Default> Matrix<T> {
|
||||
// FIXME: Wrong indexing order!
|
||||
// === Trait Impls ===
|
||||
|
||||
impl<T> Index<(usize,usize)> for Matrix<T> {
|
||||
impl<T> Index<(usize, usize)> for Matrix<T> {
|
||||
type Output = T;
|
||||
fn index(&self, index:(usize,usize)) -> &T {
|
||||
&self.matrix[index.0*self.columns+index.1]
|
||||
fn index(&self, index: (usize, usize)) -> &T {
|
||||
&self.matrix[index.0 * self.columns + index.1]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<(usize,usize)> for Matrix<T> {
|
||||
fn index_mut(&mut self, index:(usize,usize)) -> &mut T {
|
||||
&mut self.matrix[index.0*self.columns+index.1]
|
||||
impl<T> IndexMut<(usize, usize)> for Matrix<T> {
|
||||
fn index_mut(&mut self, index: (usize, usize)) -> &mut T {
|
||||
&mut self.matrix[index.0 * self.columns + index.1]
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,79 +103,79 @@ mod tests {
|
||||
#[test]
|
||||
fn default_size() {
|
||||
let default = Matrix::<usize>::default();
|
||||
assert_eq!(default.rows,0);
|
||||
assert_eq!(default.columns,0);
|
||||
assert_eq!(default.rows, 0);
|
||||
assert_eq!(default.columns, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construct_with_dimensions() {
|
||||
let matrix = Matrix::<usize>::new(5, 10);
|
||||
assert_eq!(matrix.rows,5);
|
||||
assert_eq!(matrix.columns,10);
|
||||
assert_eq!(matrix.rows, 5);
|
||||
assert_eq!(matrix.columns, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_row() {
|
||||
let mut matrix = Matrix::<usize>::new(2, 2);
|
||||
matrix[(0,0)] = 1;
|
||||
matrix[(0,1)] = 2;
|
||||
matrix[(1,0)] = 3;
|
||||
matrix[(1,1)] = 4;
|
||||
assert_eq!(matrix.rows,2);
|
||||
assert_eq!(matrix.columns,2);
|
||||
matrix[(0, 0)] = 1;
|
||||
matrix[(0, 1)] = 2;
|
||||
matrix[(1, 0)] = 3;
|
||||
matrix[(1, 1)] = 4;
|
||||
assert_eq!(matrix.rows, 2);
|
||||
assert_eq!(matrix.columns, 2);
|
||||
matrix.new_row();
|
||||
assert_eq!(matrix.rows,3);
|
||||
assert_eq!(matrix.columns,2);
|
||||
assert_eq!(matrix[(0,0)],1);
|
||||
assert_eq!(matrix[(0,1)],2);
|
||||
assert_eq!(matrix[(1,0)],3);
|
||||
assert_eq!(matrix[(1,1)],4);
|
||||
assert_eq!(matrix[(2,0)],0);
|
||||
assert_eq!(matrix[(2,1)],0);
|
||||
assert_eq!(matrix.rows, 3);
|
||||
assert_eq!(matrix.columns, 2);
|
||||
assert_eq!(matrix[(0, 0)], 1);
|
||||
assert_eq!(matrix[(0, 1)], 2);
|
||||
assert_eq!(matrix[(1, 0)], 3);
|
||||
assert_eq!(matrix[(1, 1)], 4);
|
||||
assert_eq!(matrix[(2, 0)], 0);
|
||||
assert_eq!(matrix[(2, 1)], 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_column() {
|
||||
let mut matrix = Matrix::<usize>::new(2,2);
|
||||
matrix[(0,0)] = 1;
|
||||
matrix[(0,1)] = 2;
|
||||
matrix[(1,0)] = 3;
|
||||
matrix[(1,1)] = 4;
|
||||
assert_eq!(matrix.rows,2);
|
||||
assert_eq!(matrix.columns,2);
|
||||
let mut matrix = Matrix::<usize>::new(2, 2);
|
||||
matrix[(0, 0)] = 1;
|
||||
matrix[(0, 1)] = 2;
|
||||
matrix[(1, 0)] = 3;
|
||||
matrix[(1, 1)] = 4;
|
||||
assert_eq!(matrix.rows, 2);
|
||||
assert_eq!(matrix.columns, 2);
|
||||
matrix.new_column();
|
||||
assert_eq!(matrix.rows,2);
|
||||
assert_eq!(matrix.columns,3);
|
||||
assert_eq!(matrix[(0,0)],1);
|
||||
assert_eq!(matrix[(0,1)],2);
|
||||
assert_eq!(matrix[(1,0)],3);
|
||||
assert_eq!(matrix[(1,1)],4);
|
||||
assert_eq!(matrix[(0,2)],0);
|
||||
assert_eq!(matrix[(1,2)],0);
|
||||
assert_eq!(matrix.rows, 2);
|
||||
assert_eq!(matrix.columns, 3);
|
||||
assert_eq!(matrix[(0, 0)], 1);
|
||||
assert_eq!(matrix[(0, 1)], 2);
|
||||
assert_eq!(matrix[(1, 0)], 3);
|
||||
assert_eq!(matrix[(1, 1)], 4);
|
||||
assert_eq!(matrix[(0, 2)], 0);
|
||||
assert_eq!(matrix[(1, 2)], 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn row_column_indexing() {
|
||||
let mut matrix = Matrix::<usize>::new(2,2);
|
||||
matrix[(0,0)] = 1;
|
||||
matrix[(0,1)] = 2;
|
||||
matrix[(1,0)] = 3;
|
||||
matrix[(1,1)] = 4;
|
||||
let mut matrix = Matrix::<usize>::new(2, 2);
|
||||
matrix[(0, 0)] = 1;
|
||||
matrix[(0, 1)] = 2;
|
||||
matrix[(1, 0)] = 3;
|
||||
matrix[(1, 1)] = 4;
|
||||
let mut output = Vec::default();
|
||||
for row in 0..2 {
|
||||
for col in 0..2 {
|
||||
output.push(matrix[(row,col)]);
|
||||
output.push(matrix[(row, col)]);
|
||||
}
|
||||
}
|
||||
assert_eq!(output,vec![1,2,3,4]);
|
||||
assert_eq!(output, vec![1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn safe_indexing() {
|
||||
let matrix = Matrix::<usize>::new(2,2);
|
||||
let exists = matrix.safe_index(0,0);
|
||||
let does_not_exist = matrix.safe_index(3,0);
|
||||
assert_eq!(exists,Some(0));
|
||||
assert_eq!(does_not_exist,None);
|
||||
let matrix = Matrix::<usize>::new(2, 2);
|
||||
let exists = matrix.safe_index(0, 0);
|
||||
let does_not_exist = matrix.safe_index(3, 0);
|
||||
assert_eq!(exists, Some(0));
|
||||
assert_eq!(does_not_exist, None);
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::symbol::Symbol;
|
||||
use crate::alphabet;
|
||||
use crate::state;
|
||||
use crate::data::matrix::Matrix;
|
||||
use crate::nfa;
|
||||
use crate::nfa::Nfa;
|
||||
use crate::state;
|
||||
use crate::symbol::Symbol;
|
||||
|
||||
|
||||
|
||||
@ -35,10 +35,10 @@ pub type State = state::State<Dfa>;
|
||||
/// │ 0 │ ----> │ 1 │ ----> │ 2 │ ----> │ 3 │
|
||||
/// └───┘ └───┘ └───┘ └───┘
|
||||
/// ```
|
||||
#[derive(Clone,Debug,Default,Eq,PartialEq)]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Dfa {
|
||||
/// A set of disjoint intervals over the allowable input alphabet.
|
||||
pub alphabet : alphabet::SealedSegmentation,
|
||||
pub alphabet: alphabet::SealedSegmentation,
|
||||
/// The transition matrix for the Dfa.
|
||||
///
|
||||
/// It represents a function of type `(state, symbol) -> state`, returning the identifier for
|
||||
@ -52,39 +52,38 @@ pub struct Dfa {
|
||||
/// |:-:|:-:|:-:|
|
||||
/// | 0 | 1 | - |
|
||||
/// | 1 | - | 0 |
|
||||
///
|
||||
pub links : Matrix<State>,
|
||||
pub links: Matrix<State>,
|
||||
/// For each DFA state contains a list of NFA states it was constructed from.
|
||||
pub sources : Vec<Vec<nfa::State>>,
|
||||
pub sources: Vec<Vec<nfa::State>>,
|
||||
}
|
||||
|
||||
impl Dfa {
|
||||
/// The start state of the automata.
|
||||
pub const START_STATE : State = State::new(0);
|
||||
pub const START_STATE: State = State::new(0);
|
||||
}
|
||||
|
||||
impl Dfa {
|
||||
/// Simulate the DFA transition with the provided input symbol.
|
||||
pub fn next_state(&self, current_state:State, symbol:&Symbol) -> State {
|
||||
pub fn next_state(&self, current_state: State, symbol: &Symbol) -> State {
|
||||
let ix = self.alphabet.index_of_symbol(symbol);
|
||||
self.links.safe_index(current_state.id(),ix).unwrap_or_default()
|
||||
self.links.safe_index(current_state.id(), ix).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Convert the automata to GraphViz Dot code for the deubgging purposes.
|
||||
pub fn as_graphviz_code(&self) -> String {
|
||||
let mut out = String::new();
|
||||
for row in 0 .. self.links.rows {
|
||||
out += &format!("node_{}[label=\"{}\"]\n",row,row);
|
||||
for column in 0 .. self.links.columns {
|
||||
let state = self.links[(row,column)];
|
||||
for row in 0..self.links.rows {
|
||||
out += &format!("node_{}[label=\"{}\"]\n", row, row);
|
||||
for column in 0..self.links.columns {
|
||||
let state = self.links[(row, column)];
|
||||
if !state.is_invalid() {
|
||||
out += &format!("node_{} -> node_{}\n",row,state.id());
|
||||
out += &format!("node_{} -> node_{}\n", row, state.id());
|
||||
}
|
||||
}
|
||||
}
|
||||
let opts = "node [shape=circle style=filled fillcolor=\"#4385f5\" fontcolor=\"#FFFFFF\" \
|
||||
color=white penwidth=5.0 margin=0.1 width=0.5 height=0.5 fixedsize=true]";
|
||||
format!("digraph G {{\n{}\n{}\n}}\n",opts,out)
|
||||
format!("digraph G {{\n{}\n{}\n}}\n", opts, out)
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,13 +91,13 @@ impl Dfa {
|
||||
// === Trait Impls ===
|
||||
|
||||
impl From<Vec<Vec<usize>>> for Matrix<State> {
|
||||
fn from(input:Vec<Vec<usize>>) -> Self {
|
||||
let rows = input.len();
|
||||
let columns = if rows == 0 {0} else {input[0].len()};
|
||||
let mut matrix = Self::new(rows,columns);
|
||||
fn from(input: Vec<Vec<usize>>) -> Self {
|
||||
let rows = input.len();
|
||||
let columns = if rows == 0 { 0 } else { input[0].len() };
|
||||
let mut matrix = Self::new(rows, columns);
|
||||
for row in 0..rows {
|
||||
for column in 0..columns {
|
||||
matrix[(row,column)] = State::new(input[row][column]);
|
||||
matrix[(row, column)] = State::new(input[row][column]);
|
||||
}
|
||||
}
|
||||
matrix
|
||||
@ -115,36 +114,36 @@ impl From<&Nfa> for Dfa {
|
||||
/// Transforms an Nfa into a Dfa, based on the algorithm described
|
||||
/// [here](https://www.youtube.com/watch?v=taClnxU-nao).
|
||||
/// The asymptotic complexity is quadratic in number of states.
|
||||
fn from(nfa:&Nfa) -> Self {
|
||||
let nfa_mat = nfa.nfa_matrix();
|
||||
let eps_mat = nfa.eps_matrix();
|
||||
let mut dfa_mat = Matrix::new(0,nfa.alphabet.divisions.len());
|
||||
fn from(nfa: &Nfa) -> Self {
|
||||
let nfa_mat = nfa.nfa_matrix();
|
||||
let eps_mat = nfa.eps_matrix();
|
||||
let mut dfa_mat = Matrix::new(0, nfa.alphabet.divisions.len());
|
||||
let mut dfa_eps_ixs = Vec::<nfa::StateSetId>::new();
|
||||
let mut dfa_eps_map = HashMap::<nfa::StateSetId,State>::new();
|
||||
let mut dfa_eps_map = HashMap::<nfa::StateSetId, State>::new();
|
||||
|
||||
dfa_eps_ixs.push(eps_mat[0].clone());
|
||||
dfa_eps_map.insert(eps_mat[0].clone(),Dfa::START_STATE);
|
||||
dfa_eps_map.insert(eps_mat[0].clone(), Dfa::START_STATE);
|
||||
|
||||
let mut i = 0;
|
||||
while i < dfa_eps_ixs.len() {
|
||||
while i < dfa_eps_ixs.len() {
|
||||
dfa_mat.new_row();
|
||||
for voc_ix in 0..nfa.alphabet.divisions.len() {
|
||||
let mut eps_set = nfa::StateSetId::new();
|
||||
for &eps_ix in &dfa_eps_ixs[i] {
|
||||
let tgt = nfa_mat[(eps_ix.id(),voc_ix)];
|
||||
let tgt = nfa_mat[(eps_ix.id(), voc_ix)];
|
||||
if tgt != nfa::State::INVALID {
|
||||
eps_set.extend(eps_mat[tgt.id()].iter());
|
||||
}
|
||||
}
|
||||
if !eps_set.is_empty() {
|
||||
dfa_mat[(i,voc_ix)] = match dfa_eps_map.get(&eps_set) {
|
||||
dfa_mat[(i, voc_ix)] = match dfa_eps_map.get(&eps_set) {
|
||||
Some(&id) => id,
|
||||
None => {
|
||||
let id = State::new(dfa_eps_ixs.len());
|
||||
dfa_eps_ixs.push(eps_set.clone());
|
||||
dfa_eps_map.insert(eps_set,id);
|
||||
dfa_eps_map.insert(eps_set, id);
|
||||
id
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -157,8 +156,8 @@ impl From<&Nfa> for Dfa {
|
||||
}
|
||||
|
||||
let alphabet = (&nfa.alphabet).into();
|
||||
let links = dfa_mat;
|
||||
Dfa {alphabet,links,sources}
|
||||
let links = dfa_mat;
|
||||
Dfa { alphabet, links, sources }
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,8 +172,8 @@ pub mod tests {
|
||||
extern crate test;
|
||||
use super::*;
|
||||
use crate::nfa;
|
||||
use test::Bencher;
|
||||
use crate::nfa::tests::NfaTest;
|
||||
use test::Bencher;
|
||||
|
||||
|
||||
// === Utilities ===
|
||||
@ -183,15 +182,15 @@ pub mod tests {
|
||||
State::INVALID.id()
|
||||
}
|
||||
|
||||
fn assert_same_alphabet(dfa:&Dfa, nfa:&Nfa) {
|
||||
assert_eq!(dfa.alphabet,nfa.alphabet.seal());
|
||||
fn assert_same_alphabet(dfa: &Dfa, nfa: &Nfa) {
|
||||
assert_eq!(dfa.alphabet, nfa.alphabet.seal());
|
||||
}
|
||||
|
||||
fn assert_same_matrix(dfa:&Dfa, expected:&Matrix<State>) {
|
||||
assert_eq!(dfa.links,*expected);
|
||||
fn assert_same_matrix(dfa: &Dfa, expected: &Matrix<State>) {
|
||||
assert_eq!(dfa.links, *expected);
|
||||
}
|
||||
|
||||
fn get_name<'a>(nfa:&'a NfaTest, dfa:&Dfa, state:State) -> Option<&'a String> {
|
||||
fn get_name<'a>(nfa: &'a NfaTest, dfa: &Dfa, state: State) -> Option<&'a String> {
|
||||
let sources = &dfa.sources[state.id()];
|
||||
let mut result = None;
|
||||
for source in sources.iter() {
|
||||
@ -204,7 +203,7 @@ pub mod tests {
|
||||
result
|
||||
}
|
||||
|
||||
fn make_state(ix:usize) -> State {
|
||||
fn make_state(ix: usize) -> State {
|
||||
State::new(ix)
|
||||
}
|
||||
|
||||
@ -215,119 +214,98 @@ pub mod tests {
|
||||
fn dfa_pattern_range() {
|
||||
let nfa = nfa::tests::pattern_range();
|
||||
let dfa = Dfa::from(&nfa.nfa);
|
||||
assert_same_alphabet(&dfa,&nfa);
|
||||
let expected = Matrix::from(
|
||||
vec![
|
||||
vec![invalid() , 1 , invalid()],
|
||||
vec![invalid() , invalid() , invalid()],
|
||||
]
|
||||
);
|
||||
assert_same_matrix(&dfa,&expected);
|
||||
assert_same_alphabet(&dfa, &nfa);
|
||||
let expected = Matrix::from(vec![vec![invalid(), 1, invalid()], vec![
|
||||
invalid(),
|
||||
invalid(),
|
||||
invalid(),
|
||||
]]);
|
||||
assert_same_matrix(&dfa, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dfa_pattern_or() {
|
||||
let nfa = nfa::tests::pattern_or();
|
||||
let dfa = Dfa::from(&nfa.nfa);
|
||||
assert_same_alphabet(&dfa,&nfa);
|
||||
let expected = Matrix::from(
|
||||
vec![
|
||||
vec![invalid() , 1 , invalid() , 2 , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , invalid() , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , invalid() , invalid()],
|
||||
]
|
||||
);
|
||||
assert_same_matrix(&dfa,&expected);
|
||||
assert_same_alphabet(&dfa, &nfa);
|
||||
let expected = Matrix::from(vec![
|
||||
vec![invalid(), 1, invalid(), 2, invalid()],
|
||||
vec![invalid(), invalid(), invalid(), invalid(), invalid()],
|
||||
vec![invalid(), invalid(), invalid(), invalid(), invalid()],
|
||||
]);
|
||||
assert_same_matrix(&dfa, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dfa_pattern_seq() {
|
||||
let nfa = nfa::tests::pattern_seq();
|
||||
let dfa = Dfa::from(&nfa.nfa);
|
||||
assert_same_alphabet(&dfa,&nfa);
|
||||
let expected = Matrix::from(
|
||||
vec![
|
||||
vec![invalid() , 1 , invalid() , invalid() , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , 2 , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , invalid() , invalid()],
|
||||
]
|
||||
);
|
||||
assert_same_matrix(&dfa,&expected);
|
||||
assert_same_alphabet(&dfa, &nfa);
|
||||
let expected = Matrix::from(vec![
|
||||
vec![invalid(), 1, invalid(), invalid(), invalid()],
|
||||
vec![invalid(), invalid(), invalid(), 2, invalid()],
|
||||
vec![invalid(), invalid(), invalid(), invalid(), invalid()],
|
||||
]);
|
||||
assert_same_matrix(&dfa, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dfa_pattern_many() {
|
||||
let nfa = nfa::tests::pattern_many();
|
||||
let dfa = Dfa::from(&nfa.nfa);
|
||||
assert_same_alphabet(&dfa,&nfa);
|
||||
let expected = Matrix::from(
|
||||
vec![
|
||||
vec![invalid() , 1 , invalid()],
|
||||
vec![invalid() , 1 , invalid()],
|
||||
]
|
||||
);
|
||||
assert_same_matrix(&dfa,&expected);
|
||||
assert_same_alphabet(&dfa, &nfa);
|
||||
let expected =
|
||||
Matrix::from(vec![vec![invalid(), 1, invalid()], vec![invalid(), 1, invalid()]]);
|
||||
assert_same_matrix(&dfa, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dfa_pattern_always() {
|
||||
let nfa = nfa::tests::pattern_always();
|
||||
let dfa = Dfa::from(&nfa.nfa);
|
||||
assert_same_alphabet(&dfa,&nfa);
|
||||
let expected = Matrix::from(
|
||||
vec![
|
||||
vec![invalid()]
|
||||
]
|
||||
);
|
||||
assert_same_matrix(&dfa,&expected);
|
||||
assert_same_alphabet(&dfa, &nfa);
|
||||
let expected = Matrix::from(vec![vec![invalid()]]);
|
||||
assert_same_matrix(&dfa, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dfa_pattern_never() {
|
||||
let nfa = nfa::tests::pattern_never();
|
||||
let dfa = Dfa::from(&nfa.nfa);
|
||||
assert_same_alphabet(&dfa,&nfa);
|
||||
let expected = Matrix::from(
|
||||
vec![
|
||||
vec![invalid()]
|
||||
]
|
||||
);
|
||||
assert_same_matrix(&dfa,&expected);
|
||||
assert_same_alphabet(&dfa, &nfa);
|
||||
let expected = Matrix::from(vec![vec![invalid()]]);
|
||||
assert_same_matrix(&dfa, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dfa_simple_rules() {
|
||||
let nfa = nfa::tests::simple_rules();
|
||||
let dfa = Dfa::from(&nfa.nfa);
|
||||
assert_same_alphabet(&dfa,&nfa);
|
||||
let expected = Matrix::from(
|
||||
vec![
|
||||
vec![invalid() , 1 , invalid() , invalid()],
|
||||
vec![invalid() , invalid() , 2 , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , invalid()],
|
||||
]
|
||||
);
|
||||
assert_same_matrix(&dfa,&expected);
|
||||
assert_same_alphabet(&dfa, &nfa);
|
||||
let expected = Matrix::from(vec![
|
||||
vec![invalid(), 1, invalid(), invalid()],
|
||||
vec![invalid(), invalid(), 2, invalid()],
|
||||
vec![invalid(), invalid(), invalid(), invalid()],
|
||||
]);
|
||||
assert_same_matrix(&dfa, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dfa_complex_rules() {
|
||||
let nfa = nfa::tests::complex_rules();
|
||||
let dfa = Dfa::from(&nfa.nfa);
|
||||
assert_same_alphabet(&dfa,&nfa);
|
||||
let expected = Matrix::from(
|
||||
vec![
|
||||
vec![1 , 2 , 1 , 1 , 1 , 1 , 3 ],
|
||||
vec![invalid() , invalid() , invalid() , invalid() , invalid() , invalid() , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , 4 , 5 , invalid() , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , invalid() , invalid() , invalid() , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , 6 , invalid() , invalid() , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , invalid() , 7 , invalid() , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , 6 , invalid() , invalid() , invalid()],
|
||||
vec![invalid() , invalid() , invalid() , invalid() , 7 , invalid() , invalid()],
|
||||
]
|
||||
);
|
||||
assert_same_matrix(&dfa,&expected);
|
||||
assert_same_alphabet(&dfa, &nfa);
|
||||
let expected = Matrix::from(vec![
|
||||
vec![1, 2, 1, 1, 1, 1, 3],
|
||||
vec![invalid(), invalid(), invalid(), invalid(), invalid(), invalid(), invalid()],
|
||||
vec![invalid(), invalid(), invalid(), 4, 5, invalid(), invalid()],
|
||||
vec![invalid(), invalid(), invalid(), invalid(), invalid(), invalid(), invalid()],
|
||||
vec![invalid(), invalid(), invalid(), 6, invalid(), invalid(), invalid()],
|
||||
vec![invalid(), invalid(), invalid(), invalid(), 7, invalid(), invalid()],
|
||||
vec![invalid(), invalid(), invalid(), 6, invalid(), invalid(), invalid()],
|
||||
vec![invalid(), invalid(), invalid(), invalid(), 7, invalid(), invalid()],
|
||||
]);
|
||||
assert_same_matrix(&dfa, &expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -335,53 +313,53 @@ pub mod tests {
|
||||
let nfa = nfa::tests::named_rules();
|
||||
let dfa = Dfa::from(&nfa.nfa);
|
||||
assert_same_alphabet(&dfa, &nfa);
|
||||
assert_eq!(dfa.sources.len(),5);
|
||||
assert_eq!(get_name(&nfa,&dfa,make_state(0)),None);
|
||||
assert_eq!(get_name(&nfa,&dfa,make_state(1)),Some(&String::from("rule_1")));
|
||||
assert_eq!(get_name(&nfa,&dfa,make_state(2)),Some(&String::from("rule_2")));
|
||||
assert_eq!(get_name(&nfa,&dfa,make_state(3)),Some(&String::from("rule_1")));
|
||||
assert_eq!(get_name(&nfa,&dfa,make_state(4)),Some(&String::from("rule_2")));
|
||||
assert_eq!(dfa.sources.len(), 5);
|
||||
assert_eq!(get_name(&nfa, &dfa, make_state(0)), None);
|
||||
assert_eq!(get_name(&nfa, &dfa, make_state(1)), Some(&String::from("rule_1")));
|
||||
assert_eq!(get_name(&nfa, &dfa, make_state(2)), Some(&String::from("rule_2")));
|
||||
assert_eq!(get_name(&nfa, &dfa, make_state(3)), Some(&String::from("rule_1")));
|
||||
assert_eq!(get_name(&nfa, &dfa, make_state(4)), Some(&String::from("rule_2")));
|
||||
}
|
||||
|
||||
// === The Benchmarks ===
|
||||
|
||||
#[bench]
|
||||
fn bench_to_dfa_pattern_range(bencher:&mut Bencher) {
|
||||
fn bench_to_dfa_pattern_range(bencher: &mut Bencher) {
|
||||
bencher.iter(|| Dfa::from(&nfa::tests::pattern_range().nfa))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_to_dfa_pattern_or(bencher:&mut Bencher) {
|
||||
fn bench_to_dfa_pattern_or(bencher: &mut Bencher) {
|
||||
bencher.iter(|| Dfa::from(&nfa::tests::pattern_or().nfa))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_to_dfa_pattern_seq(bencher:&mut Bencher) {
|
||||
fn bench_to_dfa_pattern_seq(bencher: &mut Bencher) {
|
||||
bencher.iter(|| Dfa::from(&nfa::tests::pattern_seq().nfa))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_to_dfa_pattern_many(bencher:&mut Bencher) {
|
||||
fn bench_to_dfa_pattern_many(bencher: &mut Bencher) {
|
||||
bencher.iter(|| Dfa::from(&nfa::tests::pattern_many().nfa))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_to_dfa_pattern_always(bencher:&mut Bencher) {
|
||||
fn bench_to_dfa_pattern_always(bencher: &mut Bencher) {
|
||||
bencher.iter(|| Dfa::from(&nfa::tests::pattern_always().nfa))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_to_dfa_pattern_never(bencher:&mut Bencher) {
|
||||
fn bench_to_dfa_pattern_never(bencher: &mut Bencher) {
|
||||
bencher.iter(|| Dfa::from(&nfa::tests::pattern_never().nfa))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_to_dfa_simple_rules(bencher:&mut Bencher) {
|
||||
fn bench_to_dfa_simple_rules(bencher: &mut Bencher) {
|
||||
bencher.iter(|| Dfa::from(&nfa::tests::simple_rules().nfa))
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_to_dfa_complex_rules(bencher:&mut Bencher) {
|
||||
fn bench_to_dfa_complex_rules(bencher: &mut Bencher) {
|
||||
bencher.iter(|| Dfa::from(&nfa::tests::complex_rules().nfa))
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
#![warn(unused_import_braces)]
|
||||
#![warn(unused_qualifications)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#![feature(test)]
|
||||
|
||||
pub mod alphabet;
|
||||
|
@ -3,11 +3,11 @@
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::alphabet;
|
||||
use crate::pattern::Pattern;
|
||||
use crate::state::Transition;
|
||||
use crate::state;
|
||||
use crate::symbol::Symbol;
|
||||
use crate::data::matrix::Matrix;
|
||||
use crate::pattern::Pattern;
|
||||
use crate::state;
|
||||
use crate::state::Transition;
|
||||
use crate::symbol::Symbol;
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::ops::RangeInclusive;
|
||||
@ -46,21 +46,21 @@ pub type StateSetId = BTreeSet<State>;
|
||||
/// │ 0 │ ----> │ 1 │ -> │ 2 │ ----> │ 3 │ -> │ 3 │ ----> │ 3 │
|
||||
/// └───┘ └───┘ ε └───┘ └───┘ ε └───┘ └───┘
|
||||
/// ```
|
||||
#[derive(Clone,Debug,PartialEq,Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Nfa {
|
||||
pub start : State,
|
||||
pub(crate) alphabet : alphabet::Segmentation,
|
||||
pub(crate) states : Vec<state::Data>,
|
||||
pub start: State,
|
||||
pub(crate) alphabet: alphabet::Segmentation,
|
||||
pub(crate) states: Vec<state::Data>,
|
||||
}
|
||||
|
||||
impl Nfa {
|
||||
/// Constructor.
|
||||
pub fn new() -> Self {
|
||||
let start = default();
|
||||
let start = default();
|
||||
let alphabet = default();
|
||||
let states = default();
|
||||
Self {start,alphabet,states}.init_start_state()
|
||||
let states = default();
|
||||
Self { start, alphabet, states }.init_start_state()
|
||||
}
|
||||
|
||||
/// Initialize the start state of the automaton.
|
||||
@ -99,7 +99,7 @@ impl Nfa {
|
||||
///
|
||||
/// Whenever the automaton happens to be in `source` state it can immediately transition to the
|
||||
/// `target` state. It is, however, not _required_ to do so.
|
||||
pub fn connect(&mut self, source:State, target:State) {
|
||||
pub fn connect(&mut self, source: State, target: State) {
|
||||
self[source].epsilon_links.push(target);
|
||||
}
|
||||
|
||||
@ -107,9 +107,9 @@ impl Nfa {
|
||||
///
|
||||
/// If any symbol from such range happens to be the input when the automaton is in the `source`
|
||||
/// state, it will immediately transition to the `target` state.
|
||||
pub fn connect_via(&mut self, source:State, target:State, symbols:&RangeInclusive<Symbol>) {
|
||||
pub fn connect_via(&mut self, source: State, target: State, symbols: &RangeInclusive<Symbol>) {
|
||||
self.alphabet.insert(symbols.clone());
|
||||
self[source].links.push(Transition::new(symbols.clone(),target));
|
||||
self[source].links.push(Transition::new(symbols.clone(), target));
|
||||
}
|
||||
|
||||
// FIXME[WD]: It seems that it should be possible to simplify this function. This would
|
||||
@ -122,39 +122,39 @@ impl Nfa {
|
||||
/// Transforms a pattern to connected NFA states by using the algorithm described
|
||||
/// [here](https://www.youtube.com/watch?v=RYNN-tb9WxI).
|
||||
/// The asymptotic complexity is linear in number of symbols.
|
||||
pub fn new_pattern(&mut self, source:State, pattern:impl AsRef<Pattern>) -> State {
|
||||
pub fn new_pattern(&mut self, source: State, pattern: impl AsRef<Pattern>) -> State {
|
||||
let pattern = pattern.as_ref();
|
||||
let current = self.new_state();
|
||||
self.connect(source,current);
|
||||
self.connect(source, current);
|
||||
let state = match pattern {
|
||||
Pattern::Range(range) => {
|
||||
let state = self.new_state();
|
||||
self.connect_via(current,state,range);
|
||||
self.connect_via(current, state, range);
|
||||
state
|
||||
},
|
||||
}
|
||||
Pattern::Many(body) => {
|
||||
let s1 = self.new_state();
|
||||
let s2 = self.new_pattern(s1,body);
|
||||
let s2 = self.new_pattern(s1, body);
|
||||
let s3 = self.new_state();
|
||||
self.connect(current,s1);
|
||||
self.connect(current,s3);
|
||||
self.connect(s2,s3);
|
||||
self.connect(s3,s1);
|
||||
self.connect(current, s1);
|
||||
self.connect(current, s3);
|
||||
self.connect(s2, s3);
|
||||
self.connect(s3, s1);
|
||||
s3
|
||||
},
|
||||
Pattern::Seq(patterns) => {
|
||||
patterns.iter().fold(current,|s,pat| self.new_pattern(s,pat))
|
||||
},
|
||||
}
|
||||
Pattern::Seq(patterns) =>
|
||||
patterns.iter().fold(current, |s, pat| self.new_pattern(s, pat)),
|
||||
Pattern::Or(patterns) => {
|
||||
let states = patterns.iter().map(|pat| self.new_pattern(current,pat)).collect_vec();
|
||||
let end = self.new_state();
|
||||
let states =
|
||||
patterns.iter().map(|pat| self.new_pattern(current, pat)).collect_vec();
|
||||
let end = self.new_state();
|
||||
for state in states {
|
||||
self.connect(state,end);
|
||||
self.connect(state, end);
|
||||
}
|
||||
end
|
||||
},
|
||||
}
|
||||
Pattern::Always => current,
|
||||
Pattern::Never => self.new_state(),
|
||||
Pattern::Never => self.new_state(),
|
||||
};
|
||||
self[state].export = true;
|
||||
state
|
||||
@ -164,37 +164,36 @@ impl Nfa {
|
||||
/// [here](https://www.youtube.com/watch?v=RYNN-tb9WxI). This function is similar to
|
||||
/// `new_pattern`, but it consumes an explicit target state.
|
||||
/// The asymptotic complexity is linear in number of symbols.
|
||||
pub fn new_pattern_to(&mut self, source:State, target:State, pattern:impl AsRef<Pattern>) {
|
||||
pub fn new_pattern_to(&mut self, source: State, target: State, pattern: impl AsRef<Pattern>) {
|
||||
let pattern = pattern.as_ref();
|
||||
let current = self.new_state();
|
||||
self.connect(source,current);
|
||||
self.connect(source, current);
|
||||
match pattern {
|
||||
Pattern::Range(range) => {
|
||||
self.connect_via(current,target,range);
|
||||
},
|
||||
self.connect_via(current, target, range);
|
||||
}
|
||||
Pattern::Many(body) => {
|
||||
let s1 = self.new_state();
|
||||
let s2 = self.new_pattern(s1,body);
|
||||
let s2 = self.new_pattern(s1, body);
|
||||
let target = self.new_state();
|
||||
self.connect(current,s1);
|
||||
self.connect(current,target);
|
||||
self.connect(s2,target);
|
||||
self.connect(target,s1);
|
||||
},
|
||||
self.connect(current, s1);
|
||||
self.connect(current, target);
|
||||
self.connect(s2, target);
|
||||
self.connect(target, s1);
|
||||
}
|
||||
Pattern::Seq(patterns) => {
|
||||
let out = patterns.iter().fold(current,|s,pat| self.new_pattern(s,pat));
|
||||
self.connect(out,target)
|
||||
},
|
||||
let out = patterns.iter().fold(current, |s, pat| self.new_pattern(s, pat));
|
||||
self.connect(out, target)
|
||||
}
|
||||
Pattern::Or(patterns) => {
|
||||
let states = patterns.iter().map(|pat| self.new_pattern(current,pat)).collect_vec();
|
||||
let states =
|
||||
patterns.iter().map(|pat| self.new_pattern(current, pat)).collect_vec();
|
||||
for state in states {
|
||||
self.connect(state,target);
|
||||
self.connect(state, target);
|
||||
}
|
||||
},
|
||||
Pattern::Always => {
|
||||
self.connect(current,target)
|
||||
},
|
||||
Pattern::Never => {},
|
||||
}
|
||||
Pattern::Always => self.connect(current, target),
|
||||
Pattern::Never => {}
|
||||
};
|
||||
self[target].export = true;
|
||||
}
|
||||
@ -202,18 +201,18 @@ impl Nfa {
|
||||
/// Merges states that are connected by epsilon links, using an algorithm based on the one shown
|
||||
/// [here](https://www.youtube.com/watch?v=taClnxU-nao).
|
||||
pub fn eps_matrix(&self) -> Vec<StateSetId> {
|
||||
fn fill_eps_matrix
|
||||
( nfa : &Nfa
|
||||
, states : &mut Vec<StateSetId>
|
||||
, visited : &mut Vec<bool>
|
||||
, state : State
|
||||
fn fill_eps_matrix(
|
||||
nfa: &Nfa,
|
||||
states: &mut Vec<StateSetId>,
|
||||
visited: &mut Vec<bool>,
|
||||
state: State,
|
||||
) {
|
||||
let mut state_set = StateSetId::new();
|
||||
visited[state.id()] = true;
|
||||
state_set.insert(state);
|
||||
for &target in &nfa[state].epsilon_links {
|
||||
if !visited[target.id()] {
|
||||
fill_eps_matrix(nfa,states,visited,target);
|
||||
fill_eps_matrix(nfa, states, visited, target);
|
||||
}
|
||||
state_set.insert(target);
|
||||
state_set.extend(states[target.id()].iter());
|
||||
@ -224,19 +223,19 @@ impl Nfa {
|
||||
let mut states = vec![StateSetId::new(); self.states.len()];
|
||||
for id in 0..self.states.len() {
|
||||
let mut visited = vec![false; states.len()];
|
||||
fill_eps_matrix(self,&mut states,&mut visited,State::new(id));
|
||||
fill_eps_matrix(self, &mut states, &mut visited, State::new(id));
|
||||
}
|
||||
states
|
||||
}
|
||||
|
||||
/// Computes a transition matrix `(state, symbol) => state` for the Nfa, ignoring epsilon links.
|
||||
pub fn nfa_matrix(&self) -> Matrix<State> {
|
||||
let mut matrix = Matrix::new(self.states.len(),self.alphabet.divisions.len());
|
||||
let mut matrix = Matrix::new(self.states.len(), self.alphabet.divisions.len());
|
||||
|
||||
for (state_ix, source) in self.states.iter().enumerate() {
|
||||
let targets = source.targets(&self.alphabet);
|
||||
for (voc_ix, &target) in targets.iter().enumerate() {
|
||||
matrix[(state_ix,voc_ix)] = target;
|
||||
matrix[(state_ix, voc_ix)] = target;
|
||||
}
|
||||
}
|
||||
matrix
|
||||
@ -245,23 +244,25 @@ impl Nfa {
|
||||
/// Convert the automata to a GraphViz Dot code for the deubgging purposes.
|
||||
pub fn as_graphviz_code(&self) -> String {
|
||||
let mut out = String::new();
|
||||
for (ix,state) in self.states.iter().enumerate() {
|
||||
let opts = if state.export { "" } else {
|
||||
"[fillcolor=\"#EEEEEE\" fontcolor=\"#888888\"]"
|
||||
};
|
||||
out += &format!("node_{}[label=\"{}\"]{}\n",ix,ix,opts);
|
||||
for (ix, state) in self.states.iter().enumerate() {
|
||||
let opts =
|
||||
if state.export { "" } else { "[fillcolor=\"#EEEEEE\" fontcolor=\"#888888\"]" };
|
||||
out += &format!("node_{}[label=\"{}\"]{}\n", ix, ix, opts);
|
||||
for link in &state.links {
|
||||
out += &format!(
|
||||
"node_{} -> node_{}[label=\"{}\"]\n",ix,link.target.id(),link.display_symbols()
|
||||
"node_{} -> node_{}[label=\"{}\"]\n",
|
||||
ix,
|
||||
link.target.id(),
|
||||
link.display_symbols()
|
||||
);
|
||||
}
|
||||
for link in &state.epsilon_links {
|
||||
out += &format!("node_{} -> node_{}[style=dashed]\n",ix,link.id());
|
||||
out += &format!("node_{} -> node_{}[style=dashed]\n", ix, link.id());
|
||||
}
|
||||
}
|
||||
let opts = "node [shape=circle style=filled fillcolor=\"#4385f5\" fontcolor=\"#FFFFFF\" \
|
||||
color=white penwidth=5.0 margin=0.1 width=0.5 height=0.5 fixedsize=true]";
|
||||
format!("digraph G {{\n{}\n{}\n}}\n",opts,out)
|
||||
format!("digraph G {{\n{}\n{}\n}}\n", opts, out)
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,13 +274,13 @@ impl Default for Nfa {
|
||||
|
||||
impl Index<State> for Nfa {
|
||||
type Output = state::Data;
|
||||
fn index(&self, state:State) -> &Self::Output {
|
||||
fn index(&self, state: State) -> &Self::Output {
|
||||
&self.states[state.id()]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<State> for Nfa {
|
||||
fn index_mut(&mut self, state:State) -> &mut Self::Output {
|
||||
fn index_mut(&mut self, state: State) -> &mut Self::Output {
|
||||
&mut self.states[state.id()]
|
||||
}
|
||||
}
|
||||
@ -297,72 +298,73 @@ pub mod tests {
|
||||
// === Test Utilities ===
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone,Debug,Default,PartialEq)]
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct NfaTest {
|
||||
pub nfa : Nfa,
|
||||
pub start_state_id : State,
|
||||
pub pattern_state_ids : Vec<State>,
|
||||
pub end_state_id : State,
|
||||
pub callbacks : HashMap<State,String>,
|
||||
pub names : HashMap<State,String>,
|
||||
pub nfa: Nfa,
|
||||
pub start_state_id: State,
|
||||
pub pattern_state_ids: Vec<State>,
|
||||
pub end_state_id: State,
|
||||
pub callbacks: HashMap<State, String>,
|
||||
pub names: HashMap<State, String>,
|
||||
}
|
||||
#[allow(missing_docs)]
|
||||
impl NfaTest {
|
||||
pub fn make(patterns:Vec<Pattern>) -> Self {
|
||||
let mut nfa = Nfa::default();
|
||||
let start_state_id = nfa.start;
|
||||
pub fn make(patterns: Vec<Pattern>) -> Self {
|
||||
let mut nfa = Nfa::default();
|
||||
let start_state_id = nfa.start;
|
||||
let mut pattern_state_ids = vec![];
|
||||
let end_state_id = nfa.new_state_exported();
|
||||
let end_state_id = nfa.new_state_exported();
|
||||
for pattern in patterns {
|
||||
let id = nfa.new_pattern(start_state_id,&pattern);
|
||||
let id = nfa.new_pattern(start_state_id, &pattern);
|
||||
pattern_state_ids.push(id);
|
||||
nfa.connect(id,end_state_id);
|
||||
nfa.connect(id, end_state_id);
|
||||
}
|
||||
let callbacks = default();
|
||||
let names = default();
|
||||
Self{nfa,start_state_id,pattern_state_ids,end_state_id,callbacks,names}
|
||||
let names = default();
|
||||
Self { nfa, start_state_id, pattern_state_ids, end_state_id, callbacks, names }
|
||||
}
|
||||
|
||||
pub fn make_rules(rules:Vec<Rule>) -> Self {
|
||||
let mut nfa = Nfa::default();
|
||||
let start_state_id = nfa.start;
|
||||
let mut pattern_state_ids = vec![];
|
||||
let end_state_id = nfa.new_state_exported();
|
||||
let mut callbacks:HashMap<_,_> = default();
|
||||
let mut names:HashMap<_,_> = default();
|
||||
pub fn make_rules(rules: Vec<Rule>) -> Self {
|
||||
let mut nfa = Nfa::default();
|
||||
let start_state_id = nfa.start;
|
||||
let mut pattern_state_ids = vec![];
|
||||
let end_state_id = nfa.new_state_exported();
|
||||
let mut callbacks: HashMap<_, _> = default();
|
||||
let mut names: HashMap<_, _> = default();
|
||||
for rule in rules {
|
||||
let id = nfa.new_pattern(start_state_id,&rule.pattern);
|
||||
callbacks.insert(id,rule.callback.clone());
|
||||
names.insert(id,rule.name.clone());
|
||||
let id = nfa.new_pattern(start_state_id, &rule.pattern);
|
||||
callbacks.insert(id, rule.callback.clone());
|
||||
names.insert(id, rule.name.clone());
|
||||
pattern_state_ids.push(id);
|
||||
nfa.connect(id,end_state_id);
|
||||
nfa.connect(id, end_state_id);
|
||||
}
|
||||
Self{nfa,start_state_id,pattern_state_ids,end_state_id,callbacks,names}
|
||||
Self { nfa, start_state_id, pattern_state_ids, end_state_id, callbacks, names }
|
||||
}
|
||||
|
||||
pub fn callback(&self, state:State) -> Option<&String> {
|
||||
pub fn callback(&self, state: State) -> Option<&String> {
|
||||
self.callbacks.get(&state)
|
||||
}
|
||||
|
||||
pub fn name(&self, state:State) -> Option<&String> {
|
||||
pub fn name(&self, state: State) -> Option<&String> {
|
||||
self.names.get(&state)
|
||||
}
|
||||
|
||||
pub fn id(id:usize) -> State {
|
||||
pub fn id(id: usize) -> State {
|
||||
State::new(id)
|
||||
}
|
||||
|
||||
pub fn has_transition(&self, trigger:RangeInclusive<Symbol>, target:State) -> bool {
|
||||
self.states.iter().any(|r| r.links().iter().any(|transition | {
|
||||
pub fn has_transition(&self, trigger: RangeInclusive<Symbol>, target: State) -> bool {
|
||||
self.states.iter().any(|r| {
|
||||
r.links().iter().any(|transition| {
|
||||
(transition.symbols == trigger) && transition.target == target
|
||||
}))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_epsilon(&self, from:State, to:State) -> bool {
|
||||
self.states.iter().enumerate().fold(false,|l,(ix,r)| {
|
||||
let state_has = ix == from.id() && r.epsilon_links().iter().any(|ident| {
|
||||
*ident == to
|
||||
});
|
||||
pub fn has_epsilon(&self, from: State, to: State) -> bool {
|
||||
self.states.iter().enumerate().fold(false, |l, (ix, r)| {
|
||||
let state_has =
|
||||
ix == from.id() && r.epsilon_links().iter().any(|ident| *ident == to);
|
||||
l || state_has
|
||||
})
|
||||
}
|
||||
@ -376,19 +378,19 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Rule {
|
||||
pattern : Pattern,
|
||||
callback : String,
|
||||
name : String
|
||||
pattern: Pattern,
|
||||
callback: String,
|
||||
name: String,
|
||||
}
|
||||
#[allow(missing_docs)]
|
||||
impl Rule {
|
||||
pub fn new(pattern:&Pattern, callback:impl Str, name:impl Str) -> Rule {
|
||||
let pattern = pattern.clone();
|
||||
pub fn new(pattern: &Pattern, callback: impl Str, name: impl Str) -> Rule {
|
||||
let pattern = pattern.clone();
|
||||
let callback = callback.into();
|
||||
let name = name.into();
|
||||
Rule{pattern,callback,name}
|
||||
let name = name.into();
|
||||
Rule { pattern, callback, name }
|
||||
}
|
||||
}
|
||||
|
||||
@ -426,29 +428,29 @@ pub mod tests {
|
||||
}
|
||||
|
||||
pub fn simple_rules() -> NfaTest {
|
||||
let a = Pattern::char('a');
|
||||
let b = Pattern::char('b');
|
||||
let ab = &a >> &b;
|
||||
NfaTest::make(vec![a,ab])
|
||||
let a = Pattern::char('a');
|
||||
let b = Pattern::char('b');
|
||||
let ab = &a >> &b;
|
||||
NfaTest::make(vec![a, ab])
|
||||
}
|
||||
|
||||
pub fn complex_rules() -> NfaTest {
|
||||
let a_word = Pattern::char('a').many1();
|
||||
let b_word = Pattern::char('b').many1();
|
||||
let space = Pattern::char(' ');
|
||||
let a_word = Pattern::char('a').many1();
|
||||
let b_word = Pattern::char('b').many1();
|
||||
let space = Pattern::char(' ');
|
||||
let spaced_a_word = &space >> &a_word;
|
||||
let spaced_b_word = &space >> &b_word;
|
||||
let any = Pattern::any();
|
||||
let end = Pattern::eof();
|
||||
NfaTest::make(vec![spaced_a_word,spaced_b_word,end,any])
|
||||
let any = Pattern::any();
|
||||
let end = Pattern::eof();
|
||||
NfaTest::make(vec![spaced_a_word, spaced_b_word, end, any])
|
||||
}
|
||||
|
||||
pub fn named_rules() -> NfaTest {
|
||||
let a_word = Pattern::char('a').many1();
|
||||
let b_word = Pattern::char('b').many1();
|
||||
let rules = vec![
|
||||
Rule::new(&a_word,"self.on_a_word(reader)","rule_1"),
|
||||
Rule::new(&b_word,"self.on_b_word(reader)","rule_2"),
|
||||
Rule::new(&a_word, "self.on_a_word(reader)", "rule_1"),
|
||||
Rule::new(&b_word, "self.on_b_word(reader)", "rule_2"),
|
||||
];
|
||||
NfaTest::make_rules(rules)
|
||||
}
|
||||
@ -463,10 +465,10 @@ pub mod tests {
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(0u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(97u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(123u64)));
|
||||
assert_eq!(nfa.states.len(),4);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id,NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0],nfa.end_state_id));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('z'),nfa.pattern_state_ids[0]));
|
||||
assert_eq!(nfa.states.len(), 4);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id, NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0], nfa.end_state_id));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('z'), nfa.pattern_state_ids[0]));
|
||||
assert!(nfa[nfa.start_state_id].export);
|
||||
assert!(nfa[nfa.pattern_state_ids[0]].export);
|
||||
assert!(nfa[nfa.end_state_id].export);
|
||||
@ -481,15 +483,15 @@ pub mod tests {
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(98u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(100u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(101u64)));
|
||||
assert_eq!(nfa.states.len(),8);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id,NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(2),NfaTest::id(3)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(2),NfaTest::id(5)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(6),nfa.pattern_state_ids[0]));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(4),nfa.pattern_state_ids[0]));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0],nfa.end_state_id));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'),NfaTest::id(4)));
|
||||
assert!(nfa.has_transition(Symbol::from('d')..=Symbol::from('d'),NfaTest::id(6)));
|
||||
assert_eq!(nfa.states.len(), 8);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id, NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(2), NfaTest::id(3)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(2), NfaTest::id(5)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(6), nfa.pattern_state_ids[0]));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(4), nfa.pattern_state_ids[0]));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0], nfa.end_state_id));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'), NfaTest::id(4)));
|
||||
assert!(nfa.has_transition(Symbol::from('d')..=Symbol::from('d'), NfaTest::id(6)));
|
||||
assert!(nfa[nfa.start_state_id].export);
|
||||
assert!(nfa[nfa.pattern_state_ids[0]].export);
|
||||
assert!(nfa[nfa.end_state_id].export);
|
||||
@ -504,13 +506,13 @@ pub mod tests {
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(98u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(100u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(101u64)));
|
||||
assert_eq!(nfa.states.len(),7);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id,NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(2),NfaTest::id(3)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(4),NfaTest::id(5)));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0],nfa.end_state_id));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'),NfaTest::id(4)));
|
||||
assert!(nfa.has_transition(Symbol::from('d')..=Symbol::from('d'),NfaTest::id(6)));
|
||||
assert_eq!(nfa.states.len(), 7);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id, NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(2), NfaTest::id(3)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(4), NfaTest::id(5)));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0], nfa.end_state_id));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'), NfaTest::id(4)));
|
||||
assert!(nfa.has_transition(Symbol::from('d')..=Symbol::from('d'), NfaTest::id(6)));
|
||||
assert!(nfa[nfa.start_state_id].export);
|
||||
assert!(nfa[nfa.pattern_state_ids[0]].export);
|
||||
assert!(nfa[nfa.end_state_id].export);
|
||||
@ -523,14 +525,14 @@ pub mod tests {
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(0u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(97u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(98u64)));
|
||||
assert_eq!(nfa.states.len(),7);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id,NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(2),NfaTest::id(3)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(3),NfaTest::id(4)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(5),nfa.pattern_state_ids[0]));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0],NfaTest::id(3)));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0],nfa.end_state_id));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'),NfaTest::id(5)));
|
||||
assert_eq!(nfa.states.len(), 7);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id, NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(2), NfaTest::id(3)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(3), NfaTest::id(4)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(5), nfa.pattern_state_ids[0]));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0], NfaTest::id(3)));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0], nfa.end_state_id));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'), NfaTest::id(5)));
|
||||
assert!(nfa[nfa.start_state_id].export);
|
||||
assert!(nfa[nfa.pattern_state_ids[0]].export);
|
||||
assert!(nfa[nfa.end_state_id].export);
|
||||
@ -541,9 +543,9 @@ pub mod tests {
|
||||
let nfa = pattern_always();
|
||||
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(0u64)));
|
||||
assert_eq!(nfa.states.len(),3);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id,nfa.pattern_state_ids[0]));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0],nfa.end_state_id));
|
||||
assert_eq!(nfa.states.len(), 3);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id, nfa.pattern_state_ids[0]));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0], nfa.end_state_id));
|
||||
assert!(nfa[nfa.start_state_id].export);
|
||||
assert!(nfa[nfa.pattern_state_ids[0]].export);
|
||||
assert!(nfa[nfa.end_state_id].export);
|
||||
@ -554,9 +556,9 @@ pub mod tests {
|
||||
let nfa = pattern_never();
|
||||
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(0u64)));
|
||||
assert_eq!(nfa.states.len(),4);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id,NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(3),nfa.end_state_id));
|
||||
assert_eq!(nfa.states.len(), 4);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id, NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(3), nfa.end_state_id));
|
||||
assert!(nfa[nfa.start_state_id].export);
|
||||
assert!(nfa[nfa.pattern_state_ids[0]].export);
|
||||
assert!(nfa[nfa.end_state_id].export);
|
||||
@ -570,16 +572,16 @@ pub mod tests {
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(97u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(98u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(99u64)));
|
||||
assert_eq!(nfa.states.len(),9);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id,NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id,NfaTest::id(4)));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0],nfa.end_state_id));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(4),NfaTest::id(5)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(6),NfaTest::id(7)));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[1],nfa.end_state_id));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'),nfa.pattern_state_ids[0]));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'),NfaTest::id(6)));
|
||||
assert!(nfa.has_transition(Symbol::from('b')..=Symbol::from('b'),nfa.pattern_state_ids[1]));
|
||||
assert_eq!(nfa.states.len(), 9);
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id, NfaTest::id(2)));
|
||||
assert!(nfa.has_epsilon(nfa.start_state_id, NfaTest::id(4)));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[0], nfa.end_state_id));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(4), NfaTest::id(5)));
|
||||
assert!(nfa.has_epsilon(NfaTest::id(6), NfaTest::id(7)));
|
||||
assert!(nfa.has_epsilon(nfa.pattern_state_ids[1], nfa.end_state_id));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'), nfa.pattern_state_ids[0]));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'), NfaTest::id(6)));
|
||||
assert!(nfa.has_transition(Symbol::from('b')..=Symbol::from('b'), nfa.pattern_state_ids[1]));
|
||||
assert!(nfa[nfa.start_state_id].export);
|
||||
assert!(nfa[nfa.pattern_state_ids[0]].export);
|
||||
assert!(nfa[nfa.pattern_state_ids[1]].export);
|
||||
@ -597,15 +599,15 @@ pub mod tests {
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(98u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::from(99u64)));
|
||||
assert!(nfa.alphabet.divisions().contains(&Symbol::eof()));
|
||||
assert_eq!(nfa.states.len(),26);
|
||||
assert!(nfa.has_transition(Symbol::from(' ')..=Symbol::from(' '),NfaTest::id(4)));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'),NfaTest::id(6)));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'),NfaTest::id(10)));
|
||||
assert!(nfa.has_transition(Symbol::from(' ')..=Symbol::from(' '),NfaTest::id(14)));
|
||||
assert!(nfa.has_transition(Symbol::from('b')..=Symbol::from('b'),NfaTest::id(16)));
|
||||
assert!(nfa.has_transition(Symbol::from('b')..=Symbol::from('b'),NfaTest::id(20)));
|
||||
assert!(nfa.has_transition(Symbol::eof()..=Symbol::eof(),nfa.pattern_state_ids[2]));
|
||||
assert!(nfa.has_transition(Symbol::null()..=Symbol::eof(),nfa.pattern_state_ids[3]));
|
||||
assert_eq!(nfa.states.len(), 26);
|
||||
assert!(nfa.has_transition(Symbol::from(' ')..=Symbol::from(' '), NfaTest::id(4)));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'), NfaTest::id(6)));
|
||||
assert!(nfa.has_transition(Symbol::from('a')..=Symbol::from('a'), NfaTest::id(10)));
|
||||
assert!(nfa.has_transition(Symbol::from(' ')..=Symbol::from(' '), NfaTest::id(14)));
|
||||
assert!(nfa.has_transition(Symbol::from('b')..=Symbol::from('b'), NfaTest::id(16)));
|
||||
assert!(nfa.has_transition(Symbol::from('b')..=Symbol::from('b'), NfaTest::id(20)));
|
||||
assert!(nfa.has_transition(Symbol::eof()..=Symbol::eof(), nfa.pattern_state_ids[2]));
|
||||
assert!(nfa.has_transition(Symbol::null()..=Symbol::eof(), nfa.pattern_state_ids[3]));
|
||||
assert!(nfa[nfa.start_state_id].export);
|
||||
assert!(nfa[nfa.pattern_state_ids[0]].export);
|
||||
assert!(nfa[nfa.pattern_state_ids[1]].export);
|
||||
@ -618,7 +620,7 @@ pub mod tests {
|
||||
fn nfa_named_rules() {
|
||||
let nfa = named_rules();
|
||||
|
||||
assert_eq!(nfa.states.len(),18);
|
||||
assert_eq!(nfa.states.len(), 18);
|
||||
for (ix, _) in nfa.states.iter().enumerate() {
|
||||
let state_id = State::new(ix);
|
||||
if nfa.pattern_state_ids.contains(&state_id) {
|
||||
@ -629,12 +631,12 @@ pub mod tests {
|
||||
assert!(nfa.callback(state_id).is_none());
|
||||
}
|
||||
}
|
||||
assert_eq!(nfa.name(nfa.pattern_state_ids[0]),Some(&("rule_1".to_string())));
|
||||
assert_eq!(nfa.name(nfa.pattern_state_ids[0]), Some(&("rule_1".to_string())));
|
||||
assert_eq!(
|
||||
nfa.callback(nfa.pattern_state_ids[0]),
|
||||
Some(&("self.on_a_word(reader)".to_string()))
|
||||
);
|
||||
assert_eq!(nfa.name(nfa.pattern_state_ids[1]),Some(&("rule_2".to_string())));
|
||||
assert_eq!(nfa.name(nfa.pattern_state_ids[1]), Some(&("rule_2".to_string())));
|
||||
assert_eq!(
|
||||
nfa.callback(nfa.pattern_state_ids[1]),
|
||||
Some(&("self.on_b_word(reader)".to_string()))
|
||||
|
@ -15,7 +15,7 @@ use std::ops::Shr;
|
||||
// =============
|
||||
|
||||
/// A representation of a simple regular pattern.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Pattern {
|
||||
/// The pattern that triggers on any symbol from the given range.
|
||||
Range(RangeInclusive<Symbol>),
|
||||
@ -32,7 +32,6 @@ pub enum Pattern {
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
|
||||
/// A pattern that never triggers.
|
||||
pub fn never() -> Self {
|
||||
Pattern::Never
|
||||
@ -74,17 +73,17 @@ impl Pattern {
|
||||
}
|
||||
|
||||
/// A pattern that triggers on the given character.
|
||||
pub fn char(character:char) -> Self {
|
||||
pub fn char(character: char) -> Self {
|
||||
Self::symbol(&Symbol::from(character))
|
||||
}
|
||||
|
||||
/// A pattern that triggers on the given symbol.
|
||||
pub fn symbol(symbol:&Symbol) -> Self {
|
||||
pub fn symbol(symbol: &Symbol) -> Self {
|
||||
Pattern::symbols(symbol.clone()..=symbol.clone())
|
||||
}
|
||||
|
||||
/// A pattern that triggers on any of the provided `symbols`.
|
||||
pub fn symbols(symbols:RangeInclusive<Symbol>) -> Self {
|
||||
pub fn symbols(symbols: RangeInclusive<Symbol>) -> Self {
|
||||
Pattern::Range(symbols)
|
||||
}
|
||||
|
||||
@ -94,18 +93,18 @@ impl Pattern {
|
||||
}
|
||||
|
||||
/// A pattern that triggers on any character in the provided `range`.
|
||||
pub fn range(range:RangeInclusive<char>) -> Self {
|
||||
pub fn range(range: RangeInclusive<char>) -> Self {
|
||||
Pattern::symbols(Symbol::from(*range.start())..=Symbol::from(*range.end()))
|
||||
}
|
||||
|
||||
/// Pattern that triggers when sequence of characters given by `chars` is encountered.
|
||||
pub fn all_of(chars:&str) -> Self {
|
||||
chars.chars().fold(Self::always(),|pat,char| pat >> Self::char(char))
|
||||
pub fn all_of(chars: &str) -> Self {
|
||||
chars.chars().fold(Self::always(), |pat, char| pat >> Self::char(char))
|
||||
}
|
||||
|
||||
/// The pattern that triggers on any characters contained in `chars`.
|
||||
pub fn any_of(chars:&str) -> Self {
|
||||
chars.chars().fold(Self::never(),|pat,char| pat | Self::char(char))
|
||||
pub fn any_of(chars: &str) -> Self {
|
||||
chars.chars().fold(Self::never(), |pat, char| pat | Self::char(char))
|
||||
}
|
||||
|
||||
/// The pattern that doesn't trigger on any character contained in `chars`.
|
||||
@ -113,10 +112,10 @@ impl Pattern {
|
||||
/// This pattern will _always_ implicitly include [`Symbol::NULL`] and [`Symbol::EOF_CODE`] in
|
||||
/// the excluded characters. If you do not want this behaviour instead use
|
||||
/// [`Pattern::none_of_codes`] below.
|
||||
pub fn none_of(chars:&str) -> Self {
|
||||
let min = Symbol::null();
|
||||
let max = Symbol::eof();
|
||||
let iter = iter::once(min.index)
|
||||
pub fn none_of(chars: &str) -> Self {
|
||||
let min = Symbol::null();
|
||||
let max = Symbol::eof();
|
||||
let iter = iter::once(min.index)
|
||||
.chain(chars.chars().map(|c| c as u64))
|
||||
.chain(iter::once(max.index))
|
||||
.collect_vec();
|
||||
@ -124,49 +123,53 @@ impl Pattern {
|
||||
.chain(chars.chars().map(|c| c.to_string()))
|
||||
.chain(iter::once(max.name))
|
||||
.collect_vec();
|
||||
Self::none_of_codes(iter.as_slice(),names.as_slice())
|
||||
Self::none_of_codes(iter.as_slice(), names.as_slice())
|
||||
}
|
||||
|
||||
/// This pattern doesn't trigger on any code contained in `codes`.
|
||||
pub fn none_of_codes(codes:&[u64],names:&[String]) -> Self {
|
||||
pub fn none_of_codes(codes: &[u64], names: &[String]) -> Self {
|
||||
assert_eq!(codes.len(), names.len(), "`codes` and `names`must have the same length.");
|
||||
let mut codes = Vec::from(codes);
|
||||
codes.sort_unstable();
|
||||
codes.dedup();
|
||||
let pattern = codes.iter().tuple_windows().zip(names)
|
||||
.fold(Self::never(),|pat,((prev_code,next_code),name)| {
|
||||
let pattern = codes.iter().tuple_windows().zip(names).fold(
|
||||
Self::never(),
|
||||
|pat, ((prev_code, next_code), name)| {
|
||||
let start = prev_code + 1;
|
||||
let end = next_code - 1;
|
||||
if end < start { pat } else {
|
||||
pat | Pattern::symbols(Symbol::new_named(start,name)..=Symbol::from(end))
|
||||
let end = next_code - 1;
|
||||
if end < start {
|
||||
pat
|
||||
} else {
|
||||
pat | Pattern::symbols(Symbol::new_named(start, name)..=Symbol::from(end))
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
if codes.contains(&Symbol::null().index) && codes.contains(&Symbol::eof().index) {
|
||||
pattern
|
||||
} else if codes.contains(&Symbol::null().index) {
|
||||
let last = codes.last().unwrap() + 1;
|
||||
let last = codes.last().unwrap() + 1;
|
||||
let last_to_eof = Pattern::symbols(Symbol::from(last)..=Symbol::eof());
|
||||
pattern | last_to_eof
|
||||
} else if codes.contains(&Symbol::eof().index) {
|
||||
let first = codes.first().unwrap() - 1;
|
||||
let first = codes.first().unwrap() - 1;
|
||||
let null_to_first = Pattern::symbols(Symbol::eof()..=Symbol::from(first));
|
||||
null_to_first | pattern
|
||||
} else {
|
||||
let last = codes.last().unwrap() + 1;
|
||||
let last_to_eof = Pattern::symbols(Symbol::from(last)..=Symbol::eof());
|
||||
let first = codes.first().unwrap() - 1;
|
||||
let last = codes.last().unwrap() + 1;
|
||||
let last_to_eof = Pattern::symbols(Symbol::from(last)..=Symbol::eof());
|
||||
let first = codes.first().unwrap() - 1;
|
||||
let null_to_first = Pattern::symbols(Symbol::null()..=Symbol::from(first));
|
||||
null_to_first | pattern | last_to_eof
|
||||
}
|
||||
}
|
||||
|
||||
/// The pattern that triggers on any character but `char`.
|
||||
pub fn not(char:char) -> Self {
|
||||
pub fn not(char: char) -> Self {
|
||||
Self::none_of(&char.to_string())
|
||||
}
|
||||
|
||||
/// The pattern that triggers on any symbol but `symbol`.
|
||||
pub fn not_symbol(symbol:Symbol) -> Self {
|
||||
pub fn not_symbol(symbol: Symbol) -> Self {
|
||||
if symbol == Symbol::null() {
|
||||
Self::Range(Symbol::from(Symbol::null().index + 1)..=Symbol::eof())
|
||||
} else if symbol == Symbol::eof() {
|
||||
@ -174,20 +177,20 @@ impl Pattern {
|
||||
} else {
|
||||
let prev_code = Symbol::from(symbol.index - 1);
|
||||
let next_code = Symbol::from(symbol.index + 1);
|
||||
let before = Self::Range(Symbol::null()..=prev_code);
|
||||
let after = Self::Range(next_code..=Symbol::eof());
|
||||
let before = Self::Range(Symbol::null()..=prev_code);
|
||||
let after = Self::Range(next_code..=Symbol::eof());
|
||||
before | after
|
||||
}
|
||||
}
|
||||
|
||||
/// The pattern that triggers on `num` repetitions of `pat`.
|
||||
pub fn repeat(pat:&Pattern, num:usize) -> Self {
|
||||
(0..num).fold(Self::always(),|p,_| p >> pat.clone())
|
||||
pub fn repeat(pat: &Pattern, num: usize) -> Self {
|
||||
(0..num).fold(Self::always(), |p, _| p >> pat.clone())
|
||||
}
|
||||
|
||||
/// Pattern that triggers on `min`..`max` repetitions of `pat`.
|
||||
pub fn repeat_between(pat:&Pattern, min:usize, max:usize) -> Self {
|
||||
(min..max).fold(Self::never(),|p,n| p | Self::repeat(pat,n))
|
||||
pub fn repeat_between(pat: &Pattern, min: usize, max: usize) -> Self {
|
||||
(min..max).fold(Self::never(), |p, n| p | Self::repeat(pat, n))
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,13 +198,13 @@ impl Pattern {
|
||||
// === Trait Impls ====
|
||||
|
||||
impl From<&str> for Pattern {
|
||||
fn from(string:&str) -> Self {
|
||||
fn from(string: &str) -> Self {
|
||||
Pattern::all_of(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for Pattern {
|
||||
fn from(char:char) -> Self {
|
||||
fn from(char: char) -> Self {
|
||||
Pattern::char(char)
|
||||
}
|
||||
}
|
||||
@ -213,15 +216,23 @@ impl AsRef<Pattern> for Pattern {
|
||||
}
|
||||
|
||||
impl BitOr<Pattern> for Pattern {
|
||||
|
||||
type Output = Pattern;
|
||||
fn bitor(self, rhs:Pattern) -> Self::Output {
|
||||
fn bitor(self, rhs: Pattern) -> Self::Output {
|
||||
use Pattern::*;
|
||||
match (self, rhs) {
|
||||
(Or(mut lhs), Or( rhs)) => {lhs.extend(rhs) ; Or(lhs)},
|
||||
(Or(mut lhs), rhs ) => {lhs.push(rhs) ; Or(lhs)},
|
||||
(lhs , Or(mut rhs)) => {rhs.insert(0,lhs) ; Or(rhs)},
|
||||
(lhs , rhs ) => Or(vec![lhs,rhs]),
|
||||
(Or(mut lhs), Or(rhs)) => {
|
||||
lhs.extend(rhs);
|
||||
Or(lhs)
|
||||
}
|
||||
(Or(mut lhs), rhs) => {
|
||||
lhs.push(rhs);
|
||||
Or(lhs)
|
||||
}
|
||||
(lhs, Or(mut rhs)) => {
|
||||
rhs.insert(0, lhs);
|
||||
Or(rhs)
|
||||
}
|
||||
(lhs, rhs) => Or(vec![lhs, rhs]),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -229,7 +240,7 @@ impl BitOr<Pattern> for Pattern {
|
||||
impl BitOr<Pattern> for &Pattern {
|
||||
type Output = Pattern;
|
||||
|
||||
fn bitor(self, rhs:Pattern) -> Self::Output {
|
||||
fn bitor(self, rhs: Pattern) -> Self::Output {
|
||||
self.clone() | rhs
|
||||
}
|
||||
}
|
||||
@ -237,7 +248,7 @@ impl BitOr<Pattern> for &Pattern {
|
||||
impl BitOr<&Pattern> for Pattern {
|
||||
type Output = Pattern;
|
||||
|
||||
fn bitor(self, rhs:&Pattern) -> Self::Output {
|
||||
fn bitor(self, rhs: &Pattern) -> Self::Output {
|
||||
self | rhs.clone()
|
||||
}
|
||||
}
|
||||
@ -245,20 +256,29 @@ impl BitOr<&Pattern> for Pattern {
|
||||
impl BitOr<&Pattern> for &Pattern {
|
||||
type Output = Pattern;
|
||||
|
||||
fn bitor(self, rhs:&Pattern) -> Self::Output {
|
||||
fn bitor(self, rhs: &Pattern) -> Self::Output {
|
||||
self.clone() | rhs.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Shr<Pattern> for Pattern {
|
||||
type Output = Pattern;
|
||||
fn shr(self, rhs:Pattern) -> Self::Output {
|
||||
fn shr(self, rhs: Pattern) -> Self::Output {
|
||||
use Pattern::*;
|
||||
match (self, rhs) {
|
||||
(Seq(mut lhs), Seq(rhs) ) => {lhs.extend(rhs) ; Seq(lhs)},
|
||||
(Seq(mut lhs), rhs ) => {lhs.push(rhs) ; Seq(lhs)},
|
||||
(lhs , Seq(mut rhs)) => {rhs.insert(0,lhs) ; Seq(rhs)},
|
||||
(lhs , rhs ) => Seq(vec![lhs, rhs]),
|
||||
(Seq(mut lhs), Seq(rhs)) => {
|
||||
lhs.extend(rhs);
|
||||
Seq(lhs)
|
||||
}
|
||||
(Seq(mut lhs), rhs) => {
|
||||
lhs.push(rhs);
|
||||
Seq(lhs)
|
||||
}
|
||||
(lhs, Seq(mut rhs)) => {
|
||||
rhs.insert(0, lhs);
|
||||
Seq(rhs)
|
||||
}
|
||||
(lhs, rhs) => Seq(vec![lhs, rhs]),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -266,7 +286,7 @@ impl Shr<Pattern> for Pattern {
|
||||
impl Shr<Pattern> for &Pattern {
|
||||
type Output = Pattern;
|
||||
|
||||
fn shr(self, rhs:Pattern) -> Self::Output {
|
||||
fn shr(self, rhs: Pattern) -> Self::Output {
|
||||
self.clone() >> rhs
|
||||
}
|
||||
}
|
||||
@ -274,7 +294,7 @@ impl Shr<Pattern> for &Pattern {
|
||||
impl Shr<&Pattern> for Pattern {
|
||||
type Output = Pattern;
|
||||
|
||||
fn shr(self, rhs:&Pattern) -> Self::Output {
|
||||
fn shr(self, rhs: &Pattern) -> Self::Output {
|
||||
self >> rhs.clone()
|
||||
}
|
||||
}
|
||||
@ -282,7 +302,7 @@ impl Shr<&Pattern> for Pattern {
|
||||
impl Shr<&Pattern> for &Pattern {
|
||||
type Output = Pattern;
|
||||
|
||||
fn shr(self, rhs:&Pattern) -> Self::Output {
|
||||
fn shr(self, rhs: &Pattern) -> Self::Output {
|
||||
self.clone() >> rhs.clone()
|
||||
}
|
||||
}
|
||||
@ -300,7 +320,7 @@ impl Shr<&Pattern> for &Pattern {
|
||||
macro_rules! char {
|
||||
($char:literal) => {
|
||||
Pattern::char($char)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Quote a string as a literal pattern.
|
||||
@ -310,7 +330,7 @@ macro_rules! char {
|
||||
macro_rules! literal {
|
||||
($lit:literal) => {
|
||||
Pattern::all_of($lit)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -325,61 +345,71 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn pattern_many1() {
|
||||
let many1 = literal!("abc").many1();
|
||||
let many1 = literal!("abc").many1();
|
||||
let expected = literal!("abc") >> Pattern::Many(Box::new(literal!("abc")));
|
||||
assert_eq!(many1,expected);
|
||||
assert_eq!(many1, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_opt() {
|
||||
let opt = literal!("abc").opt();
|
||||
let opt = literal!("abc").opt();
|
||||
let expected = literal!("abc") | Pattern::Always;
|
||||
assert_eq!(opt,expected);
|
||||
assert_eq!(opt, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_all_of() {
|
||||
let all_of = Pattern::all_of("abcde");
|
||||
let all_of = Pattern::all_of("abcde");
|
||||
let expected = Pattern::Seq(vec![
|
||||
Pattern::Always,char!('a'),char!('b'),char!('c'),char!('d'),char!('e')
|
||||
Pattern::Always,
|
||||
char!('a'),
|
||||
char!('b'),
|
||||
char!('c'),
|
||||
char!('d'),
|
||||
char!('e'),
|
||||
]);
|
||||
assert_eq!(all_of,expected);
|
||||
assert_eq!(all_of, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_any_of() {
|
||||
let any_of = Pattern::any_of("abcde");
|
||||
let any_of = Pattern::any_of("abcde");
|
||||
let expected = Pattern::Or(vec![
|
||||
Pattern::Never,char!('a'),char!('b'),char!('c'),char!('d'),char!('e')
|
||||
Pattern::Never,
|
||||
char!('a'),
|
||||
char!('b'),
|
||||
char!('c'),
|
||||
char!('d'),
|
||||
char!('e'),
|
||||
]);
|
||||
assert_eq!(any_of,expected);
|
||||
assert_eq!(any_of, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_none_of() {
|
||||
let none_of = Pattern::none_of("be");
|
||||
let none_of = Pattern::none_of("be");
|
||||
let expected = Pattern::Never
|
||||
| Pattern::Range(Symbol::from(Symbol::null().index + 1)..=Symbol::from('a'))
|
||||
| Pattern::Range(Symbol::from('c')..=Symbol::from('d'))
|
||||
| Pattern::Range(Symbol::from('f')..=Symbol::from(Symbol::eof().index - 1));
|
||||
assert_eq!(none_of,expected);
|
||||
| Pattern::Range(Symbol::from(Symbol::null().index + 1)..=Symbol::from('a'))
|
||||
| Pattern::Range(Symbol::from('c')..=Symbol::from('d'))
|
||||
| Pattern::Range(Symbol::from('f')..=Symbol::from(Symbol::eof().index - 1));
|
||||
assert_eq!(none_of, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_none_of_codes() {
|
||||
let none_of = Pattern::none_of_codes(&[33,37],&["!".to_string(),"%".to_string()]);
|
||||
let none_of = Pattern::none_of_codes(&[33, 37], &["!".to_string(), "%".to_string()]);
|
||||
let expected = Pattern::Range(Symbol::null()..=Symbol::from(32u64))
|
||||
| Pattern::Never
|
||||
| Pattern::Range(Symbol::from(34u64)..=Symbol::from(36u64))
|
||||
| Pattern::Range(Symbol::from(38u64)..=Symbol::eof());
|
||||
assert_eq!(none_of,expected);
|
||||
| Pattern::Never
|
||||
| Pattern::Range(Symbol::from(34u64)..=Symbol::from(36u64))
|
||||
| Pattern::Range(Symbol::from(38u64)..=Symbol::eof());
|
||||
assert_eq!(none_of, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_not() {
|
||||
let not = Pattern::not('a');
|
||||
let not = Pattern::not('a');
|
||||
let expected = Pattern::none_of("a");
|
||||
assert_eq!(not,expected);
|
||||
assert_eq!(not, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -387,85 +417,88 @@ mod tests {
|
||||
let symbol = Symbol::from('d');
|
||||
let not_symbol = Pattern::not_symbol(symbol);
|
||||
let expected = Pattern::Range(Symbol::null()..=Symbol::from('c'))
|
||||
| Pattern::Range(Symbol::from('e')..=Symbol::eof());
|
||||
assert_eq!(not_symbol,expected);
|
||||
| Pattern::Range(Symbol::from('e')..=Symbol::eof());
|
||||
assert_eq!(not_symbol, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_repeat() {
|
||||
let repeat = Pattern::repeat(&char!('a'),5);
|
||||
let repeat = Pattern::repeat(&char!('a'), 5);
|
||||
let expected = Pattern::all_of("aaaaa");
|
||||
assert_eq!(repeat,expected);
|
||||
assert_eq!(repeat, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_repeat_between() {
|
||||
let repeat_between = Pattern::repeat_between(&char!('a'),2,4);
|
||||
let expected = Pattern::never() | Pattern::all_of("aa") | Pattern::all_of("aaa");
|
||||
assert_eq!(repeat_between,expected);
|
||||
let repeat_between = Pattern::repeat_between(&char!('a'), 2, 4);
|
||||
let expected = Pattern::never() | Pattern::all_of("aa") | Pattern::all_of("aaa");
|
||||
assert_eq!(repeat_between, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_operator_shr() {
|
||||
let pattern_left = Pattern::char('a');
|
||||
let pattern_left = Pattern::char('a');
|
||||
let pattern_right = Pattern::not_symbol(Symbol::eof());
|
||||
let val_val = pattern_left.clone() >> pattern_right.clone();
|
||||
let ref_val = &pattern_left >> pattern_right.clone();
|
||||
let val_ref = pattern_left.clone() >> &pattern_right;
|
||||
let ref_ref = &pattern_left >> &pattern_right;
|
||||
let expected = Pattern::Seq(vec![pattern_left,pattern_right]);
|
||||
assert_eq!(val_val,expected);
|
||||
assert_eq!(ref_val,expected);
|
||||
assert_eq!(val_ref,expected);
|
||||
assert_eq!(ref_ref,expected);
|
||||
let val_val = pattern_left.clone() >> pattern_right.clone();
|
||||
let ref_val = &pattern_left >> pattern_right.clone();
|
||||
let val_ref = pattern_left.clone() >> &pattern_right;
|
||||
let ref_ref = &pattern_left >> &pattern_right;
|
||||
let expected = Pattern::Seq(vec![pattern_left, pattern_right]);
|
||||
assert_eq!(val_val, expected);
|
||||
assert_eq!(ref_val, expected);
|
||||
assert_eq!(val_ref, expected);
|
||||
assert_eq!(ref_ref, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_operator_shr_collapse() {
|
||||
let seq = Pattern::Seq(vec![char!('a'),char!('b')]);
|
||||
let seq = Pattern::Seq(vec![char!('a'), char!('b')]);
|
||||
let lit = char!('c');
|
||||
assert_eq!(&seq >> &seq,Pattern::Seq(vec![char!('a'),char!('b'),char!('a'),char!('b')]));
|
||||
assert_eq!(&seq >> &lit,Pattern::Seq(vec![char!('a'),char!('b'),char!('c')]));
|
||||
assert_eq!(&lit >> &seq,Pattern::Seq(vec![char!('c'),char!('a'),char!('b')]));
|
||||
assert_eq!(&lit >> &lit,Pattern::Seq(vec![char!('c'),char!('c')]));
|
||||
assert_eq!(
|
||||
&seq >> &seq,
|
||||
Pattern::Seq(vec![char!('a'), char!('b'), char!('a'), char!('b')])
|
||||
);
|
||||
assert_eq!(&seq >> &lit, Pattern::Seq(vec![char!('a'), char!('b'), char!('c')]));
|
||||
assert_eq!(&lit >> &seq, Pattern::Seq(vec![char!('c'), char!('a'), char!('b')]));
|
||||
assert_eq!(&lit >> &lit, Pattern::Seq(vec![char!('c'), char!('c')]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_operator_bit_or() {
|
||||
let pattern_left = Pattern::char('a');
|
||||
let pattern_left = Pattern::char('a');
|
||||
let pattern_right = Pattern::not_symbol(Symbol::eof());
|
||||
let val_val = pattern_left.clone() | pattern_right.clone();
|
||||
let ref_val = &pattern_left | pattern_right.clone();
|
||||
let val_ref = pattern_left.clone() | &pattern_right;
|
||||
let ref_ref = &pattern_left | &pattern_right;
|
||||
let expected = Pattern::Or(vec![pattern_left,pattern_right]);
|
||||
assert_eq!(val_val,expected);
|
||||
assert_eq!(ref_val,expected);
|
||||
assert_eq!(val_ref,expected);
|
||||
assert_eq!(ref_ref,expected);
|
||||
let val_val = pattern_left.clone() | pattern_right.clone();
|
||||
let ref_val = &pattern_left | pattern_right.clone();
|
||||
let val_ref = pattern_left.clone() | &pattern_right;
|
||||
let ref_ref = &pattern_left | &pattern_right;
|
||||
let expected = Pattern::Or(vec![pattern_left, pattern_right]);
|
||||
assert_eq!(val_val, expected);
|
||||
assert_eq!(ref_val, expected);
|
||||
assert_eq!(val_ref, expected);
|
||||
assert_eq!(ref_ref, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_operator_bit_or_collapse() {
|
||||
let seq = Pattern::Or(vec![char!('a'),char!('b')]);
|
||||
let seq = Pattern::Or(vec![char!('a'), char!('b')]);
|
||||
let lit = char!('c');
|
||||
assert_eq!(&seq | &seq,Pattern::Or(vec![char!('a'),char!('b'),char!('a'),char!('b')]));
|
||||
assert_eq!(&seq | &lit,Pattern::Or(vec![char!('a'),char!('b'),char!('c')]));
|
||||
assert_eq!(&lit | &seq,Pattern::Or(vec![char!('c'),char!('a'),char!('b')]));
|
||||
assert_eq!(&lit | &lit,Pattern::Or(vec![char!('c'),char!('c')]));
|
||||
assert_eq!(&seq | &seq, Pattern::Or(vec![char!('a'), char!('b'), char!('a'), char!('b')]));
|
||||
assert_eq!(&seq | &lit, Pattern::Or(vec![char!('a'), char!('b'), char!('c')]));
|
||||
assert_eq!(&lit | &seq, Pattern::Or(vec![char!('c'), char!('a'), char!('b')]));
|
||||
assert_eq!(&lit | &lit, Pattern::Or(vec![char!('c'), char!('c')]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_macro_character() {
|
||||
let with_macro = char!('c');
|
||||
let explicit = Pattern::char('c');
|
||||
assert_eq!(with_macro,explicit);
|
||||
let explicit = Pattern::char('c');
|
||||
assert_eq!(with_macro, explicit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_macro_literal() {
|
||||
let with_macro = literal!("abcde");
|
||||
let explicit = Pattern::all_of("abcde");
|
||||
assert_eq!(with_macro,explicit);
|
||||
let explicit = Pattern::all_of("abcde");
|
||||
assert_eq!(with_macro, explicit);
|
||||
}
|
||||
}
|
||||
|
@ -15,32 +15,32 @@ use crate::nfa::Nfa; // FIXME
|
||||
|
||||
/// A state identifier for an arbitrary finite automaton.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(bound=""))]
|
||||
#[derivative(Copy(bound=""))]
|
||||
#[derivative(Eq(bound=""))]
|
||||
#[derivative(Hash(bound=""))]
|
||||
#[derivative(Ord(bound=""))]
|
||||
#[derivative(PartialEq(bound=""))]
|
||||
#[derivative(PartialOrd(bound=""))]
|
||||
#[derivative(Clone(bound = ""))]
|
||||
#[derivative(Copy(bound = ""))]
|
||||
#[derivative(Eq(bound = ""))]
|
||||
#[derivative(Hash(bound = ""))]
|
||||
#[derivative(Ord(bound = ""))]
|
||||
#[derivative(PartialEq(bound = ""))]
|
||||
#[derivative(PartialOrd(bound = ""))]
|
||||
#[allow(missing_docs)]
|
||||
pub struct State<T> {
|
||||
tp : PhantomData<T>,
|
||||
id : usize
|
||||
tp: PhantomData<T>,
|
||||
id: usize,
|
||||
}
|
||||
|
||||
impl<T> State<T> {
|
||||
/// An identifier representing the invalid state.
|
||||
///
|
||||
/// When in an invalid state, a finite automaton will reject the sequence of input symbols.
|
||||
pub const INVALID : State<T> = Self::new(usize::max_value());
|
||||
pub const INVALID: State<T> = Self::new(usize::max_value());
|
||||
}
|
||||
|
||||
impl<T> State<T> {
|
||||
/// Constructor. Not exposed to public as it should never be possible to construct a state
|
||||
/// from a number.
|
||||
pub(crate) const fn new(id:usize) -> Self {
|
||||
pub(crate) const fn new(id: usize) -> Self {
|
||||
let tp = PhantomData;
|
||||
Self {tp,id}
|
||||
Self { tp, id }
|
||||
}
|
||||
|
||||
/// Identifier of this state expressed as `usize`.
|
||||
@ -65,9 +65,9 @@ impl<T> Default for State<T> {
|
||||
}
|
||||
|
||||
impl<T> Debug for State<T> {
|
||||
fn fmt(&self, f:&mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let name = if *self == Self::INVALID { "INVALID".into() } else { format!("{:?}",self.id) };
|
||||
write!(f,"State({})",name)
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let name = if *self == Self::INVALID { "INVALID".into() } else { format!("{:?}", self.id) };
|
||||
write!(f, "State({})", name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,22 +78,21 @@ impl<T> Debug for State<T> {
|
||||
// ==========
|
||||
|
||||
/// A named state for a [`super::nfa::Nfa`].
|
||||
#[derive(Clone,Debug,Default,PartialEq,Eq)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Data {
|
||||
/// A set of transitions that can trigger without consuming a symbol (ε-transitions).
|
||||
pub epsilon_links: Vec<State<Nfa>>,
|
||||
/// The set of transitions that trigger while consuming a specific symbol.
|
||||
///
|
||||
/// When triggered, the automaton will transition to the [`Transition::target`].
|
||||
pub links: Vec<Transition>,
|
||||
pub links: Vec<Transition>,
|
||||
/// Information whether the state should be exported and marked as a "source" state in the DFA
|
||||
/// representation. Non exported states are considered "transitive" states and are used as
|
||||
/// helpers to design the NFA network. All user defined states are marked to be exported.
|
||||
pub export : bool,
|
||||
pub export: bool,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
|
||||
/// Get a reference to the links in this state.
|
||||
pub fn links(&self) -> &Vec<Transition> {
|
||||
&self.links
|
||||
@ -105,10 +104,10 @@ impl Data {
|
||||
}
|
||||
|
||||
/// Returns the transition (next state) for each symbol in the alphabet.
|
||||
pub fn targets(&self, alphabet:&alphabet::Segmentation) -> Vec<State<Nfa>> {
|
||||
pub fn targets(&self, alphabet: &alphabet::Segmentation) -> Vec<State<Nfa>> {
|
||||
let mut targets = vec![];
|
||||
let mut index = 0;
|
||||
let mut links = self.links.clone();
|
||||
let mut index = 0;
|
||||
let mut links = self.links.clone();
|
||||
links.sort_by_key(|link| link.symbols.start().clone());
|
||||
for symbol in &alphabet.divisions {
|
||||
while links.len() > index && links[index].symbols.end() < symbol {
|
||||
@ -131,26 +130,26 @@ impl Data {
|
||||
// ==================
|
||||
|
||||
/// A transition between states in a finite automaton that must consume a symbol to trigger.
|
||||
#[derive(Clone,Debug,PartialEq,Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Transition {
|
||||
/// The range of symbols on which this transition will trigger.
|
||||
pub symbols: RangeInclusive<Symbol>,
|
||||
/// The state that is entered after the transition has triggered.
|
||||
pub target: State<Nfa>,
|
||||
pub target: State<Nfa>,
|
||||
}
|
||||
|
||||
impl Transition {
|
||||
/// Constructor.
|
||||
pub fn new(symbols:RangeInclusive<Symbol>, target:State<Nfa>) -> Self {
|
||||
Self {symbols,target}
|
||||
pub fn new(symbols: RangeInclusive<Symbol>, target: State<Nfa>) -> Self {
|
||||
Self { symbols, target }
|
||||
}
|
||||
|
||||
/// Display the symbols range of this tansition.
|
||||
pub fn display_symbols(&self) -> String {
|
||||
if self.symbols.start() == self.symbols.end() {
|
||||
format!("{}",self.symbols.start())
|
||||
format!("{}", self.symbols.start())
|
||||
} else {
|
||||
format!("{} .. {}",self.symbols.start(),self.symbols.end())
|
||||
format!("{} .. {}", self.symbols.start(), self.symbols.end())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,16 +162,16 @@ impl Transition {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::super::alphabet;
|
||||
use super::*;
|
||||
|
||||
// === Trait Impls ====
|
||||
|
||||
impl From<Vec<Transition>> for Data {
|
||||
fn from(links:Vec<Transition>) -> Self {
|
||||
fn from(links: Vec<Transition>) -> Self {
|
||||
let epsilon_links = vec![];
|
||||
let export = false;
|
||||
Data{epsilon_links,links,export}
|
||||
let export = false;
|
||||
Data { epsilon_links, links, export }
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +180,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn state_default() {
|
||||
assert_eq!(State::<Nfa>::default(),State::<Nfa>::INVALID);
|
||||
assert_eq!(State::<Nfa>::default(), State::<Nfa>::INVALID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -194,14 +193,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn state_targets() {
|
||||
let alphabet = alphabet::Segmentation::from_divisions(&[0,5,10,15,25,50]);
|
||||
let alphabet = alphabet::Segmentation::from_divisions(&[0, 5, 10, 15, 25, 50]);
|
||||
let state = Data::from(vec![
|
||||
Transition::new(Symbol::from(0u64)..=Symbol::from(10u64),State::<Nfa>::new(1)),
|
||||
Transition::new(Symbol::from(5u64)..=Symbol::from(15u64),State::<Nfa>::new(2)),
|
||||
Transition::new(Symbol::from(0u64)..=Symbol::from(10u64), State::<Nfa>::new(1)),
|
||||
Transition::new(Symbol::from(5u64)..=Symbol::from(15u64), State::<Nfa>::new(2)),
|
||||
]);
|
||||
assert_eq!(state.links().len(),2);
|
||||
assert_eq!(state.links().len(), 2);
|
||||
let targets = state.targets(&alphabet);
|
||||
let expected_targets:Vec<State<Nfa>> = vec![
|
||||
let expected_targets: Vec<State<Nfa>> = vec![
|
||||
State::<Nfa>::new(1),
|
||||
State::<Nfa>::new(1),
|
||||
State::<Nfa>::new(1),
|
||||
@ -209,6 +208,6 @@ mod tests {
|
||||
State::<Nfa>::INVALID,
|
||||
State::<Nfa>::INVALID,
|
||||
];
|
||||
assert_eq!(expected_targets,targets);
|
||||
assert_eq!(expected_targets, targets);
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,11 @@ pub type SymbolIndex = u64;
|
||||
// ==============
|
||||
|
||||
/// An input symbol to a finite automaton.
|
||||
#[derive(Clone,Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Symbol {
|
||||
pub index : SymbolIndex,
|
||||
pub name : String
|
||||
pub index: SymbolIndex,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
|
||||
@ -56,15 +56,15 @@ impl Symbol {
|
||||
}
|
||||
|
||||
/// Constructor.
|
||||
pub fn new(index:SymbolIndex) -> Self {
|
||||
pub fn new(index: SymbolIndex) -> Self {
|
||||
let name = "unnamed".into();
|
||||
Self{index,name}
|
||||
Self { index, name }
|
||||
}
|
||||
|
||||
/// Named constructor.
|
||||
pub fn new_named(index:SymbolIndex, name:impl Into<String>) -> Self {
|
||||
pub fn new_named(index: SymbolIndex, name: impl Into<String>) -> Self {
|
||||
let name = name.into();
|
||||
Self{index,name}
|
||||
Self { index, name }
|
||||
}
|
||||
|
||||
/// Next symbol, if any.
|
||||
@ -101,8 +101,8 @@ impl Hash for Symbol {
|
||||
}
|
||||
|
||||
impl Display for Symbol {
|
||||
fn fmt(&self, f:&mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f,"{}",self.name)
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,25 +113,25 @@ impl Default for Symbol {
|
||||
}
|
||||
|
||||
impl From<u64> for Symbol {
|
||||
fn from(index:u64) -> Symbol {
|
||||
fn from(index: u64) -> Symbol {
|
||||
Symbol::new(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Symbol {
|
||||
fn from(index:u32) -> Symbol {
|
||||
fn from(index: u32) -> Symbol {
|
||||
Symbol::new(index as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for Symbol {
|
||||
fn from(ch:char) -> Symbol {
|
||||
Symbol::new_named(ch as u64,format!("{}",ch))
|
||||
fn from(ch: char) -> Symbol {
|
||||
Symbol::new_named(ch as u64, format!("{}", ch))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Symbol> for Symbol {
|
||||
fn from(symbol:&Symbol) -> Self {
|
||||
fn from(symbol: &Symbol) -> Self {
|
||||
symbol.clone()
|
||||
}
|
||||
}
|
||||
@ -149,18 +149,18 @@ mod tests {
|
||||
#[test]
|
||||
fn default() {
|
||||
let sym = Symbol::default();
|
||||
assert_eq!(sym,Symbol::null());
|
||||
assert_eq!(sym, Symbol::null());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_natural() {
|
||||
let sym = Symbol::from(12143u64);
|
||||
assert_eq!(sym.index,12143u64);
|
||||
assert_eq!(sym.index, 12143u64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_char() {
|
||||
let sym = Symbol::from('a');
|
||||
assert_eq!(sym.index,97);
|
||||
assert_eq!(sym.index, 97);
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ use enso_data::hash_map_tree::HashMapTree;
|
||||
use itertools::*;
|
||||
|
||||
use criterion::black_box;
|
||||
use criterion::Criterion;
|
||||
use criterion::criterion_group;
|
||||
use criterion::criterion_main;
|
||||
use criterion::Criterion;
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
@ -25,10 +25,10 @@ fn bench_config() -> Criterion {
|
||||
}
|
||||
|
||||
/// Create a tree where each node has `width` branches, up to a maximum depth of `depth`.
|
||||
fn gen_tree(width:usize, depth:usize) -> HashMapTree<usize,usize> {
|
||||
let mut tree = HashMapTree::<usize,usize>::default();
|
||||
let paths = (0..width).permutations(depth);
|
||||
paths.into_iter().for_each(|p| tree.set(p,1));
|
||||
fn gen_tree(width: usize, depth: usize) -> HashMapTree<usize, usize> {
|
||||
let mut tree = HashMapTree::<usize, usize>::default();
|
||||
let paths = (0..width).permutations(depth);
|
||||
paths.into_iter().for_each(|p| tree.set(p, 1));
|
||||
tree
|
||||
}
|
||||
|
||||
@ -43,18 +43,18 @@ fn gen_tree(width:usize, depth:usize) -> HashMapTree<usize,usize> {
|
||||
|
||||
/// A benchmark that tests querying a tree that has 10 branches at each node, and each node chain
|
||||
/// goes five nodes deep.
|
||||
fn wide_tree(c:&mut Criterion) {
|
||||
let tree = gen_tree(10,5);
|
||||
let query:Vec<usize> = vec![1,3,2,5,9];
|
||||
c.bench_function("Wide Tree",|b| b.iter(|| tree.get(black_box(query.clone()))));
|
||||
fn wide_tree(c: &mut Criterion) {
|
||||
let tree = gen_tree(10, 5);
|
||||
let query: Vec<usize> = vec![1, 3, 2, 5, 9];
|
||||
c.bench_function("Wide Tree", |b| b.iter(|| tree.get(black_box(query.clone()))));
|
||||
}
|
||||
|
||||
/// A benchmark that tests querying a tree that has 5 branches at each node, and each node chain
|
||||
/// goes ten nodes deep.
|
||||
fn deep_tree(c:&mut Criterion) {
|
||||
let tree = gen_tree(5,10);
|
||||
let query:Vec<usize> = vec![1,2,4,1,3];
|
||||
c.bench_function("Deep Tree",|b| b.iter(|| tree.get(black_box(query.clone()))));
|
||||
fn deep_tree(c: &mut Criterion) {
|
||||
let tree = gen_tree(5, 10);
|
||||
let query: Vec<usize> = vec![1, 2, 4, 1, 3];
|
||||
c.bench_function("Deep Tree", |b| b.iter(|| tree.get(black_box(query.clone()))));
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
@ -66,25 +66,33 @@ criterion_group! {
|
||||
|
||||
// === Traversal ===
|
||||
|
||||
fn clone(c:&mut Criterion) {
|
||||
let tree = gen_tree(10,5);
|
||||
c.bench_function("Clone",|b| b.iter(|| black_box(tree.clone())));
|
||||
fn clone(c: &mut Criterion) {
|
||||
let tree = gen_tree(10, 5);
|
||||
c.bench_function("Clone", |b| b.iter(|| black_box(tree.clone())));
|
||||
}
|
||||
|
||||
fn map(c:&mut Criterion) {
|
||||
let tree = gen_tree(10,5);
|
||||
c.bench_function("Map",|b| b.iter(|| {
|
||||
let tree = tree.clone();
|
||||
black_box(tree.iter().map(black_box(|(k,v)| (k,v*2))).collect::<HashMapTree<_,_>>());
|
||||
}));
|
||||
fn map(c: &mut Criterion) {
|
||||
let tree = gen_tree(10, 5);
|
||||
c.bench_function("Map", |b| {
|
||||
b.iter(|| {
|
||||
let tree = tree.clone();
|
||||
black_box(
|
||||
tree.iter().map(black_box(|(k, v)| (k, v * 2))).collect::<HashMapTree<_, _>>(),
|
||||
);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn map_in_place(c:&mut Criterion) {
|
||||
let tree = gen_tree(10,5);
|
||||
c.bench_function("Map in Place",|b| b.iter(|| {
|
||||
let mut tree = tree.clone();
|
||||
black_box(tree.iter_mut().for_each(black_box(|(_,v):(Vec<&usize>,&mut usize)| *v *= 2)));
|
||||
}));
|
||||
fn map_in_place(c: &mut Criterion) {
|
||||
let tree = gen_tree(10, 5);
|
||||
c.bench_function("Map in Place", |b| {
|
||||
b.iter(|| {
|
||||
let mut tree = tree.clone();
|
||||
black_box(
|
||||
tree.iter_mut().for_each(black_box(|(_, v): (Vec<&usize>, &mut usize)| *v *= 2)),
|
||||
);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group! {
|
||||
@ -99,4 +107,4 @@ criterion_group! {
|
||||
// === Runner ===
|
||||
// ==============
|
||||
|
||||
criterion_main!(tree_query_benchmarks,tree_traversal_benchmarks);
|
||||
criterion_main!(tree_query_benchmarks, tree_traversal_benchmarks);
|
||||
|
@ -16,13 +16,13 @@ use std::collections::BTreeSet;
|
||||
///
|
||||
/// Please note that the input and output edges are stored in a vector because in most cases there
|
||||
/// would be small amount of them (zero or one).
|
||||
#[derive(Clone,Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound=""))]
|
||||
#[derivative(Default(bound = ""))]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Node<Edge> {
|
||||
pub ins : Vec<Edge>,
|
||||
pub out : Vec<Edge>,
|
||||
pub ins: Vec<Edge>,
|
||||
pub out: Vec<Edge>,
|
||||
}
|
||||
|
||||
impl<Edge> Node<Edge> {
|
||||
@ -45,13 +45,13 @@ impl<Edge> Node<Edge> {
|
||||
/// automatically broken on the lowest node id.
|
||||
#[derive(Clone)]
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound="T:Eq+Hash+Ord"))]
|
||||
#[derivative(Debug(bound="T:Debug+Eq+Hash"))]
|
||||
#[derivative(Default(bound = "T:Eq+Hash+Ord"))]
|
||||
#[derivative(Debug(bound = "T:Debug+Eq+Hash"))]
|
||||
pub struct DependencyGraph<T> {
|
||||
nodes : BTreeMap<T,Node<T>>
|
||||
nodes: BTreeMap<T, Node<T>>,
|
||||
}
|
||||
|
||||
impl<T:Clone+Eq+Hash+Ord> DependencyGraph<T> {
|
||||
impl<T: Clone + Eq + Hash + Ord> DependencyGraph<T> {
|
||||
/// Constructor.
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
@ -59,11 +59,11 @@ impl<T:Clone+Eq+Hash+Ord> DependencyGraph<T> {
|
||||
|
||||
/// Insert a new dependency to the graph. Returns [`true`] if the insertion was successful
|
||||
/// (the dependency was not present already), or [`false`] otherwise.
|
||||
pub fn insert_dependency(&mut self, first:T, second:T) -> bool {
|
||||
pub fn insert_dependency(&mut self, first: T, second: T) -> bool {
|
||||
let fst_key = first.clone();
|
||||
let snd_key = second.clone();
|
||||
let fst_out = &mut self.nodes.entry(fst_key).or_default().out;
|
||||
let exists = fst_out.contains(&second);
|
||||
let exists = fst_out.contains(&second);
|
||||
if !exists {
|
||||
fst_out.push(second);
|
||||
self.nodes.entry(snd_key).or_default().ins.push(first);
|
||||
@ -73,42 +73,44 @@ impl<T:Clone+Eq+Hash+Ord> DependencyGraph<T> {
|
||||
|
||||
/// Remove a dependency from the graph. Returns [`true`] if the dependency was found, or
|
||||
/// [`false`] otherwise.
|
||||
pub fn remove_dependency(&mut self, first:T, second:T) -> bool {
|
||||
pub fn remove_dependency(&mut self, first: T, second: T) -> bool {
|
||||
let fst_found = self.nodes.get_mut(&first).map(|t| t.out.remove_item(&second).is_some());
|
||||
let snd_found = self.nodes.get_mut(&second).map(|t| t.ins.remove_item(&first).is_some());
|
||||
if self.nodes.get(&first).map(|t|t.is_empty()) == Some(true) { self.nodes.remove(&first); }
|
||||
if self.nodes.get(&second).map(|t|t.is_empty()) == Some(true) { self.nodes.remove(&second); }
|
||||
if self.nodes.get(&first).map(|t| t.is_empty()) == Some(true) {
|
||||
self.nodes.remove(&first);
|
||||
}
|
||||
if self.nodes.get(&second).map(|t| t.is_empty()) == Some(true) {
|
||||
self.nodes.remove(&second);
|
||||
}
|
||||
fst_found == Some(true) && snd_found == Some(true)
|
||||
}
|
||||
|
||||
/// Removes all (incoming and outgoing) dependencies from nodes whose indexes do not belong to
|
||||
/// the provided slice.
|
||||
pub fn keep_only(&mut self, keys:&[T]) {
|
||||
pub fn keep_only(&mut self, keys: &[T]) {
|
||||
self.unchecked_keep_only(keys.iter().cloned().sorted())
|
||||
}
|
||||
|
||||
/// Removes all (incoming and outgoing) dependencies from nodes whose indexes do not belong to
|
||||
/// the provided slice.
|
||||
pub fn kept_only(mut self, keys:&[T]) -> Self {
|
||||
pub fn kept_only(mut self, keys: &[T]) -> Self {
|
||||
self.keep_only(keys);
|
||||
self
|
||||
}
|
||||
|
||||
/// Just like [`keep_only`], but the provided slice must be sorted.
|
||||
pub fn unchecked_keep_only(&mut self, sorted_keys:impl IntoIterator<Item=T>) {
|
||||
let mut keep = sorted_keys.into_iter();
|
||||
pub fn unchecked_keep_only(&mut self, sorted_keys: impl IntoIterator<Item = T>) {
|
||||
let mut keep = sorted_keys.into_iter();
|
||||
let mut next_to_keep = keep.next();
|
||||
let keys = self.nodes.keys().cloned().collect_vec();
|
||||
let mut keys_iter = keys.iter();
|
||||
let mut opt_key = keys_iter.next();
|
||||
while let Some(key) = opt_key {
|
||||
let keys = self.nodes.keys().cloned().collect_vec();
|
||||
let mut keys_iter = keys.iter();
|
||||
let mut opt_key = keys_iter.next();
|
||||
while let Some(key) = opt_key {
|
||||
match next_to_keep.as_ref().map(|t| t.cmp(key)) {
|
||||
Some(std::cmp::Ordering::Less) => {
|
||||
next_to_keep = keep.next()
|
||||
},
|
||||
Some(std::cmp::Ordering::Less) => next_to_keep = keep.next(),
|
||||
Some(std::cmp::Ordering::Equal) => {
|
||||
next_to_keep = keep.next();
|
||||
opt_key = keys_iter.next();
|
||||
opt_key = keys_iter.next();
|
||||
}
|
||||
_ => {
|
||||
if let Some(node) = self.nodes.get_mut(key) {
|
||||
@ -127,7 +129,7 @@ impl<T:Clone+Eq+Hash+Ord> DependencyGraph<T> {
|
||||
}
|
||||
|
||||
/// Just like [`kept_only`], but the provided slice must be sorted.
|
||||
pub fn unchecked_kept_only(mut self, sorted_keys:impl IntoIterator<Item=T>) -> Self {
|
||||
pub fn unchecked_kept_only(mut self, sorted_keys: impl IntoIterator<Item = T>) -> Self {
|
||||
self.unchecked_keep_only(sorted_keys);
|
||||
self
|
||||
}
|
||||
@ -135,34 +137,37 @@ impl<T:Clone+Eq+Hash+Ord> DependencyGraph<T> {
|
||||
/// Sorts the provided indexes in topological order based on the rules recorded in the graph.
|
||||
/// In case the graph is not a DAG, it will still be sorted by breaking cycles on elements with
|
||||
/// the smallest index.
|
||||
pub fn topo_sort(&self, keys:&[T]) -> Vec<T> {
|
||||
pub fn topo_sort(&self, keys: &[T]) -> Vec<T> {
|
||||
self.unchecked_topo_sort(keys.iter().cloned().sorted().collect_vec())
|
||||
}
|
||||
|
||||
/// Just like [`topo_sort`], but consumes the current dependency graph instead of cloning it.
|
||||
pub fn into_topo_sort(self, keys:&[T]) -> Vec<T> {
|
||||
pub fn into_topo_sort(self, keys: &[T]) -> Vec<T> {
|
||||
self.into_unchecked_topo_sort(keys.iter().cloned().sorted().collect_vec())
|
||||
}
|
||||
|
||||
/// Just like [`topo_sort`], but the provided slice must be sorted.
|
||||
pub fn unchecked_topo_sort(&self, sorted_keys:Vec<T>) -> Vec<T> {
|
||||
pub fn unchecked_topo_sort(&self, sorted_keys: Vec<T>) -> Vec<T> {
|
||||
self.clone().into_unchecked_topo_sort(sorted_keys)
|
||||
}
|
||||
|
||||
/// Just like [`unchecked_topo_sort`], bbut consumes the current dependency graph instead of
|
||||
/// cloning it.
|
||||
pub fn into_unchecked_topo_sort(self, sorted_keys:Vec<T>) -> Vec<T> {
|
||||
let mut sorted = Vec::<T>::new();
|
||||
let mut orphans = BTreeSet::<T>::new();
|
||||
pub fn into_unchecked_topo_sort(self, sorted_keys: Vec<T>) -> Vec<T> {
|
||||
let mut sorted = Vec::<T>::new();
|
||||
let mut orphans = BTreeSet::<T>::new();
|
||||
let mut non_orphans = BTreeSet::<T>::new();
|
||||
let this = self.unchecked_kept_only(sorted_keys.iter().cloned());
|
||||
let this = self.unchecked_kept_only(sorted_keys.iter().cloned());
|
||||
sorted.reserve_exact(sorted_keys.len());
|
||||
|
||||
let mut nodes = this.nodes;
|
||||
for key in sorted_keys.into_iter() {
|
||||
let ins_empty = nodes.get(&key).map(|t|t.ins.is_empty()) != Some(false);
|
||||
if ins_empty { orphans.insert(key); }
|
||||
else { non_orphans.insert(key); }
|
||||
let ins_empty = nodes.get(&key).map(|t| t.ins.is_empty()) != Some(false);
|
||||
if ins_empty {
|
||||
orphans.insert(key);
|
||||
} else {
|
||||
non_orphans.insert(key);
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
@ -176,7 +181,7 @@ impl<T:Clone+Eq+Hash+Ord> DependencyGraph<T> {
|
||||
orphans.insert(ix);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Some(ix) => {
|
||||
sorted.push(ix.clone());
|
||||
orphans.remove(&ix);
|
||||
@ -199,24 +204,24 @@ impl<T:Clone+Eq+Hash+Ord> DependencyGraph<T> {
|
||||
}
|
||||
|
||||
|
||||
impl<'a,T> IntoIterator for &'a DependencyGraph<T> {
|
||||
type Item = (&'a T, &'a Node<T>);
|
||||
type IntoIter = std::collections::btree_map::Iter<'a,T,Node<T>>;
|
||||
fn into_iter(self) -> std::collections::btree_map::Iter<'a,T,Node<T>> {
|
||||
impl<'a, T> IntoIterator for &'a DependencyGraph<T> {
|
||||
type Item = (&'a T, &'a Node<T>);
|
||||
type IntoIter = std::collections::btree_map::Iter<'a, T, Node<T>>;
|
||||
fn into_iter(self) -> std::collections::btree_map::Iter<'a, T, Node<T>> {
|
||||
self.nodes.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for DependencyGraph<T> {
|
||||
type Item = (T,Node<T>);
|
||||
type IntoIter = std::collections::btree_map::IntoIter<T,Node<T>>;
|
||||
fn into_iter(self) -> std::collections::btree_map::IntoIter<T,Node<T>> {
|
||||
type Item = (T, Node<T>);
|
||||
type IntoIter = std::collections::btree_map::IntoIter<T, Node<T>>;
|
||||
fn into_iter(self) -> std::collections::btree_map::IntoIter<T, Node<T>> {
|
||||
self.nodes.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Ord> Extend<(T,Node<T>)> for DependencyGraph<T> {
|
||||
fn extend<I:IntoIterator<Item=(T,Node<T>)>>(&mut self, iter:I) {
|
||||
impl<T: Ord> Extend<(T, Node<T>)> for DependencyGraph<T> {
|
||||
fn extend<I: IntoIterator<Item = (T, Node<T>)>>(&mut self, iter: I) {
|
||||
self.nodes.extend(iter)
|
||||
}
|
||||
}
|
||||
@ -242,7 +247,7 @@ impl<T:Ord> Extend<(T,Node<T>)> for DependencyGraph<T> {
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! dependency_graph {
|
||||
macro_rules! dependency_graph {
|
||||
($($fst:tt -> $snd:tt),* $(,)?) => {
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
@ -263,9 +268,9 @@ extern crate test;
|
||||
|
||||
/// Asserts whether the graph will sort the provided slice in the same order as it was provided.
|
||||
/// Please note, that the slice is sorted in order before being sorted topologically.
|
||||
pub fn assert_valid_sort(graph:&DependencyGraph<usize>, sorted:&[usize]) {
|
||||
pub fn assert_valid_sort(graph: &DependencyGraph<usize>, sorted: &[usize]) {
|
||||
let sorted = sorted.to_vec();
|
||||
assert_eq!(graph.topo_sort(&sorted),sorted);
|
||||
assert_eq!(graph.topo_sort(&sorted), sorted);
|
||||
}
|
||||
|
||||
/// The same as [`assert_valid_sort`] but with a shorter syntax. Learn more about it by looking at
|
||||
@ -286,7 +291,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_identity() {
|
||||
assert_valid_sort!{
|
||||
assert_valid_sort! {
|
||||
[] for {}
|
||||
[0] for {}
|
||||
[0,1] for {}
|
||||
@ -297,7 +302,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_non_overlapping_rules() {
|
||||
assert_valid_sort!{
|
||||
assert_valid_sort! {
|
||||
[1,0] for {1->0}
|
||||
[0,2,1,3] for {2->1}
|
||||
[1,0,3,2] for {1->0,3->2}
|
||||
@ -306,7 +311,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_overlapping_rules() {
|
||||
assert_valid_sort!{
|
||||
assert_valid_sort! {
|
||||
[4,3,2,1,0] for {4->3,3->2,2->1,1->0}
|
||||
[4,3,2,1,0] for {3->2,4->3,1->0,2->1}
|
||||
[4,3,2,1,0] for {1->0,2->1,3->2,4->3}
|
||||
@ -316,7 +321,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_non_dag() {
|
||||
assert_valid_sort!{
|
||||
assert_valid_sort! {
|
||||
[0] for {0->0}
|
||||
[1,2,0] for {0->0}
|
||||
[0,2,1] for {1->1}
|
||||
@ -340,12 +345,14 @@ mod benches {
|
||||
/// 10^4 | 5.2 |
|
||||
/// 10^5 | 74.2 |
|
||||
#[bench]
|
||||
fn bench_ascending(b:&mut Bencher) {
|
||||
let iters = 1_000;
|
||||
let out = (0..iters).collect_vec();
|
||||
fn bench_ascending(b: &mut Bencher) {
|
||||
let iters = 1_000;
|
||||
let out = (0..iters).collect_vec();
|
||||
let mut graph = DependencyGraph::new();
|
||||
for (i,j) in out.iter().zip(out.iter().skip(1)) { graph.insert_dependency(*i,*j); }
|
||||
b.iter(move || assert_eq!(graph.topo_sort(&out),out));
|
||||
for (i, j) in out.iter().zip(out.iter().skip(1)) {
|
||||
graph.insert_dependency(*i, *j);
|
||||
}
|
||||
b.iter(move || assert_eq!(graph.topo_sort(&out), out));
|
||||
}
|
||||
|
||||
/// # Results (ms)
|
||||
@ -355,11 +362,13 @@ mod benches {
|
||||
/// 10^4 | 6.2 |
|
||||
/// 10^5 | 86.8 |
|
||||
#[bench]
|
||||
fn bench_descending(b:&mut Bencher) {
|
||||
let iters = 1_000;
|
||||
let out = (0..iters).rev().collect_vec();
|
||||
fn bench_descending(b: &mut Bencher) {
|
||||
let iters = 1_000;
|
||||
let out = (0..iters).rev().collect_vec();
|
||||
let mut graph = DependencyGraph::new();
|
||||
for (i,j) in out.iter().zip(out.iter().skip(1)) { graph.insert_dependency(*i,*j); }
|
||||
b.iter(move || assert_eq!(graph.topo_sort(&out),out));
|
||||
for (i, j) in out.iter().zip(out.iter().skip(1)) {
|
||||
graph.insert_dependency(*i, *j);
|
||||
}
|
||||
b.iter(move || assert_eq!(graph.topo_sort(&out), out));
|
||||
}
|
||||
}
|
||||
|
@ -34,34 +34,34 @@ use std::mem::MaybeUninit;
|
||||
// ================
|
||||
|
||||
/// Closed interval. For example, [`Interval(1,2)`] means `[1,2]` in math.
|
||||
#[derive(Clone,Copy,Default,Eq,PartialEq)]
|
||||
#[derive(Clone, Copy, Default, Eq, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct Interval {
|
||||
pub start : usize,
|
||||
pub end : usize,
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
}
|
||||
|
||||
/// Constructor.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn Interval(start:usize, end:usize) -> Interval {
|
||||
Interval {start,end}
|
||||
pub fn Interval(start: usize, end: usize) -> Interval {
|
||||
Interval { start, end }
|
||||
}
|
||||
|
||||
impl Debug for Interval {
|
||||
fn fmt(&self, f:&mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Interval({:?},{:?})", self.start, self.end)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Interval {
|
||||
fn from(t:usize) -> Self {
|
||||
Interval(t,t)
|
||||
fn from(t: usize) -> Self {
|
||||
Interval(t, t)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(usize,usize)> for Interval {
|
||||
fn from(t:(usize,usize)) -> Self {
|
||||
Interval(t.0,t.1)
|
||||
impl From<(usize, usize)> for Interval {
|
||||
fn from(t: (usize, usize)) -> Self {
|
||||
Interval(t.0, t.1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,15 +74,33 @@ impl From<(usize,usize)> for Interval {
|
||||
// === Helpers ===
|
||||
|
||||
macro_rules! inc {
|
||||
(2) => { 3 };
|
||||
(4) => { 5 };
|
||||
(8) => { 9 };
|
||||
(16) => { 17 };
|
||||
(32) => { 33 };
|
||||
(64) => { 65 };
|
||||
(128) => { 129 };
|
||||
(256) => { 257 };
|
||||
(512) => { 513 };
|
||||
(2) => {
|
||||
3
|
||||
};
|
||||
(4) => {
|
||||
5
|
||||
};
|
||||
(8) => {
|
||||
9
|
||||
};
|
||||
(16) => {
|
||||
17
|
||||
};
|
||||
(32) => {
|
||||
33
|
||||
};
|
||||
(64) => {
|
||||
65
|
||||
};
|
||||
(128) => {
|
||||
129
|
||||
};
|
||||
(256) => {
|
||||
257
|
||||
};
|
||||
(512) => {
|
||||
513
|
||||
};
|
||||
}
|
||||
|
||||
/// This macro is used to define trees with different child count. After upgrading the compiler, we
|
||||
@ -436,7 +454,7 @@ impl Debug for $name {
|
||||
|
||||
})*};}
|
||||
|
||||
define_trees!{
|
||||
define_trees! {
|
||||
tree2::Tree2(2)
|
||||
tree4::Tree4(4)
|
||||
tree8::Tree8(8)
|
||||
@ -459,18 +477,18 @@ mod tests {
|
||||
// === Tree4 Testing Utilities ===
|
||||
|
||||
trait FromSorted<T> {
|
||||
fn from_sorted(t:T) -> Self;
|
||||
fn from_sorted(t: T) -> Self;
|
||||
}
|
||||
|
||||
trait LeafFromSorted<T> {
|
||||
fn leaf_from_sorted(t:T) -> Self;
|
||||
fn leaf_from_sorted(t: T) -> Self;
|
||||
}
|
||||
|
||||
impl FromSorted<(Tree4,(usize,usize),Tree4)> for Tree4 {
|
||||
fn from_sorted(t:(Tree4,(usize,usize),Tree4)) -> Self {
|
||||
impl FromSorted<(Tree4, (usize, usize), Tree4)> for Tree4 {
|
||||
fn from_sorted(t: (Tree4, (usize, usize), Tree4)) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 1;
|
||||
tree.data[0] = Interval((t.1).0,(t.1).1);
|
||||
tree.data[0] = Interval((t.1).0, (t.1).1);
|
||||
let mut children = Self::empty_children_array();
|
||||
children[0] = t.0;
|
||||
children[1] = t.2;
|
||||
@ -479,11 +497,11 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSorted<(Tree4,usize,Tree4)> for Tree4 {
|
||||
fn from_sorted(t:(Tree4,usize,Tree4)) -> Self {
|
||||
impl FromSorted<(Tree4, usize, Tree4)> for Tree4 {
|
||||
fn from_sorted(t: (Tree4, usize, Tree4)) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 1;
|
||||
tree.data[0] = Interval(t.1,t.1);
|
||||
tree.data[0] = Interval(t.1, t.1);
|
||||
let mut children = Self::empty_children_array();
|
||||
children[0] = t.0;
|
||||
children[1] = t.2;
|
||||
@ -492,12 +510,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSorted<(Tree4,usize,Tree4,usize,Tree4)> for Tree4 {
|
||||
fn from_sorted(t:(Tree4,usize,Tree4,usize,Tree4)) -> Self {
|
||||
impl FromSorted<(Tree4, usize, Tree4, usize, Tree4)> for Tree4 {
|
||||
fn from_sorted(t: (Tree4, usize, Tree4, usize, Tree4)) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 2;
|
||||
tree.data[0] = Interval(t.1,t.1);
|
||||
tree.data[1] = Interval(t.3,t.3);
|
||||
tree.data[0] = Interval(t.1, t.1);
|
||||
tree.data[1] = Interval(t.3, t.3);
|
||||
let mut children = Self::empty_children_array();
|
||||
children[0] = t.0;
|
||||
children[1] = t.2;
|
||||
@ -507,13 +525,13 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSorted<(Tree4,usize,Tree4,usize,Tree4,usize,Tree4)> for Tree4 {
|
||||
fn from_sorted(t:(Tree4,usize,Tree4,usize,Tree4,usize,Tree4)) -> Self {
|
||||
impl FromSorted<(Tree4, usize, Tree4, usize, Tree4, usize, Tree4)> for Tree4 {
|
||||
fn from_sorted(t: (Tree4, usize, Tree4, usize, Tree4, usize, Tree4)) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 3;
|
||||
tree.data[0] = Interval(t.1,t.1);
|
||||
tree.data[1] = Interval(t.3,t.3);
|
||||
tree.data[2] = Interval(t.5,t.5);
|
||||
tree.data[0] = Interval(t.1, t.1);
|
||||
tree.data[1] = Interval(t.3, t.3);
|
||||
tree.data[2] = Interval(t.5, t.5);
|
||||
let mut children = Self::empty_children_array();
|
||||
children[0] = t.0;
|
||||
children[1] = t.2;
|
||||
@ -524,14 +542,14 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSorted<(Tree4,usize,Tree4,usize,Tree4,usize,Tree4,usize,Tree4)> for Tree4 {
|
||||
fn from_sorted(t:(Tree4,usize,Tree4,usize,Tree4,usize,Tree4,usize,Tree4)) -> Self {
|
||||
impl FromSorted<(Tree4, usize, Tree4, usize, Tree4, usize, Tree4, usize, Tree4)> for Tree4 {
|
||||
fn from_sorted(t: (Tree4, usize, Tree4, usize, Tree4, usize, Tree4, usize, Tree4)) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 4;
|
||||
tree.data[0] = Interval(t.1,t.1);
|
||||
tree.data[1] = Interval(t.3,t.3);
|
||||
tree.data[2] = Interval(t.5,t.5);
|
||||
tree.data[3] = Interval(t.7,t.7);
|
||||
tree.data[0] = Interval(t.1, t.1);
|
||||
tree.data[1] = Interval(t.3, t.3);
|
||||
tree.data[2] = Interval(t.5, t.5);
|
||||
tree.data[3] = Interval(t.7, t.7);
|
||||
let mut children = Self::empty_children_array();
|
||||
children[0] = t.0;
|
||||
children[1] = t.2;
|
||||
@ -545,8 +563,9 @@ mod tests {
|
||||
|
||||
|
||||
impl<T1> LeafFromSorted<(T1,)> for Tree4
|
||||
where T1:Into<Interval> {
|
||||
fn leaf_from_sorted(t:(T1,)) -> Self {
|
||||
where T1: Into<Interval>
|
||||
{
|
||||
fn leaf_from_sorted(t: (T1,)) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 1;
|
||||
tree.data[0] = t.0.into();
|
||||
@ -554,9 +573,12 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T1,T2> LeafFromSorted<(T1,T2)> for Tree4
|
||||
where T1:Into<Interval>, T2:Into<Interval> {
|
||||
fn leaf_from_sorted(t:(T1,T2)) -> Self {
|
||||
impl<T1, T2> LeafFromSorted<(T1, T2)> for Tree4
|
||||
where
|
||||
T1: Into<Interval>,
|
||||
T2: Into<Interval>,
|
||||
{
|
||||
fn leaf_from_sorted(t: (T1, T2)) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 2;
|
||||
tree.data[0] = t.0.into();
|
||||
@ -565,9 +587,13 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T1,T2,T3> LeafFromSorted<(T1,T2,T3)> for Tree4
|
||||
where T1:Into<Interval>, T2:Into<Interval>, T3:Into<Interval> {
|
||||
fn leaf_from_sorted(t:(T1,T2,T3)) -> Self {
|
||||
impl<T1, T2, T3> LeafFromSorted<(T1, T2, T3)> for Tree4
|
||||
where
|
||||
T1: Into<Interval>,
|
||||
T2: Into<Interval>,
|
||||
T3: Into<Interval>,
|
||||
{
|
||||
fn leaf_from_sorted(t: (T1, T2, T3)) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 3;
|
||||
tree.data[0] = t.0.into();
|
||||
@ -577,9 +603,14 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T1,T2,T3,T4> LeafFromSorted<(T1,T2,T3,T4)> for Tree4
|
||||
where T1:Into<Interval>, T2:Into<Interval>, T3:Into<Interval>, T4:Into<Interval> {
|
||||
fn leaf_from_sorted(t:(T1,T2,T3,T4)) -> Self {
|
||||
impl<T1, T2, T3, T4> LeafFromSorted<(T1, T2, T3, T4)> for Tree4
|
||||
where
|
||||
T1: Into<Interval>,
|
||||
T2: Into<Interval>,
|
||||
T3: Into<Interval>,
|
||||
T4: Into<Interval>,
|
||||
{
|
||||
fn leaf_from_sorted(t: (T1, T2, T3, T4)) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 4;
|
||||
tree.data[0] = t.0.into();
|
||||
@ -590,82 +621,84 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSorted<((usize,usize),)> for Tree4 {
|
||||
fn from_sorted(t:((usize,usize),)) -> Self {
|
||||
impl FromSorted<((usize, usize),)> for Tree4 {
|
||||
fn from_sorted(t: ((usize, usize),)) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 1;
|
||||
tree.data[0] = Interval((t.0).0,(t.0).1);
|
||||
tree.data[0] = Interval((t.0).0, (t.0).1);
|
||||
tree
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl FromSorted<((usize,usize),(usize,usize))> for Tree4 {
|
||||
fn from_sorted(t:((usize,usize),(usize,usize))) -> Self {
|
||||
impl FromSorted<((usize, usize), (usize, usize))> for Tree4 {
|
||||
fn from_sorted(t: ((usize, usize), (usize, usize))) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 2;
|
||||
tree.data[0] = Interval((t.0).0,(t.0).1);
|
||||
tree.data[1] = Interval((t.1).0,(t.1).1);
|
||||
tree.data[0] = Interval((t.0).0, (t.0).1);
|
||||
tree.data[1] = Interval((t.1).0, (t.1).1);
|
||||
tree
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSorted<((usize,usize),(usize,usize),(usize,usize))> for Tree4 {
|
||||
fn from_sorted(t:((usize,usize),(usize,usize),(usize,usize))) -> Self {
|
||||
impl FromSorted<((usize, usize), (usize, usize), (usize, usize))> for Tree4 {
|
||||
fn from_sorted(t: ((usize, usize), (usize, usize), (usize, usize))) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 3;
|
||||
tree.data[0] = Interval((t.0).0,(t.0).1);
|
||||
tree.data[1] = Interval((t.1).0,(t.1).1);
|
||||
tree.data[2] = Interval((t.2).0,(t.2).1);
|
||||
tree.data[0] = Interval((t.0).0, (t.0).1);
|
||||
tree.data[1] = Interval((t.1).0, (t.1).1);
|
||||
tree.data[2] = Interval((t.2).0, (t.2).1);
|
||||
tree
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl FromSorted<((usize,usize),(usize,usize),(usize,usize),(usize,usize))> for Tree4 {
|
||||
fn from_sorted(t:((usize,usize),(usize,usize),(usize,usize),(usize,usize))) -> Self {
|
||||
impl FromSorted<((usize, usize), (usize, usize), (usize, usize), (usize, usize))> for Tree4 {
|
||||
fn from_sorted(
|
||||
t: ((usize, usize), (usize, usize), (usize, usize), (usize, usize)),
|
||||
) -> Self {
|
||||
let mut tree = Tree4::default();
|
||||
tree.data_count = 4;
|
||||
tree.data[0] = Interval((t.0).0,(t.0).1);
|
||||
tree.data[1] = Interval((t.1).0,(t.1).1);
|
||||
tree.data[2] = Interval((t.2).0,(t.2).1);
|
||||
tree.data[3] = Interval((t.3).0,(t.3).1);
|
||||
tree.data[0] = Interval((t.0).0, (t.0).1);
|
||||
tree.data[1] = Interval((t.1).0, (t.1).1);
|
||||
tree.data[2] = Interval((t.2).0, (t.2).1);
|
||||
tree.data[3] = Interval((t.3).0, (t.3).1);
|
||||
tree
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSorted<(usize,)> for Tree4 {
|
||||
fn from_sorted(t:(usize,)) -> Self {
|
||||
Self::from_sorted(((t.0,t.0),))
|
||||
fn from_sorted(t: (usize,)) -> Self {
|
||||
Self::from_sorted(((t.0, t.0),))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSorted<(usize,usize)> for Tree4 {
|
||||
fn from_sorted(t:(usize,usize)) -> Self {
|
||||
Self::from_sorted(((t.0,t.0),(t.1,t.1)))
|
||||
impl FromSorted<(usize, usize)> for Tree4 {
|
||||
fn from_sorted(t: (usize, usize)) -> Self {
|
||||
Self::from_sorted(((t.0, t.0), (t.1, t.1)))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSorted<(usize,usize,usize)> for Tree4 {
|
||||
fn from_sorted(t:(usize,usize,usize)) -> Self {
|
||||
Self::from_sorted(((t.0,t.0),(t.1,t.1),(t.2,t.2)))
|
||||
impl FromSorted<(usize, usize, usize)> for Tree4 {
|
||||
fn from_sorted(t: (usize, usize, usize)) -> Self {
|
||||
Self::from_sorted(((t.0, t.0), (t.1, t.1), (t.2, t.2)))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSorted<(usize,usize,usize,usize)> for Tree4 {
|
||||
fn from_sorted(t:(usize,usize,usize,usize)) -> Self {
|
||||
Self::from_sorted(((t.0,t.0),(t.1,t.1),(t.2,t.2),(t.3,t.3)))
|
||||
impl FromSorted<(usize, usize, usize, usize)> for Tree4 {
|
||||
fn from_sorted(t: (usize, usize, usize, usize)) -> Self {
|
||||
Self::from_sorted(((t.0, t.0), (t.1, t.1), (t.2, t.2), (t.3, t.3)))
|
||||
}
|
||||
}
|
||||
|
||||
fn t<T>(t:T) -> Tree4
|
||||
where Tree4:FromSorted<T> {
|
||||
fn t<T>(t: T) -> Tree4
|
||||
where Tree4: FromSorted<T> {
|
||||
<Tree4 as FromSorted<T>>::from_sorted(t)
|
||||
}
|
||||
|
||||
fn _l<T>(t:T) -> Tree4
|
||||
where Tree4:LeafFromSorted<T> {
|
||||
fn _l<T>(t: T) -> Tree4
|
||||
where Tree4: LeafFromSorted<T> {
|
||||
<Tree4 as LeafFromSorted<T>>::leaf_from_sorted(t)
|
||||
}
|
||||
|
||||
@ -684,235 +717,197 @@ mod tests {
|
||||
|
||||
// === Tests ===
|
||||
|
||||
fn intervals(bounds:&[(usize,usize)]) -> Vec<Interval> {
|
||||
bounds.iter().copied().map(|(a,b)|Interval(a,b)).collect()
|
||||
fn intervals(bounds: &[(usize, usize)]) -> Vec<Interval> {
|
||||
bounds.iter().copied().map(|(a, b)| Interval(a, b)).collect()
|
||||
}
|
||||
|
||||
fn check(tree:&Tree4, bounds:&[(usize,usize)]) {
|
||||
assert_eq!(tree.to_vec(),intervals(bounds));
|
||||
fn check(tree: &Tree4, bounds: &[(usize, usize)]) {
|
||||
assert_eq!(tree.to_vec(), intervals(bounds));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaf_insertion() {
|
||||
let mut v = Tree4::default();
|
||||
check(&v,&[]);
|
||||
v.insert(1) ; check(&v,&[(1,1)]);
|
||||
v.insert(3) ; check(&v,&[(1,1),(3,3)]);
|
||||
v.insert(5) ; check(&v,&[(1,1),(3,3),(5,5)]);
|
||||
v.insert(6) ; check(&v,&[(1,1),(3,3),(5,6)]);
|
||||
v.insert(2) ; check(&v,&[(1,3),(5,6)]);
|
||||
v.insert(4) ; check(&v,&[(1,6)]);
|
||||
check(&v, &[]);
|
||||
v.insert(1);
|
||||
check(&v, &[(1, 1)]);
|
||||
v.insert(3);
|
||||
check(&v, &[(1, 1), (3, 3)]);
|
||||
v.insert(5);
|
||||
check(&v, &[(1, 1), (3, 3), (5, 5)]);
|
||||
v.insert(6);
|
||||
check(&v, &[(1, 1), (3, 3), (5, 6)]);
|
||||
v.insert(2);
|
||||
check(&v, &[(1, 3), (5, 6)]);
|
||||
v.insert(4);
|
||||
check(&v, &[(1, 6)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deep_insertion() {
|
||||
let mut v = Tree4::default();
|
||||
check(&v,&[]);
|
||||
v.insert(10) ; check(&v,&[(10,10)]);
|
||||
v.insert(30) ; check(&v,&[(10,10),(30,30)]);
|
||||
v.insert(50) ; check(&v,&[(10,10),(30,30),(50,50)]);
|
||||
v.insert(70) ; assert_eq!(v,t!(10,30,50,70));
|
||||
v.insert(90) ; assert_eq!(v,t!( t!(10,30), 50, t!(70,90) ));
|
||||
v.insert(110) ; assert_eq!(v,t!( t!(10,30), 50, t!(70,90,110) ));
|
||||
v.insert(130) ; assert_eq!(v,t!( t!(10,30), 50, t!(70,90,110,130) ));
|
||||
v.insert(150) ; assert_eq!(v,t!( t!(10,30), 50, t!(70,90), 110, t!(130,150) ));
|
||||
v.insert(72) ; assert_eq!(v,t!( t!(10,30), 50, t!(70,72,90), 110, t!(130,150) ));
|
||||
v.insert(74) ; assert_eq!(v,t!( t!(10,30), 50, t!(70,72,74,90), 110, t!(130,150) ));
|
||||
v.insert(76) ; assert_eq!(v,t!( t!(10,30), 50, t!(70,72), 74, t!(76,90), 110, t!(130,150) ));
|
||||
v.insert(32) ; assert_eq!(v,t!( t!(10,30,32), 50, t!(70,72), 74, t!(76,90), 110, t!(130,150) ));
|
||||
v.insert(34) ; assert_eq!(v,t!( t!(10,30,32,34), 50, t!(70,72), 74, t!(76,90), 110, t!(130,150) ));
|
||||
v.insert(36) ; assert_eq!(v,t!( t!(10,30), 32, t!(34,36), 50, t!(70,72), 74, t!(76,90), 110, t!(130,150) ));
|
||||
v.insert(52) ; assert_eq!(v,t!( t!(10,30), 32, t!(34,36), 50, t!(52,70,72), 74, t!(76,90), 110, t!(130,150) ));
|
||||
v.insert(54) ; assert_eq!(v,t!( t!(10,30), 32, t!(34,36), 50, t!(52,54,70,72), 74, t!(76,90), 110, t!(130,150) ));
|
||||
check(&v, &[]);
|
||||
v.insert(10);
|
||||
check(&v, &[(10, 10)]);
|
||||
v.insert(30);
|
||||
check(&v, &[(10, 10), (30, 30)]);
|
||||
v.insert(50);
|
||||
check(&v, &[(10, 10), (30, 30), (50, 50)]);
|
||||
v.insert(70);
|
||||
assert_eq!(v, t!(10, 30, 50, 70));
|
||||
v.insert(90);
|
||||
assert_eq!(v, t!(t!(10, 30), 50, t!(70, 90)));
|
||||
v.insert(110);
|
||||
assert_eq!(v, t!(t!(10, 30), 50, t!(70, 90, 110)));
|
||||
v.insert(130);
|
||||
assert_eq!(v, t!(t!(10, 30), 50, t!(70, 90, 110, 130)));
|
||||
v.insert(150);
|
||||
assert_eq!(v, t!(t!(10, 30), 50, t!(70, 90), 110, t!(130, 150)));
|
||||
v.insert(72);
|
||||
assert_eq!(v, t!(t!(10, 30), 50, t!(70, 72, 90), 110, t!(130, 150)));
|
||||
v.insert(74);
|
||||
assert_eq!(v, t!(t!(10, 30), 50, t!(70, 72, 74, 90), 110, t!(130, 150)));
|
||||
v.insert(76);
|
||||
assert_eq!(v, t!(t!(10, 30), 50, t!(70, 72), 74, t!(76, 90), 110, t!(130, 150)));
|
||||
v.insert(32);
|
||||
assert_eq!(v, t!(t!(10, 30, 32), 50, t!(70, 72), 74, t!(76, 90), 110, t!(130, 150)));
|
||||
v.insert(34);
|
||||
assert_eq!(v, t!(t!(10, 30, 32, 34), 50, t!(70, 72), 74, t!(76, 90), 110, t!(130, 150)));
|
||||
v.insert(36);
|
||||
assert_eq!(
|
||||
v,
|
||||
t!(t!(10, 30), 32, t!(34, 36), 50, t!(70, 72), 74, t!(76, 90), 110, t!(130, 150))
|
||||
);
|
||||
v.insert(52);
|
||||
assert_eq!(
|
||||
v,
|
||||
t!(t!(10, 30), 32, t!(34, 36), 50, t!(52, 70, 72), 74, t!(76, 90), 110, t!(130, 150))
|
||||
);
|
||||
v.insert(54);
|
||||
assert_eq!(
|
||||
v,
|
||||
t!(
|
||||
t!(10, 30),
|
||||
32,
|
||||
t!(34, 36),
|
||||
50,
|
||||
t!(52, 54, 70, 72),
|
||||
74,
|
||||
t!(76, 90),
|
||||
110,
|
||||
t!(130, 150)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_case_1() {
|
||||
let mut v = t!(10,20) ; v.insert(0) ; assert_eq!(v,t!(0,10,20));
|
||||
let mut v = t!(10,20) ; v.insert(15) ; assert_eq!(v,t!(10,15,20));
|
||||
let mut v = t!(10,20) ; v.insert(30) ; assert_eq!(v,t!(10,20,30));
|
||||
let mut v = t!(10, 20);
|
||||
v.insert(0);
|
||||
assert_eq!(v, t!(0, 10, 20));
|
||||
let mut v = t!(10, 20);
|
||||
v.insert(15);
|
||||
assert_eq!(v, t!(10, 15, 20));
|
||||
let mut v = t!(10, 20);
|
||||
v.insert(30);
|
||||
assert_eq!(v, t!(10, 20, 30));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_case_2() {
|
||||
let mut v1 = t!(t!(10,20,30,40),50,t!(60,70,80,90));
|
||||
let mut v1 = t!(t!(10, 20, 30, 40), 50, t!(60, 70, 80, 90));
|
||||
let mut v2 = v1.clone();
|
||||
v1.insert(25) ; assert_eq!(v1,t!(t!(10,20),25,t!(30,40),50,t!(60,70,80,90)));
|
||||
v2.insert(75) ; assert_eq!(v2,t!(t!(10,20,30,40),50,t!(60,70),75,t!(80,90)));
|
||||
v1.insert(25);
|
||||
assert_eq!(v1, t!(t!(10, 20), 25, t!(30, 40), 50, t!(60, 70, 80, 90)));
|
||||
v2.insert(75);
|
||||
assert_eq!(v2, t!(t!(10, 20, 30, 40), 50, t!(60, 70), 75, t!(80, 90)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_case_3() {
|
||||
let mut v1 = t!(t!(10,20,30,40),50,t!(60,70,80,90));
|
||||
let mut v1 = t!(t!(10, 20, 30, 40), 50, t!(60, 70, 80, 90));
|
||||
let mut v2 = v1.clone();
|
||||
v1.insert(15) ; assert_eq!(v1,t!(t!(10,15),20,t!(30,40),50,t!(60,70,80,90)));
|
||||
v2.insert(0) ; assert_eq!(v2,t!(t!(0,10) ,20,t!(30,40),50,t!(60,70,80,90)));
|
||||
v1.insert(15);
|
||||
assert_eq!(v1, t!(t!(10, 15), 20, t!(30, 40), 50, t!(60, 70, 80, 90)));
|
||||
v2.insert(0);
|
||||
assert_eq!(v2, t!(t!(0, 10), 20, t!(30, 40), 50, t!(60, 70, 80, 90)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_case_4() {
|
||||
let mut v1 = t!(t!(10,20,30,40),50,t!(60,70,80,90));
|
||||
let mut v1 = t!(t!(10, 20, 30, 40), 50, t!(60, 70, 80, 90));
|
||||
let mut v2 = v1.clone();
|
||||
v1.insert(35) ; assert_eq!(v1,t!(t!(10,20),30,t!(35,40),50,t!(60,70,80,90)));
|
||||
v2.insert(45) ; assert_eq!(v2,t!(t!(10,20),30,t!(40,45),50,t!(60,70,80,90)));
|
||||
v1.insert(35);
|
||||
assert_eq!(v1, t!(t!(10, 20), 30, t!(35, 40), 50, t!(60, 70, 80, 90)));
|
||||
v2.insert(45);
|
||||
assert_eq!(v2, t!(t!(10, 20), 30, t!(40, 45), 50, t!(60, 70, 80, 90)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_case_5() {
|
||||
let mut v = t!(t!(10), 20, t!(30), 40, t!(50,52,54,56), 60, t!(70), 80, t!(90));
|
||||
let mut v = t!(t!(10), 20, t!(30), 40, t!(50, 52, 54, 56), 60, t!(70), 80, t!(90));
|
||||
v.insert(58);
|
||||
assert_eq!(v,t!(t!(t!(10),20,t!(30),40,t!(50,52)), 54, t!(t!(56,58),60,t!(70),80,t!(90))));
|
||||
assert_eq!(
|
||||
v,
|
||||
t!(t!(t!(10), 20, t!(30), 40, t!(50, 52)), 54, t!(t!(56, 58), 60, t!(70), 80, t!(90)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_case_6() {
|
||||
let mut v = t!(t!(10,12,14,16), 20, t!(30), 40, t!(50), 60, t!(70), 80, t!(90));
|
||||
let mut v = t!(t!(10, 12, 14, 16), 20, t!(30), 40, t!(50), 60, t!(70), 80, t!(90));
|
||||
v.insert(18);
|
||||
assert_eq!(v,
|
||||
t!(
|
||||
t!(
|
||||
t!(10,12),
|
||||
14,
|
||||
t!(16,18),
|
||||
20,
|
||||
t!(30)
|
||||
),
|
||||
40,
|
||||
t!(
|
||||
t!(50),
|
||||
60,
|
||||
t!(70),
|
||||
80,
|
||||
t!(90)
|
||||
)
|
||||
)
|
||||
assert_eq!(
|
||||
v,
|
||||
t!(t!(t!(10, 12), 14, t!(16, 18), 20, t!(30)), 40, t!(t!(50), 60, t!(70), 80, t!(90)))
|
||||
);
|
||||
|
||||
|
||||
let mut v = t!(t!(10), 20, t!(30,32,34,36), 40, t!(50), 60, t!(70), 80, t!(90));
|
||||
let mut v = t!(t!(10), 20, t!(30, 32, 34, 36), 40, t!(50), 60, t!(70), 80, t!(90));
|
||||
v.insert(38);
|
||||
assert_eq!(v,
|
||||
t!(
|
||||
t!(
|
||||
t!(10),
|
||||
20,
|
||||
t!(30,32),
|
||||
34,
|
||||
t!(36,38)
|
||||
),
|
||||
40,
|
||||
t!(
|
||||
t!(50),
|
||||
60,
|
||||
t!(70),
|
||||
80,
|
||||
t!(90)
|
||||
)
|
||||
)
|
||||
assert_eq!(
|
||||
v,
|
||||
t!(t!(t!(10), 20, t!(30, 32), 34, t!(36, 38)), 40, t!(t!(50), 60, t!(70), 80, t!(90)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_case_7() {
|
||||
let mut v = t!(t!(10), 20, t!(30), 40, t!(50), 60, t!(70,72,74,76), 80, t!(90));
|
||||
let mut v = t!(t!(10), 20, t!(30), 40, t!(50), 60, t!(70, 72, 74, 76), 80, t!(90));
|
||||
v.insert(78);
|
||||
assert_eq!(v,
|
||||
t!(
|
||||
t!(
|
||||
t!(10),
|
||||
20,
|
||||
t!(30),
|
||||
40,
|
||||
t!(50)
|
||||
),
|
||||
60,
|
||||
t!(
|
||||
t!(70,72),
|
||||
74,
|
||||
t!(76,78),
|
||||
80,
|
||||
t!(90)
|
||||
)
|
||||
)
|
||||
assert_eq!(
|
||||
v,
|
||||
t!(t!(t!(10), 20, t!(30), 40, t!(50)), 60, t!(t!(70, 72), 74, t!(76, 78), 80, t!(90)))
|
||||
);
|
||||
|
||||
let mut v = t!(t!(10), 20, t!(30), 40, t!(50), 60, t!(70), 80, t!(90,92,94,96));
|
||||
let mut v = t!(t!(10), 20, t!(30), 40, t!(50), 60, t!(70), 80, t!(90, 92, 94, 96));
|
||||
v.insert(98);
|
||||
assert_eq!(v,
|
||||
t!(
|
||||
t!(
|
||||
t!(10),
|
||||
20,
|
||||
t!(30),
|
||||
40,
|
||||
t!(50)
|
||||
),
|
||||
60,
|
||||
t!(
|
||||
t!(70),
|
||||
80,
|
||||
t!(90,92),
|
||||
94,
|
||||
t!(96,98)
|
||||
)
|
||||
)
|
||||
assert_eq!(
|
||||
v,
|
||||
t!(t!(t!(10), 20, t!(30), 40, t!(50)), 60, t!(t!(70), 80, t!(90, 92), 94, t!(96, 98)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_deep_bubble() {
|
||||
let mut v =
|
||||
t!(
|
||||
t!(100),
|
||||
200,
|
||||
t!(300),
|
||||
400,
|
||||
t!(
|
||||
t!(500),
|
||||
510,
|
||||
t!(520),
|
||||
530,
|
||||
t!(540,542,544,546),
|
||||
550,
|
||||
t!(560),
|
||||
570,
|
||||
t!(580)
|
||||
),
|
||||
600,
|
||||
t!(700),
|
||||
800,
|
||||
t!(900)
|
||||
);
|
||||
let mut v = t!(
|
||||
t!(100),
|
||||
200,
|
||||
t!(300),
|
||||
400,
|
||||
t!(t!(500), 510, t!(520), 530, t!(540, 542, 544, 546), 550, t!(560), 570, t!(580)),
|
||||
600,
|
||||
t!(700),
|
||||
800,
|
||||
t!(900)
|
||||
);
|
||||
v.insert(548);
|
||||
assert_eq!(v,
|
||||
assert_eq!(
|
||||
v,
|
||||
t!(
|
||||
t!(
|
||||
t!(100),
|
||||
200,
|
||||
t!(300),
|
||||
400,
|
||||
t!(
|
||||
t!(500),
|
||||
510,
|
||||
t!(520),
|
||||
530,
|
||||
t!(540,542)
|
||||
)
|
||||
),
|
||||
t!(t!(100), 200, t!(300), 400, t!(t!(500), 510, t!(520), 530, t!(540, 542))),
|
||||
544,
|
||||
t!(
|
||||
t!(
|
||||
t!(546,548),
|
||||
550,
|
||||
t!(560),
|
||||
570,
|
||||
t!(580)
|
||||
),
|
||||
600,
|
||||
t!(700),
|
||||
800,
|
||||
t!(900)
|
||||
)
|
||||
t!(t!(t!(546, 548), 550, t!(560), 570, t!(580)), 600, t!(700), 800, t!(900))
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -932,7 +927,7 @@ extern crate test;
|
||||
// `start` = `end`).
|
||||
#[cfg(test)]
|
||||
impl PartialOrd for Interval {
|
||||
fn partial_cmp(&self, other:&Self) -> Option<std::cmp::Ordering> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
@ -941,11 +936,15 @@ impl PartialOrd for Interval {
|
||||
// `std::collections::BTreeSet`. It works correctly only for inserting unit intervals (where
|
||||
// `start` = `end`).
|
||||
#[cfg(test)]
|
||||
impl Ord for Interval{
|
||||
fn cmp(&self, other:&Self) -> std::cmp::Ordering {
|
||||
if other.start + 1 < self.start { std::cmp::Ordering::Greater }
|
||||
else if other.start > self.end + 1 { std::cmp::Ordering::Less }
|
||||
else { std::cmp::Ordering::Equal }
|
||||
impl Ord for Interval {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
if other.start + 1 < self.start {
|
||||
std::cmp::Ordering::Greater
|
||||
} else if other.start > self.end + 1 {
|
||||
std::cmp::Ordering::Less
|
||||
} else {
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -972,7 +971,6 @@ impl Ord for Interval{
|
||||
/// descending order.
|
||||
///
|
||||
/// 5. This implementation is 4x FASTER than the `lz_diet` crate.
|
||||
///
|
||||
#[cfg(test)]
|
||||
mod benches {
|
||||
use super::*;
|
||||
@ -985,11 +983,11 @@ mod benches {
|
||||
/// 10^5 | | 63.9 | 31.6 | 18.3 | 21.2 |
|
||||
/// 10^6 | | | | 285.5 | |
|
||||
#[bench]
|
||||
fn bench_insert_ascending(b:&mut Bencher) {
|
||||
fn bench_insert_ascending(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let mut v = Tree16::default();
|
||||
for i in 0 .. test::black_box(1000) {
|
||||
v.insert(i*2);
|
||||
for i in 0..test::black_box(1000) {
|
||||
v.insert(i * 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1001,12 +999,12 @@ mod benches {
|
||||
/// 10^5 | 200 | 62 | 27.5 | 12 | 12 | 18.9 |
|
||||
/// 10^6 | | | | 212 | | |
|
||||
#[bench]
|
||||
fn bench_insert_descending(b:&mut Bencher) {
|
||||
fn bench_insert_descending(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let max = test::black_box(100_000);
|
||||
let max = test::black_box(100_000);
|
||||
let mut v = Tree16::default();
|
||||
for i in 0 .. max {
|
||||
v.insert((max-i)*2);
|
||||
for i in 0..max {
|
||||
v.insert((max - i) * 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1017,13 +1015,19 @@ mod benches {
|
||||
/// 10^4 | 14.1 | 5.2 | 3.4 | 2.7 | 3.32 | 4.8 |
|
||||
/// 10^5 | | | 43.9 | 32 | 39.5 | |
|
||||
#[bench]
|
||||
fn bench_insert_ascending_growing(b:&mut Bencher) {
|
||||
fn bench_insert_ascending_growing(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let max = test::black_box(1000);
|
||||
let mut v = Tree16::default();
|
||||
for i in 0 .. max { v.insert(i*4); }
|
||||
for i in 0 .. max { v.insert(i*4+1); }
|
||||
for i in 0 .. max { v.insert(i*4+2); }
|
||||
for i in 0..max {
|
||||
v.insert(i * 4);
|
||||
}
|
||||
for i in 0..max {
|
||||
v.insert(i * 4 + 1);
|
||||
}
|
||||
for i in 0..max {
|
||||
v.insert(i * 4 + 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1033,12 +1037,12 @@ mod benches {
|
||||
/// 10^5 | 11.8 |
|
||||
/// 10^6 | 149 |
|
||||
#[bench]
|
||||
fn bench_insert_ascending_std(b:&mut Bencher) {
|
||||
fn bench_insert_ascending_std(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let mut v = std::collections::BTreeSet::<Interval>::default();
|
||||
for i in 1 .. test::black_box(1000) {
|
||||
let j = i*2;
|
||||
v.insert(Interval(j,j));
|
||||
for i in 1..test::black_box(1000) {
|
||||
let j = i * 2;
|
||||
v.insert(Interval(j, j));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1049,13 +1053,13 @@ mod benches {
|
||||
/// 10^5 | 8.8 |
|
||||
/// 10^6 | 101 |
|
||||
#[bench]
|
||||
fn bench_insert_descending_std(b:&mut Bencher) {
|
||||
fn bench_insert_descending_std(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let mut v = std::collections::BTreeSet::<Interval>::default();
|
||||
let max = test::black_box(1000);
|
||||
for i in 0 .. max {
|
||||
let j = (max-i)*2;
|
||||
v.insert(Interval(j,j));
|
||||
let max = test::black_box(1000);
|
||||
for i in 0..max {
|
||||
let j = (max - i) * 2;
|
||||
v.insert(Interval(j, j));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1066,11 +1070,11 @@ mod benches {
|
||||
/// 10^5 | 9.7 |
|
||||
/// 10^6 | 115.5 |
|
||||
#[bench]
|
||||
fn bench_insert_ascending_std_usize(b:&mut Bencher) {
|
||||
fn bench_insert_ascending_std_usize(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let mut v = std::collections::BTreeSet::<usize>::default();
|
||||
for i in 1 .. test::black_box(1_000_000) {
|
||||
v.insert(i*2);
|
||||
for i in 1..test::black_box(1_000_000) {
|
||||
v.insert(i * 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1080,11 +1084,11 @@ mod benches {
|
||||
/// 10^5 | 0.1 |
|
||||
/// 10^6 | 2.5 |
|
||||
#[bench]
|
||||
fn bench_insert_vec_non_sorted(b:&mut Bencher) {
|
||||
fn bench_insert_vec_non_sorted(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let mut v = Vec::<usize>::default();
|
||||
for i in 1 .. test::black_box(1000) {
|
||||
v.push(i*2);
|
||||
for i in 1..test::black_box(1000) {
|
||||
v.push(i * 2);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1096,12 +1100,12 @@ mod benches {
|
||||
/// 10^5 | 26 | 3.5 | 0.9 |
|
||||
/// 10^6 | | | 63.8 |
|
||||
#[bench]
|
||||
fn bench_insert_vec_sort_every_x(b:&mut Bencher) {
|
||||
fn bench_insert_vec_sort_every_x(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let sort_every = test::black_box(10000);
|
||||
let mut v = Vec::<usize>::default();
|
||||
for i in 1 .. test::black_box(1000) {
|
||||
v.push(i*2);
|
||||
for i in 1..test::black_box(1000) {
|
||||
v.push(i * 2);
|
||||
if i % sort_every == 0 {
|
||||
v.sort_unstable()
|
||||
}
|
||||
@ -1116,15 +1120,13 @@ mod benches {
|
||||
/// 10^7 | 7.5 |
|
||||
/// 10^8 | 89.4 |
|
||||
#[bench]
|
||||
fn vec_sort_already_almost_sorted(b:&mut Bencher) {
|
||||
fn vec_sort_already_almost_sorted(b: &mut Bencher) {
|
||||
let mut v = Vec::<usize>::default();
|
||||
let num = test::black_box(1000);
|
||||
for i in 0 .. num {
|
||||
for i in 0..num {
|
||||
v.push(num - i);
|
||||
}
|
||||
b.iter(|| {
|
||||
v.sort_unstable()
|
||||
});
|
||||
b.iter(|| v.sort_unstable());
|
||||
}
|
||||
|
||||
/// # Results (ms)
|
||||
@ -1137,13 +1139,16 @@ mod benches {
|
||||
/// before using it in EnsoGL attribute manageent sytem. Read the docs of
|
||||
/// [`ensogl::AttributeScopeData`] to learn more.
|
||||
#[bench]
|
||||
fn mode_rc_cell_num(b:&mut Bencher) {
|
||||
fn mode_rc_cell_num(b: &mut Bencher) {
|
||||
let v = Rc::new(Cell::new(0));
|
||||
let num = test::black_box(100_000_000);
|
||||
b.iter(|| {
|
||||
for i in 0 .. num {
|
||||
if i % 2 == 0 { v.set(v.get() + 1) }
|
||||
else { v.set(v.get() - 1) }
|
||||
for i in 0..num {
|
||||
if i % 2 == 0 {
|
||||
v.set(v.get() + 1)
|
||||
} else {
|
||||
v.set(v.get() - 1)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1152,13 +1157,16 @@ mod benches {
|
||||
///
|
||||
/// 10^8 | 8 |
|
||||
#[bench]
|
||||
fn mode_num(b:&mut Bencher) {
|
||||
fn mode_num(b: &mut Bencher) {
|
||||
let mut v = 0;
|
||||
let num = test::black_box(100_000_000);
|
||||
b.iter(|| {
|
||||
for i in 0 .. num {
|
||||
if i % 2 == 0 { v += 1 }
|
||||
else { v -= 1 }
|
||||
for i in 0..num {
|
||||
if i % 2 == 0 {
|
||||
v += 1
|
||||
} else {
|
||||
v -= 1
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -14,24 +14,24 @@ use std::hash::BuildHasher;
|
||||
pub trait KeyBounds = Clone + Eq + Hash + PartialEq;
|
||||
|
||||
/// The type of branches in the tree.
|
||||
pub type Branches<K,V,S> = HashMap<K,HashMapTree<K,V,S>,S>;
|
||||
pub type Branches<K, V, S> = HashMap<K, HashMapTree<K, V, S>, S>;
|
||||
|
||||
/// A tree built on top of a [`std::collections::HashMap`]. Each node in the tree can have zero or
|
||||
/// more branches accessible by the given key type.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone)]
|
||||
#[derivative(Debug(bound = "K:Eq+Hash+Debug , V:Debug , S:BuildHasher"))]
|
||||
#[derivative(Default(bound = "K:Eq+Hash , V:Default , S:BuildHasher+Default"))]
|
||||
#[derivative(Debug(bound = "K:Eq+Hash+Debug , V:Debug , S:BuildHasher"))]
|
||||
#[derivative(Default(bound = "K:Eq+Hash , V:Default , S:BuildHasher+Default"))]
|
||||
#[derivative(PartialEq(bound = "K:Eq+Hash , V:PartialEq , S:BuildHasher"))]
|
||||
#[derivative(Eq(bound = "K:Eq+Hash , V:Eq , S:BuildHasher"))]
|
||||
pub struct HashMapTree<K,V,S=RandomState> {
|
||||
#[derivative(Eq(bound = "K:Eq+Hash , V:Eq , S:BuildHasher"))]
|
||||
pub struct HashMapTree<K, V, S = RandomState> {
|
||||
/// Value of the current tree node.
|
||||
pub value : V,
|
||||
pub value: V,
|
||||
/// Branches of the current tree node.
|
||||
pub branches : Branches<K,V,S>,
|
||||
pub branches: Branches<K, V, S>,
|
||||
}
|
||||
|
||||
impl<K,V,S> HashMapTree<K,V,S> {
|
||||
impl<K, V, S> HashMapTree<K, V, S> {
|
||||
/// Check if `self` is a leaf of the tree.
|
||||
pub fn is_leaf(&self) -> bool {
|
||||
self.branches.is_empty()
|
||||
@ -43,71 +43,87 @@ impl<K,V,S> HashMapTree<K,V,S> {
|
||||
}
|
||||
|
||||
/// Obtain an iterator over the tree.
|
||||
pub fn iter(&self) -> Iter<K,V,S> {
|
||||
pub fn iter(&self) -> Iter<K, V, S> {
|
||||
let root_item = Some(&self.value);
|
||||
let iters = vec![self.branches.iter()];
|
||||
let path = default();
|
||||
Iter{root_item,iters,path}
|
||||
let iters = vec![self.branches.iter()];
|
||||
let path = default();
|
||||
Iter { root_item, iters, path }
|
||||
}
|
||||
|
||||
/// Obtain a mutable iterator over the tree.
|
||||
pub fn iter_mut(&mut self) -> IterMut<K,V,S> {
|
||||
pub fn iter_mut(&mut self) -> IterMut<K, V, S> {
|
||||
let root_item = Some(&mut self.value);
|
||||
let iters = vec![self.branches.iter_mut()];
|
||||
let path = default();
|
||||
IterMut{root_item,iters,path}
|
||||
let iters = vec![self.branches.iter_mut()];
|
||||
let path = default();
|
||||
IterMut { root_item, iters, path }
|
||||
}
|
||||
}
|
||||
|
||||
impl<K,T,S> HashMapTree<K,T,S>
|
||||
where K : Eq+Hash,
|
||||
S : BuildHasher+Default {
|
||||
impl<K, T, S> HashMapTree<K, T, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
S: BuildHasher + Default,
|
||||
{
|
||||
/// Constructor.
|
||||
pub fn new() -> Self where T:Default {
|
||||
pub fn new() -> Self
|
||||
where T: Default {
|
||||
default()
|
||||
}
|
||||
|
||||
/// Constructor with explicit root value.
|
||||
pub fn from_value(value:T) -> Self {
|
||||
pub fn from_value(value: T) -> Self {
|
||||
let branches = default();
|
||||
Self{value,branches}
|
||||
Self { value, branches }
|
||||
}
|
||||
|
||||
/// Sets the value at position described by `path`. In case a required sub-branch does not
|
||||
/// exist, a default instance will be created.
|
||||
#[inline]
|
||||
pub fn set<P,I>(&mut self, path:P, value:T)
|
||||
where P:IntoIterator<Item=I>, T:Default, I:Into<K> {
|
||||
pub fn set<P, I>(&mut self, path: P, value: T)
|
||||
where
|
||||
P: IntoIterator<Item = I>,
|
||||
T: Default,
|
||||
I: Into<K>, {
|
||||
self.get_or_create_node(path).value = value;
|
||||
}
|
||||
|
||||
/// Sets the value at position described by `path`. In case a required sub-branch does not
|
||||
/// exist, uses `cons_missing` to create it.
|
||||
#[inline]
|
||||
pub fn set_with<P,I,F>(&mut self, path:P, value:T, cons_missing:F)
|
||||
where P:IntoIterator<Item=I>, T:Default, I:Into<K>, F:FnMut()->T {
|
||||
self.get_or_create_node_with(path,cons_missing).value = value;
|
||||
pub fn set_with<P, I, F>(&mut self, path: P, value: T, cons_missing: F)
|
||||
where
|
||||
P: IntoIterator<Item = I>,
|
||||
T: Default,
|
||||
I: Into<K>,
|
||||
F: FnMut() -> T, {
|
||||
self.get_or_create_node_with(path, cons_missing).value = value;
|
||||
}
|
||||
|
||||
/// Gets a reference to a value at the specified path if the path exists in the tree.
|
||||
#[inline]
|
||||
pub fn get<P,I>(&self, segments:P) -> Option<&T>
|
||||
where P:IntoIterator<Item=I>, I:Into<K> {
|
||||
pub fn get<P, I>(&self, segments: P) -> Option<&T>
|
||||
where
|
||||
P: IntoIterator<Item = I>,
|
||||
I: Into<K>, {
|
||||
self.get_node(segments).map(|node| &node.value)
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to a value at the specified path if the path exists in the tree.
|
||||
#[inline]
|
||||
pub fn get_mut<P,I>(&mut self, segments:P) -> Option<&mut T>
|
||||
where P:IntoIterator<Item=I>, I:Into<K> {
|
||||
pub fn get_mut<P, I>(&mut self, segments: P) -> Option<&mut T>
|
||||
where
|
||||
P: IntoIterator<Item = I>,
|
||||
I: Into<K>, {
|
||||
self.get_node_mut(segments).map(|node| &mut node.value)
|
||||
}
|
||||
|
||||
/// Gets a reference to a node at the specified path if the node exists.
|
||||
#[inline]
|
||||
pub fn get_node<P,I>(&self, segments:P) -> Option<&HashMapTree<K,T,S>>
|
||||
where P:IntoIterator<Item=I>, I:Into<K> {
|
||||
segments.into_iter().fold(Some(self),|map,t| {
|
||||
pub fn get_node<P, I>(&self, segments: P) -> Option<&HashMapTree<K, T, S>>
|
||||
where
|
||||
P: IntoIterator<Item = I>,
|
||||
I: Into<K>, {
|
||||
segments.into_iter().fold(Some(self), |map, t| {
|
||||
map.and_then(|m| {
|
||||
let key = t.into();
|
||||
m.branches.get(&key)
|
||||
@ -117,9 +133,11 @@ where K : Eq+Hash,
|
||||
|
||||
/// Gets a mutable reference to a node at the specified path if the node exists.
|
||||
#[inline]
|
||||
pub fn get_node_mut<P,I>(&mut self, segments:P) -> Option<&mut HashMapTree<K,T,S>>
|
||||
where P:IntoIterator<Item=I>, I:Into<K> {
|
||||
segments.into_iter().fold(Some(self),|map,t| {
|
||||
pub fn get_node_mut<P, I>(&mut self, segments: P) -> Option<&mut HashMapTree<K, T, S>>
|
||||
where
|
||||
P: IntoIterator<Item = I>,
|
||||
I: Into<K>, {
|
||||
segments.into_iter().fold(Some(self), |map, t| {
|
||||
map.and_then(|m| {
|
||||
let key = t.into();
|
||||
m.branches.get_mut(&key)
|
||||
@ -129,13 +147,14 @@ where K : Eq+Hash,
|
||||
|
||||
/// Removes the node at the specified path.
|
||||
#[inline]
|
||||
pub fn remove<P,I>(&mut self, segments:P) -> Option<T>
|
||||
where P:IntoIterator<Item=I>, I:Into<K> {
|
||||
let mut segments = segments.into_iter().map(|t|t.into()).collect_vec();
|
||||
pub fn remove<P, I>(&mut self, segments: P) -> Option<T>
|
||||
where
|
||||
P: IntoIterator<Item = I>,
|
||||
I: Into<K>, {
|
||||
let mut segments = segments.into_iter().map(|t| t.into()).collect_vec();
|
||||
segments.pop().and_then(|last| {
|
||||
self.get_node_mut(segments).and_then(|node| {
|
||||
node.branches.remove(&last).map(|branch| branch.value)
|
||||
})
|
||||
self.get_node_mut(segments)
|
||||
.and_then(|node| node.branches.remove(&last).map(|branch| branch.value))
|
||||
})
|
||||
}
|
||||
|
||||
@ -143,42 +162,69 @@ where K : Eq+Hash,
|
||||
/// the branch does not exist, a default instance will be created. Returns mutable reference to
|
||||
/// the target tree node.
|
||||
#[inline]
|
||||
pub fn get_or_create_node<P,I>(&mut self, path:P) -> &mut HashMapTree<K,T,S>
|
||||
where P:IntoIterator<Item=I>, T:Default, I:Into<K> {
|
||||
self.get_or_create_node_with(path,default)
|
||||
pub fn get_or_create_node<P, I>(&mut self, path: P) -> &mut HashMapTree<K, T, S>
|
||||
where
|
||||
P: IntoIterator<Item = I>,
|
||||
T: Default,
|
||||
I: Into<K>, {
|
||||
self.get_or_create_node_with(path, default)
|
||||
}
|
||||
|
||||
/// Iterates over keys in `path`. For each key, traverses into the appropriate branch. In case
|
||||
/// the branch does not exist, uses `cons_missing` to construct it. Returns mutable reference to
|
||||
/// the target tree node.
|
||||
#[inline]
|
||||
pub fn get_or_create_node_with<P,I,F>
|
||||
(&mut self, path:P, cons_missing:F) -> &mut HashMapTree<K,T,S>
|
||||
where P:IntoIterator<Item=I>, I:Into<K>, F:FnMut()->T {
|
||||
self.get_or_create_node_traversing_with(path,cons_missing,|_|{})
|
||||
pub fn get_or_create_node_with<P, I, F>(
|
||||
&mut self,
|
||||
path: P,
|
||||
cons_missing: F,
|
||||
) -> &mut HashMapTree<K, T, S>
|
||||
where
|
||||
P: IntoIterator<Item = I>,
|
||||
I: Into<K>,
|
||||
F: FnMut() -> T,
|
||||
{
|
||||
self.get_or_create_node_traversing_with(path, cons_missing, |_| {})
|
||||
}
|
||||
|
||||
/// Iterates over keys in `path`. For each key, traverses into the appropriate branch. In case
|
||||
/// the branch does not exist, uses `cons_missing` provided with the current path to construct
|
||||
/// it. Returns mutable reference to the target tree node.
|
||||
#[inline]
|
||||
pub fn get_or_create_node_path_with<P,I,F>
|
||||
(&mut self, path:P, cons_missing:F) -> &mut HashMapTree<K,T,S>
|
||||
where K:Clone, P:IntoIterator<Item=I>, I:Into<K>, F:FnMut(&[K])->T {
|
||||
self.get_or_create_node_traversing_path_with(path,cons_missing,|_|{})
|
||||
pub fn get_or_create_node_path_with<P, I, F>(
|
||||
&mut self,
|
||||
path: P,
|
||||
cons_missing: F,
|
||||
) -> &mut HashMapTree<K, T, S>
|
||||
where
|
||||
K: Clone,
|
||||
P: IntoIterator<Item = I>,
|
||||
I: Into<K>,
|
||||
F: FnMut(&[K]) -> T,
|
||||
{
|
||||
self.get_or_create_node_traversing_path_with(path, cons_missing, |_| {})
|
||||
}
|
||||
|
||||
/// Iterates over keys in `path`. For each key, traverses into the appropriate branch. In case
|
||||
/// the branch does not exist, uses `cons_missing` to construct it. Moreover, for each traversed
|
||||
/// branch the `callback` is evaluated. Returns mutable reference to the target tree node.
|
||||
#[inline]
|
||||
pub fn get_or_create_node_traversing_with<P,I,F,M>
|
||||
(&mut self, segments:P, mut cons_missing:F, mut callback:M) -> &mut HashMapTree<K,T,S>
|
||||
where P:IntoIterator<Item=I>, I:Into<K>, F:FnMut()->T, M:FnMut(&mut HashMapTree<K,T,S>) {
|
||||
segments.into_iter().fold(self,|map,t| {
|
||||
let key = t.into();
|
||||
pub fn get_or_create_node_traversing_with<P, I, F, M>(
|
||||
&mut self,
|
||||
segments: P,
|
||||
mut cons_missing: F,
|
||||
mut callback: M,
|
||||
) -> &mut HashMapTree<K, T, S>
|
||||
where
|
||||
P: IntoIterator<Item = I>,
|
||||
I: Into<K>,
|
||||
F: FnMut() -> T,
|
||||
M: FnMut(&mut HashMapTree<K, T, S>),
|
||||
{
|
||||
segments.into_iter().fold(self, |map, t| {
|
||||
let key = t.into();
|
||||
let entry = map.branches.entry(key);
|
||||
let node = entry.or_insert_with(|| HashMapTree::from_value(cons_missing()));
|
||||
let node = entry.or_insert_with(|| HashMapTree::from_value(cons_missing()));
|
||||
callback(node);
|
||||
node
|
||||
})
|
||||
@ -189,19 +235,25 @@ where K : Eq+Hash,
|
||||
/// it. Moreover, for each traversed branch the `callback` is evaluated. Returns mutable
|
||||
/// reference to the target tree node.
|
||||
#[inline]
|
||||
pub fn get_or_create_node_traversing_path_with<P,I,F,M>
|
||||
(&mut self, segments:P, mut cons_missing:F, mut callback:M) -> &mut HashMapTree<K,T,S>
|
||||
where K : Clone,
|
||||
P : IntoIterator<Item=I>,
|
||||
I : Into<K>,
|
||||
F : FnMut(&[K])->T,
|
||||
M : FnMut(&mut HashMapTree<K,T,S>) {
|
||||
pub fn get_or_create_node_traversing_path_with<P, I, F, M>(
|
||||
&mut self,
|
||||
segments: P,
|
||||
mut cons_missing: F,
|
||||
mut callback: M,
|
||||
) -> &mut HashMapTree<K, T, S>
|
||||
where
|
||||
K: Clone,
|
||||
P: IntoIterator<Item = I>,
|
||||
I: Into<K>,
|
||||
F: FnMut(&[K]) -> T,
|
||||
M: FnMut(&mut HashMapTree<K, T, S>),
|
||||
{
|
||||
let mut path = Vec::new();
|
||||
segments.into_iter().fold(self,|map,t| {
|
||||
segments.into_iter().fold(self, |map, t| {
|
||||
let key = t.into();
|
||||
path.push(key.clone());
|
||||
let entry = map.branches.entry(key);
|
||||
let node = entry.or_insert_with(|| HashMapTree::from_value(cons_missing(&path)));
|
||||
let node = entry.or_insert_with(|| HashMapTree::from_value(cons_missing(&path)));
|
||||
callback(node);
|
||||
node
|
||||
})
|
||||
@ -209,68 +261,92 @@ where K : Eq+Hash,
|
||||
|
||||
/// Zips two trees together into a new tree with cloned values.
|
||||
#[inline]
|
||||
pub fn zip_clone<T2>
|
||||
(&self, other:&HashMapTree<K,T2,S>) -> HashMapTree<K,AtLeastOneOfTwo<T,T2>,S>
|
||||
where K:Clone, T:Clone, T2:Clone {
|
||||
Self::zip_clone_branches(Some(self),Some(other))
|
||||
pub fn zip_clone<T2>(
|
||||
&self,
|
||||
other: &HashMapTree<K, T2, S>,
|
||||
) -> HashMapTree<K, AtLeastOneOfTwo<T, T2>, S>
|
||||
where
|
||||
K: Clone,
|
||||
T: Clone,
|
||||
T2: Clone,
|
||||
{
|
||||
Self::zip_clone_branches(Some(self), Some(other))
|
||||
}
|
||||
|
||||
fn zip_clone_branches<T2>
|
||||
(tree1:Option<&HashMapTree<K,T,S>>, tree2:Option<&HashMapTree<K,T2,S>>)
|
||||
-> HashMapTree<K,AtLeastOneOfTwo<T,T2>,S>
|
||||
where K:Clone, T:Clone, T2:Clone {
|
||||
match (tree1,tree2) {
|
||||
(Some(tree1),Some(tree2)) => {
|
||||
let value = AtLeastOneOfTwo::Both(tree1.value.clone(),tree2.value.clone());
|
||||
fn zip_clone_branches<T2>(
|
||||
tree1: Option<&HashMapTree<K, T, S>>,
|
||||
tree2: Option<&HashMapTree<K, T2, S>>,
|
||||
) -> HashMapTree<K, AtLeastOneOfTwo<T, T2>, S>
|
||||
where
|
||||
K: Clone,
|
||||
T: Clone,
|
||||
T2: Clone,
|
||||
{
|
||||
match (tree1, tree2) {
|
||||
(Some(tree1), Some(tree2)) => {
|
||||
let value = AtLeastOneOfTwo::Both(tree1.value.clone(), tree2.value.clone());
|
||||
let mut keys = tree1.branches.keys().cloned().collect::<HashSet<K>>();
|
||||
keys.extend(tree2.branches.keys().cloned());
|
||||
let branches = keys.into_iter().map(|key| {
|
||||
let branch1 = tree1.branches.get(&key);
|
||||
let branch2 = tree2.branches.get(&key);
|
||||
(key,Self::zip_clone_branches(branch1,branch2))
|
||||
}).collect();
|
||||
HashMapTree{value,branches}
|
||||
let branches = keys
|
||||
.into_iter()
|
||||
.map(|key| {
|
||||
let branch1 = tree1.branches.get(&key);
|
||||
let branch2 = tree2.branches.get(&key);
|
||||
(key, Self::zip_clone_branches(branch1, branch2))
|
||||
})
|
||||
.collect();
|
||||
HashMapTree { value, branches }
|
||||
}
|
||||
|
||||
(Some(tree),None) => {
|
||||
let value = AtLeastOneOfTwo::First(tree.value.clone());
|
||||
(Some(tree), None) => {
|
||||
let value = AtLeastOneOfTwo::First(tree.value.clone());
|
||||
let mut keys = tree.branches.keys().cloned().collect::<HashSet<K>>();
|
||||
keys.extend(tree.branches.keys().cloned());
|
||||
let branches = tree.branches.iter().map(|(key,branch)| {
|
||||
(key.clone(),Self::zip_clone_branches(Some(branch),None))
|
||||
}).collect();
|
||||
HashMapTree{value,branches}
|
||||
let branches = tree
|
||||
.branches
|
||||
.iter()
|
||||
.map(|(key, branch)| {
|
||||
(key.clone(), Self::zip_clone_branches(Some(branch), None))
|
||||
})
|
||||
.collect();
|
||||
HashMapTree { value, branches }
|
||||
}
|
||||
|
||||
(None,Some(tree)) => {
|
||||
let value = AtLeastOneOfTwo::Second(tree.value.clone());
|
||||
(None, Some(tree)) => {
|
||||
let value = AtLeastOneOfTwo::Second(tree.value.clone());
|
||||
let mut keys = tree.branches.keys().cloned().collect::<HashSet<K>>();
|
||||
keys.extend(tree.branches.keys().cloned());
|
||||
let branches = tree.branches.iter().map(|(key,branch)| {
|
||||
(key.clone(),Self::zip_clone_branches(None,Some(branch)))
|
||||
}).collect();
|
||||
HashMapTree{value,branches}
|
||||
let branches = tree
|
||||
.branches
|
||||
.iter()
|
||||
.map(|(key, branch)| {
|
||||
(key.clone(), Self::zip_clone_branches(None, Some(branch)))
|
||||
})
|
||||
.collect();
|
||||
HashMapTree { value, branches }
|
||||
}
|
||||
_ => panic!("Impossible")
|
||||
_ => panic!("Impossible"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K,T,S> HashMapTree<K,Option<T>,S>
|
||||
where K:Eq+Hash {
|
||||
impl<K, T, S> HashMapTree<K, Option<T>, S>
|
||||
where K: Eq + Hash
|
||||
{
|
||||
/// Gets the current value or creates new default one if missing.
|
||||
pub fn value_or_default(&mut self) -> &mut T where T:Default {
|
||||
pub fn value_or_default(&mut self) -> &mut T
|
||||
where T: Default {
|
||||
self.value_or_set_with(default)
|
||||
}
|
||||
|
||||
/// Gets the current value or creates new one if missing.
|
||||
pub fn value_or_set(&mut self, val:T) -> &mut T {
|
||||
pub fn value_or_set(&mut self, val: T) -> &mut T {
|
||||
self.value_or_set_with(move || val)
|
||||
}
|
||||
|
||||
/// Gets the current value or creates new one if missing.
|
||||
pub fn value_or_set_with<F>(&mut self, cons:F) -> &mut T
|
||||
where F:FnOnce()->T {
|
||||
pub fn value_or_set_with<F>(&mut self, cons: F) -> &mut T
|
||||
where F: FnOnce() -> T {
|
||||
if self.value.is_none() {
|
||||
self.value = Some(cons());
|
||||
};
|
||||
@ -281,23 +357,27 @@ where K:Eq+Hash {
|
||||
|
||||
// === Impls ===
|
||||
|
||||
impl<K,V,S> PartialSemigroup<HashMapTree<K,V,S>> for HashMapTree<K,V,S>
|
||||
where K : Eq+Hash+Clone,
|
||||
V : Semigroup,
|
||||
S : BuildHasher+Clone {
|
||||
impl<K, V, S> PartialSemigroup<HashMapTree<K, V, S>> for HashMapTree<K, V, S>
|
||||
where
|
||||
K: Eq + Hash + Clone,
|
||||
V: Semigroup,
|
||||
S: BuildHasher + Clone,
|
||||
{
|
||||
fn concat_mut(&mut self, other: Self) {
|
||||
self.value.concat_mut(&other.value);
|
||||
PartialSemigroup::concat_mut(&mut self.branches,other.branches);
|
||||
PartialSemigroup::concat_mut(&mut self.branches, other.branches);
|
||||
}
|
||||
}
|
||||
|
||||
impl<K,V,S> PartialSemigroup<&HashMapTree<K,V,S>> for HashMapTree<K,V,S>
|
||||
where K : Eq+Hash+Clone,
|
||||
V : Semigroup,
|
||||
S : BuildHasher+Clone {
|
||||
fn concat_mut(&mut self, other:&Self) {
|
||||
impl<K, V, S> PartialSemigroup<&HashMapTree<K, V, S>> for HashMapTree<K, V, S>
|
||||
where
|
||||
K: Eq + Hash + Clone,
|
||||
V: Semigroup,
|
||||
S: BuildHasher + Clone,
|
||||
{
|
||||
fn concat_mut(&mut self, other: &Self) {
|
||||
self.value.concat_mut(&other.value);
|
||||
PartialSemigroup::concat_mut(&mut self.branches,&other.branches);
|
||||
PartialSemigroup::concat_mut(&mut self.branches, &other.branches);
|
||||
}
|
||||
}
|
||||
|
||||
@ -363,14 +443,16 @@ macro_rules! define_borrow_iterator {
|
||||
define_borrow_iterator!(Iter iter);
|
||||
define_borrow_iterator!(IterMut iter_mut mut);
|
||||
|
||||
impl<K,V,S> FromIterator<(Vec<K>,V)> for HashMapTree<K,V,S>
|
||||
where K : Eq + Hash,
|
||||
V : Default,
|
||||
S : BuildHasher + Default {
|
||||
fn from_iter<T: IntoIterator<Item=(Vec<K>,V)>>(iter: T) -> Self {
|
||||
impl<K, V, S> FromIterator<(Vec<K>, V)> for HashMapTree<K, V, S>
|
||||
where
|
||||
K: Eq + Hash,
|
||||
V: Default,
|
||||
S: BuildHasher + Default,
|
||||
{
|
||||
fn from_iter<T: IntoIterator<Item = (Vec<K>, V)>>(iter: T) -> Self {
|
||||
let mut new_tree = HashMapTree::new();
|
||||
for (path, val) in iter {
|
||||
new_tree.set(path,val);
|
||||
new_tree.set(path, val);
|
||||
}
|
||||
new_tree
|
||||
}
|
||||
@ -389,34 +471,34 @@ mod tests {
|
||||
#[test]
|
||||
fn single_insert_get() {
|
||||
let value = "String";
|
||||
let path = vec![1,2,4,3];
|
||||
let mut tree = HashMapTree::<i32,String>::new();
|
||||
tree.set(path.clone(),value.to_string());
|
||||
let path = vec![1, 2, 4, 3];
|
||||
let mut tree = HashMapTree::<i32, String>::new();
|
||||
tree.set(path.clone(), value.to_string());
|
||||
let obtained_val = tree.get(path);
|
||||
assert!(obtained_val.is_some());
|
||||
assert_eq!(obtained_val.unwrap().as_str(),value);
|
||||
assert_eq!(obtained_val.unwrap().as_str(), value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_insert_get() {
|
||||
let mut tree = HashMapTree::<i32,i32>::new();
|
||||
let values = vec![1,2,3,4,5];
|
||||
let paths = vec![vec![1,2],vec![2,2,1,3],vec![1,3],vec![1,2,4,1],vec![1,3,1]];
|
||||
for (val,path) in values.iter().zip(&paths) {
|
||||
tree.set(path.clone(),*val)
|
||||
let mut tree = HashMapTree::<i32, i32>::new();
|
||||
let values = vec![1, 2, 3, 4, 5];
|
||||
let paths = vec![vec![1, 2], vec![2, 2, 1, 3], vec![1, 3], vec![1, 2, 4, 1], vec![1, 3, 1]];
|
||||
for (val, path) in values.iter().zip(&paths) {
|
||||
tree.set(path.clone(), *val)
|
||||
}
|
||||
for (val,path) in values.iter().zip(&paths) {
|
||||
for (val, path) in values.iter().zip(&paths) {
|
||||
let obtained_val = tree.get(path.clone());
|
||||
assert!(obtained_val.is_some());
|
||||
assert_eq!(obtained_val.unwrap(),val)
|
||||
assert_eq!(obtained_val.unwrap(), val)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_leaf() {
|
||||
let tree_1 = HashMapTree::<i32,i32>::from_value(1);
|
||||
let tree_2 = HashMapTree::<i32,i32>::new();
|
||||
let mut tree_3 = HashMapTree::<i32,i32>::new();
|
||||
let tree_1 = HashMapTree::<i32, i32>::from_value(1);
|
||||
let tree_2 = HashMapTree::<i32, i32>::new();
|
||||
let mut tree_3 = HashMapTree::<i32, i32>::new();
|
||||
tree_3.set(vec![1], 1);
|
||||
assert!(tree_1.is_leaf());
|
||||
assert!(tree_2.is_leaf());
|
||||
@ -425,15 +507,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn map() {
|
||||
let mut tree = HashMapTree::<i32, i32>::new();
|
||||
let values = vec![1,2,3,4,5];
|
||||
let paths:Vec<Vec<i32>> = vec![vec![],vec![1,2],vec![2,2,1,3],vec![1,3],vec![1,2,4,1],vec![1,3,1]];
|
||||
let mut tree = HashMapTree::<i32, i32>::new();
|
||||
let values = vec![1, 2, 3, 4, 5];
|
||||
let paths: Vec<Vec<i32>> =
|
||||
vec![vec![], vec![1, 2], vec![2, 2, 1, 3], vec![1, 3], vec![1, 2, 4, 1], vec![1, 3, 1]];
|
||||
for (val, path) in values.iter().zip(&paths) {
|
||||
tree.set(path.clone(), *val)
|
||||
}
|
||||
let new_tree:HashMapTree<_,_,RandomState> = tree.iter().map(|(p,v)| (p,format!("{}",v))).collect();
|
||||
let new_tree: HashMapTree<_, _, RandomState> =
|
||||
tree.iter().map(|(p, v)| (p, format!("{}", v))).collect();
|
||||
for (val, path) in values.iter().zip(&paths) {
|
||||
let path = path.clone();
|
||||
let path = path.clone();
|
||||
let output = new_tree.get(path.iter()).unwrap().clone();
|
||||
assert_eq!(output, val.to_string());
|
||||
}
|
||||
@ -442,8 +526,9 @@ mod tests {
|
||||
#[test]
|
||||
fn map_mutable() {
|
||||
let mut tree = HashMapTree::<i32, i32>::new();
|
||||
let values = vec![10,1,2,3,4,5];
|
||||
let paths = vec![vec![],vec![1,2],vec![2,2,1,3],vec![1,3],vec![1,2,4,1],vec![1,3,1]];
|
||||
let values = vec![10, 1, 2, 3, 4, 5];
|
||||
let paths =
|
||||
vec![vec![], vec![1, 2], vec![2, 2, 1, 3], vec![1, 3], vec![1, 2, 4, 1], vec![1, 3, 1]];
|
||||
for (val, path) in values.iter().zip(&paths) {
|
||||
tree.set(path.clone(), *val)
|
||||
}
|
||||
|
@ -12,22 +12,22 @@ use crate::prelude::*;
|
||||
/// Typed newtype for `usize` meant to be used as a typed index.
|
||||
pub struct Index<T> {
|
||||
/// Raw value.
|
||||
pub raw : usize,
|
||||
phantom : PhantomData<T>
|
||||
pub raw: usize,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Index<T> {
|
||||
/// Constructor
|
||||
pub fn new(raw:usize) -> Self {
|
||||
pub fn new(raw: usize) -> Self {
|
||||
let phantom = default();
|
||||
Self {raw,phantom}
|
||||
Self { raw, phantom }
|
||||
}
|
||||
}
|
||||
|
||||
// === Impls ===
|
||||
|
||||
impl<T> Copy for Index<T> {}
|
||||
impl<T> Eq for Index<T> {}
|
||||
impl<T> Eq for Index<T> {}
|
||||
|
||||
impl<T> Clone for Index<T> {
|
||||
fn clone(&self) -> Self {
|
||||
@ -36,49 +36,49 @@ impl<T> Clone for Index<T> {
|
||||
}
|
||||
|
||||
impl<T> Hash for Index<T> {
|
||||
fn hash<H:std::hash::Hasher>(&self, state:&mut H) {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.raw.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Index<T> {
|
||||
fn eq(&self, other:&Self) -> bool {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.raw == other.raw
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Index<T>> for usize {
|
||||
fn from(t:Index<T>) -> Self {
|
||||
fn from(t: Index<T>) -> Self {
|
||||
t.raw
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<&Index<T>> for usize {
|
||||
fn from(t:&Index<T>) -> Self {
|
||||
fn from(t: &Index<T>) -> Self {
|
||||
t.raw
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<usize> for Index<T> {
|
||||
fn from(t:usize) -> Self {
|
||||
fn from(t: usize) -> Self {
|
||||
Self::new(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<&usize> for Index<T> {
|
||||
fn from(t:&usize) -> Self {
|
||||
fn from(t: &usize) -> Self {
|
||||
Self::new(*t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Debug for Index<T> {
|
||||
fn fmt(&self, f:&mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f,"{}",self.raw)
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Display for Index<T> {
|
||||
fn fmt(&self, f:&mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f,"{}",self.raw)
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.raw)
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,7 @@
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(test)]
|
||||
#![feature(trait_alias)]
|
||||
|
||||
#![deny(unconditional_recursion)]
|
||||
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
@ -15,9 +13,9 @@
|
||||
#![warn(unused_import_braces)]
|
||||
|
||||
pub mod dependency_graph;
|
||||
pub mod diet;
|
||||
pub mod hash_map_tree;
|
||||
pub mod index;
|
||||
pub mod diet;
|
||||
pub mod opt_vec;
|
||||
pub mod text;
|
||||
|
||||
|
@ -17,36 +17,36 @@ use std::slice;
|
||||
/// parametrized with optional `Index` type variable which will be used for indexing the vector.
|
||||
/// Index have to implement the `Index` trait.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound=""))]
|
||||
#[derive(Clone,Debug,Shrinkwrap)]
|
||||
pub struct OptVec<T,Index=usize> {
|
||||
#[derivative(Default(bound = ""))]
|
||||
#[derive(Clone, Debug, Shrinkwrap)]
|
||||
pub struct OptVec<T, Index = usize> {
|
||||
#[shrinkwrap(main_field)]
|
||||
items : Vec<Option<T>>,
|
||||
free_ixs : SmallVec<[Index; 128]>,
|
||||
items: Vec<Option<T>>,
|
||||
free_ixs: SmallVec<[Index; 128]>,
|
||||
}
|
||||
|
||||
|
||||
// === Types ===
|
||||
|
||||
/// A trait for any vector index type.
|
||||
pub trait Index = Debug + Copy + Into<usize> where usize : Into<Self>;
|
||||
pub trait Index = Debug + Copy + Into<usize> where usize: Into<Self>;
|
||||
|
||||
/// Iterator type of this vector.
|
||||
pub type Iter<'t,T> = FilterMap<slice::Iter<'t,Option<T>>, OptionAsRef<T>>;
|
||||
pub type Iter<'t, T> = FilterMap<slice::Iter<'t, Option<T>>, OptionAsRef<T>>;
|
||||
|
||||
/// Mutable iterator type of this vector.
|
||||
pub type IterMut<'t,T> = FilterMap<slice::IterMut<'t, Option<T>>, OptionAsRefMut<T>>;
|
||||
pub type IterMut<'t, T> = FilterMap<slice::IterMut<'t, Option<T>>, OptionAsRefMut<T>>;
|
||||
|
||||
/// Subtype of `Iter`.
|
||||
pub type OptionAsRef <T> = for<'r> fn(&'r Option<T>) -> Option<&'r T>;
|
||||
pub type OptionAsRef<T> = for<'r> fn(&'r Option<T>) -> Option<&'r T>;
|
||||
|
||||
/// Subtype of `IterMut`.
|
||||
pub type OptionAsRefMut <T> = for<'r> fn(&'r mut Option<T>) -> Option<&'r mut T>;
|
||||
pub type OptionAsRefMut<T> = for<'r> fn(&'r mut Option<T>) -> Option<&'r mut T>;
|
||||
|
||||
|
||||
// === Construction ===
|
||||
|
||||
impl<T,I:Index> OptVec<T,I> {
|
||||
impl<T, I: Index> OptVec<T, I> {
|
||||
/// Constructs a new, empty `Vec<T>`. It will not allocate until elements are pushed onto it.
|
||||
pub fn new() -> Self {
|
||||
default()
|
||||
@ -56,7 +56,7 @@ impl<T,I:Index> OptVec<T,I> {
|
||||
|
||||
// === Status Checks ===
|
||||
|
||||
impl<T,I:Index> OptVec<T,I> {
|
||||
impl<T, I: Index> OptVec<T, I> {
|
||||
/// Returns the number of elements in the vector, including reserved indexes. Also referred to
|
||||
/// as its 'length'.
|
||||
pub fn len(&self) -> usize {
|
||||
@ -72,7 +72,7 @@ impl<T,I:Index> OptVec<T,I> {
|
||||
|
||||
// === Modifiers ===
|
||||
|
||||
impl<T,I:Index> OptVec<T,I> {
|
||||
impl<T, I: Index> OptVec<T, I> {
|
||||
/// Inserts the provided element to the vector. It reuses free indexes if any.
|
||||
pub fn insert(&mut self, item: T) -> I {
|
||||
self.insert_with_ix_(|_| item)
|
||||
@ -84,19 +84,19 @@ impl<T,I:Index> OptVec<T,I> {
|
||||
/// This code is very similar to [`insert_with_ix_`]. We duplicate it in order to be sure that
|
||||
/// it will be correctly optimized as it is used in a very performance sensitive algorithms of
|
||||
/// managing GPU buffers.
|
||||
pub fn insert_with_ix<S,F>(&mut self, f:F) -> (I,S)
|
||||
where F : FnOnce(I) -> (T,S) {
|
||||
pub fn insert_with_ix<S, F>(&mut self, f: F) -> (I, S)
|
||||
where F: FnOnce(I) -> (T, S) {
|
||||
match self.free_ixs.pop() {
|
||||
None => {
|
||||
let index = self.items.len().into();
|
||||
let (item,out) = f(index);
|
||||
let (item, out) = f(index);
|
||||
self.items.push(Some(item));
|
||||
(index,out)
|
||||
(index, out)
|
||||
}
|
||||
Some(index) => {
|
||||
let (item,out) = f(index);
|
||||
let (item, out) = f(index);
|
||||
self.items[index.into()] = Some(item);
|
||||
(index,out)
|
||||
(index, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,8 +108,8 @@ impl<T,I:Index> OptVec<T,I> {
|
||||
/// This code is very similar to [`insert_with_ix`]. We duplicate it in order to be sure that
|
||||
/// it will be correctly optimized as it is used in a very performance sensitive algorithms of
|
||||
/// managing GPU buffers.
|
||||
pub fn insert_with_ix_<F>(&mut self, f:F) -> I
|
||||
where F : FnOnce(I) -> T {
|
||||
pub fn insert_with_ix_<F>(&mut self, f: F) -> I
|
||||
where F: FnOnce(I) -> T {
|
||||
match self.free_ixs.pop() {
|
||||
None => {
|
||||
let index = self.items.len().into();
|
||||
@ -134,13 +134,13 @@ impl<T,I:Index> OptVec<T,I> {
|
||||
}
|
||||
|
||||
/// Sets the value at given index. Panics if the index was already freed.
|
||||
pub fn set(&mut self, index:I, t:T) {
|
||||
pub fn set(&mut self, index: I, t: T) {
|
||||
self.items[index.into()] = Some(t);
|
||||
}
|
||||
|
||||
/// Removes the element at provided index and marks the index to be reused. Does nothing if the
|
||||
/// index was already empty. Panics if the index was out of bounds.
|
||||
pub fn remove(&mut self, index:I) -> Option<T> {
|
||||
pub fn remove(&mut self, index: I) -> Option<T> {
|
||||
let item = self.items[index.into()].take();
|
||||
item.iter().for_each(|_| self.free_ixs.push(index));
|
||||
item
|
||||
@ -150,29 +150,29 @@ impl<T,I:Index> OptVec<T,I> {
|
||||
|
||||
// === Indexing ===
|
||||
|
||||
impl<T,I:Index> OptVec<T,I> {
|
||||
impl<T, I: Index> OptVec<T, I> {
|
||||
/// Index into vector. Returns `None` if the key was already freed.
|
||||
pub fn safe_index(&self, index:I) -> Option<&T> {
|
||||
pub fn safe_index(&self, index: I) -> Option<&T> {
|
||||
self.items[index.into()].as_ref()
|
||||
}
|
||||
|
||||
/// Index into vector. Returns `None` if the key was already freed.
|
||||
pub fn safe_index_mut(&mut self, index:I) -> Option<&mut T> {
|
||||
pub fn safe_index_mut(&mut self, index: I) -> Option<&mut T> {
|
||||
self.items[index.into()].as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T,I:Index> std::ops::Index<I> for OptVec<T,I> {
|
||||
impl<T, I: Index> std::ops::Index<I> for OptVec<T, I> {
|
||||
type Output = T;
|
||||
fn index(&self, index:I) -> &Self::Output {
|
||||
let error = || panic!("Trying to access removed index `{:?}`.",index);
|
||||
fn index(&self, index: I) -> &Self::Output {
|
||||
let error = || panic!("Trying to access removed index `{:?}`.", index);
|
||||
self.items.index(index.into()).as_ref().unwrap_or_else(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T,I:Index> std::ops::IndexMut<I> for OptVec<T,I> {
|
||||
fn index_mut(&mut self, index:I) -> &mut Self::Output {
|
||||
let error = || panic!("Trying to access removed index `{:?}`.",index);
|
||||
impl<T, I: Index> std::ops::IndexMut<I> for OptVec<T, I> {
|
||||
fn index_mut(&mut self, index: I) -> &mut Self::Output {
|
||||
let error = || panic!("Trying to access removed index `{:?}`.", index);
|
||||
self.items.index_mut(index.into()).as_mut().unwrap_or_else(error)
|
||||
}
|
||||
}
|
||||
@ -180,7 +180,7 @@ impl<T,I:Index> std::ops::IndexMut<I> for OptVec<T,I> {
|
||||
|
||||
// === Iterators ===
|
||||
|
||||
impl<T,I:Index> OptVec<T,I> {
|
||||
impl<T, I: Index> OptVec<T, I> {
|
||||
/// Iterator.
|
||||
pub fn iter(&self) -> Iter<T> {
|
||||
self.items.iter().filter_map(Option::as_ref)
|
||||
@ -192,17 +192,17 @@ impl<T,I:Index> OptVec<T,I> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,T,I:Index> IntoIterator for &'a OptVec<T,I> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = Iter<'a,T>;
|
||||
impl<'a, T, I: Index> IntoIterator for &'a OptVec<T, I> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = Iter<'a, T>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,T,I:Index> IntoIterator for &'a mut OptVec<T,I> {
|
||||
type Item = &'a mut T;
|
||||
type IntoIter = IterMut<'a,T>;
|
||||
impl<'a, T, I: Index> IntoIterator for &'a mut OptVec<T, I> {
|
||||
type Item = &'a mut T;
|
||||
type IntoIter = IterMut<'a, T>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
@ -224,46 +224,46 @@ mod tests {
|
||||
assert!(v.is_empty());
|
||||
|
||||
let ix1 = v.insert(1);
|
||||
assert_eq!(ix1,0);
|
||||
assert_eq!(v.len(),1);
|
||||
assert_eq!(ix1, 0);
|
||||
assert_eq!(v.len(), 1);
|
||||
assert!(!v.is_empty());
|
||||
|
||||
let ix2 = v.insert(2);
|
||||
assert_eq!(ix2,1);
|
||||
assert_eq!(v.len(),2);
|
||||
assert_eq!(ix2, 1);
|
||||
assert_eq!(v.len(), 2);
|
||||
|
||||
v.remove(ix1);
|
||||
assert_eq!(v.len(),1);
|
||||
assert_eq!(v.len(), 1);
|
||||
|
||||
v.remove(ix2);
|
||||
assert_eq!(v.len(),0);
|
||||
assert_eq!(v.len(), 0);
|
||||
assert!(v.is_empty());
|
||||
|
||||
let ix3 = v.insert(3);
|
||||
assert_eq!(v.len(),1);
|
||||
assert_eq!(v.len(), 1);
|
||||
|
||||
let ix4 = v.insert(4);
|
||||
assert_eq!(ix3,1);
|
||||
assert_eq!(ix4,0);
|
||||
assert_eq!(v.len(),2);
|
||||
assert_eq!(ix3, 1);
|
||||
assert_eq!(ix4, 0);
|
||||
assert_eq!(v.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iter() {
|
||||
let mut v = OptVec::<usize>::new();
|
||||
|
||||
let ix1 = v.insert(0);
|
||||
let ix1 = v.insert(0);
|
||||
let _ix2 = v.insert(1);
|
||||
let _ix3 = v.insert(2);
|
||||
assert_eq!(v.len(),3);
|
||||
assert_eq!(v.len(), 3);
|
||||
|
||||
for (i,value) in v.into_iter().enumerate() {
|
||||
for (i, value) in v.into_iter().enumerate() {
|
||||
assert_eq!(i, *value);
|
||||
}
|
||||
|
||||
v.remove(ix1);
|
||||
assert_eq!(v.len(),2);
|
||||
for (i,value) in v.into_iter().enumerate() {
|
||||
assert_eq!(v.len(), 2);
|
||||
for (i, value) in v.into_iter().enumerate() {
|
||||
assert_eq!(i + 1, *value);
|
||||
}
|
||||
}
|
||||
@ -272,15 +272,17 @@ mod tests {
|
||||
fn test_iter_mut() {
|
||||
let mut v = OptVec::<usize>::new();
|
||||
|
||||
let ix1 = v.insert(0);
|
||||
let ix1 = v.insert(0);
|
||||
let _ix2 = v.insert(1);
|
||||
let _ix3 = v.insert(2);
|
||||
assert_eq!(v.len(),3);
|
||||
assert_eq!(v.len(), 3);
|
||||
|
||||
v.remove(ix1);
|
||||
assert_eq!(v.len(),2);
|
||||
assert_eq!(v.len(), 2);
|
||||
|
||||
for value in &mut v { *value *= 2; }
|
||||
for value in &mut v {
|
||||
*value *= 2;
|
||||
}
|
||||
for (i, value) in v.into_iter().enumerate() {
|
||||
assert_eq!((i + 1) * 2, *value);
|
||||
}
|
||||
|
@ -20,30 +20,32 @@ use std::ops::SubAssign;
|
||||
|
||||
/// Strongly typed index into container.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone,Copy,Debug,Default,Hash,PartialEq,Eq,PartialOrd,Ord,Serialize,Deserialize)]
|
||||
pub struct Index { pub value:usize }
|
||||
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Index {
|
||||
pub value: usize,
|
||||
}
|
||||
|
||||
impl Index {
|
||||
/// Initializes Index with given value.
|
||||
pub fn new(value:usize) -> Self {
|
||||
Index {value}
|
||||
pub fn new(value: usize) -> Self {
|
||||
Index { value }
|
||||
}
|
||||
|
||||
/// Create char index from the byte index. It must traverse the content to count chars.
|
||||
pub fn convert_byte_index(content:impl Str, index:ByteIndex) -> Self {
|
||||
pub fn convert_byte_index(content: impl Str, index: ByteIndex) -> Self {
|
||||
let slice = &content.as_ref()[..index.value];
|
||||
Self::new(slice.chars().count())
|
||||
}
|
||||
|
||||
/// Checked subtraction. Computes `self - rhs`, returning `None` if overflow occurred.
|
||||
pub fn checked_sub(self, rhs:Size) -> Option<Self> {
|
||||
pub fn checked_sub(self, rhs: Size) -> Option<Self> {
|
||||
self.value.checked_sub(rhs.value).map(Self::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Index {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f,"{}",self.value)
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,23 +57,25 @@ impl Display for Index {
|
||||
//TODO[ao] We should use structures from ensogl::math::topology to represent different quantities
|
||||
// and units.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone,Copy,Debug,Default,Hash,PartialEq,Eq,PartialOrd,Ord,Serialize,Deserialize)]
|
||||
pub struct ByteIndex { pub value:usize }
|
||||
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct ByteIndex {
|
||||
pub value: usize,
|
||||
}
|
||||
|
||||
impl ByteIndex {
|
||||
/// Initializes Index with given value.
|
||||
pub fn new(value:usize) -> Self {
|
||||
ByteIndex {value}
|
||||
pub fn new(value: usize) -> Self {
|
||||
ByteIndex { value }
|
||||
}
|
||||
|
||||
/// Map given Range<usize> into Range<ByteIndex>.
|
||||
pub fn new_range(value:Range<usize>) -> Range<Self> {
|
||||
ByteIndex::new(value.start) .. ByteIndex::new(value.end)
|
||||
pub fn new_range(value: Range<usize>) -> Range<Self> {
|
||||
ByteIndex::new(value.start)..ByteIndex::new(value.end)
|
||||
}
|
||||
|
||||
/// Index of the next byte.
|
||||
pub fn next(self) -> Self {
|
||||
ByteIndex {value: self.value + 1}
|
||||
ByteIndex { value: self.value + 1 }
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,17 +84,19 @@ impl ByteIndex {
|
||||
|
||||
/// Strongly typed size of container.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone,Copy,Debug,Default,Hash,PartialEq,Eq,PartialOrd,Ord,Serialize,Deserialize)]
|
||||
pub struct Size { pub value:usize }
|
||||
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Size {
|
||||
pub value: usize,
|
||||
}
|
||||
|
||||
impl Size {
|
||||
/// Initializes Size with given value.
|
||||
pub fn new(value:usize) -> Self {
|
||||
Size {value}
|
||||
pub fn new(value: usize) -> Self {
|
||||
Size { value }
|
||||
}
|
||||
|
||||
/// Obtain a size of given string value.
|
||||
pub fn from_text(value:impl AsRef<str>) -> Self {
|
||||
pub fn from_text(value: impl AsRef<str>) -> Self {
|
||||
Size::new(value.as_ref().chars().count())
|
||||
}
|
||||
|
||||
@ -105,15 +111,15 @@ impl Size {
|
||||
}
|
||||
|
||||
/// Checked subtraction. Computes `self - rhs`, returning `None` if overflow occurred.
|
||||
pub fn checked_sub(self, rhs:Size) -> Option<Self> {
|
||||
pub fn checked_sub(self, rhs: Size) -> Option<Self> {
|
||||
self.value.checked_sub(rhs.value).map(Self::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Size {
|
||||
type Output = Size;
|
||||
fn add(self, rhs:Size) -> Size {
|
||||
Size {value:self.value + rhs.value}
|
||||
fn add(self, rhs: Size) -> Size {
|
||||
Size { value: self.value + rhs.value }
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,8 +131,8 @@ impl AddAssign for Size {
|
||||
|
||||
impl Sub for Size {
|
||||
type Output = Size;
|
||||
fn sub(self, rhs:Size) -> Size {
|
||||
Size{value: self.value - rhs.value}
|
||||
fn sub(self, rhs: Size) -> Size {
|
||||
Size { value: self.value - rhs.value }
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +144,7 @@ impl SubAssign for Size {
|
||||
|
||||
impl Display for Size {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f,"{}",self.value)
|
||||
write!(f, "{}", self.value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,34 +153,37 @@ impl Display for Size {
|
||||
|
||||
/// Strongly typed span into container with index and size.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone,Copy,Debug,Default,Hash,PartialEq,Eq,PartialOrd,Ord,Serialize,Deserialize)]
|
||||
pub struct Span { pub index:Index, pub size:Size }
|
||||
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct Span {
|
||||
pub index: Index,
|
||||
pub size: Size,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
/// Initializes Span with given values.
|
||||
pub fn new(index:Index, size:Size) -> Self {
|
||||
Span {index,size}
|
||||
pub fn new(index: Index, size: Size) -> Self {
|
||||
Span { index, size }
|
||||
}
|
||||
|
||||
/// Creates a span describing a range between two indices.
|
||||
pub fn from_indices(begin:Index, end:Index) -> Self {
|
||||
pub fn from_indices(begin: Index, end: Index) -> Self {
|
||||
if end < begin {
|
||||
Self::from_indices(end,begin)
|
||||
Self::from_indices(end, begin)
|
||||
} else {
|
||||
let index = begin;
|
||||
let size = end - begin;
|
||||
Span {index,size}
|
||||
let size = end - begin;
|
||||
Span { index, size }
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a span from zero up to given index.
|
||||
pub fn from_beginning_to(index:Index) -> Self {
|
||||
pub fn from_beginning_to(index: Index) -> Self {
|
||||
Span::from_indices(Index::new(0), index)
|
||||
}
|
||||
|
||||
/// Creates a span from zero index with given length.
|
||||
pub fn from_beginning(size:Size) -> Self {
|
||||
Span {index:Index::new(0), size}
|
||||
pub fn from_beginning(size: Size) -> Self {
|
||||
Span { index: Index::new(0), size }
|
||||
}
|
||||
|
||||
/// Get the index of the last character in the span.
|
||||
@ -196,63 +205,63 @@ impl Span {
|
||||
}
|
||||
|
||||
/// Check if this span contains character under `index`.
|
||||
pub fn contains(&self, index:Index) -> bool {
|
||||
pub fn contains(&self, index: Index) -> bool {
|
||||
self.index <= index && self.end() > index
|
||||
}
|
||||
|
||||
/// Check if this span contains the whole another span.
|
||||
pub fn contains_span(&self, span:&Span) -> bool {
|
||||
pub fn contains_span(&self, span: &Span) -> bool {
|
||||
self.index <= span.index && self.end() >= span.end()
|
||||
}
|
||||
|
||||
/// Converts span to `Range<usize>`.
|
||||
pub fn range(self) -> Range<usize> {
|
||||
let start = self.index.value;
|
||||
let end = self.end().value;
|
||||
start .. end
|
||||
let end = self.end().value;
|
||||
start..end
|
||||
}
|
||||
|
||||
/// Expand the span by moving its left (start) index.
|
||||
pub fn extend_left(&mut self, size:Size) {
|
||||
pub fn extend_left(&mut self, size: Size) {
|
||||
self.index -= size;
|
||||
self.size += size;
|
||||
}
|
||||
|
||||
/// Expand the span by moving its right (end) index.
|
||||
pub fn extend_right(&mut self, size:Size) {
|
||||
pub fn extend_right(&mut self, size: Size) {
|
||||
self.size += size;
|
||||
}
|
||||
|
||||
/// Shrink the span by moving its left (start) index.
|
||||
pub fn shrink_left(&mut self, size:Size) {
|
||||
pub fn shrink_left(&mut self, size: Size) {
|
||||
self.index += size;
|
||||
self.size -= size;
|
||||
}
|
||||
|
||||
/// Shrink the span by moving its right (end) index.
|
||||
pub fn shrink_right(&mut self, size:Size) {
|
||||
pub fn shrink_right(&mut self, size: Size) {
|
||||
self.size -= size;
|
||||
}
|
||||
|
||||
/// Move the whole span left, maintaining its size.
|
||||
pub fn move_left(&mut self, size:Size) {
|
||||
pub fn move_left(&mut self, size: Size) {
|
||||
self.index -= size;
|
||||
}
|
||||
|
||||
/// Move the whole span right, maintaining its size.
|
||||
pub fn move_right(&mut self, size:Size) {
|
||||
pub fn move_right(&mut self, size: Size) {
|
||||
self.index += size;
|
||||
}
|
||||
|
||||
/// Move the start index of the span, adjusting the size.
|
||||
pub fn set_left(&mut self, new_left:Index) {
|
||||
pub fn set_left(&mut self, new_left: Index) {
|
||||
let end = self.end();
|
||||
self.index = new_left;
|
||||
self.size = end - new_left;
|
||||
}
|
||||
|
||||
/// Move the end index of the span, adjusting the size.
|
||||
pub fn set_right(&mut self, new_right:Index) {
|
||||
pub fn set_right(&mut self, new_right: Index) {
|
||||
self.size = new_right - self.index;
|
||||
}
|
||||
|
||||
@ -271,32 +280,32 @@ impls! { From + &From<Span> for Range<usize> { |this|
|
||||
}}
|
||||
|
||||
impl PartialEq<Range<usize>> for Span {
|
||||
fn eq(&self, other:&Range<usize>) -> bool {
|
||||
fn eq(&self, other: &Range<usize>) -> bool {
|
||||
&self.range() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f,"{}..{}",self.index.value,self.end().value)
|
||||
write!(f, "{}..{}", self.index.value, self.end().value)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Index<Span> for str {
|
||||
type Output = str;
|
||||
|
||||
fn index(&self, span:Span) -> &Self::Output {
|
||||
fn index(&self, span: Span) -> &Self::Output {
|
||||
// Note: Unwraps in this method are justified, as OOB access panic is expected behavior
|
||||
// for []-style indexing operations.
|
||||
let mut iter = self.char_indices();
|
||||
let first = iter.nth(span.index.value).unwrap();
|
||||
let to_last = span.last().map(|last| last - span.index);
|
||||
let mut iter = self.char_indices();
|
||||
let first = iter.nth(span.index.value).unwrap();
|
||||
let to_last = span.last().map(|last| last - span.index);
|
||||
let last_as_nth = to_last.and_then(|i| i.checked_sub(Size::new(1)));
|
||||
let last = last_as_nth.map_or(first,|nth| iter.nth(nth.value).unwrap());
|
||||
let last = last_as_nth.map_or(first, |nth| iter.nth(nth.value).unwrap());
|
||||
if span.is_empty() {
|
||||
&self[first.0 .. first.0]
|
||||
&self[first.0..first.0]
|
||||
} else {
|
||||
&self[first.0 .. last.0 + last.1.len_utf8()]
|
||||
&self[first.0..last.0 + last.1.len_utf8()]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -304,14 +313,14 @@ impl std::ops::Index<Span> for str {
|
||||
impl std::ops::Index<Span> for String {
|
||||
type Output = str;
|
||||
|
||||
fn index(&self, index:Span) -> &Self::Output {
|
||||
fn index(&self, index: Span) -> &Self::Output {
|
||||
&self.as_str()[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Range<Index>> for Span {
|
||||
fn from(range:Range<Index>) -> Self {
|
||||
Span::from_indices(range.start,range.end)
|
||||
fn from(range: Range<Index>) -> Self {
|
||||
Span::from_indices(range.start, range.end)
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,8 +329,8 @@ impl From<Range<Index>> for Span {
|
||||
|
||||
impl Add<Size> for Index {
|
||||
type Output = Index;
|
||||
fn add(self, rhs:Size) -> Index {
|
||||
Index {value:self.value + rhs.value}
|
||||
fn add(self, rhs: Size) -> Index {
|
||||
Index { value: self.value + rhs.value }
|
||||
}
|
||||
}
|
||||
|
||||
@ -333,8 +342,8 @@ impl AddAssign<Size> for Index {
|
||||
|
||||
impl Sub<Size> for Index {
|
||||
type Output = Index;
|
||||
fn sub(self, rhs:Size) -> Index {
|
||||
Index {value:self.value - rhs.value}
|
||||
fn sub(self, rhs: Size) -> Index {
|
||||
Index { value: self.value - rhs.value }
|
||||
}
|
||||
}
|
||||
|
||||
@ -346,8 +355,8 @@ impl SubAssign<Size> for Index {
|
||||
|
||||
impl Sub for Index {
|
||||
type Output = Size;
|
||||
fn sub(self, rhs:Index) -> Size {
|
||||
Size {value:self.value - rhs.value}
|
||||
fn sub(self, rhs: Index) -> Size {
|
||||
Size { value: self.value - rhs.value }
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,10 +364,10 @@ impl Sub for Index {
|
||||
// === TextLocation ===
|
||||
|
||||
/// A position of character in a multiline text.
|
||||
#[derive(Copy,Clone,Debug,PartialEq,Eq,PartialOrd,Ord)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct TextLocation {
|
||||
/// Line index.
|
||||
pub line: usize,
|
||||
pub line: usize,
|
||||
/// Column is a index of char in given line.
|
||||
pub column: usize,
|
||||
}
|
||||
@ -366,35 +375,29 @@ pub struct TextLocation {
|
||||
/// Short pretty print representation in the form of `line:column`.
|
||||
impl Display for TextLocation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f,"{}:{}",self.line,self.column)
|
||||
write!(f, "{}:{}", self.line, self.column)
|
||||
}
|
||||
}
|
||||
|
||||
impl TextLocation {
|
||||
/// Create location at begin of given line.
|
||||
pub fn at_line_begin(line_index:usize) -> Self {
|
||||
TextLocation {
|
||||
line : line_index,
|
||||
column : 0,
|
||||
}
|
||||
pub fn at_line_begin(line_index: usize) -> Self {
|
||||
TextLocation { line: line_index, column: 0 }
|
||||
}
|
||||
|
||||
/// Create location at begin of the whole document.
|
||||
pub fn at_document_begin() -> Self {
|
||||
TextLocation {
|
||||
line : 0,
|
||||
column : 0,
|
||||
}
|
||||
TextLocation { line: 0, column: 0 }
|
||||
}
|
||||
|
||||
/// Create location at and of the whole document. It iterates over all the content.
|
||||
pub fn at_document_end(content:impl Str) -> Self {
|
||||
pub fn at_document_end(content: impl Str) -> Self {
|
||||
Self::after_chars(content.as_ref().chars())
|
||||
}
|
||||
|
||||
/// Convert from index of document with `content`. It iterates over all characters before
|
||||
/// `index`.
|
||||
pub fn from_index(content:impl Str, index:Index) -> Self {
|
||||
pub fn from_index(content: impl Str, index: Index) -> Self {
|
||||
let before = content.as_ref().chars().take(index.value);
|
||||
Self::after_chars(before)
|
||||
}
|
||||
@ -404,7 +407,7 @@ impl TextLocation {
|
||||
/// This operation involves iterating over content characters and is O(n).
|
||||
///
|
||||
/// Behavior for out-of-bounds index conversion is unspecified but will never panic.
|
||||
pub fn to_index(self, content:impl AsRef<str>) -> Index {
|
||||
pub fn to_index(self, content: impl AsRef<str>) -> Index {
|
||||
let line_index = match self.line {
|
||||
0 => 0,
|
||||
_ => {
|
||||
@ -417,35 +420,35 @@ impl TextLocation {
|
||||
|
||||
/// Converts a range of indices into a range of TextLocation. It iterates over all characters
|
||||
/// before range's end.
|
||||
pub fn convert_range(content:impl Str, range:&Range<Index>) -> Range<Self> {
|
||||
pub fn convert_range(content: impl Str, range: &Range<Index>) -> Range<Self> {
|
||||
let content = content.as_ref();
|
||||
Self::from_index(content,range.start)..Self::from_index(content,range.end)
|
||||
Self::from_index(content, range.start)..Self::from_index(content, range.end)
|
||||
}
|
||||
|
||||
/// Converts a span into a range of TextLocation. It iterates over all characters before range's
|
||||
/// end.
|
||||
pub fn convert_span(content:impl Str, span:&Span) -> Range<Self> {
|
||||
pub fn convert_span(content: impl Str, span: &Span) -> Range<Self> {
|
||||
let range = span.index..span.end();
|
||||
Self::convert_range(content,&range)
|
||||
Self::convert_range(content, &range)
|
||||
}
|
||||
|
||||
/// Converts a range in bytes into a range of TextLocation. It iterates over all characters
|
||||
/// before range's end.
|
||||
pub fn convert_byte_range(content:impl Str, range:&Range<ByteIndex>) -> Range<Self> {
|
||||
pub fn convert_byte_range(content: impl Str, range: &Range<ByteIndex>) -> Range<Self> {
|
||||
let start = Index::convert_byte_index(content.as_ref(), range.start);
|
||||
let end = Index::convert_byte_index(content.as_ref(), range.end);
|
||||
Self::convert_range(content,&(start..end))
|
||||
let end = Index::convert_byte_index(content.as_ref(), range.end);
|
||||
Self::convert_range(content, &(start..end))
|
||||
}
|
||||
|
||||
fn after_chars<IntoCharsIter>(chars:IntoCharsIter) -> Self
|
||||
where IntoCharsIter : IntoIterator<Item=char, IntoIter:Clone> {
|
||||
let iter = chars.into_iter();
|
||||
let len = iter.clone().count();
|
||||
let newlines = iter.enumerate().filter(|(_,c)| *c == '\n');
|
||||
let newlines_indices = newlines.map(|(i,_)| i);
|
||||
fn after_chars<IntoCharsIter>(chars: IntoCharsIter) -> Self
|
||||
where IntoCharsIter: IntoIterator<Item = char, IntoIter: Clone> {
|
||||
let iter = chars.into_iter();
|
||||
let len = iter.clone().count();
|
||||
let newlines = iter.enumerate().filter(|(_, c)| *c == '\n');
|
||||
let newlines_indices = newlines.map(|(i, _)| i);
|
||||
TextLocation {
|
||||
line : newlines_indices.clone().count(),
|
||||
column : len - newlines_indices.last().map_or(0, |i| i + 1),
|
||||
line: newlines_indices.clone().count(),
|
||||
column: len - newlines_indices.last().map_or(0, |i| i + 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -461,8 +464,8 @@ impl TextLocation {
|
||||
/// This is a generalized template, because we use different representation for both index
|
||||
/// (e.g. `Index` or `TextLocation`) and inserted content (it may be just String, but also e.g.
|
||||
/// Vec<char>, or Vec<Vec<char>> split by newlines).
|
||||
#[derive(Clone,Debug,Eq,Hash,PartialEq)]
|
||||
pub struct TextChangeTemplate<Index,Content> {
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct TextChangeTemplate<Index, Content> {
|
||||
/// Text fragment to be replaced. If we don't mean to remove any text, this should be an empty
|
||||
/// range with start set at position there `lines` will be inserted
|
||||
/// (see `TextChangeTemplate::insert` definition).
|
||||
@ -472,42 +475,39 @@ pub struct TextChangeTemplate<Index,Content> {
|
||||
}
|
||||
|
||||
/// The simplest change representation.
|
||||
pub type TextChange = TextChangeTemplate<Index,String>;
|
||||
pub type TextChange = TextChangeTemplate<Index, String>;
|
||||
|
||||
|
||||
// === Constructors ===
|
||||
|
||||
impl<Index:Copy,Content> TextChangeTemplate<Index,Content> {
|
||||
impl<Index: Copy, Content> TextChangeTemplate<Index, Content> {
|
||||
/// Creates operation which inserts text at given position.
|
||||
pub fn insert(at:Index, text:Content) -> Self {
|
||||
TextChangeTemplate {
|
||||
replaced : at..at,
|
||||
inserted: text,
|
||||
}
|
||||
pub fn insert(at: Index, text: Content) -> Self {
|
||||
TextChangeTemplate { replaced: at..at, inserted: text }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Index,Content> TextChangeTemplate<Index,Content> {
|
||||
impl<Index, Content> TextChangeTemplate<Index, Content> {
|
||||
/// Creates operation which replaces text at given range with given string.
|
||||
pub fn replace(replaced:Range<Index>, text:Content) -> Self {
|
||||
pub fn replace(replaced: Range<Index>, text: Content) -> Self {
|
||||
let inserted = text;
|
||||
TextChangeTemplate {replaced,inserted}
|
||||
TextChangeTemplate { replaced, inserted }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Index:Sub+Clone,Content> TextChangeTemplate<Index,Content> {
|
||||
impl<Index: Sub + Clone, Content> TextChangeTemplate<Index, Content> {
|
||||
/// Calculate the size of the replaced text.
|
||||
pub fn replaced_size(&self) -> Index::Output {
|
||||
self.replaced.end.clone() - self.replaced.start.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Content> TextChangeTemplate<Index,Content> {
|
||||
impl<Content> TextChangeTemplate<Index, Content> {
|
||||
/// Calculate the size of the replaced text.
|
||||
pub fn replaced_span(&self) -> Span {
|
||||
let index = self.replaced.start;
|
||||
let size = self.replaced_size();
|
||||
Span {index,size}
|
||||
let size = self.replaced_size();
|
||||
Span { index, size }
|
||||
}
|
||||
|
||||
/// Applies the text edit on given `String` value.
|
||||
@ -515,11 +515,12 @@ impl<Content> TextChangeTemplate<Index,Content> {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the replaced span is out of the string value bounds.
|
||||
pub fn apply(&self, target:&mut String) where Content:AsRef<str> {
|
||||
pub fn apply(&self, target: &mut String)
|
||||
where Content: AsRef<str> {
|
||||
//debug!(logger, "change: {change:?}, my code: \n```\n{code}\n```");
|
||||
let replaced_indices = self.replaced.start.value..self.replaced.end.value;
|
||||
let replaced_indices = self.replaced.start.value..self.replaced.end.value;
|
||||
//debug!(logger, "replacing range {replaced_indices:?} with {change.inserted}");
|
||||
target.replace_range(replaced_indices,self.inserted.as_ref());
|
||||
target.replace_range(replaced_indices, self.inserted.as_ref());
|
||||
}
|
||||
|
||||
/// Applies the text edit on string and returns the result.
|
||||
@ -527,20 +528,18 @@ impl<Content> TextChangeTemplate<Index,Content> {
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the replaced span is out of the string value bounds.
|
||||
pub fn applied(&self, target:&str) -> String where Content:AsRef<str> {
|
||||
pub fn applied(&self, target: &str) -> String
|
||||
where Content: AsRef<str> {
|
||||
let mut target = target.to_string();
|
||||
self.apply(&mut target);
|
||||
target
|
||||
}
|
||||
}
|
||||
|
||||
impl<Index,Content:Default> TextChangeTemplate<Index,Content> {
|
||||
impl<Index, Content: Default> TextChangeTemplate<Index, Content> {
|
||||
/// Creates operation which deletes text at given range.
|
||||
pub fn delete(range:Range<Index>) -> Self {
|
||||
TextChangeTemplate {
|
||||
replaced : range,
|
||||
inserted : default(),
|
||||
}
|
||||
pub fn delete(range: Range<Index>) -> Self {
|
||||
TextChangeTemplate { replaced: range, inserted: default() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -551,28 +550,28 @@ impl<Index,Content:Default> TextChangeTemplate<Index,Content> {
|
||||
// =================
|
||||
|
||||
/// Get indices (char-counting) of the new line characters.
|
||||
pub fn newline_indices(text:&str) -> impl Iterator<Item=usize> + '_ {
|
||||
text.chars().enumerate().filter_map(|(ix,c)| (c == '\n').as_some(ix))
|
||||
pub fn newline_indices(text: &str) -> impl Iterator<Item = usize> + '_ {
|
||||
text.chars().enumerate().filter_map(|(ix, c)| (c == '\n').as_some(ix))
|
||||
}
|
||||
|
||||
/// Get indices (byte-counting) of the new line characters.
|
||||
pub fn newline_byte_indices(text:&str) -> impl Iterator<Item=usize> + '_ {
|
||||
text.as_bytes().iter().enumerate().filter_map(|(ix,c)|(*c == b'\n').as_some(ix))
|
||||
pub fn newline_byte_indices(text: &str) -> impl Iterator<Item = usize> + '_ {
|
||||
text.as_bytes().iter().enumerate().filter_map(|(ix, c)| (*c == b'\n').as_some(ix))
|
||||
}
|
||||
|
||||
/// Get indices (byte-counting) of the new line characters, beginning from the text end.
|
||||
pub fn rev_newline_byte_indices(text:&str) -> impl Iterator<Item=usize> + '_ {
|
||||
text.as_bytes().iter().enumerate().rev().filter_map(|(ix,c)| (*c == b'\n').as_some(ix))
|
||||
pub fn rev_newline_byte_indices(text: &str) -> impl Iterator<Item = usize> + '_ {
|
||||
text.as_bytes().iter().enumerate().rev().filter_map(|(ix, c)| (*c == b'\n').as_some(ix))
|
||||
}
|
||||
|
||||
/// Split text to lines handling both CR and CRLF line endings.
|
||||
pub fn split_to_lines(text:&str) -> impl Iterator<Item=String> + '_ {
|
||||
pub fn split_to_lines(text: &str) -> impl Iterator<Item = String> + '_ {
|
||||
text.split('\n').map(cut_cr_at_end_of_line).map(|s| s.to_string())
|
||||
}
|
||||
|
||||
/// Returns slice without carriage return (also known as CR or `'\r'`) at line's end
|
||||
#[rustversion::since(2020-02-01)]
|
||||
fn cut_cr_at_end_of_line(from:&str) -> &str {
|
||||
fn cut_cr_at_end_of_line(from: &str) -> &str {
|
||||
from.strip_suffix('\r').unwrap_or(from)
|
||||
}
|
||||
|
||||
@ -588,48 +587,48 @@ mod test {
|
||||
|
||||
use super::Index;
|
||||
|
||||
fn assert_round_trip(str:&str, index:Index, location:TextLocation) {
|
||||
assert_eq!(TextLocation::from_index(str,index),location);
|
||||
assert_eq!(location.to_index(str),index);
|
||||
fn assert_round_trip(str: &str, index: Index, location: TextLocation) {
|
||||
assert_eq!(TextLocation::from_index(str, index), location);
|
||||
assert_eq!(location.to_index(str), index);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn converting_index_to_location() {
|
||||
let str = "first\nsecond\nthird";
|
||||
assert_round_trip(str,Index::new(0), TextLocation {line:0, column:0});
|
||||
assert_round_trip(str,Index::new(5), TextLocation {line:0, column:5});
|
||||
assert_round_trip(str,Index::new(6), TextLocation {line:1, column:0});
|
||||
assert_round_trip(str,Index::new(9), TextLocation {line:1, column:3});
|
||||
assert_round_trip(str,Index::new(12), TextLocation {line:1, column:6});
|
||||
assert_round_trip(str,Index::new(13), TextLocation {line:2, column:0});
|
||||
assert_round_trip(str,Index::new(18), TextLocation {line:2, column:5});
|
||||
assert_round_trip(str, Index::new(0), TextLocation { line: 0, column: 0 });
|
||||
assert_round_trip(str, Index::new(5), TextLocation { line: 0, column: 5 });
|
||||
assert_round_trip(str, Index::new(6), TextLocation { line: 1, column: 0 });
|
||||
assert_round_trip(str, Index::new(9), TextLocation { line: 1, column: 3 });
|
||||
assert_round_trip(str, Index::new(12), TextLocation { line: 1, column: 6 });
|
||||
assert_round_trip(str, Index::new(13), TextLocation { line: 2, column: 0 });
|
||||
assert_round_trip(str, Index::new(18), TextLocation { line: 2, column: 5 });
|
||||
|
||||
let str = "";
|
||||
assert_round_trip(str,Index::new(0), TextLocation {line:0, column:0});
|
||||
assert_round_trip(str, Index::new(0), TextLocation { line: 0, column: 0 });
|
||||
//assert_eq!(TextLocation {line:0, column:0}, TextLocation::from_index(str,Index::new(0)));
|
||||
|
||||
let str= "\n";
|
||||
assert_round_trip(str,Index::new(0), TextLocation {line:0, column:0});
|
||||
assert_round_trip(str,Index::new(1), TextLocation {line:1, column:0});
|
||||
let str = "\n";
|
||||
assert_round_trip(str, Index::new(0), TextLocation { line: 0, column: 0 });
|
||||
assert_round_trip(str, Index::new(1), TextLocation { line: 1, column: 0 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_location_at_end() {
|
||||
let str = "first\nsecond\nthird";
|
||||
assert_eq!(TextLocation::at_document_end(str) , TextLocation {line:2, column:5});
|
||||
assert_eq!(TextLocation::at_document_end("") , TextLocation {line:0, column:0});
|
||||
assert_eq!(TextLocation::at_document_end("\n"), TextLocation {line:1, column:0});
|
||||
assert_eq!(TextLocation::at_document_end(str), TextLocation { line: 2, column: 5 });
|
||||
assert_eq!(TextLocation::at_document_end(""), TextLocation { line: 0, column: 0 });
|
||||
assert_eq!(TextLocation::at_document_end("\n"), TextLocation { line: 1, column: 0 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indexing_utf8() {
|
||||
let str = "zazó黄ć gęślą jaźń";
|
||||
assert_eq!(&str[Span::from(2..5)],"zó黄");
|
||||
assert_eq!(&str[Span::from(5..5)],"");
|
||||
assert_eq!(&str[Span::from(2..5)], "zó黄");
|
||||
assert_eq!(&str[Span::from(5..5)], "");
|
||||
assert_eq!(Size::from_text("日本語").value, 3);
|
||||
assert_eq!(&"日本語"[Span::from(0..0)],"");
|
||||
assert_eq!(&"日本語"[Span::from(0..3)],"日本語");
|
||||
assert_eq!(&"日本語"[Span::from(0..1)],"日");
|
||||
assert_eq!(&"日本語"[Span::from(2..3)],"語");
|
||||
assert_eq!(&"日本語"[Span::from(0..0)], "");
|
||||
assert_eq!(&"日本語"[Span::from(0..3)], "日本語");
|
||||
assert_eq!(&"日本語"[Span::from(0..1)], "日");
|
||||
assert_eq!(&"日本語"[Span::from(2..3)], "語");
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,8 @@
|
||||
//! `lexer_generated_api_test` file. This is to present the full view of what each portion of the
|
||||
//! process looks like.
|
||||
|
||||
use enso_flexer::*;
|
||||
use enso_flexer::prelude::*;
|
||||
use enso_flexer::*;
|
||||
|
||||
use enso_flexer::automata::pattern::Pattern;
|
||||
use enso_flexer::group::Registry;
|
||||
@ -48,7 +48,7 @@ type Logger = Disabled;
|
||||
// ===========
|
||||
|
||||
/// A very simple AST, sufficient for the simple language being defined.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Token {
|
||||
/// A word from the input, consisting of a sequence of all `a` or all `b`.
|
||||
Word(String),
|
||||
@ -57,26 +57,26 @@ pub enum Token {
|
||||
}
|
||||
impl Token {
|
||||
/// Construct a new word token.
|
||||
pub fn word(name:impl Into<String>) -> Token {
|
||||
pub fn word(name: impl Into<String>) -> Token {
|
||||
Token::Word(name.into())
|
||||
}
|
||||
|
||||
/// Construct a new unrecognized token.
|
||||
pub fn unrecognized(name:impl Into<String>) -> Token {
|
||||
pub fn unrecognized(name: impl Into<String>) -> Token {
|
||||
Token::Unrecognized(name.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A representation of a stream of tokens.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone,Debug,Default,PartialEq)]
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct TokenStream {
|
||||
tokens:Vec<Token>
|
||||
tokens: Vec<Token>,
|
||||
}
|
||||
|
||||
impl TokenStream {
|
||||
/// Append the provided token to the token stream.
|
||||
pub fn push(&mut self,token:Token) {
|
||||
pub fn push(&mut self, token: Token) {
|
||||
self.tokens.push(token);
|
||||
}
|
||||
}
|
||||
@ -86,7 +86,7 @@ impl TokenStream {
|
||||
|
||||
impl From<Vec<Token>> for TokenStream {
|
||||
fn from(tokens: Vec<Token>) -> Self {
|
||||
TokenStream {tokens}
|
||||
TokenStream { tokens }
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,11 +99,11 @@ impl From<Vec<Token>> for TokenStream {
|
||||
/// The definition of a test lexer for the above-described language.
|
||||
#[derive(Debug)]
|
||||
pub struct TestLexer {
|
||||
lexer:Flexer<TestState,TokenStream,Logger>
|
||||
lexer: Flexer<TestState, TokenStream, Logger>,
|
||||
}
|
||||
|
||||
impl Deref for TestLexer {
|
||||
type Target = Flexer<TestState,TokenStream,Logger>;
|
||||
type Target = Flexer<TestState, TokenStream, Logger>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.lexer
|
||||
}
|
||||
@ -119,15 +119,15 @@ impl TestLexer {
|
||||
/// Creates a new instance of this lexer.
|
||||
pub fn new() -> Self {
|
||||
let logger = Logger::new("TestLexer");
|
||||
let lexer = Flexer::new(logger);
|
||||
TestLexer{lexer}
|
||||
let lexer = Flexer::new(logger);
|
||||
TestLexer { lexer }
|
||||
}
|
||||
}
|
||||
|
||||
/// Rules for the root state.
|
||||
#[allow(dead_code,missing_docs)]
|
||||
#[allow(dead_code, missing_docs)]
|
||||
impl TestLexer {
|
||||
fn on_first_word<R:ReaderOps>(&mut self, _reader:&mut R) {
|
||||
fn on_first_word<R: ReaderOps>(&mut self, _reader: &mut R) {
|
||||
let str = self.current_match.clone();
|
||||
let ast = Token::Word(str);
|
||||
self.output.push(ast);
|
||||
@ -135,64 +135,64 @@ impl TestLexer {
|
||||
self.push_state(id);
|
||||
}
|
||||
|
||||
fn on_err_suffix_first_word<R:ReaderOps>(&mut self, _reader:&mut R) {
|
||||
fn on_err_suffix_first_word<R: ReaderOps>(&mut self, _reader: &mut R) {
|
||||
let ast = Token::Unrecognized(self.current_match.clone());
|
||||
self.output.push(ast);
|
||||
}
|
||||
|
||||
fn on_no_err_suffix_first_word<R:ReaderOps>(&mut self, _reader:&mut R) {}
|
||||
fn on_no_err_suffix_first_word<R: ReaderOps>(&mut self, _reader: &mut R) {}
|
||||
|
||||
fn rules_in_root(lexer:&mut TestLexer) {
|
||||
let a_word = Pattern::char('a').many1();
|
||||
let b_word = Pattern::char('b').many1();
|
||||
let any = Pattern::any();
|
||||
let end = Pattern::eof();
|
||||
fn rules_in_root(lexer: &mut TestLexer) {
|
||||
let a_word = Pattern::char('a').many1();
|
||||
let b_word = Pattern::char('b').many1();
|
||||
let any = Pattern::any();
|
||||
let end = Pattern::eof();
|
||||
|
||||
let root_group_id = lexer.initial_state;
|
||||
let root_group = lexer.groups_mut().group_mut(root_group_id);
|
||||
let root_group = lexer.groups_mut().group_mut(root_group_id);
|
||||
|
||||
root_group.create_rule(&a_word,"self.on_first_word(reader)");
|
||||
root_group.create_rule(&b_word,"self.on_first_word(reader)");
|
||||
root_group.create_rule(&end, "self.on_no_err_suffix_first_word(reader)");
|
||||
root_group.create_rule(&any, "self.on_err_suffix_first_word(reader)");
|
||||
root_group.create_rule(&a_word, "self.on_first_word(reader)");
|
||||
root_group.create_rule(&b_word, "self.on_first_word(reader)");
|
||||
root_group.create_rule(&end, "self.on_no_err_suffix_first_word(reader)");
|
||||
root_group.create_rule(&any, "self.on_err_suffix_first_word(reader)");
|
||||
}
|
||||
}
|
||||
|
||||
/// Rules for the "seen first word" state.
|
||||
#[allow(dead_code,missing_docs)]
|
||||
#[allow(dead_code, missing_docs)]
|
||||
impl TestLexer {
|
||||
fn on_spaced_word<R:ReaderOps>(&mut self, _reader:&mut R, _test_arg:bool) {
|
||||
fn on_spaced_word<R: ReaderOps>(&mut self, _reader: &mut R, _test_arg: bool) {
|
||||
let str = self.current_match.clone();
|
||||
let ast = Token::Word(String::from(str.trim()));
|
||||
self.output.push(ast);
|
||||
}
|
||||
|
||||
fn on_err_suffix<R:ReaderOps>(&mut self, reader:&mut R) {
|
||||
fn on_err_suffix<R: ReaderOps>(&mut self, reader: &mut R) {
|
||||
self.on_err_suffix_first_word(reader);
|
||||
self.pop_state();
|
||||
}
|
||||
|
||||
fn on_no_err_suffix<R:ReaderOps>(&mut self, reader:&mut R) {
|
||||
fn on_no_err_suffix<R: ReaderOps>(&mut self, reader: &mut R) {
|
||||
self.on_no_err_suffix_first_word(reader);
|
||||
self.pop_state();
|
||||
}
|
||||
|
||||
fn rules_in_seen_first_word(lexer:&mut TestLexer) {
|
||||
let a_word = Pattern::char('a').many1();
|
||||
let b_word = Pattern::char('b').many1();
|
||||
let space = Pattern::char(' ');
|
||||
fn rules_in_seen_first_word(lexer: &mut TestLexer) {
|
||||
let a_word = Pattern::char('a').many1();
|
||||
let b_word = Pattern::char('b').many1();
|
||||
let space = Pattern::char(' ');
|
||||
let spaced_a_word = &space >> &a_word;
|
||||
let spaced_b_word = &space >> &b_word;
|
||||
let any = Pattern::any();
|
||||
let end = Pattern::eof();
|
||||
let any = Pattern::any();
|
||||
let end = Pattern::eof();
|
||||
|
||||
let seen_first_word_group_id = lexer.seen_first_word_state;
|
||||
let seen_first_word_group = lexer.groups_mut().group_mut(seen_first_word_group_id);
|
||||
let seen_first_word_group = lexer.groups_mut().group_mut(seen_first_word_group_id);
|
||||
|
||||
seen_first_word_group.create_rule(&spaced_a_word,"self.on_spaced_word(reader,true)");
|
||||
seen_first_word_group.create_rule(&spaced_b_word,"self.on_spaced_word(reader,false)");
|
||||
seen_first_word_group.create_rule(&end, "self.on_no_err_suffix(reader)");
|
||||
seen_first_word_group.create_rule(&any, "self.on_err_suffix(reader)");
|
||||
seen_first_word_group.create_rule(&spaced_a_word, "self.on_spaced_word(reader,true)");
|
||||
seen_first_word_group.create_rule(&spaced_b_word, "self.on_spaced_word(reader,false)");
|
||||
seen_first_word_group.create_rule(&end, "self.on_no_err_suffix(reader)");
|
||||
seen_first_word_group.create_rule(&any, "self.on_err_suffix(reader)");
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,25 +234,25 @@ impl Default for TestLexer {
|
||||
#[derive(Debug)]
|
||||
pub struct TestState {
|
||||
/// The registry for groups in the lexer.
|
||||
lexer_states:group::Registry,
|
||||
lexer_states: group::Registry,
|
||||
/// The initial state of the lexer.
|
||||
initial_state:group::Identifier,
|
||||
initial_state: group::Identifier,
|
||||
/// The state entered when the first word has been seen.
|
||||
seen_first_word_state:group::Identifier,
|
||||
seen_first_word_state: group::Identifier,
|
||||
/// The bookmarks for this lexer.
|
||||
bookmarks:BookmarkManager
|
||||
bookmarks: BookmarkManager,
|
||||
}
|
||||
|
||||
|
||||
// === Trait Impls ===
|
||||
|
||||
impl enso_flexer::State for TestState {
|
||||
fn new(_logger:&impl AnyLogger) -> Self {
|
||||
let mut lexer_states = group::Registry::default();
|
||||
let initial_state = lexer_states.define_group("ROOT",None);
|
||||
let seen_first_word_state = lexer_states.define_group("SEEN FIRST WORD",None);
|
||||
let bookmarks = BookmarkManager::new();
|
||||
Self{lexer_states,initial_state,seen_first_word_state,bookmarks}
|
||||
fn new(_logger: &impl AnyLogger) -> Self {
|
||||
let mut lexer_states = group::Registry::default();
|
||||
let initial_state = lexer_states.define_group("ROOT", None);
|
||||
let seen_first_word_state = lexer_states.define_group("SEEN FIRST WORD", None);
|
||||
let bookmarks = BookmarkManager::new();
|
||||
Self { lexer_states, initial_state, seen_first_word_state, bookmarks }
|
||||
}
|
||||
|
||||
fn initial_state(&self) -> group::Identifier {
|
||||
@ -275,7 +275,7 @@ impl enso_flexer::State for TestState {
|
||||
&mut self.bookmarks
|
||||
}
|
||||
|
||||
fn specialize(&self) -> Result<String,GenError> {
|
||||
generate::specialize(self,"TestLexer","TokenStream")
|
||||
fn specialize(&self) -> Result<String, GenError> {
|
||||
generate::specialize(self, "TestLexer", "TokenStream")
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use flexer_test_definition::TestLexer;
|
||||
use enso_flexer::Definition;
|
||||
use enso_flexer::State;
|
||||
use flexer_test_definition::TestLexer;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
|
||||
|
||||
|
||||
@ -10,18 +10,16 @@ use enso_flexer::State;
|
||||
///
|
||||
/// The content of the generated file can be used with the `include!` macro.
|
||||
fn generate_engine() {
|
||||
let definition_path = "../definition/src/lib.rs";
|
||||
let definition_path = "../definition/src/lib.rs";
|
||||
let output_directory = "src/generated";
|
||||
let _ = std::fs::create_dir(output_directory);
|
||||
let output_path = "src/generated/engine.rs";
|
||||
let mut lexer_def = File::open(definition_path).unwrap_or_else(|_| {
|
||||
panic!("The lexer definition should exist at {}.",definition_path)
|
||||
});
|
||||
let _ = std::fs::create_dir(output_directory);
|
||||
let output_path = "src/generated/engine.rs";
|
||||
let mut lexer_def = File::open(definition_path)
|
||||
.unwrap_or_else(|_| panic!("The lexer definition should exist at {}.", definition_path));
|
||||
let mut contents = String::new();
|
||||
let mut file = File::create(output_path).unwrap_or_else(|_| {
|
||||
panic!("Cannot open output file at {}.",output_path)
|
||||
});
|
||||
let lexer = TestLexer::define();
|
||||
let mut file = File::create(output_path)
|
||||
.unwrap_or_else(|_| panic!("Cannot open output file at {}.", output_path));
|
||||
let lexer = TestLexer::define();
|
||||
let engine = lexer.specialize().unwrap();
|
||||
lexer_def.read_to_string(&mut contents).expect("Unable to read lexer definition.");
|
||||
file.write_all(contents.as_bytes()).expect("Unable to write lexer definition.");
|
||||
|
@ -16,4 +16,5 @@
|
||||
#![warn(unsafe_code)]
|
||||
#![warn(unused_import_braces)]
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub mod generated;
|
||||
|
@ -24,52 +24,52 @@ use flexer_test_generation::generated::engine::TokenStream;
|
||||
// =============
|
||||
|
||||
/// Executes the test on the provided input string slice.
|
||||
fn run_test_on(str:impl AsRef<str>) -> TokenStream {
|
||||
fn run_test_on(str: impl AsRef<str>) -> TokenStream {
|
||||
// Hardcoded for ease of use here.
|
||||
let reader = Reader::new(str.as_ref().as_bytes(), DecoderUTF8());
|
||||
let mut lexer = TestLexer::new();
|
||||
let reader = Reader::new(str.as_ref().as_bytes(), DecoderUTF8());
|
||||
let mut lexer = TestLexer::new();
|
||||
let run_result = lexer.run(reader);
|
||||
|
||||
match run_result.kind {
|
||||
enso_flexer::ResultKind::Success => run_result.tokens,
|
||||
_ => default()
|
||||
_ => default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_a_word() {
|
||||
let input = "aaaaa";
|
||||
let input = "aaaaa";
|
||||
let expected_output = TokenStream::from(vec![Token::word(input)]);
|
||||
let result = run_test_on(input);
|
||||
let result = run_test_on(input);
|
||||
assert_eq!(result, expected_output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_b_word() {
|
||||
let input = "bbbbb";
|
||||
let input = "bbbbb";
|
||||
let expected_output = TokenStream::from(vec![Token::word(input)]);
|
||||
let result = run_test_on(input);
|
||||
let result = run_test_on(input);
|
||||
assert_eq!(result, expected_output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_two_word() {
|
||||
let input = "aaaaa bbbbb";
|
||||
let input = "aaaaa bbbbb";
|
||||
let expected_output = TokenStream::from(vec![Token::word("aaaaa"), Token::word("bbbbb")]);
|
||||
let result = run_test_on(input);
|
||||
let result = run_test_on(input);
|
||||
assert_eq!(result, expected_output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_word() {
|
||||
let input = "bbb aa a b bbbbb aa";
|
||||
let input = "bbb aa a b bbbbb aa";
|
||||
let expected_output = TokenStream::from(vec![
|
||||
Token::word("bbb"),
|
||||
Token::word("aa"),
|
||||
Token::word("a"),
|
||||
Token::word("b"),
|
||||
Token::word("bbbbb"),
|
||||
Token::word("aa")
|
||||
Token::word("aa"),
|
||||
]);
|
||||
let result = run_test_on(input);
|
||||
assert_eq!(result, expected_output);
|
||||
@ -77,15 +77,15 @@ fn test_multi_word() {
|
||||
|
||||
#[test]
|
||||
fn test_invalid_single_word() {
|
||||
let input = "c";
|
||||
let input = "c";
|
||||
let expected_output = TokenStream::from(vec![Token::unrecognized(input)]);
|
||||
let result = run_test_on(input);
|
||||
let result = run_test_on(input);
|
||||
assert_eq!(result, expected_output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_word_invalid() {
|
||||
let input = "aaaaaa c bbbbbb";
|
||||
let input = "aaaaaa c bbbbbb";
|
||||
let expected_output = TokenStream::from(vec![
|
||||
Token::word("aaaaaa"),
|
||||
Token::unrecognized(" "),
|
||||
@ -99,7 +99,7 @@ fn test_multi_word_invalid() {
|
||||
|
||||
#[test]
|
||||
fn test_end_invalid() {
|
||||
let input = "bbbbbb c";
|
||||
let input = "bbbbbb c";
|
||||
let expected_output = TokenStream::from(vec![
|
||||
Token::word("bbbbbb"),
|
||||
Token::unrecognized(" "),
|
||||
|
@ -5,19 +5,19 @@ use crate::prelude::*;
|
||||
use quote::*;
|
||||
use syn::*;
|
||||
|
||||
use crate::automata::dfa;
|
||||
use crate::automata::dfa::Dfa;
|
||||
use crate::automata::nfa;
|
||||
use crate::automata::dfa;
|
||||
use crate::automata::state::State;
|
||||
use crate::group::Group;
|
||||
use crate::group::AutomatonData;
|
||||
use crate::group;
|
||||
use crate::group::AutomatonData;
|
||||
use crate::group::Group;
|
||||
|
||||
use enso_macro_utils::repr;
|
||||
use proc_macro2::Literal;
|
||||
use std::fmt;
|
||||
use std::hash::BuildHasher;
|
||||
use std::result::Result;
|
||||
use std::fmt;
|
||||
|
||||
use crate as flexer;
|
||||
|
||||
@ -32,22 +32,19 @@ use crate as flexer;
|
||||
/// This specialized code is a highly-optimised and tailored lexer that dispatches based on simple
|
||||
/// code-point switches, with no dynamic lookup. This means that it is very fast, and very low
|
||||
/// overhead.
|
||||
pub fn specialize
|
||||
( definition : &impl flexer::State
|
||||
, state_type_name : impl Str
|
||||
, output_type_name : impl Str
|
||||
) -> Result<String,GenError> {
|
||||
pub fn specialize(
|
||||
definition: &impl flexer::State,
|
||||
state_type_name: impl Str,
|
||||
output_type_name: impl Str,
|
||||
) -> Result<String, GenError> {
|
||||
let group_registry = definition.groups();
|
||||
let mut body_items = vec![
|
||||
run_function(output_type_name)?,
|
||||
run_current_state_function(),
|
||||
step(group_registry),
|
||||
];
|
||||
let mut body_items =
|
||||
vec![run_function(output_type_name)?, run_current_state_function(), step(group_registry)];
|
||||
for group in group_registry.all().iter() {
|
||||
body_items.extend(automaton_for_group(group,group_registry)?)
|
||||
body_items.extend(automaton_for_group(group, group_registry)?)
|
||||
}
|
||||
let result = wrap_in_impl_for(state_type_name,body_items)?;
|
||||
let code = show_code(&result);
|
||||
let result = wrap_in_impl_for(state_type_name, body_items)?;
|
||||
let code = show_code(&result);
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
@ -55,12 +52,12 @@ pub fn specialize
|
||||
// === Whole-Lexer Codegen Utilities ===
|
||||
|
||||
/// Wrap the provided implementation items into an `impl` block for the provided `state_name` type.
|
||||
pub fn wrap_in_impl_for
|
||||
( state_name : impl Into<String>
|
||||
, body : Vec<ImplItem>
|
||||
) -> Result<ItemImpl,GenError> {
|
||||
let state_name:Ident = str_to_ident(state_name.into().as_str())?;
|
||||
let mut tree:ItemImpl = parse_quote! {
|
||||
pub fn wrap_in_impl_for(
|
||||
state_name: impl Into<String>,
|
||||
body: Vec<ImplItem>,
|
||||
) -> Result<ItemImpl, GenError> {
|
||||
let state_name: Ident = str_to_ident(state_name.into().as_str())?;
|
||||
let mut tree: ItemImpl = parse_quote! {
|
||||
#[allow(missing_docs,dead_code,clippy::all)]
|
||||
impl #state_name {}
|
||||
};
|
||||
@ -71,9 +68,9 @@ pub fn wrap_in_impl_for
|
||||
/// Generate the `run` function for the specialized lexer.
|
||||
///
|
||||
/// This function is what the user of the lexer will call to begin execution.
|
||||
pub fn run_function(output_type_name:impl Str) -> Result<ImplItem,GenError> {
|
||||
pub fn run_function(output_type_name: impl Str) -> Result<ImplItem, GenError> {
|
||||
let output_type_name = str_to_path(output_type_name)?;
|
||||
let tree:ImplItem = parse_quote! {
|
||||
let tree: ImplItem = parse_quote! {
|
||||
pub fn run<R:ReaderOps>(&mut self, mut reader:R) -> LexingResult<#output_type_name> {
|
||||
self.set_up();
|
||||
reader.advance_char(&mut self.bookmarks);
|
||||
@ -96,7 +93,7 @@ pub fn run_function(output_type_name:impl Str) -> Result<ImplItem,GenError> {
|
||||
|
||||
/// Generate the function responsible for executing the lexer in its current state.
|
||||
pub fn run_current_state_function() -> ImplItem {
|
||||
let tree:ImplItem = parse_quote! {
|
||||
let tree: ImplItem = parse_quote! {
|
||||
fn run_current_state<R:ReaderOps>(&mut self, reader:&mut R) -> StageStatus {
|
||||
self.status = StageStatus::Initial;
|
||||
let mut finished = false;
|
||||
@ -145,7 +142,7 @@ pub fn run_current_state_function() -> ImplItem {
|
||||
///
|
||||
/// This function is responsible for dispatching based on the current state, consuming a character,
|
||||
/// and returning the state to transition to.
|
||||
pub fn step(groups:&group::Registry) -> ImplItem {
|
||||
pub fn step(groups: &group::Registry) -> ImplItem {
|
||||
let arms = groups.all().iter().map(|g| step_match_arm(g.id.into())).collect_vec();
|
||||
parse_quote! {
|
||||
fn step<R:ReaderOps>(&mut self, next_state:SubStateId, reader:&mut R) -> StageStatus {
|
||||
@ -161,11 +158,11 @@ pub fn step(groups:&group::Registry) -> ImplItem {
|
||||
/// Generate a match arm for the step function.
|
||||
///
|
||||
/// There is one match arm per lexer state.
|
||||
pub fn step_match_arm(number:usize) -> Arm {
|
||||
let literal = Literal::usize_unsuffixed(number);
|
||||
let function_name_str = format!("dispatch_in_state_{}",number);
|
||||
let func_name:Ident = parse_str(function_name_str.as_str()).unwrap();
|
||||
let arm:Arm = parse_quote! {
|
||||
pub fn step_match_arm(number: usize) -> Arm {
|
||||
let literal = Literal::usize_unsuffixed(number);
|
||||
let function_name_str = format!("dispatch_in_state_{}", number);
|
||||
let func_name: Ident = parse_str(function_name_str.as_str()).unwrap();
|
||||
let arm: Arm = parse_quote! {
|
||||
#literal => self.#func_name(next_state,reader),
|
||||
};
|
||||
arm
|
||||
@ -175,52 +172,53 @@ pub fn step_match_arm(number:usize) -> Arm {
|
||||
// === Generation for a Specific Lexer State ===
|
||||
|
||||
/// Generate the functions that implement the lexer automaton for a given lexer state.
|
||||
pub fn automaton_for_group
|
||||
( group : &Group
|
||||
, registry : &group::Registry
|
||||
) -> Result<Vec<ImplItem>,GenError> {
|
||||
let mut nfa = registry.to_nfa_from(group.id);
|
||||
pub fn automaton_for_group(
|
||||
group: &Group,
|
||||
registry: &group::Registry,
|
||||
) -> Result<Vec<ImplItem>, GenError> {
|
||||
let mut nfa = registry.to_nfa_from(group.id);
|
||||
let mut rules = Vec::with_capacity(nfa.states().len());
|
||||
for state in nfa.public_states().iter() {
|
||||
if nfa.name(*state).is_some() {
|
||||
rules.push(rule_for_state(*state,&nfa)?);
|
||||
rules.push(rule_for_state(*state, &nfa)?);
|
||||
}
|
||||
}
|
||||
let mut dfa = Dfa::from(nfa.automaton());
|
||||
let dispatch_for_dfa = dispatch_in_state(&dfa,group.id.into())?;
|
||||
let mut dfa_transitions = transitions_for_dfa(&mut dfa,&mut nfa,group.id.into())?;
|
||||
let mut dfa = Dfa::from(nfa.automaton());
|
||||
let dispatch_for_dfa = dispatch_in_state(&dfa, group.id.into())?;
|
||||
let mut dfa_transitions = transitions_for_dfa(&mut dfa, &mut nfa, group.id.into())?;
|
||||
dfa_transitions.push(dispatch_for_dfa);
|
||||
dfa_transitions.extend(rules);
|
||||
Ok(dfa_transitions)
|
||||
}
|
||||
|
||||
/// Generate a set of transition functions for the provided `dfa`, with identifier `id`.
|
||||
pub fn transitions_for_dfa
|
||||
( dfa : &mut Dfa
|
||||
, data : &mut AutomatonData
|
||||
, id : usize
|
||||
) -> Result<Vec<ImplItem>,GenError> {
|
||||
let mut state_has_overlapping_rules:HashMap<usize,bool> = HashMap::new();
|
||||
state_has_overlapping_rules.insert(0,false);
|
||||
let state_names:Vec<_> = dfa.links.row_indices().map(|ix| (ix,name_for_step(id,ix))).collect();
|
||||
let mut transitions = Vec::with_capacity(state_names.len());
|
||||
for (ix,name) in state_names.into_iter() {
|
||||
transitions.push(transition_for_dfa(dfa,name,data,ix,&mut state_has_overlapping_rules)?)
|
||||
pub fn transitions_for_dfa(
|
||||
dfa: &mut Dfa,
|
||||
data: &mut AutomatonData,
|
||||
id: usize,
|
||||
) -> Result<Vec<ImplItem>, GenError> {
|
||||
let mut state_has_overlapping_rules: HashMap<usize, bool> = HashMap::new();
|
||||
state_has_overlapping_rules.insert(0, false);
|
||||
let state_names: Vec<_> =
|
||||
dfa.links.row_indices().map(|ix| (ix, name_for_step(id, ix))).collect();
|
||||
let mut transitions = Vec::with_capacity(state_names.len());
|
||||
for (ix, name) in state_names.into_iter() {
|
||||
transitions.push(transition_for_dfa(dfa, name, data, ix, &mut state_has_overlapping_rules)?)
|
||||
}
|
||||
Ok(transitions)
|
||||
}
|
||||
|
||||
/// Generate a specific transition function for
|
||||
#[allow(clippy::implicit_hasher)]
|
||||
pub fn transition_for_dfa<S:BuildHasher>
|
||||
( dfa : &mut Dfa
|
||||
, transition_name : Ident
|
||||
, data : &mut AutomatonData
|
||||
, state_ix : usize
|
||||
, has_overlaps : &mut HashMap<usize,bool,S>
|
||||
) -> Result<ImplItem,GenError> {
|
||||
let match_expr:Expr = match_for_transition(dfa,state_ix,data,has_overlaps)?;
|
||||
let function:ImplItem = parse_quote! {
|
||||
pub fn transition_for_dfa<S: BuildHasher>(
|
||||
dfa: &mut Dfa,
|
||||
transition_name: Ident,
|
||||
data: &mut AutomatonData,
|
||||
state_ix: usize,
|
||||
has_overlaps: &mut HashMap<usize, bool, S>,
|
||||
) -> Result<ImplItem, GenError> {
|
||||
let match_expr: Expr = match_for_transition(dfa, state_ix, data, has_overlaps)?;
|
||||
let function: ImplItem = parse_quote! {
|
||||
fn #transition_name<R:ReaderOps>(&mut self, reader:&mut R) -> StageStatus {
|
||||
#match_expr
|
||||
}
|
||||
@ -229,35 +227,37 @@ pub fn transition_for_dfa<S:BuildHasher>
|
||||
}
|
||||
|
||||
/// Generate the pattern match for a given transition function.
|
||||
pub fn match_for_transition<S:BuildHasher>
|
||||
( dfa : &mut Dfa
|
||||
, state_ix : usize
|
||||
, data : &mut AutomatonData
|
||||
, has_overlaps : &mut HashMap<usize,bool,S>
|
||||
) -> Result<Expr,GenError> {
|
||||
let overlaps = *has_overlaps.get(&state_ix).unwrap_or(&false);
|
||||
let mut trigger_state = dfa.links[(state_ix,0)];
|
||||
let mut range_start = enso_automata::symbol::SymbolIndex::min_value();
|
||||
let divisions = dfa.alphabet.division_map.clone();
|
||||
let mut branches = Vec::with_capacity(divisions.len());
|
||||
pub fn match_for_transition<S: BuildHasher>(
|
||||
dfa: &mut Dfa,
|
||||
state_ix: usize,
|
||||
data: &mut AutomatonData,
|
||||
has_overlaps: &mut HashMap<usize, bool, S>,
|
||||
) -> Result<Expr, GenError> {
|
||||
let overlaps = *has_overlaps.get(&state_ix).unwrap_or(&false);
|
||||
let mut trigger_state = dfa.links[(state_ix, 0)];
|
||||
let mut range_start = enso_automata::symbol::SymbolIndex::min_value();
|
||||
let divisions = dfa.alphabet.division_map.clone();
|
||||
let mut branches = Vec::with_capacity(divisions.len());
|
||||
for (sym, ix) in divisions.into_iter() {
|
||||
let new_trigger_state = dfa.links[(state_ix,ix)];
|
||||
let new_trigger_state = dfa.links[(state_ix, ix)];
|
||||
if new_trigger_state != trigger_state {
|
||||
let range_end = if sym.index != 0 { sym.index - 1 } else { sym.index };
|
||||
let range_end = if sym.index != 0 { sym.index - 1 } else { sym.index };
|
||||
let current_trigger_state = trigger_state;
|
||||
let current_range_start = range_start;
|
||||
trigger_state = new_trigger_state;
|
||||
range_start = sym.index;
|
||||
let current_range_start = range_start;
|
||||
trigger_state = new_trigger_state;
|
||||
range_start = sym.index;
|
||||
let body =
|
||||
branch_body(dfa,current_trigger_state,state_ix,data,has_overlaps,overlaps)?;
|
||||
branches.push(Branch::new(Some(current_range_start..=range_end),body));
|
||||
} else {}
|
||||
branch_body(dfa, current_trigger_state, state_ix, data, has_overlaps, overlaps)?;
|
||||
branches.push(Branch::new(Some(current_range_start..=range_end), body));
|
||||
} else {
|
||||
}
|
||||
}
|
||||
let catch_all_branch_body = branch_body(dfa,trigger_state,state_ix,data,has_overlaps,overlaps)?;
|
||||
let catch_all_branch = Branch::new(None,catch_all_branch_body);
|
||||
let catch_all_branch_body =
|
||||
branch_body(dfa, trigger_state, state_ix, data, has_overlaps, overlaps)?;
|
||||
let catch_all_branch = Branch::new(None, catch_all_branch_body);
|
||||
branches.push(catch_all_branch);
|
||||
let arms:Vec<Arm> = branches.into_iter().map(Into::into).collect();
|
||||
let mut match_expr:ExprMatch = parse_quote! {
|
||||
let arms: Vec<Arm> = branches.into_iter().map(Into::into).collect();
|
||||
let mut match_expr: ExprMatch = parse_quote! {
|
||||
match u64::from(reader.character()) {
|
||||
#(#arms)*
|
||||
}
|
||||
@ -267,27 +267,25 @@ pub fn match_for_transition<S:BuildHasher>
|
||||
}
|
||||
|
||||
/// Generate the branch body for a transition in the DFA.
|
||||
pub fn branch_body<S:BuildHasher>
|
||||
( dfa : &mut Dfa
|
||||
, target_state : State<Dfa>
|
||||
, state_ix : usize
|
||||
, data : &mut AutomatonData
|
||||
, has_overlaps : &mut HashMap<usize,bool,S>
|
||||
, rules_overlap : bool
|
||||
) -> Result<Block,GenError> {
|
||||
let sources = dfa.sources.get(state_ix).expect("Internal error.");
|
||||
pub fn branch_body<S: BuildHasher>(
|
||||
dfa: &mut Dfa,
|
||||
target_state: State<Dfa>,
|
||||
state_ix: usize,
|
||||
data: &mut AutomatonData,
|
||||
has_overlaps: &mut HashMap<usize, bool, S>,
|
||||
rules_overlap: bool,
|
||||
) -> Result<Block, GenError> {
|
||||
let sources = dfa.sources.get(state_ix).expect("Internal error.");
|
||||
let rule_name_for_state = data.name_for_dfa_state(sources);
|
||||
if target_state == State::<Dfa>::INVALID {
|
||||
match rule_name_for_state {
|
||||
None => {
|
||||
Ok(parse_quote! {{
|
||||
StageStatus::ExitFail
|
||||
}})
|
||||
},
|
||||
None => Ok(parse_quote! {{
|
||||
StageStatus::ExitFail
|
||||
}}),
|
||||
Some(rule) => {
|
||||
let rule:Expr = match parse_str(rule) {
|
||||
let rule: Expr = match parse_str(rule) {
|
||||
Ok(rule) => rule,
|
||||
Err(_) => return Err(GenError::BadExpression(rule.to_string()))
|
||||
Err(_) => return Err(GenError::BadExpression(rule.to_string())),
|
||||
};
|
||||
if rules_overlap {
|
||||
Ok(parse_quote! {{
|
||||
@ -312,18 +310,19 @@ pub fn branch_body<S:BuildHasher>
|
||||
}
|
||||
} else {
|
||||
let target_state_has_no_rule = match rule_name_for_state {
|
||||
Some(_) => if !dfa_has_rule_name_for(data, dfa, target_state) {
|
||||
dfa.sources[target_state.id()] = (*sources).clone();
|
||||
has_overlaps.insert(target_state.id(),true);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
},
|
||||
None => false
|
||||
Some(_) =>
|
||||
if !dfa_has_rule_name_for(data, dfa, target_state) {
|
||||
dfa.sources[target_state.id()] = (*sources).clone();
|
||||
has_overlaps.insert(target_state.id(), true);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
},
|
||||
None => false,
|
||||
};
|
||||
|
||||
let state_id = Literal::usize_unsuffixed(target_state.id());
|
||||
let ret:Expr = parse_quote! {
|
||||
let ret: Expr = parse_quote! {
|
||||
StageStatus::ContinueWith(#state_id.into())
|
||||
};
|
||||
|
||||
@ -345,25 +344,25 @@ pub fn branch_body<S:BuildHasher>
|
||||
///
|
||||
/// This dispatch function is responsible for dispatching based on the sub-state of any given lexer
|
||||
/// state, and is the main part of implementing the actual lexer transitions.
|
||||
pub fn dispatch_in_state(dfa:&Dfa, id:usize) -> Result<ImplItem,GenError> {
|
||||
let dispatch_name:Ident = str_to_ident(format!("dispatch_in_state_{}",id))?;
|
||||
let state_names = dfa.links.row_indices().map(|ix| (ix, name_for_step(id,ix))).collect_vec();
|
||||
pub fn dispatch_in_state(dfa: &Dfa, id: usize) -> Result<ImplItem, GenError> {
|
||||
let dispatch_name: Ident = str_to_ident(format!("dispatch_in_state_{}", id))?;
|
||||
let state_names = dfa.links.row_indices().map(|ix| (ix, name_for_step(id, ix))).collect_vec();
|
||||
let mut branches = Vec::with_capacity(state_names.len());
|
||||
for (ix,name) in state_names.into_iter() {
|
||||
for (ix, name) in state_names.into_iter() {
|
||||
let literal = Literal::usize_unsuffixed(ix);
|
||||
let arm:Arm = parse_quote! {
|
||||
let arm: Arm = parse_quote! {
|
||||
#literal => self.#name(reader),
|
||||
};
|
||||
branches.push(arm);
|
||||
}
|
||||
|
||||
let pattern_match:ExprMatch = parse_quote! {
|
||||
let pattern_match: ExprMatch = parse_quote! {
|
||||
match new_state_index.into() {
|
||||
#(#branches)*
|
||||
_ => unreachable_panic!("Unreachable state reached in lexer.")
|
||||
}
|
||||
};
|
||||
let func:ImplItem = parse_quote! {
|
||||
let func: ImplItem = parse_quote! {
|
||||
fn #dispatch_name<R:ReaderOps>
|
||||
( &mut self
|
||||
, new_state_index:SubStateId
|
||||
@ -377,27 +376,27 @@ pub fn dispatch_in_state(dfa:&Dfa, id:usize) -> Result<ImplItem,GenError> {
|
||||
}
|
||||
|
||||
/// Generate a name for a given step function.
|
||||
pub fn name_for_step(in_state:usize, to_state:usize) -> Ident {
|
||||
let name_str = format!("state_{}_to_{}",in_state,to_state);
|
||||
pub fn name_for_step(in_state: usize, to_state: usize) -> Ident {
|
||||
let name_str = format!("state_{}_to_{}", in_state, to_state);
|
||||
parse_str(name_str.as_str()).expect("Impossible to not be a valid identifier.")
|
||||
}
|
||||
|
||||
/// Generate an executable rule function for a given lexer state.
|
||||
pub fn rule_for_state(state:nfa::State, automaton:&AutomatonData) -> Result<ImplItem,GenError> {
|
||||
pub fn rule_for_state(state: nfa::State, automaton: &AutomatonData) -> Result<ImplItem, GenError> {
|
||||
let state_name = automaton.name(state);
|
||||
match state_name {
|
||||
None => unreachable_panic!("Rule for state requested, but state has none."),
|
||||
Some(name) => {
|
||||
let rule_name = str_to_ident(name)?;
|
||||
let callback = automaton.code(state).expect("If it is named it has a callback.");
|
||||
let code:Expr = match parse_str(callback) {
|
||||
let callback = automaton.code(state).expect("If it is named it has a callback.");
|
||||
let code: Expr = match parse_str(callback) {
|
||||
Ok(expr) => expr,
|
||||
Err(_) => return Err(GenError::BadExpression(callback.into()))
|
||||
Err(_) => return Err(GenError::BadExpression(callback.into())),
|
||||
};
|
||||
if !has_reader_arg(&code) {
|
||||
return Err(GenError::BadCallbackArgument)
|
||||
return Err(GenError::BadCallbackArgument);
|
||||
}
|
||||
let tree:ImplItem = parse_quote! {
|
||||
let tree: ImplItem = parse_quote! {
|
||||
fn #rule_name<R:ReaderOps>(&mut self, reader:&mut R) {
|
||||
#code
|
||||
}
|
||||
@ -409,31 +408,23 @@ pub fn rule_for_state(state:nfa::State, automaton:&AutomatonData) -> Result<Impl
|
||||
|
||||
/// Checks if the given `expr` is a call with a single argument "reader" being passed.
|
||||
#[allow(clippy::cmp_owned)]
|
||||
pub fn has_reader_arg(expr:&Expr) -> bool {
|
||||
pub fn has_reader_arg(expr: &Expr) -> bool {
|
||||
match expr {
|
||||
Expr::MethodCall(expr) => match expr.args.first() {
|
||||
Some(Expr::Path(path)) => {
|
||||
match path.path.segments.first() {
|
||||
Some(segment) => {
|
||||
segment.ident.to_string() == "reader"
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
Some(Expr::Path(path)) => match path.path.segments.first() {
|
||||
Some(segment) => segment.ident.to_string() == "reader",
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
},
|
||||
Expr::Call(expr) => match expr.args.last() {
|
||||
Some(Expr::Path(path)) => {
|
||||
match path.path.segments.first() {
|
||||
Some(segment) => {
|
||||
segment.ident.to_string() == "reader"
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
_ => false
|
||||
Some(Expr::Path(path)) => match path.path.segments.first() {
|
||||
Some(segment) => segment.ident.to_string() == "reader",
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -444,7 +435,7 @@ pub fn has_reader_arg(expr:&Expr) -> bool {
|
||||
// ================
|
||||
|
||||
/// Errors that arise during code generation.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum GenError {
|
||||
/// The callback function does not take a single argument `reader`.
|
||||
BadCallbackArgument,
|
||||
@ -462,15 +453,16 @@ pub enum GenError {
|
||||
// === Trait Impls ===
|
||||
|
||||
impl Display for GenError {
|
||||
fn fmt(&self, f:&mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
GenError::BadCallbackArgument => write!(f,
|
||||
GenError::BadCallbackArgument => write!(
|
||||
f,
|
||||
"Bad argument to a callback function. It must take a single argument `reader`."
|
||||
),
|
||||
GenError::BadIdentifier(str) => write!(f,"`{}` is not a valid rust identifier.",str),
|
||||
GenError::BadExpression(str) => write!(f,"`{}` is not a valid rust expression.",str),
|
||||
GenError::BadLiteral(str) => write!(f,"`{}` is not a valid rust literal.",str),
|
||||
GenError::BadPath(str) => write!(f,"`{}` is not a valid rust path.",str),
|
||||
GenError::BadIdentifier(str) => write!(f, "`{}` is not a valid rust identifier.", str),
|
||||
GenError::BadExpression(str) => write!(f, "`{}` is not a valid rust expression.", str),
|
||||
GenError::BadLiteral(str) => write!(f, "`{}` is not a valid rust literal.", str),
|
||||
GenError::BadPath(str) => write!(f, "`{}` is not a valid rust path.", str),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -483,19 +475,19 @@ impl Display for GenError {
|
||||
|
||||
/// A representation of a dispatch branch for helping to generate pattern arms.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Branch {
|
||||
pub range : Option<RangeInclusive<enso_automata::symbol::SymbolIndex>>,
|
||||
pub body : Block
|
||||
pub range: Option<RangeInclusive<enso_automata::symbol::SymbolIndex>>,
|
||||
pub body: Block,
|
||||
}
|
||||
|
||||
impl Branch {
|
||||
/// Create a new branch, from the provided `range` and with `body` as the code it executes.
|
||||
pub fn new
|
||||
( range : Option<RangeInclusive<enso_automata::symbol::SymbolIndex>>
|
||||
, body : Block
|
||||
pub fn new(
|
||||
range: Option<RangeInclusive<enso_automata::symbol::SymbolIndex>>,
|
||||
body: Block,
|
||||
) -> Branch {
|
||||
Branch {range,body}
|
||||
Branch { range, body }
|
||||
}
|
||||
}
|
||||
|
||||
@ -503,12 +495,12 @@ impl Branch {
|
||||
// === Trait Impls ===
|
||||
|
||||
impl From<Branch> for Arm {
|
||||
fn from(value:Branch) -> Self {
|
||||
fn from(value: Branch) -> Self {
|
||||
let body = value.body;
|
||||
match value.range {
|
||||
Some(range) => {
|
||||
let range_start = Literal::u64_unsuffixed(*range.start());
|
||||
let range_end = Literal::u64_unsuffixed(*range.end());
|
||||
let range_end = Literal::u64_unsuffixed(*range.end());
|
||||
if range.start() == range.end() {
|
||||
parse_quote! {
|
||||
#range_start => #body,
|
||||
@ -521,7 +513,7 @@ impl From<Branch> for Arm {
|
||||
}
|
||||
None => parse_quote! {
|
||||
_ => #body,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -533,21 +525,21 @@ impl From<Branch> for Arm {
|
||||
// =================
|
||||
|
||||
/// Check if the DFA has a rule name for the provided target `state`.
|
||||
pub fn dfa_has_rule_name_for(nfa:&AutomatonData, dfa:&Dfa, state:dfa::State) -> bool {
|
||||
pub fn dfa_has_rule_name_for(nfa: &AutomatonData, dfa: &Dfa, state: dfa::State) -> bool {
|
||||
nfa.name_for_dfa_state(&dfa.sources[state.id()]).is_some()
|
||||
}
|
||||
|
||||
/// Convert a string to an identifier.
|
||||
pub fn str_to_ident(str:impl Str) -> Result<Ident,GenError> {
|
||||
pub fn str_to_ident(str: impl Str) -> Result<Ident, GenError> {
|
||||
parse_str(str.as_ref()).map_err(|_| GenError::BadIdentifier(str.into()))
|
||||
}
|
||||
|
||||
/// Convert a string to a path.
|
||||
pub fn str_to_path(str:impl Str) -> Result<Path,GenError> {
|
||||
pub fn str_to_path(str: impl Str) -> Result<Path, GenError> {
|
||||
parse_str(str.as_ref()).map_err(|_| GenError::BadPath(str.into()))
|
||||
}
|
||||
|
||||
/// Convert the syntax tree into a string.
|
||||
pub fn show_code(tokens:&impl ToTokens) -> String {
|
||||
pub fn show_code(tokens: &impl ToTokens) -> String {
|
||||
repr(tokens)
|
||||
}
|
||||
|
@ -2,15 +2,16 @@
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::automata::nfa;
|
||||
use crate::automata::nfa::Nfa;
|
||||
use crate::automata::{nfa, state};
|
||||
use crate::automata::pattern::Pattern;
|
||||
use crate::automata::state;
|
||||
use crate::group::rule::Rule;
|
||||
|
||||
use itertools::Itertools;
|
||||
use std::fmt::Display;
|
||||
use crate::prelude::fmt::Formatter;
|
||||
use crate::prelude::HashMap;
|
||||
use itertools::Itertools;
|
||||
use std::fmt::Display;
|
||||
|
||||
pub mod rule;
|
||||
|
||||
@ -24,31 +25,31 @@ pub mod rule;
|
||||
///
|
||||
/// It allows groups to contain associations between themselves, and also implements useful
|
||||
/// conversions for groups.
|
||||
#[derive(Clone,Debug,Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Registry {
|
||||
/// The groups defined for the lexer.
|
||||
groups:Vec<Group>,
|
||||
groups: Vec<Group>,
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
/// Defines a new group of rules for the lexer with the specified `name` and `parent`.
|
||||
///
|
||||
/// It returns the identifier of the newly-created group.
|
||||
pub fn define_group
|
||||
( &mut self
|
||||
, name : impl Into<String>
|
||||
, parent_index : Option<Identifier>
|
||||
pub fn define_group(
|
||||
&mut self,
|
||||
name: impl Into<String>,
|
||||
parent_index: Option<Identifier>,
|
||||
) -> Identifier {
|
||||
let id = self.next_id();
|
||||
let group = Group::new(id,name.into(),parent_index);
|
||||
let id = self.next_id();
|
||||
let group = Group::new(id, name.into(), parent_index);
|
||||
self.groups.push(group);
|
||||
id
|
||||
}
|
||||
|
||||
/// Adds an existing `group` to the registry, updating and returning its identifier.
|
||||
pub fn add_group(&mut self, mut group:Group) -> Identifier {
|
||||
pub fn add_group(&mut self, mut group: Group) -> Identifier {
|
||||
let new_id = self.next_id();
|
||||
group.id = new_id;
|
||||
group.id = new_id;
|
||||
self.groups.push(group);
|
||||
new_id
|
||||
}
|
||||
@ -56,15 +57,15 @@ impl Registry {
|
||||
/// Creates a rule that matches `pattern` for the group identified by `group_id`.
|
||||
///
|
||||
/// Panics if `group_id` refers to a nonexistent group.
|
||||
pub fn create_rule(&mut self, group:Identifier, pattern:&Pattern, callback:impl AsRef<str>) {
|
||||
pub fn create_rule(&mut self, group: Identifier, pattern: &Pattern, callback: impl AsRef<str>) {
|
||||
let group = self.group_mut(group);
|
||||
group.create_rule(pattern,callback.as_ref());
|
||||
group.create_rule(pattern, callback.as_ref());
|
||||
}
|
||||
|
||||
/// Associates the provided `rule` with the group identified by `group_id`.
|
||||
///
|
||||
/// Panics if `group_id` refers to a nonexistent group.
|
||||
pub fn add_rule(&mut self, group:Identifier, rule:Rule) {
|
||||
pub fn add_rule(&mut self, group: Identifier, rule: Rule) {
|
||||
let group = self.group_mut(group);
|
||||
group.add_rule(rule);
|
||||
}
|
||||
@ -73,10 +74,10 @@ impl Registry {
|
||||
/// by `group_id` as active.
|
||||
///
|
||||
/// This set of rules includes the rules inherited from any parent groups.
|
||||
pub fn rules_for(&self, group:Identifier) -> Vec<&Rule> {
|
||||
pub fn rules_for(&self, group: Identifier) -> Vec<&Rule> {
|
||||
let group_handle = self.group(group);
|
||||
let mut parent = group_handle.parent_index.map(|p| self.group(p));
|
||||
let mut rules = (&group_handle.rules).iter().collect_vec();
|
||||
let mut parent = group_handle.parent_index.map(|p| self.group(p));
|
||||
let mut rules = (&group_handle.rules).iter().collect_vec();
|
||||
while let Some(parent_group) = parent {
|
||||
if parent_group.id == group_handle.id {
|
||||
panic!("There should not be cycles in parent links for lexer groups.")
|
||||
@ -91,7 +92,7 @@ impl Registry {
|
||||
///
|
||||
/// As group identifiers can only be created by use of this `Registry`, this will always
|
||||
/// succeed.
|
||||
pub fn group(&self, group:Identifier) -> &Group {
|
||||
pub fn group(&self, group: Identifier) -> &Group {
|
||||
self.groups.get(group.0).expect("The group must exist.")
|
||||
}
|
||||
|
||||
@ -99,28 +100,28 @@ impl Registry {
|
||||
///
|
||||
/// As group identifiers can only be created by use of this `Registry`, this will always
|
||||
/// succeed.
|
||||
pub fn group_mut(&mut self, group:Identifier) -> &mut Group {
|
||||
pub fn group_mut(&mut self, group: Identifier) -> &mut Group {
|
||||
self.groups.get_mut(group.0).expect("The group should exist.")
|
||||
}
|
||||
|
||||
/// Converts the group identified by `group_id` into an NFA.
|
||||
///
|
||||
/// Returns `None` if the group does not exist, or if the conversion fails.
|
||||
pub fn to_nfa_from(&self, group_id:Identifier) -> AutomatonData {
|
||||
let group = self.group(group_id);
|
||||
let mut nfa = AutomatonData::default();
|
||||
let start = nfa.automaton.start;
|
||||
pub fn to_nfa_from(&self, group_id: Identifier) -> AutomatonData {
|
||||
let group = self.group(group_id);
|
||||
let mut nfa = AutomatonData::default();
|
||||
let start = nfa.automaton.start;
|
||||
nfa.add_public_state(start);
|
||||
let build = |rule:&Rule| nfa.new_pattern(start,&rule.pattern);
|
||||
let rules = self.rules_for(group.id);
|
||||
let build = |rule: &Rule| nfa.new_pattern(start, &rule.pattern);
|
||||
let rules = self.rules_for(group.id);
|
||||
let callbacks = rules.iter().map(|r| r.callback.clone()).collect_vec();
|
||||
let states = rules.into_iter().map(build).collect_vec();
|
||||
let end = nfa.new_state_exported();
|
||||
for (ix,state) in states.into_iter().enumerate() {
|
||||
let states = rules.into_iter().map(build).collect_vec();
|
||||
let end = nfa.new_state_exported();
|
||||
for (ix, state) in states.into_iter().enumerate() {
|
||||
nfa.add_public_state(state);
|
||||
nfa.set_name(state,group.callback_name(ix));
|
||||
nfa.set_code(state,callbacks.get(ix).unwrap().clone());
|
||||
nfa.connect(state,end);
|
||||
nfa.set_name(state, group.callback_name(ix));
|
||||
nfa.set_code(state, callbacks.get(ix).unwrap().clone());
|
||||
nfa.connect(state, end);
|
||||
}
|
||||
nfa.add_public_state(end);
|
||||
nfa
|
||||
@ -144,42 +145,42 @@ impl Registry {
|
||||
// ====================
|
||||
|
||||
/// Storage for the generated automaton and auxiliary data required for code generation.
|
||||
#[derive(Clone,Debug,Default,PartialEq,Eq)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct AutomatonData {
|
||||
/// The non-deterministic finite automaton implementing the group of rules it was generated
|
||||
/// from.
|
||||
automaton : Nfa,
|
||||
automaton: Nfa,
|
||||
/// The states defined in the automaton.
|
||||
states : Vec<nfa::State>,
|
||||
states: Vec<nfa::State>,
|
||||
/// The names of callbacks, where provided.
|
||||
transition_names : HashMap<nfa::State,String>,
|
||||
transition_names: HashMap<nfa::State, String>,
|
||||
/// The code to execute on a callback, where available.
|
||||
callback_code : HashMap<nfa::State,String>,
|
||||
callback_code: HashMap<nfa::State, String>,
|
||||
}
|
||||
|
||||
impl AutomatonData {
|
||||
/// Set the name for the provided `state_id`.
|
||||
pub fn set_name(&mut self, state_id:nfa::State,name:impl Str) {
|
||||
self.transition_names.insert(state_id,name.into());
|
||||
pub fn set_name(&mut self, state_id: nfa::State, name: impl Str) {
|
||||
self.transition_names.insert(state_id, name.into());
|
||||
}
|
||||
|
||||
/// Set the callback code for the provided `state_id`.
|
||||
pub fn set_code(&mut self, state_id:nfa::State,code:impl Str) {
|
||||
self.callback_code.insert(state_id,code.into());
|
||||
pub fn set_code(&mut self, state_id: nfa::State, code: impl Str) {
|
||||
self.callback_code.insert(state_id, code.into());
|
||||
}
|
||||
|
||||
/// Add the provided `state` to the state registry.
|
||||
pub fn add_public_state(&mut self, state:nfa::State) {
|
||||
pub fn add_public_state(&mut self, state: nfa::State) {
|
||||
self.states.push(state);
|
||||
}
|
||||
|
||||
/// Get the name for the provided `state_id`, if present.
|
||||
pub fn name(&self, state_id:nfa::State) -> Option<&str> {
|
||||
pub fn name(&self, state_id: nfa::State) -> Option<&str> {
|
||||
self.transition_names.get(&state_id).map(|s| s.as_str())
|
||||
}
|
||||
|
||||
/// Get the callback code for the provided `state_id`, if present.
|
||||
pub fn code(&self, state_id:nfa::State) -> Option<&str> {
|
||||
pub fn code(&self, state_id: nfa::State) -> Option<&str> {
|
||||
self.callback_code.get(&state_id).map(|s| s.as_str())
|
||||
}
|
||||
|
||||
@ -196,12 +197,12 @@ impl AutomatonData {
|
||||
}
|
||||
|
||||
/// Get a reference to the state names for this automaton.
|
||||
pub fn names(&self) -> &HashMap<nfa::State,String> {
|
||||
pub fn names(&self) -> &HashMap<nfa::State, String> {
|
||||
&self.transition_names
|
||||
}
|
||||
|
||||
/// Get a reference to the callbacks for this automaton.
|
||||
pub fn callbacks(&self) -> &HashMap<nfa::State,String> {
|
||||
pub fn callbacks(&self) -> &HashMap<nfa::State, String> {
|
||||
&self.callback_code
|
||||
}
|
||||
|
||||
@ -211,7 +212,7 @@ impl AutomatonData {
|
||||
}
|
||||
|
||||
/// Get the rule name for a the provided state.
|
||||
pub fn name_for_dfa_state(&self, sources:&[nfa::State]) -> Option<&str> {
|
||||
pub fn name_for_dfa_state(&self, sources: &[nfa::State]) -> Option<&str> {
|
||||
let mut result = None;
|
||||
for source in sources.iter() {
|
||||
let name = self.name(*source);
|
||||
@ -225,7 +226,7 @@ impl AutomatonData {
|
||||
}
|
||||
|
||||
/// Errors that can occur when querying callbacks for a DFA state.
|
||||
#[derive(Copy,Clone,Debug,Display,Eq,PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Display, Eq, PartialEq)]
|
||||
pub enum CallbackError {
|
||||
/// There are no available callbacks for this state.
|
||||
NoCallback,
|
||||
@ -258,26 +259,26 @@ impl DerefMut for AutomatonData {
|
||||
|
||||
/// An identifier for a group.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Copy,Clone,Debug,Default,Eq,PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Identifier(usize);
|
||||
|
||||
|
||||
// === Trait Impls ===
|
||||
|
||||
impl From<usize> for Identifier {
|
||||
fn from(id:usize) -> Self {
|
||||
fn from(id: usize) -> Self {
|
||||
Identifier(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&usize> for Identifier {
|
||||
fn from(id:&usize) -> Self {
|
||||
fn from(id: &usize) -> Self {
|
||||
Identifier(*id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Identifier> for usize {
|
||||
fn from(value:Identifier) -> Self {
|
||||
fn from(value: Identifier) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
@ -305,56 +306,55 @@ impl From<Identifier> for usize {
|
||||
/// current group or even enter a new one. As a result, groups allow us to elegantly model a
|
||||
/// situation where certain parts of a program (e.g. within a string literal) have very different
|
||||
/// lexing rules than other portions of a program (e.g. the body of a function).
|
||||
#[derive(Clone,Debug,Default)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Group {
|
||||
/// A unique identifier for the group.
|
||||
pub id:Identifier,
|
||||
pub id: Identifier,
|
||||
/// A name for the group (useful in debugging).
|
||||
pub name:String,
|
||||
pub name: String,
|
||||
/// The parent group from which rules are inherited.
|
||||
///
|
||||
/// It is ensured that the group is held mutably.
|
||||
pub parent_index:Option<Identifier>,
|
||||
pub parent_index: Option<Identifier>,
|
||||
/// A set of flexer rules.
|
||||
pub rules:Vec<Rule>,
|
||||
pub rules: Vec<Rule>,
|
||||
/// The names for the user-defined states.
|
||||
pub state_names:HashMap<usize,String>,
|
||||
pub state_names: HashMap<usize, String>,
|
||||
/// The callback functions for the user-defined states.
|
||||
pub state_callbacks:HashMap<usize,String>,
|
||||
pub state_callbacks: HashMap<usize, String>,
|
||||
}
|
||||
|
||||
impl Group {
|
||||
|
||||
/// Creates a new group.
|
||||
pub fn new(id:Identifier, name:impl Into<String>, parent_index:Option<Identifier>) -> Self {
|
||||
let rules = default();
|
||||
let state_names = default();
|
||||
pub fn new(id: Identifier, name: impl Into<String>, parent_index: Option<Identifier>) -> Self {
|
||||
let rules = default();
|
||||
let state_names = default();
|
||||
let state_callbacks = default();
|
||||
Group{id,name:name.into(),parent_index,rules,state_names,state_callbacks}
|
||||
Group { id, name: name.into(), parent_index, rules, state_names, state_callbacks }
|
||||
}
|
||||
|
||||
/// Adds a new rule to the current group.
|
||||
pub fn add_rule(&mut self, rule:Rule) {
|
||||
pub fn add_rule(&mut self, rule: Rule) {
|
||||
self.rules.push(rule)
|
||||
}
|
||||
|
||||
/// Creates a new rule.
|
||||
pub fn create_rule(&mut self, pattern:&Pattern, code:&str) {
|
||||
pub fn create_rule(&mut self, pattern: &Pattern, code: &str) {
|
||||
let pattern_clone = pattern.clone();
|
||||
let rule = Rule::new(pattern_clone,code);
|
||||
let rule = Rule::new(pattern_clone, code);
|
||||
self.rules.push(rule)
|
||||
}
|
||||
|
||||
/// The canonical name for a given rule.
|
||||
pub fn callback_name(&self, rule_ix:usize) -> String {
|
||||
format!("group_{}_rule_{}",self.id.0,rule_ix)
|
||||
pub fn callback_name(&self, rule_ix: usize) -> String {
|
||||
format!("group_{}_rule_{}", self.id.0, rule_ix)
|
||||
}
|
||||
}
|
||||
|
||||
// === Trait Impls ===
|
||||
|
||||
impl From<Group> for Registry {
|
||||
fn from(value:Group) -> Self {
|
||||
fn from(value: Group) -> Self {
|
||||
let mut registry = Registry::default();
|
||||
registry.add_group(value);
|
||||
registry
|
||||
@ -362,8 +362,8 @@ impl From<Group> for Registry {
|
||||
}
|
||||
|
||||
impl Display for Group {
|
||||
fn fmt(&self, f:&mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f,"Group {}",self.name)
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Group {}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,49 +379,49 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn group_create_rule() {
|
||||
let pattern = Pattern::all_of("abcde");
|
||||
let mut group = Group::new(0.into(),"Test Name",None);
|
||||
group.create_rule(&pattern,"code");
|
||||
let rule = Rule::new(pattern,"code");
|
||||
let pattern = Pattern::all_of("abcde");
|
||||
let mut group = Group::new(0.into(), "Test Name", None);
|
||||
group.create_rule(&pattern, "code");
|
||||
let rule = Rule::new(pattern, "code");
|
||||
assert!(group.rules.contains(&rule));
|
||||
assert_eq!(group.rules[0].callback,"code".to_string());
|
||||
assert_eq!(group.rules[0].callback, "code".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_callback_name() {
|
||||
let pattern_1 = Pattern::all_of("abcde");
|
||||
let pattern_2 = Pattern::all_of("abcde");
|
||||
let mut group = Group::new(0.into(),"Test Name",None);
|
||||
group.create_rule(&pattern_1,"code");
|
||||
group.create_rule(&pattern_2,"code");
|
||||
assert_eq!(group.callback_name(0),"group_0_rule_0");
|
||||
assert_eq!(group.callback_name(1),"group_0_rule_1");
|
||||
let mut group = Group::new(0.into(), "Test Name", None);
|
||||
group.create_rule(&pattern_1, "code");
|
||||
group.create_rule(&pattern_2, "code");
|
||||
assert_eq!(group.callback_name(0), "group_0_rule_0");
|
||||
assert_eq!(group.callback_name(1), "group_0_rule_1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_registry_define_group() {
|
||||
let mut registry = Registry::default();
|
||||
registry.define_group("TEST_GROUP",None);
|
||||
registry.define_group("TEST_GROUP", None);
|
||||
assert!(registry.all().iter().any(|g| g.name == *"TEST_GROUP"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_registry_create_rule() {
|
||||
let pattern = Pattern::none_of("abcde");
|
||||
let pattern = Pattern::none_of("abcde");
|
||||
let mut registry = Registry::default();
|
||||
let group_1_id = registry.define_group("GROUP_1",None);
|
||||
let group_2_id = registry.define_group("GROUP_2",None);
|
||||
let group_1_id = registry.define_group("GROUP_1", None);
|
||||
let group_2_id = registry.define_group("GROUP_2", None);
|
||||
|
||||
let group_1 = registry.group_mut(group_1_id);
|
||||
group_1.create_rule(&pattern,"rule_1");
|
||||
let group_1 = registry.group_mut(group_1_id);
|
||||
group_1.create_rule(&pattern, "rule_1");
|
||||
|
||||
let group_2 = registry.group_mut(group_2_id);
|
||||
group_2.create_rule(&pattern,"rule_2");
|
||||
let group_2 = registry.group_mut(group_2_id);
|
||||
group_2.create_rule(&pattern, "rule_2");
|
||||
|
||||
let rules_1 = registry.rules_for(group_1_id);
|
||||
let rules_2 = registry.rules_for(group_2_id);
|
||||
assert!(rules_1.iter().any(|r| **r == Rule::new(pattern.clone(),"rule_1")));
|
||||
assert!(rules_2.iter().any(|r| **r == Rule::new(pattern.clone(),"rule_2")));
|
||||
assert!(rules_1.iter().any(|r| **r == Rule::new(pattern.clone(), "rule_1")));
|
||||
assert!(rules_2.iter().any(|r| **r == Rule::new(pattern.clone(), "rule_2")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -446,8 +446,8 @@ pub mod tests {
|
||||
|
||||
let rules = registry.rules_for(group_3_id);
|
||||
assert_eq!(rules.len(), 3);
|
||||
assert!(rules.iter().any(|r| **r == Rule::new(pattern_1.clone(),"rule_1")));
|
||||
assert!(rules.iter().any(|r| **r == Rule::new(pattern_2.clone(),"rule_2")));
|
||||
assert!(rules.iter().any(|r| **r == Rule::new(pattern_3.clone(),"rule_3")));
|
||||
assert!(rules.iter().any(|r| **r == Rule::new(pattern_1.clone(), "rule_1")));
|
||||
assert!(rules.iter().any(|r| **r == Rule::new(pattern_2.clone(), "rule_2")));
|
||||
assert!(rules.iter().any(|r| **r == Rule::new(pattern_3.clone(), "rule_3")));
|
||||
}
|
||||
}
|
||||
|
@ -12,23 +12,23 @@ use crate::automata::pattern::Pattern;
|
||||
// ==========
|
||||
|
||||
/// A flexer rule.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Rule {
|
||||
/// The pattern that triggers the callback.
|
||||
pub pattern:Pattern,
|
||||
pub pattern: Pattern,
|
||||
|
||||
/// The code to execute when [`Rule::pattern`] matches, containing rust code as a
|
||||
/// [`std::string::String`].
|
||||
///
|
||||
/// This code will be called directly from a method defined on your Lexer (the one that contains
|
||||
/// a [`crate::Flexer`] instance. To this end, the code you provide as a string must be valid in
|
||||
/// that context.
|
||||
pub callback:String,
|
||||
/// This code will be called directly from a method defined on your Lexer (the one that
|
||||
/// contains a [`crate::Flexer`] instance. To this end, the code you provide as a string
|
||||
/// must be valid in that context.
|
||||
pub callback: String,
|
||||
}
|
||||
|
||||
impl Rule {
|
||||
/// Creates a new rule.
|
||||
pub fn new(pattern:Pattern, callback:impl Into<String>) -> Self {
|
||||
Rule{pattern,callback:callback.into()}
|
||||
pub fn new(pattern: Pattern, callback: impl Into<String>) -> Self {
|
||||
Rule { pattern, callback: callback.into() }
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user