build(rust): vendor rnix parser crate

This commit was merged in pull request #57.
This commit is contained in:
2026-09-16 15:51:43 -04:00
parent cbdf482422
commit 82ad65a3a4
492 changed files with 67785 additions and 1 deletions

View File

@@ -0,0 +1 @@
{"$comment":"This file only protects against accidental modifications. It is not a security mechanism and does not protect against malicious changes.","files":{".cargo_vcs_info.json":"7d471d4f2d4a2cb6b406b0bf3d1be7d7735dc4dcfdb165cb8766a4ca48497b8c","Cargo.lock":"24dfd51d2e1720257b6a678ff5e03a594cbcf195b8fbebca0cba04c1d551c073","Cargo.toml":"cbcc91fcc6cc6034ab7b436569da02d4b9bed9eb4f70bc7f9748d580e3adfe7f","Cargo.toml.orig":"e617868c3807e231c943088bad45232395108ed4ee1991df88ddf949206d85af","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"69657dd291f7b245820ee01fa7bdbfdc5dc2b47284767641ffb8f94e9f9bc522","examples/math.rs":"e568a39431b26b20362a8fea7728efc2295f39bbb7dace3789756cfa58c7b02a","examples/s_expressions.rs":"6d963a8c030978e2648a059f2dcd5314d9fef47f0dda45e4134caedae1429eee","src/api.rs":"31c2ae43432bae7dff12d4d2d1a76c62c3f2d74a9d00828b82d38a01d61334c8","src/arc.rs":"fce42117614b3016ee7db92ec603d68ce0d06d77bd7bb5d4f569d1789a3f1809","src/ast.rs":"f6e803e1cd1099a6d80a27c8b7fda965dc664bad6d541788699fe083169971e2","src/cow_mut.rs":"c90f47a013ae8cfb00bee089b75bbc2e493424e344fb4aaeff78cb2db8558ec2","src/cursor.rs":"0b191dd7b9946e8ed0e436b9af5d13e57911a755fec788d1d87f3aee365d7489","src/green.rs":"34d58d497d2af2bbd1533aea6b4aa91496d65275b3b6def8dd6f5b1210ec0ce0","src/green/builder.rs":"cc5857e04b8b0c252c9c644397e63d204b3025bda8cb68d58750b085062f117b","src/green/element.rs":"df199a8b6adefef54398f209e7316525eaf84133e952325d46641b66cb89ada9","src/green/node.rs":"2b7358abbd7446f31bf00f4ba252682a6c6f702ca0d05e869b3ebc465879b314","src/green/node_cache.rs":"f85ca104edb7d2261fd111a6fa4165f076e802aa9414457bdb028788db8dde9d","src/green/token.rs":"fe99fdf7a76227b0025461a928d6c1484d4c70cceb326189387b1f4a37e28ea1","src/lib.rs":"e0eb1ba73978636b577094ebb726e2e222f347587928ba3930b196b809f17f86","src/serde_impls.rs":"603a459c1e3ad6edd2915e6ef3d103bae36364e01d781a8521486d618ba7b678","src/sll.rs":"3428e62b22017f89b0366e5be882a17cb449c118b99805e0c219090a41845b9a","src/syntax_text.rs":"e387fa481a53aa9dd220a350acabd150daa8440c07ade49ea8a2f69275c6b366","src/utility_types.rs":"ce0874ae3e2b1eccb77025322c4453ceafef983f5d41b442448fb848259edf06","tests/tidy.rs":"15f655d7022a23cea887c6ac52727811f72c7d6a0fd54cdbb0476521ab4fea7f"},"package":"417a3a9f582e349834051b8a10c8d71ca88da4211e4093528e36b9845f6b5f21"}

View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "e25dbdf9cb7f7757eb85c74a69c8a7d4a4fae67b"
},
"path_in_vcs": ""
}

View File

