Refactoring: merge utils into prelude; merge workspaces. (#3151)

This commit is contained in:
Adam Obuchowicz 2021-11-10 14:36:08 +01:00 committed by GitHub
parent ea1a411f9a
commit 942464cbaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
196 changed files with 10808 additions and 12285 deletions

View File

@ -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

View File

@ -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:

View File

@ -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: >-

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -0,0 +1,2 @@
# This file should be ignored by cargo watch, but not by git
.github

3087
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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]

View File

@ -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'])
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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' }

View File

@ -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" }

View File

@ -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"}

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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" }

View File

@ -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() {

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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.

View File

@ -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" }

View File

@ -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());

View File

@ -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 {

View File

@ -19,8 +19,6 @@ use crate::SectionRight;
use crate::SectionSides;
use crate::Shape;
use utils::vec::VecExt;
// =================

View File

@ -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]

View File

@ -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

View File

@ -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" }

View File

@ -22,7 +22,6 @@ use futures::Stream;
use futures::StreamExt;
use serde::de::DeserializeOwned;
use std::future::Future;
use utils::channel;

View File

@ -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;

View File

@ -12,7 +12,6 @@ use futures::channel::mpsc::UnboundedSender;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::collections::VecDeque;
use utils::channel;

View File

@ -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>;

View File

@ -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" }

View File

@ -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,
}

View File

@ -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 {

View File

@ -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 }
}
}

View File

@ -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;

View File

@ -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" }

View File

@ -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::*;

View File

@ -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"

View File

@ -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())
}

View File

@ -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;

View File

@ -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)
}

View File

@ -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> {}

View File

@ -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;

View File

@ -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;

View File

@ -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::*;

View File

@ -395,7 +395,6 @@ mod test {
use futures::future;
use futures::SinkExt;
use mockall::Sequence;
use utils::test::traits::*;
// === Test Providers ===

View File

@ -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);
}

View File

@ -6,7 +6,6 @@ use crate::executor::global::set_spawner;
use crate::executor::global::spawn;
use futures::executor;
use utils::test::traits::*;

View File

@ -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)

View File

@ -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;

View File

@ -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(&registry);
let id1 = Id::new_v4();

View File

@ -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 {

View File

@ -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)]

View File

@ -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() {

View File

@ -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;

View File

@ -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)]

View File

@ -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;

View File

@ -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() {

View File

@ -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 {

View File

@ -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 {

View File

@ -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"] }

View File

@ -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(),
});
}
}

View File

@ -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

View File

@ -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 })
}
}

View File

@ -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());
}
""
}

View File

@ -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::*;

View File

@ -1,4 +1,4 @@
use ast::generation::ScalaGenerator;
use ast_new::generation::ScalaGenerator;
use std::fs::File;
use std::io::Write;

View File

@ -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)));
}

View File

@ -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);
}
}

View File

@ -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))
}
}

View File

@ -9,7 +9,6 @@
#![warn(unused_import_braces)]
#![warn(unused_qualifications)]
#![warn(missing_docs)]
#![feature(test)]
pub mod alphabet;

View File

@ -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()))

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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));
}
}

View File

@ -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
}
}
});
}

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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)], "");
}
}

View File

@ -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")
}
}

View File

@ -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.");

View File

@ -16,4 +16,5 @@
#![warn(unsafe_code)]
#![warn(unused_import_braces)]
#[rustfmt::skip]
pub mod generated;

View File

@ -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(" "),

View File

@ -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)
}

View File

@ -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")));
}
}

View File

@ -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