@@ -0,0 +1,116 @@
###############################################################################
# @generated
# DO NOT MODIFY: This file is auto-generated by a crate_universe tool. To
# regenerate this file, run the following:
#
# bazel run @@//third_party:vendor_crates
###############################################################################
load("@rules_rust//cargo:defs.bzl", "cargo_toml_env_vars")
load("@rules_rust//rust:defs.bzl", "rust_library")
package(default_visibility = ["//visibility:public"])
cargo_toml_env_vars(
name = "cargo_toml_env_vars",
src = "Cargo.toml",
)
rust_library(
name = "rowan",
srcs = glob(
include = ["**/*.rs"],
allow_empty = False,
),
compile_data = glob(
include = ["**"],
allow_empty = True,
exclude = [
"**/* *",
".tmp_git_root/**/*",
"BUILD",
"BUILD.bazel",
"WORKSPACE",
"WORKSPACE.bazel",
],
),
crate_root = "src/lib.rs",
edition = "2021",
rustc_env_files = [
":cargo_toml_env_vars",
],
rustc_flags = [
"--cap-lints=allow",
],
tags = [
"cargo-bazel",
"crate-name=rowan",
"manual",
"noclippy",
"norustfmt",
],
target_compatible_with = select({
"@rules_rust//rust/platform:aarch64-apple-darwin": [],
"@rules_rust//rust/platform:aarch64-apple-ios": [],
"@rules_rust//rust/platform:aarch64-apple-ios-macabi": [],
"@rules_rust//rust/platform:aarch64-apple-ios-sim": [],
"@rules_rust//rust/platform:aarch64-linux-android": [],
"@rules_rust//rust/platform:aarch64-pc-windows-msvc": [],
"@rules_rust//rust/platform:aarch64-unknown-fuchsia": [],
"@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [],
"@rules_rust//rust/platform:aarch64-unknown-nixos-gnu": [],
"@rules_rust//rust/platform:aarch64-unknown-none": [],
"@rules_rust//rust/platform:aarch64-unknown-nto-qnx710": [],
"@rules_rust//rust/platform:aarch64-unknown-uefi": [],
"@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [],
"@rules_rust//rust/platform:arm-unknown-linux-musleabi": [],
"@rules_rust//rust/platform:armv7-linux-androideabi": [],
"@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [],
"@rules_rust//rust/platform:i686-apple-darwin": [],
"@rules_rust//rust/platform:i686-linux-android": [],
"@rules_rust//rust/platform:i686-pc-windows-msvc": [],
"@rules_rust//rust/platform:i686-unknown-freebsd": [],
"@rules_rust//rust/platform:i686-unknown-linux-gnu": [],
"@rules_rust//rust/platform:loongarch64-unknown-linux-gnu": [],
"@rules_rust//rust/platform:mips-unknown-linux-gnu": [],
"@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [],
"@rules_rust//rust/platform:riscv32imac-unknown-none-elf": [],
"@rules_rust//rust/platform:riscv32imc-unknown-none-elf": [],
"@rules_rust//rust/platform:riscv64gc-unknown-linux-gnu": [],
"@rules_rust//rust/platform:riscv64gc-unknown-none-elf": [],
"@rules_rust//rust/platform:s390x-unknown-linux-gnu": [],
"@rules_rust//rust/platform:sparc64-unknown-linux-gnu": [],
"@rules_rust//rust/platform:sparc64-unknown-netbsd": [],
"@rules_rust//rust/platform:sparc64-unknown-openbsd": [],
"@rules_rust//rust/platform:thumbv6m-none-eabi": [],
"@rules_rust//rust/platform:thumbv7em-none-eabi": [],
"@rules_rust//rust/platform:thumbv7em-none-eabihf": [],
"@rules_rust//rust/platform:thumbv7m-none-eabi": [],
"@rules_rust//rust/platform:thumbv8m.main-none-eabi": [],
"@rules_rust//rust/platform:thumbv8m.main-none-eabihf": [],
"@rules_rust//rust/platform:wasm32-unknown-emscripten": [],
"@rules_rust//rust/platform:wasm32-unknown-unknown": [],
"@rules_rust//rust/platform:wasm32-wasip1": [],
"@rules_rust//rust/platform:wasm32-wasip1-threads": [],
"@rules_rust//rust/platform:wasm32-wasip2": [],
"@rules_rust//rust/platform:x86_64-apple-darwin": [],
"@rules_rust//rust/platform:x86_64-apple-ios": [],
"@rules_rust//rust/platform:x86_64-apple-ios-macabi": [],
"@rules_rust//rust/platform:x86_64-linux-android": [],
"@rules_rust//rust/platform:x86_64-pc-windows-msvc": [],
"@rules_rust//rust/platform:x86_64-unknown-freebsd": [],
"@rules_rust//rust/platform:x86_64-unknown-fuchsia": [],
"@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [],
"@rules_rust//rust/platform:x86_64-unknown-nixos-gnu": [],
"@rules_rust//rust/platform:x86_64-unknown-none": [],
"@rules_rust//rust/platform:x86_64-unknown-uefi": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
version = "0.16.1",
deps = [
"//third_party/crates/countme-3.0.1:countme",
"//third_party/crates/hashbrown-0.14.5:hashbrown",
"//third_party/crates/rustc-hash-1.1.0:rustc_hash",
"//third_party/crates/text-size-1.1.1:text_size",
],
)

150
third_party/crates/rowan-0.16.1/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,150 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "countme"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636"
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "m_lexer"
version = "0.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7e51ebf91162d585a5bae05e4779efc4a276171cb880d61dd6fab11c98467a7"
dependencies = [
"regex",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "proc-macro2"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rowan"
version = "0.16.1"
dependencies = [
"countme",
"hashbrown",
"m_lexer",
"rustc-hash",
"serde",
"text-size",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "serde"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "2.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "text-size"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
dependencies = [
"serde",
]
[[package]]
name = "unicode-ident"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"

View File

@@ -0,0 +1,75 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
rust-version = "1.77.0"
name = "rowan"
version = "0.16.1"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
build = false
exclude = [
".github/",
"bors.toml",
"rustfmt.toml",
]
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Library for generic lossless syntax trees"
readme = "README.md"
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-analyzer/rowan"
[lib]
name = "rowan"
path = "src/lib.rs"
[[example]]
name = "math"
path = "examples/math.rs"
[[example]]
name = "s_expressions"
path = "examples/s_expressions.rs"
[[test]]
name = "tidy"
path = "tests/tidy.rs"
[dependencies.countme]
version = "3.0.0"
[dependencies.hashbrown]
version = "0.14.3"
features = ["inline-more"]
default-features = false
[dependencies.rustc-hash]
version = "1.0.1"
[dependencies.serde]
version = "1.0.89"
optional = true
default-features = false
[dependencies.text-size]
version = "1.1.0"
[dev-dependencies.m_lexer]
version = "0.0.4"
[features]
serde1 = [
"serde",
"text-size/serde",
]

29
third_party/crates/rowan-0.16.1/Cargo.toml.orig generated vendored Normal file
View File

@@ -0,0 +1,29 @@
[package]
name = "rowan"
version = "0.16.1"
authors = ["Aleksey Kladov <aleksey.kladov@gmail.com>"]
repository = "https://github.com/rust-analyzer/rowan"
license = "MIT OR Apache-2.0"
description = "Library for generic lossless syntax trees"
edition = "2021"
rust-version = "1.77.0"
exclude = [".github/", "bors.toml", "rustfmt.toml"]
[workspace]
members = ["xtask"]
[dependencies]
rustc-hash = "1.0.1"
hashbrown = { version = "0.14.3", features = [
"inline-more",
], default-features = false }
text-size = "1.1.0"
countme = "3.0.0"
serde = { version = "1.0.89", optional = true, default-features = false }
[dev-dependencies]
m_lexer = "0.0.4"
[features]
serde1 = ["serde", "text-size/serde"]

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,23 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,22 @@
# Rowan
[![Crates.io](https://img.shields.io/crates/v/rowan.svg)](https://crates.io/crates/rowan)
[![Crates.io](https://img.shields.io/crates/d/rowan.svg)](https://crates.io/crates/rowan)
Rowan is a library for lossless syntax trees, inspired in part by
Swift's [libsyntax](https://github.com/apple/swift/tree/5e2c815edfd758f9b1309ce07bfc01c4bc20ec23/lib/Syntax).
A conceptual overview is available in the [rust-analyzer repo](https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/syntax.md).
See `examples/s_expressions` for a tutorial, and [rust-analyzer](https://github.com/rust-analyzer/rust-analyzer/) for real-world usage.
## Testing
This crate is primarily tested by various integration tests in rust-analyzer.
## License
Rowan is primarily distributed under the terms of both the MIT
license and the Apache License (Version 2.0).
See LICENSE-APACHE and LICENSE-MIT for details.

View File

@@ -0,0 +1,152 @@
//! Example that takes the input
//! 1 + 2 * 3 + 4
//! and builds the tree
//! - Marker(Root)
//! - Marker(Operation)
//! - Marker(Operation)
//! - "1" Token(Number)
//! - "+" Token(Add)
//! - Marker(Operation)
//! - "2" Token(Number)
//! - "*" Token(Mul)
//! - "3" Token(Number)
//! - "+" Token(Add)
//! - "4" Token(Number)
use rowan::{GreenNodeBuilder, NodeOrToken};
use std::iter::Peekable;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(non_camel_case_types)]
#[repr(u16)]
enum SyntaxKind {
WHITESPACE = 0,
ADD,
SUB,
MUL,
DIV,
NUMBER,
ERROR,
OPERATION,
ROOT,
}
use SyntaxKind::*;
impl From<SyntaxKind> for rowan::SyntaxKind {
fn from(kind: SyntaxKind) -> Self {
Self(kind as u16)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum Lang {}
impl rowan::Language for Lang {
type Kind = SyntaxKind;
fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
assert!(raw.0 <= ROOT as u16);
unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
}
fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
kind.into()
}
}
type SyntaxNode = rowan::SyntaxNode<Lang>;
#[allow(unused)]
type SyntaxToken = rowan::SyntaxToken<Lang>;
#[allow(unused)]
type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
struct Parser<I: Iterator<Item = (SyntaxKind, String)>> {
builder: GreenNodeBuilder<'static>,
iter: Peekable<I>,
}
impl<I: Iterator<Item = (SyntaxKind, String)>> Parser<I> {
fn peek(&mut self) -> Option<SyntaxKind> {
while self.iter.peek().map(|&(t, _)| t == WHITESPACE).unwrap_or(false) {
self.bump();
}
self.iter.peek().map(|&(t, _)| t)
}
fn bump(&mut self) {
if let Some((token, string)) = self.iter.next() {
self.builder.token(token.into(), string.as_str());
}
}
fn parse_val(&mut self) {
match self.peek() {
Some(NUMBER) => self.bump(),
_ => {
self.builder.start_node(ERROR.into());
self.bump();
self.builder.finish_node();
}
}
}
fn handle_operation(&mut self, tokens: &[SyntaxKind], next: fn(&mut Self)) {
let checkpoint = self.builder.checkpoint();
next(self);
while self.peek().map(|t| tokens.contains(&t)).unwrap_or(false) {
self.builder.start_node_at(checkpoint, OPERATION.into());
self.bump();
next(self);
self.builder.finish_node();
}
}
fn parse_mul(&mut self) {
self.handle_operation(&[MUL, DIV], Self::parse_val)
}
fn parse_add(&mut self) {
self.handle_operation(&[ADD, SUB], Self::parse_mul)
}
fn parse(mut self) -> SyntaxNode {
self.builder.start_node(ROOT.into());
self.parse_add();
self.builder.finish_node();
SyntaxNode::new_root(self.builder.finish())
}
}
fn print(indent: usize, element: SyntaxElement) {
let kind: SyntaxKind = element.kind();
print!("{:indent$}", "", indent = indent);
match element {
NodeOrToken::Node(node) => {
println!("- {:?}", kind);
for child in node.children_with_tokens() {
print(indent + 2, child);
}
}
NodeOrToken::Token(token) => println!("- {:?} {:?}", token.text(), kind),
}
}
fn main() {
let ast = Parser {
builder: GreenNodeBuilder::new(),
iter: vec![
// 1 + 2 * 3 + 4
(NUMBER, "1".into()),
(WHITESPACE, " ".into()),
(ADD, "+".into()),
(WHITESPACE, " ".into()),
(NUMBER, "2".into()),
(WHITESPACE, " ".into()),
(MUL, "*".into()),
(WHITESPACE, " ".into()),
(NUMBER, "3".into()),
(WHITESPACE, " ".into()),
(ADD, "+".into()),
(WHITESPACE, " ".into()),
(NUMBER, "4".into()),
]
.into_iter()
.peekable(),
}
.parse();
print(0, ast.into());
}

View File

@@ -0,0 +1,425 @@
//! In this tutorial, we will write parser
//! and evaluator of arithmetic S-expressions,
//! which look like this:
//! ```
//! (+ (* 15 2) 62)
//! ```
//!
//! It's suggested to read the conceptual overview of the design
//! alongside this tutorial:
//! https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/syntax.md
/// Let's start with defining all kinds of tokens and
/// composite nodes.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(non_camel_case_types)]
#[repr(u16)]
enum SyntaxKind {
L_PAREN = 0, // '('
R_PAREN, // ')'
WORD, // '+', '15'
WHITESPACE, // whitespaces is explicit
ERROR, // as well as errors
// composite nodes
LIST, // `(+ 2 3)`
ATOM, // `+`, `15`, wraps a WORD token
ROOT, // top-level node: a list of s-expressions
}
use SyntaxKind::*;
/// Some boilerplate is needed, as rowan settled on using its own
/// `struct SyntaxKind(u16)` internally, instead of accepting the
/// user's `enum SyntaxKind` as a type parameter.
///
/// First, to easily pass the enum variants into rowan via `.into()`:
impl From<SyntaxKind> for rowan::SyntaxKind {
fn from(kind: SyntaxKind) -> Self {
Self(kind as u16)
}
}
/// Second, implementing the `Language` trait teaches rowan to convert between
/// these two SyntaxKind types, allowing for a nicer SyntaxNode API where
/// "kinds" are values from our `enum SyntaxKind`, instead of plain u16 values.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum Lang {}
impl rowan::Language for Lang {
type Kind = SyntaxKind;
fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
assert!(raw.0 <= ROOT as u16);
unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
}
fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
kind.into()
}
}
/// GreenNode is an immutable tree, which is cheap to change,
/// but doesn't contain offsets and parent pointers.
use rowan::GreenNode;
/// You can construct GreenNodes by hand, but a builder
/// is helpful for top-down parsers: it maintains a stack
/// of currently in-progress nodes
use rowan::GreenNodeBuilder;
/// The parse results are stored as a "green tree".
/// We'll discuss working with the results later
struct Parse {
green_node: GreenNode,
#[allow(unused)]
errors: Vec<String>,
}
/// Now, let's write a parser.
/// Note that `parse` does not return a `Result`:
/// by design, syntax tree can be built even for
/// completely invalid source code.
fn parse(text: &str) -> Parse {
struct Parser {
/// input tokens, including whitespace,
/// in *reverse* order.
tokens: Vec<(SyntaxKind, String)>,
/// the in-progress tree.
builder: GreenNodeBuilder<'static>,
/// the list of syntax errors we've accumulated
/// so far.
errors: Vec<String>,
}
/// The outcome of parsing a single S-expression
enum SexpRes {
/// An S-expression (i.e. an atom, or a list) was successfully parsed
Ok,
/// Nothing was parsed, as no significant tokens remained
Eof,
/// An unexpected ')' was found
RParen,
}
impl Parser {
fn parse(mut self) -> Parse {
// Make sure that the root node covers all source
self.builder.start_node(ROOT.into());
// Parse zero or more S-expressions
loop {
match self.sexp() {
SexpRes::Eof => break,
SexpRes::RParen => {
self.builder.start_node(ERROR.into());
self.errors.push("unmatched `)`".to_string());
self.bump(); // be sure to chug along in case of error
self.builder.finish_node();
}
SexpRes::Ok => (),
}
}
// Don't forget to eat *trailing* whitespace
self.skip_ws();
// Close the root node.
self.builder.finish_node();
// Turn the builder into a GreenNode
Parse { green_node: self.builder.finish(), errors: self.errors }
}
fn list(&mut self) {
assert_eq!(self.current(), Some(L_PAREN));
// Start the list node
self.builder.start_node(LIST.into());
self.bump(); // '('
loop {
match self.sexp() {
SexpRes::Eof => {
self.errors.push("expected `)`".to_string());
break;
}
SexpRes::RParen => {
self.bump();
break;
}
SexpRes::Ok => (),
}
}
// close the list node
self.builder.finish_node();
}
fn sexp(&mut self) -> SexpRes {
// Eat leading whitespace
self.skip_ws();
// Either a list, an atom, a closing paren,
// or an eof.
let t = match self.current() {
None => return SexpRes::Eof,
Some(R_PAREN) => return SexpRes::RParen,
Some(t) => t,
};
match t {
L_PAREN => self.list(),
WORD => {
self.builder.start_node(ATOM.into());
self.bump();
self.builder.finish_node();
}
ERROR => self.bump(),
_ => unreachable!(),
}
SexpRes::Ok
}
/// Advance one token, adding it to the current branch of the tree builder.
fn bump(&mut self) {
let (kind, text) = self.tokens.pop().unwrap();
self.builder.token(kind.into(), text.as_str());
}
/// Peek at the first unprocessed token
fn current(&self) -> Option<SyntaxKind> {
self.tokens.last().map(|(kind, _)| *kind)
}
fn skip_ws(&mut self) {
while self.current() == Some(WHITESPACE) {
self.bump()
}
}
}
let mut tokens = lex(text);
tokens.reverse();
Parser { tokens, builder: GreenNodeBuilder::new(), errors: Vec::new() }.parse()
}
/// To work with the parse results we need a view into the
/// green tree - the Syntax tree.
/// It is also immutable, like a GreenNode,
/// but it contains parent pointers, offsets, and
/// has identity semantics.
type SyntaxNode = rowan::SyntaxNode<Lang>;
#[allow(unused)]
type SyntaxToken = rowan::SyntaxToken<Lang>;
#[allow(unused)]
type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
impl Parse {
fn syntax(&self) -> SyntaxNode {
SyntaxNode::new_root(self.green_node.clone())
}
}
/// Let's check that the parser works as expected
#[test]
fn test_parser() {
let text = "(+ (* 15 2) 62)";
let node = parse(text).syntax();
assert_eq!(
format!("{:?}", node),
"ROOT@0..15", // root node, spanning 15 bytes
);
assert_eq!(node.children().count(), 1);
let list = node.children().next().unwrap();
let children = list
.children_with_tokens()
.map(|child| format!("{:?}@{:?}", child.kind(), child.text_range()))
.collect::<Vec<_>>();
assert_eq!(
children,
vec![
"L_PAREN@0..1".to_string(),
"ATOM@1..2".to_string(),
"WHITESPACE@2..3".to_string(), // note, explicit whitespace!
"LIST@3..11".to_string(),
"WHITESPACE@11..12".to_string(),
"ATOM@12..14".to_string(),
"R_PAREN@14..15".to_string(),
]
);
}
/// So far, we've been working with a homogeneous untyped tree.
/// It's nice to provide generic tree operations, like traversals,
/// but it's a bad fit for semantic analysis.
/// This crate itself does not provide AST facilities directly,
/// but it is possible to layer AST on top of `SyntaxNode` API.
/// Let's write a function to evaluate S-expression.
///
/// For that, let's define AST nodes.
/// It'll be quite a bunch of repetitive code, so we'll use a macro.
///
/// For a real language, you'd want to generate an AST. I find a
/// combination of `serde`, `ron` and `tera` crates invaluable for that!
macro_rules! ast_node {
($ast:ident, $kind:ident) => {
#[derive(PartialEq, Eq, Hash)]
#[repr(transparent)]
struct $ast(SyntaxNode);
impl $ast {
#[allow(unused)]
fn cast(node: SyntaxNode) -> Option<Self> {
if node.kind() == $kind {
Some(Self(node))
} else {
None
}
}
}
};
}
ast_node!(Root, ROOT);
ast_node!(Atom, ATOM);
ast_node!(List, LIST);
// Sexp is slightly different, so let's do it by hand.
#[derive(PartialEq, Eq, Hash)]
#[repr(transparent)]
struct Sexp(SyntaxNode);
enum SexpKind {
Atom(Atom),
List(List),
}
impl Sexp {
fn cast(node: SyntaxNode) -> Option<Self> {
if Atom::cast(node.clone()).is_some() || List::cast(node.clone()).is_some() {
Some(Sexp(node))
} else {
None
}
}
fn kind(&self) -> SexpKind {
Atom::cast(self.0.clone())
.map(SexpKind::Atom)
.or_else(|| List::cast(self.0.clone()).map(SexpKind::List))
.unwrap()
}
}
// Let's enhance AST nodes with ancillary functions and
// eval.
impl Root {
fn sexps(&self) -> impl Iterator<Item = Sexp> + '_ {
self.0.children().filter_map(Sexp::cast)
}
}
enum Op {
Add,
Sub,
Div,
Mul,
}
impl Atom {
fn eval(&self) -> Option<i64> {
self.text().parse().ok()
}
fn as_op(&self) -> Option<Op> {
let op = match self.text().as_str() {
"+" => Op::Add,
"-" => Op::Sub,
"*" => Op::Mul,
"/" => Op::Div,
_ => return None,
};
Some(op)
}
fn text(&self) -> String {
match self.0.green().children().next() {
Some(rowan::NodeOrToken::Token(token)) => token.text().to_string(),
_ => unreachable!(),
}
}
}
impl List {
fn sexps(&self) -> impl Iterator<Item = Sexp> + '_ {
self.0.children().filter_map(Sexp::cast)
}
fn eval(&self) -> Option<i64> {
let op = match self.sexps().nth(0)?.kind() {
SexpKind::Atom(atom) => atom.as_op()?,
_ => return None,
};
let arg1 = self.sexps().nth(1)?.eval()?;
let arg2 = self.sexps().nth(2)?.eval()?;
let res = match op {
Op::Add => arg1 + arg2,
Op::Sub => arg1 - arg2,
Op::Mul => arg1 * arg2,
Op::Div if arg2 == 0 => return None,
Op::Div => arg1 / arg2,
};
Some(res)
}
}
impl Sexp {
fn eval(&self) -> Option<i64> {
match self.kind() {
SexpKind::Atom(atom) => atom.eval(),
SexpKind::List(list) => list.eval(),
}
}
}
impl Parse {
fn root(&self) -> Root {
Root::cast(self.syntax()).unwrap()
}
}
/// Let's test the eval!
fn main() {
let sexps = "
92
(+ 62 30)
(/ 92 0)
nan
(+ (* 15 2) 62)
";
let root = parse(sexps).root();
let res = root.sexps().map(|it| it.eval()).collect::<Vec<_>>();
eprintln!("{:?}", res);
assert_eq!(res, vec![Some(92), Some(92), None, None, Some(92),])
}
/// Split the input string into a flat list of tokens
/// (such as L_PAREN, WORD, and WHITESPACE)
fn lex(text: &str) -> Vec<(SyntaxKind, String)> {
fn tok(t: SyntaxKind) -> m_lexer::TokenKind {
m_lexer::TokenKind(rowan::SyntaxKind::from(t).0)
}
fn kind(t: m_lexer::TokenKind) -> SyntaxKind {
match t.0 {
0 => L_PAREN,
1 => R_PAREN,
2 => WORD,
3 => WHITESPACE,
4 => ERROR,
_ => unreachable!(),
}
}
let lexer = m_lexer::LexerBuilder::new()
.error_token(tok(ERROR))
.tokens(&[
(tok(L_PAREN), r"\("),
(tok(R_PAREN), r"\)"),
(tok(WORD), r"[^\s()]+"),
(tok(WHITESPACE), r"\s+"),
])
.build();
lexer
.tokenize(text)
.into_iter()
.map(|t| (t.len, kind(t.kind)))
.scan(0usize, |start_offset, (len, kind)| {
let s: String = text[*start_offset..*start_offset + len].into();
*start_offset += len;
Some((kind, s))
})
.collect()
}

View File

@@ -0,0 +1,554 @@
use std::{borrow::Cow, fmt, iter, marker::PhantomData, ops::Range};
use crate::{
cursor, green::GreenTokenData, Direction, GreenNode, GreenNodeData, GreenToken, NodeOrToken,
SyntaxKind, SyntaxText, TextRange, TextSize, TokenAtOffset, WalkEvent,
};
pub trait Language: Sized + Copy + fmt::Debug + Eq + Ord + std::hash::Hash {
type Kind: Sized + Copy + fmt::Debug + Eq + Ord + std::hash::Hash;
fn kind_from_raw(raw: SyntaxKind) -> Self::Kind;
fn kind_to_raw(kind: Self::Kind) -> SyntaxKind;
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct SyntaxNode<L: Language> {
raw: cursor::SyntaxNode,
_p: PhantomData<L>,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct SyntaxToken<L: Language> {
raw: cursor::SyntaxToken,
_p: PhantomData<L>,
}
pub type SyntaxElement<L> = NodeOrToken<SyntaxNode<L>, SyntaxToken<L>>;
impl<L: Language> fmt::Debug for SyntaxNode<L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
let mut level = 0;
for event in self.preorder_with_tokens() {
match event {
WalkEvent::Enter(element) => {
for _ in 0..level {
write!(f, " ")?;
}
match element {
NodeOrToken::Node(node) => writeln!(f, "{:?}", node)?,
NodeOrToken::Token(token) => writeln!(f, "{:?}", token)?,
}
level += 1;
}
WalkEvent::Leave(_) => level -= 1,
}
}
assert_eq!(level, 0);
Ok(())
} else {
write!(f, "{:?}@{:?}", self.kind(), self.text_range())
}
}
}
impl<L: Language> fmt::Display for SyntaxNode<L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.raw, f)
}
}
impl<L: Language> fmt::Debug for SyntaxToken<L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}@{:?}", self.kind(), self.text_range())?;
if self.text().len() < 25 {
return write!(f, " {:?}", self.text());
}
let text = self.text();
for idx in 21..25 {
if text.is_char_boundary(idx) {
let text = format!("{} ...", &text[..idx]);
return write!(f, " {:?}", text);
}
}
unreachable!()
}
}
impl<L: Language> fmt::Display for SyntaxToken<L> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.raw, f)
}
}
impl<L: Language> From<SyntaxNode<L>> for SyntaxElement<L> {
fn from(node: SyntaxNode<L>) -> SyntaxElement<L> {
NodeOrToken::Node(node)
}
}
impl<L: Language> From<SyntaxToken<L>> for SyntaxElement<L> {
fn from(token: SyntaxToken<L>) -> SyntaxElement<L> {
NodeOrToken::Token(token)
}
}
impl<L: Language> SyntaxNode<L> {
pub fn new_root(green: GreenNode) -> SyntaxNode<L> {
SyntaxNode::from(cursor::SyntaxNode::new_root(green))
}
pub fn new_root_mut(green: GreenNode) -> SyntaxNode<L> {
SyntaxNode::from(cursor::SyntaxNode::new_root_mut(green))
}
/// Returns a green tree, equal to the green tree this node
/// belongs to, except with this node substituted. The complexity
/// of the operation is proportional to the depth of the tree.
pub fn replace_with(&self, replacement: GreenNode) -> GreenNode {
self.raw.replace_with(replacement)
}
pub fn kind(&self) -> L::Kind {
L::kind_from_raw(self.raw.kind())
}
pub fn text_range(&self) -> TextRange {
self.raw.text_range()
}
pub fn index(&self) -> usize {
self.raw.index()
}
pub fn text(&self) -> SyntaxText {
self.raw.text()
}
pub fn green(&self) -> Cow<'_, GreenNodeData> {
self.raw.green()
}
pub fn parent(&self) -> Option<SyntaxNode<L>> {
self.raw.parent().map(Self::from)
}
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> {
self.raw.ancestors().map(SyntaxNode::from)
}
pub fn children(&self) -> SyntaxNodeChildren<L> {
SyntaxNodeChildren { raw: self.raw.children(), _p: PhantomData }
}
pub fn children_with_tokens(&self) -> SyntaxElementChildren<L> {
SyntaxElementChildren { raw: self.raw.children_with_tokens(), _p: PhantomData }
}
pub fn first_child(&self) -> Option<SyntaxNode<L>> {
self.raw.first_child().map(Self::from)
}
pub fn first_child_by_kind(&self, matcher: &impl Fn(L::Kind) -> bool) -> Option<SyntaxNode<L>> {
self.raw
.first_child_by_kind(&|raw_kind| matcher(L::kind_from_raw(raw_kind)))
.map(Self::from)
}
pub fn last_child(&self) -> Option<SyntaxNode<L>> {
self.raw.last_child().map(Self::from)
}
pub fn first_child_or_token(&self) -> Option<SyntaxElement<L>> {
self.raw.first_child_or_token().map(NodeOrToken::from)
}
pub fn first_child_or_token_by_kind(
&self,
matcher: &impl Fn(L::Kind) -> bool,
) -> Option<SyntaxElement<L>> {
self.raw
.first_child_or_token_by_kind(&|raw_kind| matcher(L::kind_from_raw(raw_kind)))
.map(NodeOrToken::from)
}
pub fn last_child_or_token(&self) -> Option<SyntaxElement<L>> {
self.raw.last_child_or_token().map(NodeOrToken::from)
}
pub fn next_sibling(&self) -> Option<SyntaxNode<L>> {
self.raw.next_sibling().map(Self::from)
}
pub fn next_sibling_by_kind(
&self,
matcher: &impl Fn(L::Kind) -> bool,
) -> Option<SyntaxNode<L>> {
self.raw
.next_sibling_by_kind(&|raw_kind| matcher(L::kind_from_raw(raw_kind)))
.map(Self::from)
}
pub fn prev_sibling(&self) -> Option<SyntaxNode<L>> {
self.raw.prev_sibling().map(Self::from)
}
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement<L>> {
self.raw.next_sibling_or_token().map(NodeOrToken::from)
}
pub fn next_sibling_or_token_by_kind(
&self,
matcher: &impl Fn(L::Kind) -> bool,
) -> Option<SyntaxElement<L>> {
self.raw
.next_sibling_or_token_by_kind(&|raw_kind| matcher(L::kind_from_raw(raw_kind)))
.map(NodeOrToken::from)
}
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement<L>> {
self.raw.prev_sibling_or_token().map(NodeOrToken::from)
}
/// Return the leftmost token in the subtree of this node.
pub fn first_token(&self) -> Option<SyntaxToken<L>> {
self.raw.first_token().map(SyntaxToken::from)
}
/// Return the rightmost token in the subtree of this node.
pub fn last_token(&self) -> Option<SyntaxToken<L>> {
self.raw.last_token().map(SyntaxToken::from)
}
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = SyntaxNode<L>> {
self.raw.siblings(direction).map(SyntaxNode::from)
}
pub fn siblings_with_tokens(
&self,
direction: Direction,
) -> impl Iterator<Item = SyntaxElement<L>> {
self.raw.siblings_with_tokens(direction).map(SyntaxElement::from)
}
pub fn descendants(&self) -> impl Iterator<Item = SyntaxNode<L>> {
self.raw.descendants().map(SyntaxNode::from)
}
pub fn descendants_with_tokens(&self) -> impl Iterator<Item = SyntaxElement<L>> {
self.raw.descendants_with_tokens().map(NodeOrToken::from)
}
/// Traverse the subtree rooted at the current node (including the current
/// node) in preorder, excluding tokens.
pub fn preorder(&self) -> Preorder<L> {
Preorder { raw: self.raw.preorder(), _p: PhantomData }
}
/// Traverse the subtree rooted at the current node (including the current
/// node) in preorder, including tokens.
pub fn preorder_with_tokens(&self) -> PreorderWithTokens<L> {
PreorderWithTokens { raw: self.raw.preorder_with_tokens(), _p: PhantomData }
}
/// Find a token in the subtree corresponding to this node, which covers the offset.
/// Precondition: offset must be within node's range.
pub fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken<L>> {
self.raw.token_at_offset(offset).map(SyntaxToken::from)
}
/// Return the deepest node or token in the current subtree that fully
/// contains the range. If the range is empty and is contained in two leaf
/// nodes, either one can be returned. Precondition: range must be contained
/// within the current node
pub fn covering_element(&self, range: TextRange) -> SyntaxElement<L> {
NodeOrToken::from(self.raw.covering_element(range))
}
/// Finds a [`SyntaxElement`] which intersects with a given `range`. If
/// there are several intersecting elements, any one can be returned.
///
/// The method uses binary search internally, so it's complexity is
/// `O(log(N))` where `N = self.children_with_tokens().count()`.
pub fn child_or_token_at_range(&self, range: TextRange) -> Option<SyntaxElement<L>> {
self.raw.child_or_token_at_range(range).map(SyntaxElement::from)
}
/// Returns an independent copy of the subtree rooted at this node.
///
/// The parent of the returned node will be `None`, the start offset will be
/// zero, but, otherwise, it'll be equivalent to the source node.
pub fn clone_subtree(&self) -> SyntaxNode<L> {
SyntaxNode::from(self.raw.clone_subtree())
}
pub fn clone_for_update(&self) -> SyntaxNode<L> {
SyntaxNode::from(self.raw.clone_for_update())
}
pub fn is_mutable(&self) -> bool {
self.raw.is_mutable()
}
pub fn detach(&self) {
self.raw.detach()
}
pub fn splice_children<I: IntoIterator<Item = SyntaxElement<L>>>(
&self,
to_delete: Range<usize>,
to_insert: I,
) {
let to_insert = to_insert.into_iter().map(cursor::SyntaxElement::from);
self.raw.splice_children(to_delete, to_insert)
}
}
impl<L: Language> SyntaxToken<L> {
/// Returns a green tree, equal to the green tree this token
/// belongs to, except with this token substituted. The complexity
/// of the operation is proportional to the depth of the tree.
pub fn replace_with(&self, new_token: GreenToken) -> GreenNode {
self.raw.replace_with(new_token)
}
pub fn kind(&self) -> L::Kind {
L::kind_from_raw(self.raw.kind())
}
pub fn text_range(&self) -> TextRange {
self.raw.text_range()
}
pub fn index(&self) -> usize {
self.raw.index()
}
pub fn text(&self) -> &str {
self.raw.text()
}
pub fn green(&self) -> &GreenTokenData {
self.raw.green()
}
pub fn parent(&self) -> Option<SyntaxNode<L>> {
self.raw.parent().map(SyntaxNode::from)
}
/// Iterator over all the ancestors of this token excluding itself.
#[deprecated = "use `SyntaxToken::parent_ancestors` instead"]
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> {
self.parent_ancestors()
}
/// Iterator over all the ancestors of this token excluding itself.
pub fn parent_ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> {
self.raw.ancestors().map(SyntaxNode::from)
}
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement<L>> {
self.raw.next_sibling_or_token().map(NodeOrToken::from)
}
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement<L>> {
self.raw.prev_sibling_or_token().map(NodeOrToken::from)
}
pub fn siblings_with_tokens(
&self,
direction: Direction,
) -> impl Iterator<Item = SyntaxElement<L>> {
self.raw.siblings_with_tokens(direction).map(SyntaxElement::from)
}
/// Next token in the tree (i.e, not necessary a sibling).
pub fn next_token(&self) -> Option<SyntaxToken<L>> {
self.raw.next_token().map(SyntaxToken::from)
}
/// Previous token in the tree (i.e, not necessary a sibling).
pub fn prev_token(&self) -> Option<SyntaxToken<L>> {
self.raw.prev_token().map(SyntaxToken::from)
}
pub fn detach(&self) {
self.raw.detach()
}
}
impl<L: Language> SyntaxElement<L> {
pub fn text_range(&self) -> TextRange {
match self {
NodeOrToken::Node(it) => it.text_range(),
NodeOrToken::Token(it) => it.text_range(),
}
}
pub fn index(&self) -> usize {
match self {
NodeOrToken::Node(it) => it.index(),
NodeOrToken::Token(it) => it.index(),
}
}
pub fn kind(&self) -> L::Kind {
match self {
NodeOrToken::Node(it) => it.kind(),
NodeOrToken::Token(it) => it.kind(),
}
}
pub fn parent(&self) -> Option<SyntaxNode<L>> {
match self {
NodeOrToken::Node(it) => it.parent(),
NodeOrToken::Token(it) => it.parent(),
}
}
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode<L>> {
let first = match self {
NodeOrToken::Node(it) => Some(it.clone()),
NodeOrToken::Token(it) => it.parent(),
};
iter::successors(first, SyntaxNode::parent)
}
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement<L>> {
match self {
NodeOrToken::Node(it) => it.next_sibling_or_token(),
NodeOrToken::Token(it) => it.next_sibling_or_token(),
}
}
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement<L>> {
match self {
NodeOrToken::Node(it) => it.prev_sibling_or_token(),
NodeOrToken::Token(it) => it.prev_sibling_or_token(),
}
}
pub fn detach(&self) {
match self {
NodeOrToken::Node(it) => it.detach(),
NodeOrToken::Token(it) => it.detach(),
}
}
}
#[derive(Debug, Clone)]
pub struct SyntaxNodeChildren<L: Language> {
raw: cursor::SyntaxNodeChildren,
_p: PhantomData<L>,
}
impl<L: Language> Iterator for SyntaxNodeChildren<L> {
type Item = SyntaxNode<L>;
fn next(&mut self) -> Option<Self::Item> {
self.raw.next().map(SyntaxNode::from)
}
}
impl<L: Language> SyntaxNodeChildren<L> {
pub fn by_kind(self, matcher: impl Fn(L::Kind) -> bool) -> impl Iterator<Item = SyntaxNode<L>> {
self.raw
.by_kind(move |raw_kind| matcher(L::kind_from_raw(raw_kind)))
.into_iter()
.map(SyntaxNode::from)
}
}
#[derive(Debug, Clone)]
pub struct SyntaxElementChildren<L: Language> {
raw: cursor::SyntaxElementChildren,
_p: PhantomData<L>,
}
impl<L: Language> Iterator for SyntaxElementChildren<L> {
type Item = SyntaxElement<L>;
fn next(&mut self) -> Option<Self::Item> {
self.raw.next().map(NodeOrToken::from)
}
}
impl<L: Language> SyntaxElementChildren<L> {
pub fn by_kind(
self,
matcher: impl Fn(L::Kind) -> bool,
) -> impl Iterator<Item = SyntaxElement<L>> {
self.raw.by_kind(move |raw_kind| matcher(L::kind_from_raw(raw_kind))).map(NodeOrToken::from)
}
}
pub struct Preorder<L: Language> {
raw: cursor::Preorder,
_p: PhantomData<L>,
}
impl<L: Language> Preorder<L> {
pub fn skip_subtree(&mut self) {
self.raw.skip_subtree()
}
}
impl<L: Language> Iterator for Preorder<L> {
type Item = WalkEvent<SyntaxNode<L>>;
fn next(&mut self) -> Option<Self::Item> {
self.raw.next().map(|it| it.map(SyntaxNode::from))
}
}
pub struct PreorderWithTokens<L: Language> {
raw: cursor::PreorderWithTokens,
_p: PhantomData<L>,
}
impl<L: Language> PreorderWithTokens<L> {
pub fn skip_subtree(&mut self) {
self.raw.skip_subtree()
}
}
impl<L: Language> Iterator for PreorderWithTokens<L> {
type Item = WalkEvent<SyntaxElement<L>>;
fn next(&mut self) -> Option<Self::Item> {
self.raw.next().map(|it| it.map(SyntaxElement::from))
}
}
impl<L: Language> From<cursor::SyntaxNode> for SyntaxNode<L> {
fn from(raw: cursor::SyntaxNode) -> SyntaxNode<L> {
SyntaxNode { raw, _p: PhantomData }
}
}
impl<L: Language> From<SyntaxNode<L>> for cursor::SyntaxNode {
fn from(node: SyntaxNode<L>) -> cursor::SyntaxNode {
node.raw
}
}
impl<L: Language> From<cursor::SyntaxToken> for SyntaxToken<L> {
fn from(raw: cursor::SyntaxToken) -> SyntaxToken<L> {
SyntaxToken { raw, _p: PhantomData }
}
}
impl<L: Language> From<SyntaxToken<L>> for cursor::SyntaxToken {
fn from(token: SyntaxToken<L>) -> cursor::SyntaxToken {
token.raw
}
}
impl<L: Language> From<cursor::SyntaxElement> for SyntaxElement<L> {
fn from(raw: cursor::SyntaxElement) -> SyntaxElement<L> {
match raw {
NodeOrToken::Node(it) => NodeOrToken::Node(it.into()),
NodeOrToken::Token(it) => NodeOrToken::Token(it.into()),
}
}
}
impl<L: Language> From<SyntaxElement<L>> for cursor::SyntaxElement {
fn from(element: SyntaxElement<L>) -> cursor::SyntaxElement {
match element {
NodeOrToken::Node(it) => NodeOrToken::Node(it.into()),
NodeOrToken::Token(it) => NodeOrToken::Token(it.into()),
}
}
}

View File

@@ -0,0 +1,458 @@
//! Vendored and stripped down version of triomphe
use std::{
alloc::{self, Layout},
cmp::Ordering,
hash::{Hash, Hasher},
marker::PhantomData,
mem::{self, offset_of, ManuallyDrop},
ops::Deref,
ptr,
sync::atomic::{
self,
Ordering::{Acquire, Relaxed, Release},
},
};
/// A soft limit on the amount of references that may be made to an `Arc`.
///
/// Going above this limit will abort your program (although not
/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references.
const MAX_REFCOUNT: usize = (isize::MAX) as usize;
/// The object allocated by an Arc<T>
#[repr(C)]
pub(crate) struct ArcInner<T: ?Sized> {
pub(crate) count: atomic::AtomicUsize,
pub(crate) data: T,
}
unsafe impl<T: ?Sized + Sync + Send> Send for ArcInner<T> {}
unsafe impl<T: ?Sized + Sync + Send> Sync for ArcInner<T> {}
/// An atomically reference counted shared pointer
///
/// See the documentation for [`Arc`] in the standard library. Unlike the
/// standard library `Arc`, this `Arc` does not support weak reference counting.
///
/// [`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html
#[repr(transparent)]
pub(crate) struct Arc<T: ?Sized> {
pub(crate) p: ptr::NonNull<ArcInner<T>>,
pub(crate) phantom: PhantomData<T>,
}
unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
impl<T> Arc<T> {
/// Reconstruct the Arc<T> from a raw pointer obtained from into_raw()
///
/// Note: This raw pointer will be offset in the allocation and must be preceded
/// by the atomic count.
///
/// It is recommended to use OffsetArc for this
#[inline]
pub(crate) unsafe fn from_raw(ptr: *const T) -> Self {
// To find the corresponding pointer to the `ArcInner` we need
// to subtract the offset of the `data` field from the pointer.
let ptr = (ptr as *const u8).sub(offset_of!(ArcInner<T>, data));
Arc { p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner<T>), phantom: PhantomData }
}
}
impl<T: ?Sized> Arc<T> {
#[inline]
fn inner(&self) -> &ArcInner<T> {
// This unsafety is ok because while this arc is alive we're guaranteed
// that the inner pointer is valid. Furthermore, we know that the
// `ArcInner` structure itself is `Sync` because the inner data is
// `Sync` as well, so we're ok loaning out an immutable pointer to these
// contents.
unsafe { &*self.ptr() }
}
// Non-inlined part of `drop`. Just invokes the destructor.
#[inline(never)]
unsafe fn drop_slow(&mut self) {
let _ = Box::from_raw(self.ptr());
}
/// Test pointer equality between the two Arcs, i.e. they must be the _same_
/// allocation
#[inline]
pub(crate) fn ptr_eq(this: &Self, other: &Self) -> bool {
std::ptr::addr_eq(this.ptr(), other.ptr())
}
pub(crate) fn ptr(&self) -> *mut ArcInner<T> {
self.p.as_ptr()
}
}
impl<T: ?Sized> Clone for Arc<T> {
#[inline]
fn clone(&self) -> Self {
// Using a relaxed ordering is alright here, as knowledge of the
// original reference prevents other threads from erroneously deleting
// the object.
//
// As explained in the [Boost documentation][1], Increasing the
// reference counter can always be done with memory_order_relaxed: New
// references to an object can only be formed from an existing
// reference, and passing an existing reference from one thread to
// another must already provide any required synchronization.
//
// [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)
let old_size = self.inner().count.fetch_add(1, Relaxed);
// However we need to guard against massive refcounts in case someone
// is `mem::forget`ing Arcs. If we don't do this the count can overflow
// and users will use-after free. We racily saturate to `isize::MAX` on
// the assumption that there aren't ~2 billion threads incrementing
// the reference count at once. This branch will never be taken in
// any realistic program.
//
// We abort because such a program is incredibly degenerate, and we
// don't care to support it.
if old_size > MAX_REFCOUNT {
std::process::abort();
}
unsafe { Arc { p: ptr::NonNull::new_unchecked(self.ptr()), phantom: PhantomData } }
}
}
impl<T: ?Sized> Deref for Arc<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.inner().data
}
}
impl<T: ?Sized> Arc<T> {
/// Provides mutable access to the contents _if_ the `Arc` is uniquely owned.
#[inline]
pub(crate) fn get_mut(this: &mut Self) -> Option<&mut T> {
if this.is_unique() {
unsafe {
// See make_mut() for documentation of the threadsafety here.
Some(&mut (*this.ptr()).data)
}
} else {
None
}
}
/// Whether or not the `Arc` is uniquely owned (is the refcount 1?).
pub(crate) fn is_unique(&self) -> bool {
// See the extensive discussion in [1] for why this needs to be Acquire.
//
// [1] https://github.com/servo/servo/issues/21186
self.inner().count.load(Acquire) == 1
}
}
impl<T: ?Sized> Drop for Arc<T> {
#[inline]
fn drop(&mut self) {
// Because `fetch_sub` is already atomic, we do not need to synchronize
// with other threads unless we are going to delete the object.
if self.inner().count.fetch_sub(1, Release) != 1 {
return;
}
// FIXME(bholley): Use the updated comment when [2] is merged.
//
// This load is needed to prevent reordering of use of the data and
// deletion of the data. Because it is marked `Release`, the decreasing
// of the reference count synchronizes with this `Acquire` load. This
// means that use of the data happens before decreasing the reference
// count, which happens before this load, which happens before the
// deletion of the data.
//
// As explained in the [Boost documentation][1],
//
// > It is important to enforce any possible access to the object in one
// > thread (through an existing reference) to *happen before* deleting
// > the object in a different thread. This is achieved by a "release"
// > operation after dropping a reference (any access to the object
// > through this reference must obviously happened before), and an
// > "acquire" operation before deleting the object.
//
// [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)
// [2]: https://github.com/rust-lang/rust/pull/41714
self.inner().count.load(Acquire);
unsafe {
self.drop_slow();
}
}
}
impl<T: ?Sized + PartialEq> PartialEq for Arc<T> {
fn eq(&self, other: &Arc<T>) -> bool {
Self::ptr_eq(self, other) || *(*self) == *(*other)
}
fn ne(&self, other: &Arc<T>) -> bool {
!Self::ptr_eq(self, other) && *(*self) != *(*other)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd for Arc<T> {
fn partial_cmp(&self, other: &Arc<T>) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
fn lt(&self, other: &Arc<T>) -> bool {
*(*self) < *(*other)
}
fn le(&self, other: &Arc<T>) -> bool {
*(*self) <= *(*other)
}
fn gt(&self, other: &Arc<T>) -> bool {
*(*self) > *(*other)
}
fn ge(&self, other: &Arc<T>) -> bool {
*(*self) >= *(*other)
}
}
impl<T: ?Sized + Ord> Ord for Arc<T> {
fn cmp(&self, other: &Arc<T>) -> Ordering {
(**self).cmp(&**other)
}
}
impl<T: ?Sized + Eq> Eq for Arc<T> {}
impl<T: ?Sized + Hash> Hash for Arc<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
#[derive(Debug, Eq, PartialEq, Hash, PartialOrd)]
#[repr(C)]
pub(crate) struct HeaderSlice<H, T: ?Sized> {
pub(crate) header: H,
length: usize,
slice: T,
}
impl<H, T> HeaderSlice<H, [T]> {
pub(crate) fn slice(&self) -> &[T] {
&self.slice
}
}
impl<H, T> Deref for HeaderSlice<H, [T; 0]> {
type Target = HeaderSlice<H, [T]>;
fn deref(&self) -> &Self::Target {
let len = self.length;
let fake_slice: *const [T] = ptr::slice_from_raw_parts(self as *const _ as *const T, len);
unsafe { &*(fake_slice as *const HeaderSlice<H, [T]>) }
}
}
/// A "thin" `Arc` containing dynamically sized data
///
/// This is functionally equivalent to `Arc<(H, [T])>`
///
/// When you create an `Arc` containing a dynamically sized type
/// like `HeaderSlice<H, [T]>`, the `Arc` is represented on the stack
/// as a "fat pointer", where the length of the slice is stored
/// alongside the `Arc`'s pointer. In some situations you may wish to
/// have a thin pointer instead, perhaps for FFI compatibility
/// or space efficiency.
///
/// Note that we use `[T; 0]` in order to have the right alignment for `T`.
///
/// `ThinArc` solves this by storing the length in the allocation itself,
/// via `HeaderSlice`.
#[repr(transparent)]
pub(crate) struct ThinArc<H, T> {
ptr: ptr::NonNull<ArcInner<HeaderSlice<H, [T; 0]>>>,
phantom: PhantomData<(H, T)>,
}
unsafe impl<H: Sync + Send, T: Sync + Send> Send for ThinArc<H, T> {}
unsafe impl<H: Sync + Send, T: Sync + Send> Sync for ThinArc<H, T> {}
// Synthesize a fat pointer from a thin pointer.
fn thin_to_thick<H, T>(
thin: *mut ArcInner<HeaderSlice<H, [T; 0]>>,
) -> *mut ArcInner<HeaderSlice<H, [T]>> {
let len = unsafe { (*thin).data.length };
let fake_slice: *mut [T] = ptr::slice_from_raw_parts_mut(thin as *mut T, len);
// Transplants metadata.
fake_slice as *mut ArcInner<HeaderSlice<H, [T]>>
}
impl<H, T> ThinArc<H, T> {
/// Temporarily converts |self| into a bonafide Arc and exposes it to the
/// provided callback. The refcount is not modified.
#[inline]
pub(crate) fn with_arc<F, U>(&self, f: F) -> U
where
F: FnOnce(&Arc<HeaderSlice<H, [T]>>) -> U,
{
// Synthesize transient Arc, which never touches the refcount of the ArcInner.
let transient = unsafe {
ManuallyDrop::new(Arc {
p: ptr::NonNull::new_unchecked(thin_to_thick(self.ptr.as_ptr())),
phantom: PhantomData,
})
};
// Expose the transient Arc to the callback, which may clone it if it wants.
let result = f(&transient);
// Forward the result.
result
}
/// Creates a `ThinArc` for a HeaderSlice using the given header struct and
/// iterator to generate the slice.
pub(crate) fn from_header_and_iter<I>(header: H, mut items: I) -> Self
where
I: Iterator<Item = T> + ExactSizeIterator,
{
assert_ne!(mem::size_of::<T>(), 0, "Need to think about ZST");
let num_items = items.len();
// Offset of the start of the slice in the allocation.
let inner_to_data_offset = offset_of!(ArcInner<HeaderSlice<H, [T; 0]>>, data);
let data_to_slice_offset = offset_of!(HeaderSlice<H, [T; 0]>, slice);
let slice_offset = inner_to_data_offset + data_to_slice_offset;
// Compute the size of the real payload.
let slice_size = mem::size_of::<T>().checked_mul(num_items).expect("size overflows");
let usable_size = slice_offset.checked_add(slice_size).expect("size overflows");
// Round up size to alignment.
let align = mem::align_of::<ArcInner<HeaderSlice<H, [T; 0]>>>();
let size = usable_size.wrapping_add(align - 1) & !(align - 1);
assert!(size >= usable_size, "size overflows");
let layout = Layout::from_size_align(size, align).expect("invalid layout");
let ptr: *mut ArcInner<HeaderSlice<H, [T; 0]>>;
unsafe {
let buffer = alloc::alloc(layout);
if buffer.is_null() {
alloc::handle_alloc_error(layout);
}
// // Synthesize the fat pointer. We do this by claiming we have a direct
// // pointer to a [T], and then changing the type of the borrow. The key
// // point here is that the length portion of the fat pointer applies
// // only to the number of elements in the dynamically-sized portion of
// // the type, so the value will be the same whether it points to a [T]
// // or something else with a [T] as its last member.
// let fake_slice: &mut [T] = slice::from_raw_parts_mut(buffer as *mut T, num_items);
// ptr = fake_slice as *mut [T] as *mut ArcInner<HeaderSlice<H, [T]>>;
ptr = buffer as *mut _;
let count = atomic::AtomicUsize::new(1);
// Write the data.
//
// Note that any panics here (i.e. from the iterator) are safe, since
// we'll just leak the uninitialized memory.
ptr::write(ptr::addr_of_mut!((*ptr).count), count);
ptr::write(ptr::addr_of_mut!((*ptr).data.header), header);
ptr::write(ptr::addr_of_mut!((*ptr).data.length), num_items);
if num_items != 0 {
let mut current = ptr::addr_of_mut!((*ptr).data.slice) as *mut T;
debug_assert_eq!(current as usize - buffer as usize, slice_offset);
for _ in 0..num_items {
ptr::write(
current,
items.next().expect("ExactSizeIterator over-reported length"),
);
current = current.offset(1);
}
assert!(items.next().is_none(), "ExactSizeIterator under-reported length");
// We should have consumed the buffer exactly.
debug_assert_eq!(current as *mut u8, buffer.add(usable_size));
}
assert!(items.next().is_none(), "ExactSizeIterator under-reported length");
}
ThinArc { ptr: unsafe { ptr::NonNull::new_unchecked(ptr) }, phantom: PhantomData }
}
}
impl<H, T> Deref for ThinArc<H, T> {
type Target = HeaderSlice<H, [T]>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &(*thin_to_thick(self.ptr.as_ptr())).data }
}
}
impl<H, T> Clone for ThinArc<H, T> {
#[inline]
fn clone(&self) -> Self {
ThinArc::with_arc(self, |a| Arc::into_thin(a.clone()))
}
}
impl<H, T> Drop for ThinArc<H, T> {
#[inline]
fn drop(&mut self) {
let _ = Arc::from_thin(ThinArc { ptr: self.ptr, phantom: PhantomData });
}
}
impl<H, T> Arc<HeaderSlice<H, [T]>> {
/// Converts an `Arc` into a `ThinArc`. This consumes the `Arc`, so the refcount
/// is not modified.
#[inline]
pub(crate) fn into_thin(a: Self) -> ThinArc<H, T> {
assert_eq!(a.length, a.slice.len(), "Length needs to be correct for ThinArc to work");
let fat_ptr: *mut ArcInner<HeaderSlice<H, [T]>> = a.ptr();
mem::forget(a);
let thin_ptr = fat_ptr as *mut [usize] as *mut usize;
ThinArc {
ptr: unsafe {
ptr::NonNull::new_unchecked(thin_ptr as *mut ArcInner<HeaderSlice<H, [T; 0]>>)
},
phantom: PhantomData,
}
}
/// Converts a `ThinArc` into an `Arc`. This consumes the `ThinArc`, so the refcount
/// is not modified.
#[inline]
pub(crate) fn from_thin(a: ThinArc<H, T>) -> Self {
let ptr = thin_to_thick(a.ptr.as_ptr());
mem::forget(a);
unsafe { Arc { p: ptr::NonNull::new_unchecked(ptr), phantom: PhantomData } }
}
}
impl<H: PartialEq, T: PartialEq> PartialEq for ThinArc<H, T> {
#[inline]
fn eq(&self, other: &ThinArc<H, T>) -> bool {
**self == **other
}
}
impl<H: Eq, T: Eq> Eq for ThinArc<H, T> {}
impl<H: Hash, T: Hash> Hash for ThinArc<H, T> {
fn hash<HSR: Hasher>(&self, state: &mut HSR) {
(**self).hash(state)
}
}

View File

@@ -0,0 +1,287 @@
//! Working with abstract syntax trees.
//!
//! In rowan, syntax trees are transient objects. That means that we create
//! trees when we need them, and tear them down to save memory. In this
//! architecture, hanging on to a particular syntax node for a long time is
//! ill-advisable, as that keeps the whole tree resident.
//!
//! Instead, we provide a [`SyntaxNodePtr`] type, which stores information about
//! the _location_ of a particular syntax node in a tree. It's a small type
//! which can be cheaply stored, and which can be resolved to a real
//! [`SyntaxNode`] when necessary.
//!
//! We also provide an [`AstNode`] trait for typed AST wrapper APIs over rowan
//! nodes.
use std::{
fmt,
hash::{Hash, Hasher},
iter::successors,
marker::PhantomData,
};
use crate::{Language, SyntaxNode, SyntaxNodeChildren, TextRange};
/// The main trait to go from untyped [`SyntaxNode`] to a typed AST. The
/// conversion itself has zero runtime cost: AST and syntax nodes have exactly
/// the same representation: a pointer to the tree root and a pointer to the
/// node itself.
pub trait AstNode {
type Language: Language;
fn can_cast(kind: <Self::Language as Language>::Kind) -> bool
where
Self: Sized;
fn cast(node: SyntaxNode<Self::Language>) -> Option<Self>
where
Self: Sized;
fn syntax(&self) -> &SyntaxNode<Self::Language>;
fn clone_for_update(&self) -> Self
where
Self: Sized,
{
Self::cast(self.syntax().clone_for_update()).unwrap()
}
fn clone_subtree(&self) -> Self
where
Self: Sized,
{
Self::cast(self.syntax().clone_subtree()).unwrap()
}
}
/// A "pointer" to a [`SyntaxNode`], via location in the source code.
///
/// ## Note
/// Since the location is source code dependent, this must not be used
/// with mutable syntax trees. Any changes made in such trees causes
/// the pointed node's source location to change, invalidating the pointer.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct SyntaxNodePtr<L: Language> {
kind: L::Kind,
range: TextRange,
}
impl<L: Language> SyntaxNodePtr<L> {
/// Returns a [`SyntaxNodePtr`] for the node.
///
/// Panics if the provided node is mutable
pub fn new(node: &SyntaxNode<L>) -> Self {
assert!(!node.is_mutable(), "tree is mutable");
Self { kind: node.kind(), range: node.text_range() }
}
/// Like [`Self::try_to_node`] but panics instead of returning `None` on
/// failure.
pub fn to_node(&self, root: &SyntaxNode<L>) -> SyntaxNode<L> {
self.try_to_node(root).unwrap_or_else(|| panic!("can't resolve {self:?} with {root:?}"))
}
/// "Dereferences" the pointer to get the [`SyntaxNode`] it points to.
///
/// Returns `None` if the node is not found, so make sure that the `root`
/// syntax tree is equivalent to (i.e. is build from the same text from) the
/// tree which was originally used to get this [`SyntaxNodePtr`].
///
/// Also returns `None` if `root` is not actually a root (i.e. it has a
/// parent).
///
/// NOTE: If this function is called on a mutable tree, it will panic
///
/// The complexity is linear in the depth of the tree and logarithmic in
/// tree width. As most trees are shallow, thinking about this as
/// `O(log(N))` in the size of the tree is not too wrong!
pub fn try_to_node(&self, root: &SyntaxNode<L>) -> Option<SyntaxNode<L>> {
assert!(!root.is_mutable(), "tree is mutable");
if root.parent().is_some() {
return None;
}
successors(Some(root.clone()), |node| node.child_or_token_at_range(self.range)?.into_node())
.find(|it| it.text_range() == self.range && it.kind() == self.kind)
}
/// Casts this to an [`AstPtr`] to the given node type if possible.
pub fn cast<N: AstNode<Language = L>>(self) -> Option<AstPtr<N>> {
if !N::can_cast(self.kind) {
return None;
}
Some(AstPtr { raw: self })
}
/// Returns the kind of the syntax node this points to.
pub fn kind(&self) -> L::Kind {
self.kind
}
/// Returns the range of the syntax node this points to.
pub fn text_range(&self) -> TextRange {
self.range
}
}
/// Like [`SyntaxNodePtr`], but remembers the type of node.
///
/// ## Note
/// As with [`SyntaxNodePtr`], this must not be used on mutable
/// syntax trees, since any mutation can cause the pointed node's
/// source location to change, invalidating the pointer
pub struct AstPtr<N: AstNode> {
raw: SyntaxNodePtr<N::Language>,
}
impl<N: AstNode> AstPtr<N> {
/// Returns an [`AstPtr`] for the node.
///
/// Panics if the provided node is mutable
pub fn new(node: &N) -> Self {
// The above mentioned panic is handled by SyntaxNodePtr
Self { raw: SyntaxNodePtr::new(node.syntax()) }
}
/// Like `Self::try_to_node` but panics on failure.
pub fn to_node(&self, root: &SyntaxNode<N::Language>) -> N {
self.try_to_node(root).unwrap_or_else(|| panic!("can't resolve {self:?} with {root:?}"))
}
/// Given the root node containing the node `n` that `self` is a pointer to,
/// returns `n` if possible. Panics if `root` is mutable. See [`SyntaxNodePtr::try_to_node`].
pub fn try_to_node(&self, root: &SyntaxNode<N::Language>) -> Option<N> {
// The above mentioned panic is handled by SyntaxNodePtr
N::cast(self.raw.try_to_node(root)?)
}
/// Returns the underlying [`SyntaxNodePtr`].
pub fn syntax_node_ptr(&self) -> SyntaxNodePtr<N::Language> {
self.raw.clone()
}
/// Casts this to an [`AstPtr`] to the given node type if possible.
pub fn cast<U: AstNode<Language = N::Language>>(self) -> Option<AstPtr<U>> {
if !U::can_cast(self.raw.kind) {
return None;
}
Some(AstPtr { raw: self.raw })
}
}
impl<N: AstNode> fmt::Debug for AstPtr<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AstPtr").field("raw", &self.raw).finish()
}
}
impl<N: AstNode> Clone for AstPtr<N> {
fn clone(&self) -> Self {
Self { raw: self.raw.clone() }
}
}
impl<N: AstNode> PartialEq for AstPtr<N> {
fn eq(&self, other: &AstPtr<N>) -> bool {
self.raw == other.raw
}
}
impl<N: AstNode> Eq for AstPtr<N> {}
impl<N: AstNode> Hash for AstPtr<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.raw.hash(state)
}
}
impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr<N::Language> {
fn from(ptr: AstPtr<N>) -> SyntaxNodePtr<N::Language> {
ptr.raw
}
}
#[derive(Debug, Clone)]
pub struct AstChildren<N: AstNode> {
inner: SyntaxNodeChildren<N::Language>,
ph: PhantomData<N>,
}
impl<N: AstNode> AstChildren<N> {
fn new(parent: &SyntaxNode<N::Language>) -> Self {
AstChildren { inner: parent.children(), ph: PhantomData }
}
}
impl<N: AstNode> Iterator for AstChildren<N> {
type Item = N;
fn next(&mut self) -> Option<N> {
self.inner.find_map(N::cast)
}
}
pub mod support {
use super::{AstChildren, AstNode};
use crate::{Language, SyntaxNode, SyntaxToken};
pub fn child<N: AstNode>(parent: &SyntaxNode<N::Language>) -> Option<N> {
parent.children().find_map(N::cast)
}
pub fn children<N: AstNode>(parent: &SyntaxNode<N::Language>) -> AstChildren<N> {
AstChildren::new(parent)
}
pub fn token<L: Language>(parent: &SyntaxNode<L>, kind: L::Kind) -> Option<SyntaxToken<L>> {
parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind)
}
}
#[cfg(test)]
mod tests {
use crate::{GreenNodeBuilder, Language, SyntaxKind, SyntaxNode};
use super::SyntaxNodePtr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct TestLanguage;
impl Language for TestLanguage {
type Kind = SyntaxKind;
fn kind_from_raw(raw: SyntaxKind) -> Self::Kind {
raw
}
fn kind_to_raw(kind: Self::Kind) -> SyntaxKind {
kind
}
}
fn build_immut_tree() -> SyntaxNode<TestLanguage> {
// Creates a single-node tree
let mut builder = GreenNodeBuilder::new();
builder.start_node(SyntaxKind(0));
builder.finish_node();
SyntaxNode::<TestLanguage>::new_root(builder.finish())
}
#[test]
#[should_panic = "tree is mutable"]
fn ensure_mut_panic_on_create() {
// Make a mutable version
let tree = build_immut_tree().clone_for_update();
SyntaxNodePtr::new(&tree);
}
#[test]
#[should_panic = "tree is mutable"]
fn ensure_mut_panic_on_deref() {
let tree = build_immut_tree();
let tree_mut = tree.clone_for_update();
// Create on immutable, convert on mutable
let syn_ptr = SyntaxNodePtr::new(&tree);
syn_ptr.to_node(&tree_mut);
}
}

View File

@@ -0,0 +1,30 @@
#[derive(Debug)]
pub(crate) enum CowMut<'a, T> {
Owned(T),
Borrowed(&'a mut T),
}
impl<T> std::ops::Deref for CowMut<'_, T> {
type Target = T;
fn deref(&self) -> &T {
match self {
CowMut::Owned(it) => it,
CowMut::Borrowed(it) => *it,
}
}
}
impl<T> std::ops::DerefMut for CowMut<'_, T> {
fn deref_mut(&mut self) -> &mut T {
match self {
CowMut::Owned(it) => it,
CowMut::Borrowed(it) => *it,
}
}
}
impl<T: Default> Default for CowMut<'_, T> {
fn default() -> Self {
CowMut::Owned(T::default())
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
mod node;
mod token;
mod element;
mod builder;
mod node_cache;
use self::element::GreenElement;
pub(crate) use self::{element::GreenElementRef, node::GreenChild};
pub use self::{
builder::{Checkpoint, GreenNodeBuilder},
node::{Children, GreenNode, GreenNodeData},
node_cache::NodeCache,
token::{GreenToken, GreenTokenData},
};
/// SyntaxKind is a type tag for each token or node.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SyntaxKind(pub u16);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn assert_send_sync() {
fn f<T: Send + Sync>() {}
f::<GreenNode>();
f::<GreenToken>();
f::<GreenElement>();
}
#[test]
fn test_size_of() {
use std::mem::size_of;
eprintln!("GreenNode {}", size_of::<GreenNode>());
eprintln!("GreenToken {}", size_of::<GreenToken>());
eprintln!("GreenElement {}", size_of::<GreenElement>());
}
}

View File

@@ -0,0 +1,131 @@
use std::num::NonZeroUsize;
use crate::{
cow_mut::CowMut,
green::{node_cache::NodeCache, GreenElement, GreenNode, SyntaxKind},
NodeOrToken,
};
/// A checkpoint for maybe wrapping a node. See `GreenNodeBuilder::checkpoint` for details.
#[derive(Clone, Copy, Debug)]
pub struct Checkpoint(NonZeroUsize);
impl Checkpoint {
fn new(inner: usize) -> Self {
Self(NonZeroUsize::new(inner + 1).unwrap())
}
fn into_inner(self) -> usize {
self.0.get() - 1
}
}
/// A builder for a green tree.
#[derive(Default, Debug)]
pub struct GreenNodeBuilder<'cache> {
cache: CowMut<'cache, NodeCache>,
parents: Vec<(SyntaxKind, usize)>,
children: Vec<(u64, GreenElement)>,
}
impl GreenNodeBuilder<'_> {
/// Creates new builder.
pub fn new() -> GreenNodeBuilder<'static> {
GreenNodeBuilder::default()
}
/// Reusing `NodeCache` between different `GreenNodeBuilder`s saves memory.
/// It allows to structurally share underlying trees.
pub fn with_cache(cache: &mut NodeCache) -> GreenNodeBuilder<'_> {
GreenNodeBuilder {
cache: CowMut::Borrowed(cache),
parents: Vec::new(),
children: Vec::new(),
}
}
/// Adds new token to the current branch.
#[inline]
pub fn token(&mut self, kind: SyntaxKind, text: &str) {
let (hash, token) = self.cache.token(kind, text);
self.children.push((hash, token.into()));
}
/// Start new node and make it current.
#[inline]
pub fn start_node(&mut self, kind: SyntaxKind) {
let len = self.children.len();
self.parents.push((kind, len));
}
/// Finish current branch and restore previous
/// branch as current.
#[inline]
pub fn finish_node(&mut self) {
let (kind, first_child) = self.parents.pop().unwrap();
let (hash, node) = self.cache.node(kind, &mut self.children, first_child);
self.children.push((hash, node.into()));
}
/// Prepare for maybe wrapping the next node.
/// The way wrapping works is that you first of all get a checkpoint,
/// then you place all tokens you want to wrap, and then *maybe* call
/// `start_node_at`.
/// Example:
/// ```rust
/// # use rowan::{GreenNodeBuilder, SyntaxKind};
/// # const PLUS: SyntaxKind = SyntaxKind(0);
/// # const OPERATION: SyntaxKind = SyntaxKind(1);
/// # struct Parser;
/// # impl Parser {
/// # fn peek(&self) -> Option<SyntaxKind> { None }
/// # fn parse_expr(&mut self) {}
/// # }
/// # let mut builder = GreenNodeBuilder::new();
/// # let mut parser = Parser;
/// let checkpoint = builder.checkpoint();
/// parser.parse_expr();
/// if parser.peek() == Some(PLUS) {
/// // 1 + 2 = Add(1, 2)
/// builder.start_node_at(checkpoint, OPERATION);
/// parser.parse_expr();
/// builder.finish_node();
/// }
/// ```
#[inline]
pub fn checkpoint(&self) -> Checkpoint {
Checkpoint::new(self.children.len())
}
/// Wrap the previous branch marked by `checkpoint` in a new branch and
/// make it current.
#[inline]
pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: SyntaxKind) {
let checkpoint = checkpoint.into_inner();
assert!(
checkpoint <= self.children.len(),
"checkpoint no longer valid, was finish_node called early?"
);
if let Some(&(_, first_child)) = self.parents.last() {
assert!(
checkpoint >= first_child,
"checkpoint no longer valid, was an unmatched start_node_at called?"
);
}
self.parents.push((kind, checkpoint));
}
/// Complete tree building. Make sure that
/// `start_node_at` and `finish_node` calls
/// are paired!
#[inline]
pub fn finish(mut self) -> GreenNode {
assert_eq!(self.children.len(), 1);
match self.children.pop().unwrap().1 {
NodeOrToken::Node(node) => node,
NodeOrToken::Token(_) => panic!(),
}
}
}

View File

@@ -0,0 +1,89 @@
use std::borrow::Cow;
use crate::{
green::{GreenNode, GreenToken, SyntaxKind},
GreenNodeData, NodeOrToken, TextSize,
};
use super::GreenTokenData;
pub(super) type GreenElement = NodeOrToken<GreenNode, GreenToken>;
pub(crate) type GreenElementRef<'a> = NodeOrToken<&'a GreenNodeData, &'a GreenTokenData>;
impl From<GreenNode> for GreenElement {
#[inline]
fn from(node: GreenNode) -> GreenElement {
NodeOrToken::Node(node)
}
}
impl<'a> From<&'a GreenNode> for GreenElementRef<'a> {
#[inline]
fn from(node: &'a GreenNode) -> GreenElementRef<'a> {
NodeOrToken::Node(node)
}
}
impl From<GreenToken> for GreenElement {
#[inline]
fn from(token: GreenToken) -> GreenElement {
NodeOrToken::Token(token)
}
}
impl From<Cow<'_, GreenNodeData>> for GreenElement {
#[inline]
fn from(cow: Cow<'_, GreenNodeData>) -> Self {
NodeOrToken::Node(cow.into_owned())
}
}
impl<'a> From<&'a GreenToken> for GreenElementRef<'a> {
#[inline]
fn from(token: &'a GreenToken) -> GreenElementRef<'a> {
NodeOrToken::Token(token)
}
}
impl GreenElementRef<'_> {
pub fn to_owned(self) -> GreenElement {
match self {
NodeOrToken::Node(it) => NodeOrToken::Node(it.to_owned()),
NodeOrToken::Token(it) => NodeOrToken::Token(it.to_owned()),
}
}
}
impl GreenElement {
/// Returns kind of this element.
#[inline]
pub fn kind(&self) -> SyntaxKind {
self.as_deref().kind()
}
/// Returns the length of the text covered by this element.
#[inline]
pub fn text_len(&self) -> TextSize {
self.as_deref().text_len()
}
}
impl GreenElementRef<'_> {
/// Returns kind of this element.
#[inline]
pub fn kind(&self) -> SyntaxKind {
match self {
NodeOrToken::Node(it) => it.kind(),
NodeOrToken::Token(it) => it.kind(),
}
}
/// Returns the length of the text covered by this element.
#[inline]
pub fn text_len(self) -> TextSize {
match self {
NodeOrToken::Node(it) => it.text_len(),
NodeOrToken::Token(it) => it.text_len(),
}
}
}

View File

@@ -0,0 +1,359 @@
use std::{
borrow::{Borrow, Cow},
fmt,
iter::{self, FusedIterator},
mem::{self, ManuallyDrop},
ops, ptr, slice,
};
use countme::Count;
use crate::{
arc::{Arc, HeaderSlice, ThinArc},
green::{GreenElement, GreenElementRef, SyntaxKind},
utility_types::static_assert,
GreenToken, NodeOrToken, TextRange, TextSize,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(super) struct GreenNodeHead {
kind: SyntaxKind,
text_len: TextSize,
_c: Count<GreenNode>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum GreenChild {
Node { rel_offset: TextSize, node: GreenNode },
Token { rel_offset: TextSize, token: GreenToken },
}
#[cfg(target_pointer_width = "64")]
static_assert!(mem::size_of::<GreenChild>() == mem::size_of::<usize>() * 2);
type Repr = HeaderSlice<GreenNodeHead, [GreenChild]>;
type ReprThin = HeaderSlice<GreenNodeHead, [GreenChild; 0]>;
#[repr(transparent)]
pub struct GreenNodeData {
data: ReprThin,
}
impl PartialEq for GreenNodeData {
fn eq(&self, other: &Self) -> bool {
self.header() == other.header() && self.slice() == other.slice()
}
}
/// Internal node in the immutable tree.
/// It has other nodes and tokens as children.
#[derive(Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct GreenNode {
ptr: ThinArc<GreenNodeHead, GreenChild>,
}
impl ToOwned for GreenNodeData {
type Owned = GreenNode;
#[inline]
fn to_owned(&self) -> GreenNode {
let green = unsafe { GreenNode::from_raw(ptr::NonNull::from(self)) };
let green = ManuallyDrop::new(green);
GreenNode::clone(&green)
}
}
impl Borrow<GreenNodeData> for GreenNode {
#[inline]
fn borrow(&self) -> &GreenNodeData {
&*self
}
}
impl From<Cow<'_, GreenNodeData>> for GreenNode {
#[inline]
fn from(cow: Cow<'_, GreenNodeData>) -> Self {
cow.into_owned()
}
}
impl fmt::Debug for GreenNodeData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GreenNode")
.field("kind", &self.kind())
.field("text_len", &self.text_len())
.field("n_children", &self.children().len())
.finish()
}
}
impl fmt::Debug for GreenNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data: &GreenNodeData = &*self;
fmt::Debug::fmt(data, f)
}
}
impl fmt::Display for GreenNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data: &GreenNodeData = &*self;
fmt::Display::fmt(data, f)
}
}
impl fmt::Display for GreenNodeData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for child in self.children() {
write!(f, "{}", child)?;
}
Ok(())
}
}
impl GreenNodeData {
#[inline]
fn header(&self) -> &GreenNodeHead {
&self.data.header
}
#[inline]
fn slice(&self) -> &[GreenChild] {
self.data.slice()
}
/// Kind of this node.
#[inline]
pub fn kind(&self) -> SyntaxKind {
self.header().kind
}
/// Returns the length of the text covered by this node.
#[inline]
pub fn text_len(&self) -> TextSize {
self.header().text_len
}
/// Children of this node.
#[inline]
pub fn children(&self) -> Children<'_> {
Children { raw: self.slice().iter() }
}
pub(crate) fn child_at_range(
&self,
rel_range: TextRange,
) -> Option<(usize, TextSize, GreenElementRef<'_>)> {
let idx = self
.slice()
.binary_search_by(|it| {
let child_range = it.rel_range();
TextRange::ordering(child_range, rel_range)
})
// XXX: this handles empty ranges
.unwrap_or_else(|it| it.saturating_sub(1));
let child = &self.slice().get(idx).filter(|it| it.rel_range().contains_range(rel_range))?;
Some((idx, child.rel_offset(), child.as_ref()))
}
#[must_use]
pub fn replace_child(&self, index: usize, new_child: GreenElement) -> GreenNode {
let mut replacement = Some(new_child);
let children = self.children().enumerate().map(|(i, child)| {
if i == index {
replacement.take().unwrap()
} else {
child.to_owned()
}
});
GreenNode::new(self.kind(), children)
}
#[must_use]
pub fn insert_child(&self, index: usize, new_child: GreenElement) -> GreenNode {
// https://github.com/rust-lang/rust/issues/34433
self.splice_children(index..index, iter::once(new_child))
}
#[must_use]
pub fn remove_child(&self, index: usize) -> GreenNode {
self.splice_children(index..=index, iter::empty())
}
#[must_use]
pub fn splice_children<R, I>(&self, range: R, replace_with: I) -> GreenNode
where
R: ops::RangeBounds<usize>,
I: IntoIterator<Item = GreenElement>,
{
let mut children: Vec<_> = self.children().map(|it| it.to_owned()).collect();
children.splice(range, replace_with);
GreenNode::new(self.kind(), children)
}
}
impl ops::Deref for GreenNode {
type Target = GreenNodeData;
#[inline]
fn deref(&self) -> &GreenNodeData {
let repr: &Repr = &self.ptr;
unsafe {
let repr: &ReprThin = &*(repr as *const Repr as *const ReprThin);
mem::transmute::<&ReprThin, &GreenNodeData>(repr)
}
}
}
impl GreenNode {
/// Creates new Node.
#[inline]
pub fn new<I>(kind: SyntaxKind, children: I) -> GreenNode
where
I: IntoIterator<Item = GreenElement>,
I::IntoIter: ExactSizeIterator,
{
let mut text_len: TextSize = 0.into();
let children = children.into_iter().map(|el| {
let rel_offset = text_len;
text_len += el.text_len();
match el {
NodeOrToken::Node(node) => GreenChild::Node { rel_offset, node },
NodeOrToken::Token(token) => GreenChild::Token { rel_offset, token },
}
});
let data = ThinArc::from_header_and_iter(
GreenNodeHead { kind, text_len: 0.into(), _c: Count::new() },
children,
);
// XXX: fixup `text_len` after construction, because we can't iterate
// `children` twice.
let data = {
let mut data = Arc::from_thin(data);
Arc::get_mut(&mut data).unwrap().header.text_len = text_len;
Arc::into_thin(data)
};
GreenNode { ptr: data }
}
#[inline]
pub(crate) fn into_raw(this: GreenNode) -> ptr::NonNull<GreenNodeData> {
let green = ManuallyDrop::new(this);
let green: &GreenNodeData = &*green;
ptr::NonNull::from(&*green)
}
#[inline]
pub(crate) unsafe fn from_raw(ptr: ptr::NonNull<GreenNodeData>) -> GreenNode {
let arc = Arc::from_raw(&ptr.as_ref().data as *const ReprThin);
let arc = mem::transmute::<Arc<ReprThin>, ThinArc<GreenNodeHead, GreenChild>>(arc);
GreenNode { ptr: arc }
}
}
impl GreenChild {
#[inline]
pub(crate) fn as_ref(&self) -> GreenElementRef {
match self {
GreenChild::Node { node, .. } => NodeOrToken::Node(node),
GreenChild::Token { token, .. } => NodeOrToken::Token(token),
}
}
#[inline]
pub(crate) fn rel_offset(&self) -> TextSize {
match self {
GreenChild::Node { rel_offset, .. } | GreenChild::Token { rel_offset, .. } => {
*rel_offset
}
}
}
#[inline]
fn rel_range(&self) -> TextRange {
let len = self.as_ref().text_len();
TextRange::at(self.rel_offset(), len)
}
}
#[derive(Debug, Clone)]
pub struct Children<'a> {
pub(crate) raw: slice::Iter<'a, GreenChild>,
}
// NB: forward everything stable that iter::Slice specializes as of Rust 1.39.0
impl ExactSizeIterator for Children<'_> {
#[inline(always)]
fn len(&self) -> usize {
self.raw.len()
}
}
impl<'a> Iterator for Children<'a> {
type Item = GreenElementRef<'a>;
#[inline]
fn next(&mut self) -> Option<GreenElementRef<'a>> {
self.raw.next().map(GreenChild::as_ref)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.raw.size_hint()
}
#[inline]
fn count(self) -> usize
where
Self: Sized,
{
self.raw.count()
}
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.raw.nth(n).map(GreenChild::as_ref)
}
#[inline]
fn last(mut self) -> Option<Self::Item>
where
Self: Sized,
{
self.next_back()
}
#[inline]
fn fold<Acc, Fold>(mut self, init: Acc, mut f: Fold) -> Acc
where
Fold: FnMut(Acc, Self::Item) -> Acc,
{
let mut accum = init;
while let Some(x) = self.next() {
accum = f(accum, x);
}
accum
}
}
impl<'a> DoubleEndedIterator for Children<'a> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.raw.next_back().map(GreenChild::as_ref)
}
#[inline]
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
self.raw.nth_back(n).map(GreenChild::as_ref)
}
#[inline]
fn rfold<Acc, Fold>(mut self, init: Acc, mut f: Fold) -> Acc
where
Fold: FnMut(Acc, Self::Item) -> Acc,
{
let mut accum = init;
while let Some(x) = self.next_back() {
accum = f(accum, x);
}
accum
}
}
impl FusedIterator for Children<'_> {}

View File

@@ -0,0 +1,157 @@
use hashbrown::hash_map::RawEntryMut;
use rustc_hash::FxHasher;
use std::hash::{BuildHasherDefault, Hash, Hasher};
use crate::{
green::GreenElementRef, GreenNode, GreenNodeData, GreenToken, GreenTokenData, NodeOrToken,
SyntaxKind,
};
use super::element::GreenElement;
type HashMap<K, V> = hashbrown::HashMap<K, V, BuildHasherDefault<FxHasher>>;
#[derive(Debug)]
struct NoHash<T>(T);
/// Interner for GreenTokens and GreenNodes
// XXX: the impl is a bit tricky. As usual when writing interners, we want to
// store all values in one HashSet.
//
// However, hashing trees is fun: hash of the tree is recursively defined. We
// maintain an invariant -- if the tree is interned, then all of its children
// are interned as well.
//
// That means that computing the hash naively is wasteful -- we just *know*
// hashes of children, and we can re-use those.
//
// So here we use *raw* API of hashbrown and provide the hashes manually,
// instead of going via a `Hash` impl. Our manual `Hash` and the
// `#[derive(Hash)]` are actually different! At some point we had a fun bug,
// where we accidentally mixed the two hashes, which made the cache much less
// efficient.
//
// To fix that, we additionally wrap the data in `NoHash` wrapper, to make sure
// we don't accidentally use the wrong hash!
#[derive(Default, Debug)]
pub struct NodeCache {
nodes: HashMap<NoHash<GreenNode>, ()>,
tokens: HashMap<NoHash<GreenToken>, ()>,
}
fn token_hash(token: &GreenTokenData) -> u64 {
let mut h = FxHasher::default();
token.kind().hash(&mut h);
token.text().hash(&mut h);
h.finish()
}
fn node_hash(node: &GreenNodeData) -> u64 {
let mut h = FxHasher::default();
node.kind().hash(&mut h);
for child in node.children() {
match child {
NodeOrToken::Node(it) => node_hash(it),
NodeOrToken::Token(it) => token_hash(it),
}
.hash(&mut h)
}
h.finish()
}
fn element_id(elem: GreenElementRef<'_>) -> *const () {
match elem {
NodeOrToken::Node(it) => it as *const GreenNodeData as *const (),
NodeOrToken::Token(it) => it as *const GreenTokenData as *const (),
}
}
impl NodeCache {
pub(crate) fn node(
&mut self,
kind: SyntaxKind,
children: &mut Vec<(u64, GreenElement)>,
first_child: usize,
) -> (u64, GreenNode) {
let build_node = move |children: &mut Vec<(u64, GreenElement)>| {
GreenNode::new(kind, children.drain(first_child..).map(|(_, it)| it))
};
let children_ref = &children[first_child..];
if children_ref.len() > 3 {
let node = build_node(children);
return (0, node);
}
let hash = {
let mut h = FxHasher::default();
kind.hash(&mut h);
for &(hash, _) in children_ref {
if hash == 0 {
let node = build_node(children);
return (0, node);
}
hash.hash(&mut h);
}
h.finish()
};
// Green nodes are fully immutable, so it's ok to deduplicate them.
// This is the same optimization that Roslyn does
// https://github.com/KirillOsenkov/Bliki/wiki/Roslyn-Immutable-Trees
//
// For example, all `#[inline]` in this file share the same green node!
// For `libsyntax/parse/parser.rs`, measurements show that deduping saves
// 17% of the memory for green nodes!
let entry = self.nodes.raw_entry_mut().from_hash(hash, |node| {
node.0.kind() == kind && node.0.children().len() == children_ref.len() && {
let lhs = node.0.children();
let rhs = children_ref.iter().map(|(_, it)| it.as_deref());
let lhs = lhs.map(element_id);
let rhs = rhs.map(element_id);
lhs.eq(rhs)
}
});
let node = match entry {
RawEntryMut::Occupied(entry) => {
drop(children.drain(first_child..));
entry.key().0.clone()
}
RawEntryMut::Vacant(entry) => {
let node = build_node(children);
entry.insert_with_hasher(hash, NoHash(node.clone()), (), |n| node_hash(&n.0));
node
}
};
(hash, node)
}
pub(crate) fn token(&mut self, kind: SyntaxKind, text: &str) -> (u64, GreenToken) {
let hash = {
let mut h = FxHasher::default();
kind.hash(&mut h);
text.hash(&mut h);
h.finish()
};
let entry = self
.tokens
.raw_entry_mut()
.from_hash(hash, |token| token.0.kind() == kind && token.0.text() == text);
let token = match entry {
RawEntryMut::Occupied(entry) => entry.key().0.clone(),
RawEntryMut::Vacant(entry) => {
let token = GreenToken::new(kind, text);
entry.insert_with_hasher(hash, NoHash(token.clone()), (), |t| token_hash(&t.0));
token
}
};
(hash, token)
}
}

View File

@@ -0,0 +1,143 @@
use std::{
borrow::Borrow,
fmt,
mem::{self, ManuallyDrop},
ops, ptr,
};
use countme::Count;
use crate::{
arc::{Arc, HeaderSlice, ThinArc},
green::SyntaxKind,
TextSize,
};
#[derive(PartialEq, Eq, Hash)]
struct GreenTokenHead {
kind: SyntaxKind,
_c: Count<GreenToken>,
}
type Repr = HeaderSlice<GreenTokenHead, [u8]>;
type ReprThin = HeaderSlice<GreenTokenHead, [u8; 0]>;
#[repr(transparent)]
pub struct GreenTokenData {
data: ReprThin,
}
impl PartialEq for GreenTokenData {
fn eq(&self, other: &Self) -> bool {
self.kind() == other.kind() && self.text() == other.text()
}
}
/// Leaf node in the immutable tree.
#[derive(PartialEq, Eq, Hash, Clone)]
#[repr(transparent)]
pub struct GreenToken {
ptr: ThinArc<GreenTokenHead, u8>,
}
impl ToOwned for GreenTokenData {
type Owned = GreenToken;
#[inline]
fn to_owned(&self) -> GreenToken {
let green = unsafe { GreenToken::from_raw(ptr::NonNull::from(self)) };
let green = ManuallyDrop::new(green);
GreenToken::clone(&green)
}
}
impl Borrow<GreenTokenData> for GreenToken {
#[inline]
fn borrow(&self) -> &GreenTokenData {
&*self
}
}
impl fmt::Debug for GreenTokenData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GreenToken")
.field("kind", &self.kind())
.field("text", &self.text())
.finish()
}
}
impl fmt::Debug for GreenToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data: &GreenTokenData = &*self;
fmt::Debug::fmt(data, f)
}
}
impl fmt::Display for GreenToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data: &GreenTokenData = &*self;
fmt::Display::fmt(data, f)
}
}
impl fmt::Display for GreenTokenData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.text())
}
}
impl GreenTokenData {
/// Kind of this Token.
#[inline]
pub fn kind(&self) -> SyntaxKind {
self.data.header.kind
}
/// Text of this Token.
#[inline]
pub fn text(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(self.data.slice()) }
}
/// Returns the length of the text covered by this token.
#[inline]
pub fn text_len(&self) -> TextSize {
TextSize::of(self.text())
}
}
impl GreenToken {
/// Creates new Token.
#[inline]
pub fn new(kind: SyntaxKind, text: &str) -> GreenToken {
let head = GreenTokenHead { kind, _c: Count::new() };
let ptr = ThinArc::from_header_and_iter(head, text.bytes());
GreenToken { ptr }
}
#[inline]
pub(crate) fn into_raw(this: GreenToken) -> ptr::NonNull<GreenTokenData> {
let green = ManuallyDrop::new(this);
let green: &GreenTokenData = &*green;
ptr::NonNull::from(&*green)
}
#[inline]
pub(crate) unsafe fn from_raw(ptr: ptr::NonNull<GreenTokenData>) -> GreenToken {
let arc = Arc::from_raw(&ptr.as_ref().data as *const ReprThin);
let arc = mem::transmute::<Arc<ReprThin>, ThinArc<GreenTokenHead, u8>>(arc);
GreenToken { ptr: arc }
}
}
impl ops::Deref for GreenToken {
type Target = GreenTokenData;
#[inline]
fn deref(&self) -> &GreenTokenData {
unsafe {
let repr: &Repr = &self.ptr;
let repr: &ReprThin = &*(repr as *const Repr as *const ReprThin);
mem::transmute::<&ReprThin, &GreenTokenData>(repr)
}
}
}

View File

@@ -0,0 +1,41 @@
//! A generic library for lossless syntax trees.
//! See `examples/s_expressions.rs` for a tutorial.
#![forbid(
// missing_debug_implementations,
unconditional_recursion,
future_incompatible,
// missing_docs,
)]
#![deny(unsafe_code)]
#[allow(unsafe_code)]
mod green;
#[allow(unsafe_code)]
pub mod cursor;
pub mod api;
mod syntax_text;
mod utility_types;
mod cow_mut;
#[allow(unsafe_code)]
mod sll;
#[allow(unsafe_code)]
mod arc;
#[cfg(feature = "serde1")]
mod serde_impls;
pub mod ast;
pub use text_size::{TextLen, TextRange, TextSize};
pub use crate::{
api::{
Language, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken,
},
green::{
Checkpoint, Children, GreenNode, GreenNodeBuilder, GreenNodeData, GreenToken,
GreenTokenData, NodeCache, SyntaxKind,
},
syntax_text::SyntaxText,
utility_types::{Direction, NodeOrToken, TokenAtOffset, WalkEvent},
};

View File

@@ -0,0 +1,66 @@
use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
use std::fmt;
use crate::{
api::{Language, SyntaxNode, SyntaxToken},
NodeOrToken,
};
struct SerDisplay<T>(T);
impl<T: fmt::Display> Serialize for SerDisplay<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&self.0)
}
}
struct DisplayDebug<T>(T);
impl<T: fmt::Debug> fmt::Display for DisplayDebug<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl<L: Language> Serialize for SyntaxNode<L> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_map(Some(3))?;
state.serialize_entry("kind", &SerDisplay(DisplayDebug(self.kind())))?;
state.serialize_entry("text_range", &self.text_range())?;
state.serialize_entry("children", &Children(self))?;
state.end()
}
}
impl<L: Language> Serialize for SyntaxToken<L> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_map(Some(3))?;
state.serialize_entry("kind", &SerDisplay(DisplayDebug(self.kind())))?;
state.serialize_entry("text_range", &self.text_range())?;
state.serialize_entry("text", &self.text())?;
state.end()
}
}
struct Children<T>(T);
impl<L: Language> Serialize for Children<&'_ SyntaxNode<L>> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_seq(None)?;
self.0.children_with_tokens().try_for_each(|element| match element {
NodeOrToken::Node(it) => state.serialize_element(&it),
NodeOrToken::Token(it) => state.serialize_element(&it),
})?;
state.end()
}
}

View File

@@ -0,0 +1,128 @@
//! Sorted Linked List
use std::{cell::Cell, cmp::Ordering, ptr};
use crate::utility_types::Delta;
pub(crate) unsafe trait Elem {
fn prev(&self) -> &Cell<*const Self>;
fn next(&self) -> &Cell<*const Self>;
fn key(&self) -> &Cell<u32>;
}
pub(crate) enum AddToSllResult<'a, E: Elem> {
NoHead,
EmptyHead(&'a Cell<*const E>),
SmallerThanHead(&'a Cell<*const E>),
SmallerThanNotHead(*const E),
AlreadyInSll(*const E),
}
impl<'a, E: Elem> AddToSllResult<'a, E> {
pub(crate) fn add_to_sll(&self, elem_ptr: *const E) {
unsafe {
(*elem_ptr).prev().set(elem_ptr);
(*elem_ptr).next().set(elem_ptr);
match self {
// Case 1: empty head, replace it.
AddToSllResult::EmptyHead(head) => head.set(elem_ptr),
// Case 2: we are smaller than the head, replace it.
AddToSllResult::SmallerThanHead(head) => {
let old_head = head.get();
let prev = (*old_head).prev().replace(elem_ptr);
(*prev).next().set(elem_ptr);
(*elem_ptr).next().set(old_head);
(*elem_ptr).prev().set(prev);
head.set(elem_ptr);
}
// Case 3: insert in place found by looping
AddToSllResult::SmallerThanNotHead(curr) => {
let next = (**curr).next().replace(elem_ptr);
(*next).prev().set(elem_ptr);
(*elem_ptr).prev().set(*curr);
(*elem_ptr).next().set(next);
}
AddToSllResult::NoHead | AddToSllResult::AlreadyInSll(_) => (),
}
}
}
}
#[cold]
pub(crate) fn init<'a, E: Elem>(
head: Option<&'a Cell<*const E>>,
elem: &E,
) -> AddToSllResult<'a, E> {
if let Some(head) = head {
link(head, elem)
} else {
AddToSllResult::NoHead
}
}
#[cold]
pub(crate) fn unlink<E: Elem>(head: &Cell<*const E>, elem: &E) {
debug_assert!(!head.get().is_null(), "invalid linked list head");
let elem_ptr: *const E = elem;
let prev = elem.prev().replace(elem_ptr);
let next = elem.next().replace(elem_ptr);
unsafe {
debug_assert_eq!((*prev).next().get(), elem_ptr, "invalid linked list links");
debug_assert_eq!((*next).prev().get(), elem_ptr, "invalid linked list links");
(*prev).next().set(next);
(*next).prev().set(prev);
}
if head.get() == elem_ptr {
head.set(if next == elem_ptr { ptr::null() } else { next })
}
}
#[cold]
pub(crate) fn link<'a, E: Elem>(head: &'a Cell<*const E>, elem: &E) -> AddToSllResult<'a, E> {
let old_head = head.get();
// Case 1: empty head, replace it.
if old_head.is_null() {
return AddToSllResult::EmptyHead(head);
}
unsafe {
// Case 2: we are smaller than the head, replace it.
if elem.key() < (*old_head).key() {
return AddToSllResult::SmallerThanHead(head);
}
// Case 3: loop *backward* until we find insertion place. Because of
// Case 2, we can't loop beyond the head.
let mut curr = (*old_head).prev().get();
loop {
match (*curr).key().cmp(elem.key()) {
Ordering::Less => return AddToSllResult::SmallerThanNotHead(curr),
Ordering::Equal => return AddToSllResult::AlreadyInSll(curr),
Ordering::Greater => curr = (*curr).prev().get(),
}
}
}
}
pub(crate) fn adjust<E: Elem>(elem: &E, from: u32, by: Delta<u32>) {
let elem_ptr: *const E = elem;
unsafe {
let mut curr = elem_ptr;
loop {
let mut key = (*curr).key().get();
if key >= from {
key += by;
(*curr).key().set(key);
}
curr = (*curr).next().get();
if curr == elem_ptr {
break;
}
}
}
}

View File

@@ -0,0 +1,311 @@
use std::fmt;
use crate::{
cursor::{SyntaxNode, SyntaxToken},
TextRange, TextSize,
};
#[derive(Clone)]
pub struct SyntaxText {
node: SyntaxNode,
range: TextRange,
}
impl SyntaxText {
pub(crate) fn new(node: SyntaxNode) -> SyntaxText {
let range = node.text_range();
SyntaxText { node, range }
}
pub fn len(&self) -> TextSize {
self.range.len()
}
pub fn is_empty(&self) -> bool {
self.range.is_empty()
}
pub fn contains_char(&self, c: char) -> bool {
self.try_for_each_chunk(|chunk| if chunk.contains(c) { Err(()) } else { Ok(()) }).is_err()
}
pub fn find_char(&self, c: char) -> Option<TextSize> {
let mut acc: TextSize = 0.into();
let res = self.try_for_each_chunk(|chunk| {
if let Some(pos) = chunk.find(c) {
let pos: TextSize = (pos as u32).into();
return Err(acc + pos);
}
acc += TextSize::of(chunk);
Ok(())
});
found(res)
}
pub fn char_at(&self, offset: TextSize) -> Option<char> {
let offset = offset.into();
let mut start: TextSize = 0.into();
let res = self.try_for_each_chunk(|chunk| {
let end = start + TextSize::of(chunk);
if start <= offset && offset < end {
let off: usize = u32::from(offset - start) as usize;
return Err(chunk[off..].chars().next().unwrap());
}
start = end;
Ok(())
});
found(res)
}
pub fn slice<R: private::SyntaxTextRange>(&self, range: R) -> SyntaxText {
let start = range.start().unwrap_or_default();
let end = range.end().unwrap_or(self.len());
assert!(start <= end);
let len = end - start;
let start = self.range.start() + start;
let end = start + len;
assert!(
start <= end,
"invalid slice, range: {:?}, slice: {:?}",
self.range,
(range.start(), range.end()),
);
let range = TextRange::new(start, end);
assert!(
self.range.contains_range(range),
"invalid slice, range: {:?}, slice: {:?}",
self.range,
range,
);
SyntaxText { node: self.node.clone(), range }
}
pub fn try_fold_chunks<T, F, E>(&self, init: T, mut f: F) -> Result<T, E>
where
F: FnMut(T, &str) -> Result<T, E>,
{
self.tokens_with_ranges()
.try_fold(init, move |acc, (token, range)| f(acc, &token.text()[range]))
}
pub fn try_for_each_chunk<F: FnMut(&str) -> Result<(), E>, E>(
&self,
mut f: F,
) -> Result<(), E> {
self.try_fold_chunks((), move |(), chunk| f(chunk))
}
pub fn for_each_chunk<F: FnMut(&str)>(&self, mut f: F) {
enum Void {}
match self.try_for_each_chunk(|chunk| Ok::<(), Void>(f(chunk))) {
Ok(()) => (),
Err(void) => match void {},
}
}
fn tokens_with_ranges(&self) -> impl Iterator<Item = (SyntaxToken, TextRange)> {
let text_range = self.range;
self.node.descendants_with_tokens().filter_map(|element| element.into_token()).filter_map(
move |token| {
let token_range = token.text_range();
let range = text_range.intersect(token_range)?;
Some((token, range - token_range.start()))
},
)
}
}
fn found<T>(res: Result<(), T>) -> Option<T> {
match res {
Ok(()) => None,
Err(it) => Some(it),
}
}
impl fmt::Debug for SyntaxText {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.to_string(), f)
}
}
impl fmt::Display for SyntaxText {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.try_for_each_chunk(|chunk| fmt::Display::fmt(chunk, f))
}
}
impl From<SyntaxText> for String {
fn from(text: SyntaxText) -> String {
text.to_string()
}
}
impl PartialEq<str> for SyntaxText {
fn eq(&self, mut rhs: &str) -> bool {
self.try_for_each_chunk(|chunk| {
if !rhs.starts_with(chunk) {
return Err(());
}
rhs = &rhs[chunk.len()..];
Ok(())
})
.is_ok()
&& rhs.is_empty()
}
}
impl PartialEq<SyntaxText> for str {
fn eq(&self, rhs: &SyntaxText) -> bool {
rhs == self
}
}
impl PartialEq<&'_ str> for SyntaxText {
fn eq(&self, rhs: &&str) -> bool {
self == *rhs
}
}
impl PartialEq<SyntaxText> for &'_ str {
fn eq(&self, rhs: &SyntaxText) -> bool {
rhs == self
}
}
impl PartialEq for SyntaxText {
fn eq(&self, other: &SyntaxText) -> bool {
if self.range.len() != other.range.len() {
return false;
}
let mut lhs = self.tokens_with_ranges();
let mut rhs = other.tokens_with_ranges();
zip_texts(&mut lhs, &mut rhs).is_none()
&& lhs.all(|it| it.1.is_empty())
&& rhs.all(|it| it.1.is_empty())
}
}
fn zip_texts<I: Iterator<Item = (SyntaxToken, TextRange)>>(xs: &mut I, ys: &mut I) -> Option<()> {
let mut x = xs.next()?;
let mut y = ys.next()?;
loop {
while x.1.is_empty() {
x = xs.next()?;
}
while y.1.is_empty() {
y = ys.next()?;
}
let x_text = &x.0.text()[x.1];
let y_text = &y.0.text()[y.1];
if !(x_text.starts_with(y_text) || y_text.starts_with(x_text)) {
return Some(());
}
let advance = std::cmp::min(x.1.len(), y.1.len());
x.1 = TextRange::new(x.1.start() + advance, x.1.end());
y.1 = TextRange::new(y.1.start() + advance, y.1.end());
}
}
impl Eq for SyntaxText {}
mod private {
use std::ops;
use crate::{TextRange, TextSize};
pub trait SyntaxTextRange {
fn start(&self) -> Option<TextSize>;
fn end(&self) -> Option<TextSize>;
}
impl SyntaxTextRange for TextRange {
fn start(&self) -> Option<TextSize> {
Some(TextRange::start(*self))
}
fn end(&self) -> Option<TextSize> {
Some(TextRange::end(*self))
}
}
impl SyntaxTextRange for ops::Range<TextSize> {
fn start(&self) -> Option<TextSize> {
Some(self.start)
}
fn end(&self) -> Option<TextSize> {
Some(self.end)
}
}
impl SyntaxTextRange for ops::RangeFrom<TextSize> {
fn start(&self) -> Option<TextSize> {
Some(self.start)
}
fn end(&self) -> Option<TextSize> {
None
}
}
impl SyntaxTextRange for ops::RangeTo<TextSize> {
fn start(&self) -> Option<TextSize> {
None
}
fn end(&self) -> Option<TextSize> {
Some(self.end)
}
}
impl SyntaxTextRange for ops::RangeFull {
fn start(&self) -> Option<TextSize> {
None
}
fn end(&self) -> Option<TextSize> {
None
}
}
}
#[cfg(test)]
mod tests {
use crate::{green::SyntaxKind, GreenNodeBuilder};
use super::*;
fn build_tree(chunks: &[&str]) -> SyntaxNode {
let mut builder = GreenNodeBuilder::new();
builder.start_node(SyntaxKind(62));
for &chunk in chunks.iter() {
builder.token(SyntaxKind(92), chunk.into())
}
builder.finish_node();
SyntaxNode::new_root(builder.finish())
}
#[test]
fn test_text_equality() {
fn do_check(t1: &[&str], t2: &[&str]) {
let t1 = build_tree(t1).text();
let t2 = build_tree(t2).text();
let expected = t1.to_string() == t2.to_string();
let actual = t1 == t2;
assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (SyntaxText)", t1, t2);
let actual = t1 == &*t2.to_string();
assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (&str)", t1, t2);
}
fn check(t1: &[&str], t2: &[&str]) {
do_check(t1, t2);
do_check(t2, t1)
}
check(&[""], &[""]);
check(&["a"], &[""]);
check(&["a"], &["a"]);
check(&["abc"], &["def"]);
check(&["hello", "world"], &["hello", "world"]);
check(&["hellowo", "rld"], &["hell", "oworld"]);
check(&["hel", "lowo", "rld"], &["helloworld"]);
check(&["{", "abc", "}"], &["{", "123", "}"]);
check(&["{", "abc", "}", "{"], &["{", "123", "}"]);
check(&["{", "abc", "}"], &["{", "123", "}", "{"]);
check(&["{", "abc", "}ab"], &["{", "abc", "}", "ab"]);
}
}

View File

@@ -0,0 +1,180 @@
use std::{
fmt,
ops::{AddAssign, Deref},
};
use text_size::TextSize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NodeOrToken<N, T> {
Node(N),
Token(T),
}
impl<N, T> NodeOrToken<N, T> {
pub fn into_node(self) -> Option<N> {
match self {
NodeOrToken::Node(node) => Some(node),
NodeOrToken::Token(_) => None,
}
}
pub fn into_token(self) -> Option<T> {
match self {
NodeOrToken::Node(_) => None,
NodeOrToken::Token(token) => Some(token),
}
}
pub fn as_node(&self) -> Option<&N> {
match self {
NodeOrToken::Node(node) => Some(node),
NodeOrToken::Token(_) => None,
}
}
pub fn as_token(&self) -> Option<&T> {
match self {
NodeOrToken::Node(_) => None,
NodeOrToken::Token(token) => Some(token),
}
}
}
impl<N: Deref, T: Deref> NodeOrToken<N, T> {
pub(crate) fn as_deref(&self) -> NodeOrToken<&N::Target, &T::Target> {
match self {
NodeOrToken::Node(node) => NodeOrToken::Node(&*node),
NodeOrToken::Token(token) => NodeOrToken::Token(&*token),
}
}
}
impl<N: fmt::Display, T: fmt::Display> fmt::Display for NodeOrToken<N, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NodeOrToken::Node(node) => fmt::Display::fmt(node, f),
NodeOrToken::Token(token) => fmt::Display::fmt(token, f),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Direction {
Next,
Prev,
}
/// `WalkEvent` describes tree walking process.
#[derive(Debug, Copy, Clone)]
pub enum WalkEvent<T> {
/// Fired before traversing the node.
Enter(T),
/// Fired after the node is traversed.
Leave(T),
}
impl<T> WalkEvent<T> {
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> WalkEvent<U> {
match self {
WalkEvent::Enter(it) => WalkEvent::Enter(f(it)),
WalkEvent::Leave(it) => WalkEvent::Leave(f(it)),
}
}
}
/// There might be zero, one or two leaves at a given offset.
#[derive(Clone, Debug)]
pub enum TokenAtOffset<T> {
/// No leaves at offset -- possible for the empty file.
None,
/// Only a single leaf at offset.
Single(T),
/// Offset is exactly between two leaves.
Between(T, T),
}
impl<T> TokenAtOffset<T> {
pub fn map<F: Fn(T) -> U, U>(self, f: F) -> TokenAtOffset<U> {
match self {
TokenAtOffset::None => TokenAtOffset::None,
TokenAtOffset::Single(it) => TokenAtOffset::Single(f(it)),
TokenAtOffset::Between(l, r) => TokenAtOffset::Between(f(l), f(r)),
}
}
/// Convert to option, preferring the right leaf in case of a tie.
pub fn right_biased(self) -> Option<T> {
match self {
TokenAtOffset::None => None,
TokenAtOffset::Single(node) => Some(node),
TokenAtOffset::Between(_, right) => Some(right),
}
}
/// Convert to option, preferring the left leaf in case of a tie.
pub fn left_biased(self) -> Option<T> {
match self {
TokenAtOffset::None => None,
TokenAtOffset::Single(node) => Some(node),
TokenAtOffset::Between(left, _) => Some(left),
}
}
}
impl<T> Iterator for TokenAtOffset<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
match std::mem::replace(self, TokenAtOffset::None) {
TokenAtOffset::None => None,
TokenAtOffset::Single(node) => {
*self = TokenAtOffset::None;
Some(node)
}
TokenAtOffset::Between(left, right) => {
*self = TokenAtOffset::Single(right);
Some(left)
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
TokenAtOffset::None => (0, Some(0)),
TokenAtOffset::Single(_) => (1, Some(1)),
TokenAtOffset::Between(_, _) => (2, Some(2)),
}
}
}
impl<T> ExactSizeIterator for TokenAtOffset<T> {}
macro_rules! _static_assert {
($expr:expr) => {
const _: i32 = 0 / $expr as i32;
};
}
pub(crate) use _static_assert as static_assert;
#[derive(Copy, Clone, Debug)]
pub(crate) enum Delta<T> {
Add(T),
Sub(T),
}
// This won't be coherent :-(
// impl<T: AddAssign + SubAssign> AddAssign<Delta<T>> for T
macro_rules! impls {
($($ty:ident)*) => {$(
impl AddAssign<Delta<$ty>> for $ty {
fn add_assign(&mut self, rhs: Delta<$ty>) {
match rhs {
Delta::Add(amt) => *self += amt,
Delta::Sub(amt) => *self -= amt,
}
}
}
)*};
}
impls!(u32 TextSize);

View File

@@ -0,0 +1,46 @@
use std::{
env,
path::{Path, PathBuf},
process::{Command, Stdio},
};
fn project_root() -> PathBuf {
PathBuf::from(
env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
)
}
fn run(cmd: &str, dir: impl AsRef<Path>) -> Result<(), ()> {
let mut args: Vec<_> = cmd.split_whitespace().collect();
let bin = args.remove(0);
println!("> {}", cmd);
let output = Command::new(bin)
.args(args)
.current_dir(dir)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.output()
.map_err(drop)?;
if output.status.success() {
Ok(())
} else {
let stdout = String::from_utf8(output.stdout).map_err(drop)?;
print!("{}", stdout);
Err(())
}
}
#[test]
fn check_code_formatting() {
let dir = project_root();
if run("rustfmt +stable --version", &dir).is_err() {
panic!(
"failed to run rustfmt from toolchain 'stable'; \
please run `rustup component add rustfmt --toolchain stable` to install it.",
);
}
if run("cargo +stable fmt -- --check", &dir).is_err() {
panic!("code is not properly formatted; please format the code by running `cargo fmt`")
}
}