mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 00:11:45 +03:00
Reduce parser dependencies (#9671)
* Reduce parser dependencies - `enso-parser-syntax-tree-visitor` is now only used when building tests and debug tools. - Remove `enso-logging` crate and its macros. - The main bin for `enso-parser` has been moved to a `check_syntax` tool in `enso-parser-debug`.
This commit is contained in:
parent
0fcb005ff8
commit
2254dfe9fa
19
Cargo.lock
generated
19
Cargo.lock
generated
@ -1468,24 +1468,6 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "enso-logging"
|
|
||||||
version = "0.3.1"
|
|
||||||
dependencies = [
|
|
||||||
"enso-logging-macros",
|
|
||||||
"web-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "enso-logging-macros"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"Inflector",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.53",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enso-macro-utils"
|
name = "enso-macro-utils"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -1610,7 +1592,6 @@ dependencies = [
|
|||||||
"boolinator",
|
"boolinator",
|
||||||
"derivative",
|
"derivative",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"enso-logging",
|
|
||||||
"enso-macros",
|
"enso-macros",
|
||||||
"enso-reflect",
|
"enso-reflect",
|
||||||
"enso-zst",
|
"enso-zst",
|
||||||
|
@ -115,8 +115,6 @@ futures = { version = "0.3" }
|
|||||||
itertools = { version = "0.12.1" }
|
itertools = { version = "0.12.1" }
|
||||||
lazy_static = { version = "1.4" }
|
lazy_static = { version = "1.4" }
|
||||||
serde_json = { version = "1.0", features = ["raw_value"] }
|
serde_json = { version = "1.0", features = ["raw_value"] }
|
||||||
smallvec = { version = "1.0.0" }
|
|
||||||
js-sys = { version = "0.3" }
|
|
||||||
owned_ttf_parser = { version = "0.15.1" }
|
owned_ttf_parser = { version = "0.15.1" }
|
||||||
convert_case = { version = "0.6.0" }
|
convert_case = { version = "0.6.0" }
|
||||||
rustybuzz = { version = "0.5.1" }
|
rustybuzz = { version = "0.5.1" }
|
||||||
@ -149,4 +147,3 @@ syn_1 = { package = "syn", version = "1.0", features = [
|
|||||||
quote = { version = "1.0.23" }
|
quote = { version = "1.0.23" }
|
||||||
semver = { version = "1.0.0", features = ["serde"] }
|
semver = { version = "1.0.0", features = ["serde"] }
|
||||||
strum = { version = "0.26.2", features = ["derive"] }
|
strum = { version = "0.26.2", features = ["derive"] }
|
||||||
thiserror = "1.0.40"
|
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "enso-logging"
|
|
||||||
version = "0.3.1"
|
|
||||||
authors = ["Enso Team <contact@luna-lang.org>"]
|
|
||||||
edition = "2021"
|
|
||||||
description = "An efficient logger for writing applications in Rust."
|
|
||||||
readme = "README.md"
|
|
||||||
homepage = "https://github.com/enso-org/enso/lib/rust/logging"
|
|
||||||
repository = "https://github.com/enso-org/enso"
|
|
||||||
license-file = "../../LICENSE"
|
|
||||||
keywords = ["logging"]
|
|
||||||
categories = ["development-tools::debugging"]
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
enso-logging-macros = { path = "macros" }
|
|
||||||
web-sys = { version = "0.3.4", features = ["console"] }
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
@ -1,17 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "enso-logging-macros"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
authors = ["Enso Team <contact@enso.org>"]
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro2 = { workspace = true }
|
|
||||||
quote = { workspace = true }
|
|
||||||
syn = { workspace = true }
|
|
||||||
Inflector = "0.11"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
@ -1,28 +0,0 @@
|
|||||||
//! Build script for [`enso_logging_macros`]. This is needed to make cargo aware that
|
|
||||||
//! the crate depends on the values of environment variables at compile time, and changes to those
|
|
||||||
//! variables should result in recompiling this crate and its dependents.
|
|
||||||
|
|
||||||
// === Non-Standard Linter Configuration ===
|
|
||||||
#![warn(missing_copy_implementations)]
|
|
||||||
#![warn(missing_debug_implementations)]
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
#![warn(trivial_casts)]
|
|
||||||
#![warn(trivial_numeric_casts)]
|
|
||||||
#![warn(unsafe_code)]
|
|
||||||
#![warn(unused_import_braces)]
|
|
||||||
#![warn(unused_qualifications)]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
declare_env_dependence("ENSO_MAX_LOG_LEVEL");
|
|
||||||
declare_env_dependence("ENSO_UNCOLLAPSED_LOG_LEVEL");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make cargo aware that the result of compiling this crate depends on an environment variable.
|
|
||||||
fn declare_env_dependence(env: &str) {
|
|
||||||
println!("cargo:rerun-if-env-changed={env}");
|
|
||||||
// This is a no-op assignment, except it makes cargo aware that the output depends on the env.
|
|
||||||
let value = std::env::var(env).unwrap_or_default();
|
|
||||||
println!("cargo:rustc-env={env}={value}");
|
|
||||||
}
|
|
@ -1,580 +0,0 @@
|
|||||||
//! Proc macros supporting the implementation of the `enso_logging` library.
|
|
||||||
|
|
||||||
// === Features ===
|
|
||||||
#![feature(anonymous_lifetime_in_impl_trait)]
|
|
||||||
#![feature(proc_macro_span)]
|
|
||||||
#![feature(let_chains)]
|
|
||||||
// === Non-Standard Linter Configuration ===
|
|
||||||
#![deny(unconditional_recursion)]
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
#![warn(trivial_casts)]
|
|
||||||
|
|
||||||
use inflector::Inflector;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// The log levels defined in the Console Web API.
|
|
||||||
/// See: https://console.spec.whatwg.org/#loglevel-severity
|
|
||||||
const WEB_LOG_LEVELS: &[&str] = &["error", "warn", "info", "debug"];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==================================
|
|
||||||
// === Compile-time configuration ===
|
|
||||||
// ==================================
|
|
||||||
|
|
||||||
const LEVEL_CONFIGURATION_ENV_VARS: LevelConfiguration<&str> = LevelConfiguration {
|
|
||||||
max_enabled: "ENSO_MAX_LOG_LEVEL",
|
|
||||||
max_uncollapsed: "ENSO_MAX_UNCOLLAPSED_LOG_LEVEL",
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =====================
|
|
||||||
// === Helper macros ===
|
|
||||||
// =====================
|
|
||||||
|
|
||||||
macro_rules! map_fns {
|
|
||||||
($Ty:tt, [$($covariant:tt),*]) => { map_fns!($Ty, [$($covariant),*], []); };
|
|
||||||
($Ty:ident, [$($covariant:ident),*], [$($invariant:ident),*]) => {
|
|
||||||
impl<T> $Ty<T> {
|
|
||||||
#[allow(unused)]
|
|
||||||
fn map<F, U>(self, f: F) -> $Ty<U> where F: Fn(T) -> U {
|
|
||||||
let Self {
|
|
||||||
$($covariant,)*
|
|
||||||
$($invariant,)*
|
|
||||||
} = self;
|
|
||||||
$Ty {
|
|
||||||
$($covariant: f($covariant),)*
|
|
||||||
$($invariant,)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn for_each<F>(self, mut f: F) where F: FnMut(T) {
|
|
||||||
$(f(self.$covariant);)*
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn as_ref(&self) -> $Ty<&T> {
|
|
||||||
let Self {
|
|
||||||
$($covariant,)*
|
|
||||||
$($invariant,)*
|
|
||||||
} = self;
|
|
||||||
$Ty {
|
|
||||||
$($covariant,)*
|
|
||||||
$($invariant: $invariant.clone(),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================
|
|
||||||
// === Interface ===
|
|
||||||
// =================
|
|
||||||
|
|
||||||
/// Implement a logging API for the spcecified log levels.
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn define_log_levels(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
||||||
use syn::parse::Parser;
|
|
||||||
let parser = syn::punctuated::Punctuated::<syn::Ident, syn::Token![,]>::parse_terminated;
|
|
||||||
let args = parser.parse(ts).unwrap();
|
|
||||||
let names: Vec<_> = args.into_iter().map(|ident| ident.to_string().to_snake_case()).collect();
|
|
||||||
let position_in_args = |name| {
|
|
||||||
names.iter().position(|arg| &name == arg).unwrap_or_else(|| {
|
|
||||||
let error = "Environment variable value must correspond to a defined log level";
|
|
||||||
panic!("{error}. Found: {name:?}, expected one of: {names:?}.")
|
|
||||||
})
|
|
||||||
};
|
|
||||||
let level_configuration = LEVEL_CONFIGURATION_ENV_VARS
|
|
||||||
.map(|var| std::env::var(var).ok().map(position_in_args).unwrap_or_default());
|
|
||||||
logging_api(names, level_configuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LevelConfiguration<T> {
|
|
||||||
max_enabled: T,
|
|
||||||
max_uncollapsed: T,
|
|
||||||
}
|
|
||||||
map_fns!(LevelConfiguration, [max_enabled, max_uncollapsed]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =====================
|
|
||||||
// === Top-level API ===
|
|
||||||
// =====================
|
|
||||||
|
|
||||||
fn logging_api(
|
|
||||||
level_names: impl IntoIterator<Item = String>,
|
|
||||||
config: LevelConfiguration<usize>,
|
|
||||||
) -> proc_macro::TokenStream {
|
|
||||||
let global_logger_ident = ident("GlobalLogger");
|
|
||||||
let global_logger_path = ident_to_path(global_logger_ident.clone());
|
|
||||||
let levels = levels(level_names, &config, global_logger_path);
|
|
||||||
let api: Api = [
|
|
||||||
span_trait(),
|
|
||||||
logger_trait(&levels),
|
|
||||||
span_api(&levels),
|
|
||||||
event_api(&levels),
|
|
||||||
global_logger(global_logger_ident, &levels),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
api.into_library().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// === Information used to construct level-specific interfaces ===
|
|
||||||
|
|
||||||
struct Level {
|
|
||||||
// Intrinsic properties of a level:
|
|
||||||
name: String,
|
|
||||||
enabled: bool,
|
|
||||||
uncollapsed: bool,
|
|
||||||
// Identifiers for API cross-references:
|
|
||||||
trait_methods: LoggerMethods<syn::Ident>,
|
|
||||||
global_logger_methods: LoggerMethods<syn::Path>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Level {
|
|
||||||
fn new(name: String, enabled: bool, uncollapsed: bool, global_logger_path: &syn::Path) -> Self {
|
|
||||||
let trait_methods = trait_methods(&name);
|
|
||||||
let global_logger_methods =
|
|
||||||
trait_methods.clone().map(|x| qualified(global_logger_path.clone(), x));
|
|
||||||
Level { global_logger_methods, trait_methods, enabled, uncollapsed, name }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn levels(
|
|
||||||
names: impl IntoIterator<Item = String>,
|
|
||||||
config: &LevelConfiguration<usize>,
|
|
||||||
global_logger_path: syn::Path,
|
|
||||||
) -> Vec<Level> {
|
|
||||||
let enabled = |i| config.max_enabled >= i;
|
|
||||||
let uncollapsed = |i| config.max_uncollapsed >= i;
|
|
||||||
let level = |(i, name)| Level::new(name, enabled(i), uncollapsed(i), &global_logger_path);
|
|
||||||
names.into_iter().enumerate().map(level).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ==============================
|
|
||||||
// === Representation of APIs ===
|
|
||||||
// ==============================
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct Api {
|
|
||||||
implementation: proc_macro2::TokenStream,
|
|
||||||
exports: Vec<Export>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Api {
|
|
||||||
fn extend(&mut self, mut other: Self) {
|
|
||||||
self.implementation.extend(other.implementation);
|
|
||||||
self.exports.append(&mut other.exports);
|
|
||||||
}
|
|
||||||
fn into_library(self) -> proc_macro2::TokenStream {
|
|
||||||
let reexport = |name: &syn::Ident| {
|
|
||||||
quote! { pub use crate::internal::#name; }
|
|
||||||
};
|
|
||||||
let prelude: proc_macro2::TokenStream =
|
|
||||||
self.exports.iter().filter(|&e| e.prelude).map(|e| reexport(&e.ident)).collect();
|
|
||||||
let exports: proc_macro2::TokenStream =
|
|
||||||
self.exports.iter().map(|e| reexport(&e.ident)).collect();
|
|
||||||
let implementation = self.implementation;
|
|
||||||
quote! {
|
|
||||||
/// Exports, intended to be used by glob-import.
|
|
||||||
pub mod prelude {
|
|
||||||
#prelude
|
|
||||||
}
|
|
||||||
#exports
|
|
||||||
/// Low-level interface, used by macro implementations.
|
|
||||||
pub mod internal {
|
|
||||||
#implementation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromIterator<Api> for Api {
|
|
||||||
fn from_iter<T: IntoIterator<Item = Api>>(iter: T) -> Self {
|
|
||||||
let mut collected: Api = Default::default();
|
|
||||||
iter.into_iter().for_each(|api| collected.extend(api));
|
|
||||||
collected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<proc_macro2::TokenStream> for Api {
|
|
||||||
fn from(implementation: proc_macro2::TokenStream) -> Self {
|
|
||||||
Self { implementation, ..Default::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Export {
|
|
||||||
ident: syn::Ident,
|
|
||||||
prelude: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Export {
|
|
||||||
fn prelude(ident: syn::Ident) -> Self {
|
|
||||||
Self { ident, prelude: true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =====================
|
|
||||||
// === LogSpan trait ===
|
|
||||||
// =====================
|
|
||||||
|
|
||||||
fn span_trait() -> Api {
|
|
||||||
let trait_name = ident("LogSpan");
|
|
||||||
let implementation = quote! {
|
|
||||||
/// Identifies a location in the source that may be traced in the logs.
|
|
||||||
pub trait #trait_name {
|
|
||||||
/// Log entry into the span, run the given closure, and log exit.
|
|
||||||
#[inline(always)]
|
|
||||||
fn in_scope<F, T>(self, f: F) -> T where Self: Sized, F: FnOnce() -> T {
|
|
||||||
let _scope = self.entered();
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
/// Log entry into the span, and log exit when the returned value is dropped.
|
|
||||||
#[inline(always)]
|
|
||||||
fn entered(self) -> Entered<Self> where Self: Sized {
|
|
||||||
Entered::new(self)
|
|
||||||
}
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
fn _enter(&self);
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
fn _exit(&self);
|
|
||||||
}
|
|
||||||
/// RAII guard that enters a span when created and exits when dropped.
|
|
||||||
pub struct Entered<S: #trait_name>(S);
|
|
||||||
impl<S: #trait_name> Entered<S> {
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub fn new(span: S) -> Self {
|
|
||||||
span._enter();
|
|
||||||
Self(span)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<S: #trait_name> Drop for Entered<S> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.0._exit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let exports = vec![Export::prelude(trait_name)];
|
|
||||||
Api { implementation, exports }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ====================
|
|
||||||
// === Logger trait ===
|
|
||||||
// ====================
|
|
||||||
|
|
||||||
fn logger_trait(levels: impl IntoIterator<Item = &Level>) -> Api {
|
|
||||||
let mut methods = proc_macro2::TokenStream::new();
|
|
||||||
for level in levels {
|
|
||||||
level.trait_methods.signatures().for_each(|x| methods.extend(x))
|
|
||||||
}
|
|
||||||
(quote! {
|
|
||||||
/// A type that serves as a destination for logging.
|
|
||||||
pub trait Logger {
|
|
||||||
#methods
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
|
||||||
struct LoggerMethods<T = proc_macro2::TokenStream> {
|
|
||||||
emit_fn: T,
|
|
||||||
enter_fn: T,
|
|
||||||
exit_fn: T,
|
|
||||||
}
|
|
||||||
map_fns!(LoggerMethods, [emit_fn, enter_fn, exit_fn]);
|
|
||||||
|
|
||||||
impl LoggerMethods<syn::Ident> {
|
|
||||||
fn signatures(&self) -> LoggerMethods {
|
|
||||||
let LoggerMethods { emit_fn, enter_fn, exit_fn } = self;
|
|
||||||
LoggerMethods {
|
|
||||||
emit_fn: quote! { #[allow(missing_docs)] fn #emit_fn(span: &str); },
|
|
||||||
enter_fn: quote! { #[allow(missing_docs)] fn #enter_fn(span: &str); },
|
|
||||||
exit_fn: quote! { #[allow(missing_docs)] fn #exit_fn(); },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_bodies(
|
|
||||||
&self,
|
|
||||||
bodies: &LoggerMethods<proc_macro2::TokenStream>,
|
|
||||||
) -> LoggerMethods<proc_macro2::TokenStream> {
|
|
||||||
let LoggerMethods { emit_fn, enter_fn, exit_fn } = self;
|
|
||||||
let LoggerMethods { emit_fn: emit_body, enter_fn: enter_body, exit_fn: exit_body } = bodies;
|
|
||||||
LoggerMethods {
|
|
||||||
emit_fn: quote! { #[inline] fn #emit_fn(span: &str) { #emit_body } },
|
|
||||||
enter_fn: quote! { #[inline] fn #enter_fn(span: &str) { #enter_body } },
|
|
||||||
exit_fn: quote! { #[inline] fn #exit_fn() { #exit_body } },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trait_methods(level: &str) -> LoggerMethods<syn::Ident> {
|
|
||||||
(LoggerMethods {
|
|
||||||
emit_fn: format!("emit_{level}"),
|
|
||||||
enter_fn: format!("enter_{level}"),
|
|
||||||
exit_fn: format!("exit_{level}"),
|
|
||||||
})
|
|
||||||
.as_ref()
|
|
||||||
.map(ident)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================
|
|
||||||
// === Event API ===
|
|
||||||
// =================
|
|
||||||
|
|
||||||
fn event_api(levels: impl IntoIterator<Item = &Level>) -> Api {
|
|
||||||
levels
|
|
||||||
.into_iter()
|
|
||||||
.map(|level| event_api_for_level(&level.name, &level.global_logger_methods, level.enabled))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn event_api_for_level(level: &str, methods: &LoggerMethods<syn::Path>, enabled: bool) -> Api {
|
|
||||||
let event_macro = ident(level);
|
|
||||||
let emit_fn = &methods.emit_fn;
|
|
||||||
let level_tag = level.to_screaming_snake_case();
|
|
||||||
let body = if enabled {
|
|
||||||
quote! {
|
|
||||||
use $crate::internal::Logger;
|
|
||||||
$crate::internal::#emit_fn(
|
|
||||||
&format!(
|
|
||||||
"[{}] {}:{} {}",
|
|
||||||
#level_tag,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
format_args!($($args)*)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
let _unused_at_this_log_level = format_args!($($args)*);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let implementation = quote! {
|
|
||||||
/// Emit a log message, if the log-level is enabled.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! #event_macro {
|
|
||||||
($($args:tt)*) => {{ #body }};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Api { implementation, ..Default::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ================
|
|
||||||
// === Span API ===
|
|
||||||
// ================
|
|
||||||
|
|
||||||
fn span_api(levels: impl IntoIterator<Item = &Level>) -> Api {
|
|
||||||
levels
|
|
||||||
.into_iter()
|
|
||||||
.map(|level| span_api_for_level(&level.name, &level.global_logger_methods, level.enabled))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn span_api_for_level(level: &str, methods: &LoggerMethods<syn::Path>, enabled: bool) -> Api {
|
|
||||||
let object_name = ident(level.to_pascal_case());
|
|
||||||
let macro_name = ident(format!("{level}_span"));
|
|
||||||
let object_contents = enabled.then_some(quote! { pub String }).unwrap_or_default();
|
|
||||||
let enter_fn = &methods.enter_fn;
|
|
||||||
let exit_fn = &methods.exit_fn;
|
|
||||||
let enter_impl = enabled
|
|
||||||
.then_some(quote! {
|
|
||||||
#enter_fn(&self.0);
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
let exit_impl = enabled
|
|
||||||
.then_some(quote! {
|
|
||||||
#exit_fn();
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
let level_tag = level.to_screaming_snake_case();
|
|
||||||
let creation_body = if enabled {
|
|
||||||
quote! {
|
|
||||||
$crate::internal::#object_name(
|
|
||||||
format!(
|
|
||||||
"[{}] {}:{} {}",
|
|
||||||
#level_tag,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
format_args!($($args)*)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
let _unused_at_this_log_level = format_args!($($args)*);
|
|
||||||
$crate::internal::#object_name()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let implementation = quote! {
|
|
||||||
/// Refers to a region in the source code that may have associated logging.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub struct #object_name(#object_contents);
|
|
||||||
impl LogSpan for #object_name {
|
|
||||||
#[inline(always)]
|
|
||||||
fn _enter(&self) {
|
|
||||||
#enter_impl
|
|
||||||
}
|
|
||||||
#[inline(always)]
|
|
||||||
fn _exit(&self) {
|
|
||||||
#exit_impl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Create an object that identifies a location in the source code for logging purposes.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! #macro_name {
|
|
||||||
($($args:tt)*) => {{
|
|
||||||
#creation_body
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
implementation.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =========================
|
|
||||||
// === Global Logger API ===
|
|
||||||
// =========================
|
|
||||||
|
|
||||||
fn global_logger(logger: syn::Ident, levels: impl IntoIterator<Item = &Level>) -> Api {
|
|
||||||
let console_logger = ident("NativeConsole");
|
|
||||||
let web_logger = ident("WebConsole");
|
|
||||||
let mut web_logger_methods = proc_macro2::TokenStream::new();
|
|
||||||
let mut console_logger_methods = proc_macro2::TokenStream::new();
|
|
||||||
let mut web_level = WEB_LOG_LEVELS.iter().copied().fuse();
|
|
||||||
let mut web = web_level.next().unwrap();
|
|
||||||
for level in levels {
|
|
||||||
level
|
|
||||||
.trait_methods
|
|
||||||
.with_bodies(&level.enabled.then(console_logger_impl).unwrap_or_default())
|
|
||||||
.for_each(|x| console_logger_methods.extend(x));
|
|
||||||
level
|
|
||||||
.trait_methods
|
|
||||||
.with_bodies(&level.enabled.then_some(web_logger_impl(web, level)).unwrap_or_default())
|
|
||||||
.for_each(|x| web_logger_methods.extend(x));
|
|
||||||
web = web_level.next().unwrap_or(web);
|
|
||||||
}
|
|
||||||
(quote! {
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
/// The currently-enabled global logger.
|
|
||||||
pub type #logger = web::#web_logger;
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
/// The currently-enabled global logger.
|
|
||||||
pub type #logger = native::#console_logger;
|
|
||||||
/// Logging support for wasm environments.
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
pub mod web {
|
|
||||||
use super::*;
|
|
||||||
/// A [`Logger`] that emits messages to the Console Web API.
|
|
||||||
pub struct #web_logger;
|
|
||||||
impl Logger for #web_logger {
|
|
||||||
#web_logger_methods
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Logging support for non-wasm environments.
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
pub mod native {
|
|
||||||
use super::*;
|
|
||||||
/// A [`Logger`] that emits messages to the native console.
|
|
||||||
pub struct #console_logger;
|
|
||||||
thread_local! {
|
|
||||||
static CONSOLE_INDENT: core::cell::Cell<usize> = core::cell::Cell::new(0);
|
|
||||||
}
|
|
||||||
impl Logger for #console_logger {
|
|
||||||
#console_logger_methods
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =============================
|
|
||||||
// === Native console logger ===
|
|
||||||
// =============================
|
|
||||||
|
|
||||||
fn console_logger_impl() -> LoggerMethods {
|
|
||||||
LoggerMethods {
|
|
||||||
emit_fn: quote! {
|
|
||||||
let indent = CONSOLE_INDENT.get();
|
|
||||||
println!("{:indent$}{}", "", span, indent=indent*4);
|
|
||||||
},
|
|
||||||
enter_fn: quote! {
|
|
||||||
let indent = CONSOLE_INDENT.get();
|
|
||||||
println!("{:indent$}{}", "", span, indent=indent*4);
|
|
||||||
CONSOLE_INDENT.set(indent + 1);
|
|
||||||
},
|
|
||||||
exit_fn: quote! {
|
|
||||||
let indent = CONSOLE_INDENT.get();
|
|
||||||
CONSOLE_INDENT.set(indent - 1);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ======================
|
|
||||||
// === Web API logger ===
|
|
||||||
// ======================
|
|
||||||
|
|
||||||
fn web_logger_impl(web_level: &str, level: &Level) -> LoggerMethods {
|
|
||||||
let event_fn = ident(format!("{web_level}_1"));
|
|
||||||
let group_fn = ident(if level.uncollapsed { "group_1" } else { "group_collapsed_1" });
|
|
||||||
LoggerMethods {
|
|
||||||
emit_fn: quote! {
|
|
||||||
web_sys::console::#event_fn(&span.into());
|
|
||||||
},
|
|
||||||
enter_fn: quote! {
|
|
||||||
web_sys::console::#group_fn(&span.into());
|
|
||||||
},
|
|
||||||
exit_fn: quote! {
|
|
||||||
web_sys::console::group_end();
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ============================
|
|
||||||
// === Syn-building helpers ===
|
|
||||||
// ============================
|
|
||||||
|
|
||||||
fn qualified(mut path: syn::Path, name: syn::Ident) -> syn::Path {
|
|
||||||
path.segments.push(path_segment(name));
|
|
||||||
path
|
|
||||||
}
|
|
||||||
|
|
||||||
fn path_segment(ident: syn::Ident) -> syn::PathSegment {
|
|
||||||
syn::PathSegment { ident, arguments: Default::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ident(name: impl AsRef<str>) -> syn::Ident {
|
|
||||||
syn::Ident::new(name.as_ref(), proc_macro2::Span::call_site())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ident_to_path(segment: syn::Ident) -> syn::Path {
|
|
||||||
syn::Path {
|
|
||||||
leading_colon: Default::default(),
|
|
||||||
segments: std::iter::once(path_segment(segment)).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
//! High-performance logging library.
|
|
||||||
|
|
||||||
// === Non-Standard Linter Configuration ===
|
|
||||||
#![deny(unconditional_recursion)]
|
|
||||||
#![warn(missing_docs)]
|
|
||||||
#![warn(trivial_casts)]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enso_logging_macros::define_log_levels![Error, Warn, Info, Debug, Trace];
|
|
@ -9,11 +9,14 @@ homepage = "https://github.com/enso-org/enso"
|
|||||||
repository = "https://github.com/enso-org/enso"
|
repository = "https://github.com/enso-org/enso"
|
||||||
license-file = "../../LICENSE"
|
license-file = "../../LICENSE"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
debug = ["dep:enso-parser-syntax-tree-visitor"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
enso-prelude = { path = "../prelude" }
|
enso-prelude = { path = "../prelude" }
|
||||||
enso-reflect = { path = "../reflect" }
|
enso-reflect = { path = "../reflect" }
|
||||||
enso-data-structures = { path = "../data-structures" }
|
enso-data-structures = { path = "../data-structures" }
|
||||||
enso-parser-syntax-tree-visitor = { path = "src/syntax/tree/visitor" }
|
enso-parser-syntax-tree-visitor = { path = "src/syntax/tree/visitor", optional = true }
|
||||||
paste = { version = "1.0" }
|
paste = { version = "1.0" }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
@ -10,7 +10,7 @@ repository = "https://github.com/enso-org/enso"
|
|||||||
license-file = "../../LICENSE"
|
license-file = "../../LICENSE"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
enso-parser = { path = "../" }
|
enso-parser = { path = "../", features = ["debug"] }
|
||||||
enso-metamodel = { path = "../../metamodel", features = ["rust"] }
|
enso-metamodel = { path = "../../metamodel", features = ["rust"] }
|
||||||
enso-metamodel-lexpr = { path = "../../metamodel/lexpr" }
|
enso-metamodel-lexpr = { path = "../../metamodel/lexpr" }
|
||||||
enso-reflect = { path = "../../reflect" }
|
enso-reflect = { path = "../../reflect" }
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
//! Tests for [`enso_parser`].
|
//! Parses Enso sources and reports any syntax errors, while performing internal consistency checks.
|
||||||
|
//! Source files may be specified as command line arguments; if none a provided, source code will be
|
||||||
|
//! read from standard input.
|
||||||
|
|
||||||
// === Non-Standard Linter Configuration ===
|
// === Non-Standard Linter Configuration ===
|
||||||
#![allow(clippy::option_map_unit_fn)]
|
#![allow(clippy::option_map_unit_fn)]
|
||||||
@ -38,7 +40,7 @@ fn check_file(path: &str, mut code: &str, parser: &mut enso_parser::Parser) {
|
|||||||
}
|
}
|
||||||
let ast = parser.run(code);
|
let ast = parser.run(code);
|
||||||
let errors = RefCell::new(vec![]);
|
let errors = RefCell::new(vec![]);
|
||||||
ast.map(|tree| {
|
ast.visit_trees(|tree| {
|
||||||
if let enso_parser::syntax::tree::Variant::Invalid(err) = &*tree.variant {
|
if let enso_parser::syntax::tree::Variant::Invalid(err) = &*tree.variant {
|
||||||
let error = format!("{}: {}", err.error.message, tree.code());
|
let error = format!("{}: {}", err.error.message, tree.code());
|
||||||
errors.borrow_mut().push((error, tree.span.clone()));
|
errors.borrow_mut().push((error, tree.span.clone()));
|
@ -1702,7 +1702,7 @@ impl Errors {
|
|||||||
let ast = parse(code);
|
let ast = parse(code);
|
||||||
expect_tree_representing_code(code, &ast);
|
expect_tree_representing_code(code, &ast);
|
||||||
let errors = core::cell::Cell::new(Errors::default());
|
let errors = core::cell::Cell::new(Errors::default());
|
||||||
ast.map(|tree| match &*tree.variant {
|
ast.visit_trees(|tree| match &*tree.variant {
|
||||||
enso_parser::syntax::tree::Variant::Invalid(_) => {
|
enso_parser::syntax::tree::Variant::Invalid(_) => {
|
||||||
errors.update(|e| Self { invalid_node: true, ..e });
|
errors.update(|e| Self { invalid_node: true, ..e });
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ serde = { workspace = true }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
enso-metamodel = { path = "../../metamodel", features = ["rust"] }
|
enso-metamodel = { path = "../../metamodel", features = ["rust"] }
|
||||||
enso-metamodel-lexpr = { path = "../../metamodel/lexpr" }
|
enso-metamodel-lexpr = { path = "../../metamodel/lexpr" }
|
||||||
|
enso-parser = { path = "..", features = ["debug"] }
|
||||||
lexpr = "0.2.6"
|
lexpr = "0.2.6"
|
||||||
pretty_assertions = "1.4"
|
pretty_assertions = "1.4"
|
||||||
|
|
||||||
|
@ -246,9 +246,7 @@ impl<'a, L: Location> Span<'a, L> {
|
|||||||
}
|
}
|
||||||
Some(_) => break,
|
Some(_) => break,
|
||||||
None => {
|
None => {
|
||||||
let unexpected_condition = "Internal error: Expected greater indent level.";
|
self.warn("Internal error: Expected greater indent level.");
|
||||||
self.warn(unexpected_condition);
|
|
||||||
warn!("{unexpected_condition}");
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ fn extract_docs(_filename: &str, mut code: &str) -> Vec<String> {
|
|||||||
}
|
}
|
||||||
let ast = enso_parser::Parser::new().run(code);
|
let ast = enso_parser::Parser::new().run(code);
|
||||||
let docs = RefCell::new(vec![]);
|
let docs = RefCell::new(vec![]);
|
||||||
ast.map(|tree| match &*tree.variant {
|
ast.visit_trees(|tree| match &*tree.variant {
|
||||||
enso_parser::syntax::tree::Variant::Documented(doc) => {
|
enso_parser::syntax::tree::Variant::Documented(doc) => {
|
||||||
docs.borrow_mut().push(doc.documentation.clone());
|
docs.borrow_mut().push(doc.documentation.clone());
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ pub extern "system" fn Java_org_enso_syntax2_Parser_parseInput(
|
|||||||
if let Some((meta_, code_)) = enso_parser::metadata::parse(input) {
|
if let Some((meta_, code_)) = enso_parser::metadata::parse(input) {
|
||||||
match meta_ {
|
match meta_ {
|
||||||
Ok(meta_) => meta = Some(meta_),
|
Ok(meta_) => meta = Some(meta_),
|
||||||
Err(e) => error!("Ignoring invalid metadata: {e}."),
|
Err(e) => eprintln!("Ignoring invalid metadata: {e}."),
|
||||||
}
|
}
|
||||||
code = code_;
|
code = code_;
|
||||||
}
|
}
|
||||||
|
@ -302,19 +302,16 @@ impl<'s> Resolver<'s> {
|
|||||||
if self.macros.len() > self.macro_scope_start() {
|
if self.macros.len() > self.macro_scope_start() {
|
||||||
let current_macro = self.macros.last_mut().unwrap();
|
let current_macro = self.macros.last_mut().unwrap();
|
||||||
if let Some(subsegments) = current_macro.possible_next_segments.get(repr) {
|
if let Some(subsegments) = current_macro.possible_next_segments.get(repr) {
|
||||||
trace!("Entering next segment of the current macro.");
|
|
||||||
let mut new_match_tree =
|
let mut new_match_tree =
|
||||||
Self::move_to_next_segment(&mut current_macro.matched_macro_def, subsegments);
|
Self::move_to_next_segment(&mut current_macro.matched_macro_def, subsegments);
|
||||||
mem::swap(&mut new_match_tree, &mut current_macro.possible_next_segments);
|
mem::swap(&mut new_match_tree, &mut current_macro.possible_next_segments);
|
||||||
return Step::StartSegment(token);
|
return Step::StartSegment(token);
|
||||||
} else if let Some(popped) = self.pop_macro_stack_if_reserved(repr) {
|
} else if let Some(popped) = self.pop_macro_stack_if_reserved(repr) {
|
||||||
trace!("Next token reserved by parent macro. Resolving current macro.");
|
|
||||||
self.resolve(popped);
|
self.resolve(popped);
|
||||||
return Step::MacroStackPop(token.into());
|
return Step::MacroStackPop(token.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(segments) = root_macro_map.get(repr, context) {
|
if let Some(segments) = root_macro_map.get(repr, context) {
|
||||||
trace!("Starting a new nested macro resolution.");
|
|
||||||
let mut matched_macro_def = default();
|
let mut matched_macro_def = default();
|
||||||
let segments_start = self.segments.len();
|
let segments_start = self.segments.len();
|
||||||
let new_macro = PartiallyMatchedMacro {
|
let new_macro = PartiallyMatchedMacro {
|
||||||
@ -328,7 +325,6 @@ impl<'s> Resolver<'s> {
|
|||||||
self.macros.push(new_macro);
|
self.macros.push(new_macro);
|
||||||
Step::StartSegment(token)
|
Step::StartSegment(token)
|
||||||
} else {
|
} else {
|
||||||
trace!("Consuming token as current segment body.");
|
|
||||||
Step::NormalToken(token.into())
|
Step::NormalToken(token.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ use crate::syntax::*;
|
|||||||
|
|
||||||
use crate::span_builder;
|
use crate::span_builder;
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
use enso_parser_syntax_tree_visitor::Visitor;
|
use enso_parser_syntax_tree_visitor::Visitor;
|
||||||
|
|
||||||
|
|
||||||
@ -61,7 +62,8 @@ impl<'s> Default for Tree<'s> {
|
|||||||
macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args)*
|
macro_rules! with_ast_definition { ($f:ident ($($args:tt)*)) => { $f! { $($args)*
|
||||||
/// [`Tree`] variants definition. See its docs to learn more.
|
/// [`Tree`] variants definition. See its docs to learn more.
|
||||||
#[tagged_enum]
|
#[tagged_enum]
|
||||||
#[derive(Clone, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
#[allow(clippy::large_enum_variant)] // Inefficient. Will be fixed in #182878443.
|
#[allow(clippy::large_enum_variant)] // Inefficient. Will be fixed in #182878443.
|
||||||
#[tagged_enum(apply_attributes_to = "variants")]
|
#[tagged_enum(apply_attributes_to = "variants")]
|
||||||
#[reflect(inline)]
|
#[reflect(inline)]
|
||||||
@ -383,7 +385,8 @@ with_ast_definition!(generate_ast_definition());
|
|||||||
// === Invalid ===
|
// === Invalid ===
|
||||||
|
|
||||||
/// Error of parsing attached to an [`Tree`] node.
|
/// Error of parsing attached to an [`Tree`] node.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[reflect(transparent)]
|
#[reflect(transparent)]
|
||||||
#[serde(from = "crate::serialization::Error")]
|
#[serde(from = "crate::serialization::Error")]
|
||||||
@ -417,7 +420,8 @@ impl<'s> span::Builder<'s> for Error {
|
|||||||
// === Argument blocks ===
|
// === Argument blocks ===
|
||||||
|
|
||||||
/// An argument specification on its own line.
|
/// An argument specification on its own line.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
pub struct ArgumentDefinitionLine<'s> {
|
pub struct ArgumentDefinitionLine<'s> {
|
||||||
/// The token beginning the line.
|
/// The token beginning the line.
|
||||||
pub newline: token::Newline<'s>,
|
pub newline: token::Newline<'s>,
|
||||||
@ -435,7 +439,8 @@ impl<'s> span::Builder<'s> for ArgumentDefinitionLine<'s> {
|
|||||||
// === Text literals ===
|
// === Text literals ===
|
||||||
|
|
||||||
/// A component of a text literal, within the quotation marks.
|
/// A component of a text literal, within the quotation marks.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
pub enum TextElement<'s> {
|
pub enum TextElement<'s> {
|
||||||
/// The text content of the literal. If it is multiline, the offset information may contain
|
/// The text content of the literal. If it is multiline, the offset information may contain
|
||||||
/// part of the content, after trimming appropriately.
|
/// part of the content, after trimming appropriately.
|
||||||
@ -481,7 +486,8 @@ impl<'s> span::Builder<'s> for TextElement<'s> {
|
|||||||
// === Documentation ===
|
// === Documentation ===
|
||||||
|
|
||||||
/// A documentation comment.
|
/// A documentation comment.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
pub struct DocComment<'s> {
|
pub struct DocComment<'s> {
|
||||||
/// The comment-initiating token.
|
/// The comment-initiating token.
|
||||||
pub open: token::TextStart<'s>,
|
pub open: token::TextStart<'s>,
|
||||||
@ -522,7 +528,8 @@ impl<'s> span::Builder<'s> for DocComment<'s> {
|
|||||||
|
|
||||||
// === Number literals ===
|
// === Number literals ===
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct FractionalDigits<'s> {
|
pub struct FractionalDigits<'s> {
|
||||||
/// The dot operator.
|
/// The dot operator.
|
||||||
@ -541,7 +548,8 @@ impl<'s> span::Builder<'s> for FractionalDigits<'s> {
|
|||||||
// === Functions ===
|
// === Functions ===
|
||||||
|
|
||||||
/// A function argument definition.
|
/// A function argument definition.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
pub struct ArgumentDefinition<'s> {
|
pub struct ArgumentDefinition<'s> {
|
||||||
/// Opening parenthesis (outer).
|
/// Opening parenthesis (outer).
|
||||||
pub open: Option<token::OpenSymbol<'s>>,
|
pub open: Option<token::OpenSymbol<'s>>,
|
||||||
@ -576,7 +584,8 @@ impl<'s> span::Builder<'s> for ArgumentDefinition<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A default value specification in a function argument definition.
|
/// A default value specification in a function argument definition.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
pub struct ArgumentDefault<'s> {
|
pub struct ArgumentDefault<'s> {
|
||||||
/// The `=` token.
|
/// The `=` token.
|
||||||
pub equals: token::Operator<'s>,
|
pub equals: token::Operator<'s>,
|
||||||
@ -591,7 +600,8 @@ impl<'s> span::Builder<'s> for ArgumentDefault<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A type ascribed to an argument definition.
|
/// A type ascribed to an argument definition.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
pub struct ArgumentType<'s> {
|
pub struct ArgumentType<'s> {
|
||||||
/// The `:` token.
|
/// The `:` token.
|
||||||
pub operator: token::Operator<'s>,
|
pub operator: token::Operator<'s>,
|
||||||
@ -607,7 +617,8 @@ impl<'s> span::Builder<'s> for ArgumentType<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A function return type specification.
|
/// A function return type specification.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
pub struct ReturnSpecification<'s> {
|
pub struct ReturnSpecification<'s> {
|
||||||
/// The `->` operator.
|
/// The `->` operator.
|
||||||
pub arrow: token::Operator<'s>,
|
pub arrow: token::Operator<'s>,
|
||||||
@ -626,7 +637,8 @@ impl<'s> span::Builder<'s> for ReturnSpecification<'s> {
|
|||||||
// === CaseOf ===
|
// === CaseOf ===
|
||||||
|
|
||||||
/// A line that may contain a case-expression in a case-of expression.
|
/// A line that may contain a case-expression in a case-of expression.
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
pub struct CaseLine<'s> {
|
pub struct CaseLine<'s> {
|
||||||
/// The token beginning the line. This will always be present, unless the first case-expression
|
/// The token beginning the line. This will always be present, unless the first case-expression
|
||||||
/// is on the same line as the initial case-of.
|
/// is on the same line as the initial case-of.
|
||||||
@ -653,7 +665,8 @@ impl<'s> span::Builder<'s> for CaseLine<'s> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A case-expression in a case-of expression.
|
/// A case-expression in a case-of expression.
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
pub struct Case<'s> {
|
pub struct Case<'s> {
|
||||||
/// Documentation, if present.
|
/// Documentation, if present.
|
||||||
pub documentation: Option<DocComment<'s>>,
|
pub documentation: Option<DocComment<'s>>,
|
||||||
@ -692,7 +705,8 @@ impl<'s> span::Builder<'s> for Case<'s> {
|
|||||||
pub type OperatorOrError<'s> = Result<token::Operator<'s>, MultipleOperatorError<'s>>;
|
pub type OperatorOrError<'s> = Result<token::Operator<'s>, MultipleOperatorError<'s>>;
|
||||||
|
|
||||||
/// Error indicating multiple operators found next to each other, like `a + * b`.
|
/// Error indicating multiple operators found next to each other, like `a + * b`.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct MultipleOperatorError<'s> {
|
pub struct MultipleOperatorError<'s> {
|
||||||
pub operators: NonEmptyVec<token::Operator<'s>>,
|
pub operators: NonEmptyVec<token::Operator<'s>>,
|
||||||
@ -731,7 +745,8 @@ impl<'s> NonEmptyOperatorSequence<'s> for OperatorOrError<'s> {
|
|||||||
// === MultiSegmentApp ===
|
// === MultiSegmentApp ===
|
||||||
|
|
||||||
/// A segment of [`MultiSegmentApp`], like `if cond` in the `if cond then ok else fail` expression.
|
/// A segment of [`MultiSegmentApp`], like `if cond` in the `if cond then ok else fail` expression.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub struct MultiSegmentAppSegment<'s> {
|
pub struct MultiSegmentAppSegment<'s> {
|
||||||
pub header: Token<'s>,
|
pub header: Token<'s>,
|
||||||
@ -748,7 +763,8 @@ impl<'s> span::Builder<'s> for MultiSegmentAppSegment<'s> {
|
|||||||
// === Array and Tuple ===
|
// === Array and Tuple ===
|
||||||
|
|
||||||
/// A node following an operator.
|
/// A node following an operator.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Visitor, Serialize, Reflect, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Reflect, Deserialize)]
|
||||||
pub struct OperatorDelimitedTree<'s> {
|
pub struct OperatorDelimitedTree<'s> {
|
||||||
/// The delimiting operator.
|
/// The delimiting operator.
|
||||||
pub operator: token::Operator<'s>,
|
pub operator: token::Operator<'s>,
|
||||||
@ -1098,25 +1114,12 @@ impl<'s> From<token::Ident<'s>> for Tree<'s> {
|
|||||||
/// could move to it as soon as this error gets resolved:
|
/// could move to it as soon as this error gets resolved:
|
||||||
/// https://github.com/rust-lang/rust/issues/96634.
|
/// https://github.com/rust-lang/rust/issues/96634.
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub trait Visitor {
|
#[cfg(feature = "debug")]
|
||||||
fn before_visiting_children(&mut self) {}
|
pub trait Visitor {}
|
||||||
fn after_visiting_children(&mut self) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The visitor trait allowing for [`Tree`] nodes traversal.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub trait TreeVisitor<'s, 'a>: Visitor {
|
|
||||||
fn visit(&mut self, ast: &'a Tree<'s>) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The visitor trait allowing for [`Span`] traversal.
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub trait SpanVisitor<'s, 'a>: Visitor {
|
|
||||||
fn visit(&mut self, ast: span::Ref<'s, 'a>) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The visitor trait allowing for [`Item`] traversal.
|
/// The visitor trait allowing for [`Item`] traversal.
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
pub trait ItemVisitor<'s, 'a>: Visitor {
|
pub trait ItemVisitor<'s, 'a>: Visitor {
|
||||||
fn visit_item(&mut self, ast: item::Ref<'s, 'a>) -> bool;
|
fn visit_item(&mut self, ast: item::Ref<'s, 'a>) -> bool;
|
||||||
}
|
}
|
||||||
@ -1129,11 +1132,13 @@ macro_rules! define_visitor {
|
|||||||
$visitable:ident
|
$visitable:ident
|
||||||
) => {
|
) => {
|
||||||
/// The visitable trait. See documentation of [`define_visitor`] to learn more.
|
/// The visitable trait. See documentation of [`define_visitor`] to learn more.
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub trait $visitable<'s, 'a> {
|
pub trait $visitable<'s, 'a> {
|
||||||
fn $visit<V: $visitor<'s, 'a>>(&'a self, _visitor: &mut V) {}
|
fn $visit<V: $visitor<'s, 'a>>(&'a self, _visitor: &mut V) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
impl<'s, 'a, T: $visitable<'s, 'a>> $visitable<'s, 'a> for Option<T> {
|
impl<'s, 'a, T: $visitable<'s, 'a>> $visitable<'s, 'a> for Option<T> {
|
||||||
fn $visit<V: $visitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
fn $visit<V: $visitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
||||||
if let Some(elem) = self {
|
if let Some(elem) = self {
|
||||||
@ -1142,6 +1147,7 @@ macro_rules! define_visitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
impl<'s, 'a, T: $visitable<'s, 'a>, E: $visitable<'s, 'a>> $visitable<'s, 'a>
|
impl<'s, 'a, T: $visitable<'s, 'a>, E: $visitable<'s, 'a>> $visitable<'s, 'a>
|
||||||
for Result<T, E>
|
for Result<T, E>
|
||||||
{
|
{
|
||||||
@ -1153,12 +1159,14 @@ macro_rules! define_visitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
impl<'s, 'a, T: $visitable<'s, 'a>> $visitable<'s, 'a> for Vec<T> {
|
impl<'s, 'a, T: $visitable<'s, 'a>> $visitable<'s, 'a> for Vec<T> {
|
||||||
fn $visit<V: $visitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
fn $visit<V: $visitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
||||||
self.iter().map(|t| $visitable::$visit(t, visitor)).for_each(drop);
|
self.iter().map(|t| $visitable::$visit(t, visitor)).for_each(drop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
impl<'s, 'a, T: $visitable<'s, 'a>> $visitable<'s, 'a> for NonEmptyVec<T> {
|
impl<'s, 'a, T: $visitable<'s, 'a>> $visitable<'s, 'a> for NonEmptyVec<T> {
|
||||||
fn $visit<V: $visitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
fn $visit<V: $visitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
||||||
self.iter().map(|t| $visitable::$visit(t, visitor)).for_each(drop);
|
self.iter().map(|t| $visitable::$visit(t, visitor)).for_each(drop);
|
||||||
@ -1167,33 +1175,14 @@ macro_rules! define_visitor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! define_visitor_for_tokens {
|
|
||||||
(
|
|
||||||
$(#$kind_meta:tt)*
|
|
||||||
pub enum $kind:ident {
|
|
||||||
$(
|
|
||||||
$(#$variant_meta:tt)*
|
|
||||||
$variant:ident $({$($args:tt)*})?
|
|
||||||
),* $(,)?
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
impl<'s, 'a> TreeVisitable<'s, 'a> for token::$kind {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
define_visitor!(Tree, visit, TreeVisitor, TreeVisitable);
|
|
||||||
define_visitor!(Span, visit_span, SpanVisitor, SpanVisitable);
|
|
||||||
define_visitor!(Item, visit_item, ItemVisitor, ItemVisitable);
|
define_visitor!(Item, visit_item, ItemVisitor, ItemVisitable);
|
||||||
|
|
||||||
crate::with_token_definition!(define_visitor_for_tokens());
|
|
||||||
|
|
||||||
|
|
||||||
// === Trait Implementations for Simple Leaf Types ===
|
// === Trait Implementations for Simple Leaf Types ===
|
||||||
|
|
||||||
macro_rules! spanless_leaf_impls {
|
macro_rules! spanless_leaf_impls {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
impl<'s, 'a> TreeVisitable<'s, 'a> for $ty {}
|
#[cfg(feature = "debug")]
|
||||||
impl<'a, 's> SpanVisitable<'s, 'a> for $ty {}
|
|
||||||
impl<'a, 's> ItemVisitable<'s, 'a> for $ty {}
|
impl<'a, 's> ItemVisitable<'s, 'a> for $ty {}
|
||||||
impl<'s> span::Builder<'s> for $ty {
|
impl<'s> span::Builder<'s> for $ty {
|
||||||
fn add_to_span(&mut self, span: Span<'s>) -> Span<'s> {
|
fn add_to_span(&mut self, span: Span<'s>) -> Span<'s> {
|
||||||
@ -1209,45 +1198,10 @@ spanless_leaf_impls!(VisibleOffset);
|
|||||||
spanless_leaf_impls!(Cow<'static, str>);
|
spanless_leaf_impls!(Cow<'static, str>);
|
||||||
|
|
||||||
|
|
||||||
// === TreeVisitable special cases ===
|
|
||||||
|
|
||||||
impl<'s, 'a> TreeVisitable<'s, 'a> for Tree<'s> {
|
|
||||||
fn visit<V: TreeVisitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
|
||||||
if visitor.visit(self) {
|
|
||||||
self.variant.visit(visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl<'s, 'a, T> TreeVisitable<'s, 'a> for Token<'s, T> {}
|
|
||||||
|
|
||||||
|
|
||||||
// === SpanVisitable special cases ===
|
|
||||||
|
|
||||||
impl<'s, 'a> SpanVisitable<'s, 'a> for Tree<'s> {
|
|
||||||
fn visit_span<V: SpanVisitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
|
||||||
if visitor.visit(span::Ref {
|
|
||||||
left_offset: &self.span.left_offset,
|
|
||||||
code_length: self.span.code_length,
|
|
||||||
}) {
|
|
||||||
self.variant.visit_span(visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl<'a, 's, T> SpanVisitable<'s, 'a> for Token<'s, T> {
|
|
||||||
fn visit_span<V: SpanVisitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
|
||||||
let code_length = self.code.length();
|
|
||||||
visitor.visit(span::Ref { left_offset: &self.left_offset, code_length });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// === ItemVisitable special cases ===
|
// === ItemVisitable special cases ===
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
impl<'s, 'a> ItemVisitable<'s, 'a> for Tree<'s> {
|
impl<'s, 'a> ItemVisitable<'s, 'a> for Tree<'s> {
|
||||||
fn visit_item<V: ItemVisitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
fn visit_item<V: ItemVisitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
||||||
if visitor.visit_item(item::Ref::Tree(self)) {
|
if visitor.visit_item(item::Ref::Tree(self)) {
|
||||||
@ -1256,6 +1210,7 @@ impl<'s, 'a> ItemVisitable<'s, 'a> for Tree<'s> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
impl<'s: 'a, 'a, T: 'a> ItemVisitable<'s, 'a> for Token<'s, T>
|
impl<'s: 'a, 'a, T: 'a> ItemVisitable<'s, 'a> for Token<'s, T>
|
||||||
where &'a Token<'s, T>: Into<token::Ref<'s, 'a>>
|
where &'a Token<'s, T>: Into<token::Ref<'s, 'a>>
|
||||||
{
|
{
|
||||||
@ -1271,13 +1226,16 @@ where &'a Token<'s, T>: Into<token::Ref<'s, 'a>>
|
|||||||
// ==========================
|
// ==========================
|
||||||
|
|
||||||
/// A visitor collecting code representation of AST nodes.
|
/// A visitor collecting code representation of AST nodes.
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
struct CodePrinterVisitor {
|
struct CodePrinterVisitor {
|
||||||
pub code: String,
|
pub code: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
impl Visitor for CodePrinterVisitor {}
|
impl Visitor for CodePrinterVisitor {}
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
impl<'s, 'a> ItemVisitor<'s, 'a> for CodePrinterVisitor {
|
impl<'s, 'a> ItemVisitor<'s, 'a> for CodePrinterVisitor {
|
||||||
fn visit_item(&mut self, item: item::Ref<'s, 'a>) -> bool {
|
fn visit_item(&mut self, item: item::Ref<'s, 'a>) -> bool {
|
||||||
match item {
|
match item {
|
||||||
@ -1291,6 +1249,7 @@ impl<'s, 'a> ItemVisitor<'s, 'a> for CodePrinterVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
impl<'s> Tree<'s> {
|
impl<'s> Tree<'s> {
|
||||||
/// Code generator of this AST.
|
/// Code generator of this AST.
|
||||||
pub fn code(&self) -> String {
|
pub fn code(&self) -> String {
|
||||||
@ -1309,35 +1268,11 @@ impl<'s> Tree<'s> {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// =================
|
// =====================
|
||||||
// === FnVisitor ===
|
|
||||||
// =================
|
|
||||||
|
|
||||||
/// A visitor allowing running a function on every [`Tree`] node.
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub struct FnVisitor<F>(pub F);
|
|
||||||
|
|
||||||
impl<F> Visitor for FnVisitor<F> {}
|
|
||||||
impl<'s: 'a, 'a, T, F: Fn(&'a Tree<'s>) -> T> TreeVisitor<'s, 'a> for FnVisitor<F> {
|
|
||||||
fn visit(&mut self, ast: &'a Tree<'s>) -> bool {
|
|
||||||
(self.0)(ast);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl<'s> Tree<'s> {
|
|
||||||
/// Map the provided function over each [`Tree`] node. The function results will be discarded.
|
|
||||||
pub fn map<T>(&self, f: impl Fn(&Tree<'s>) -> T) {
|
|
||||||
let mut visitor = FnVisitor(f);
|
|
||||||
self.visit(&mut visitor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// === ItemFnVisitor ===
|
// === ItemFnVisitor ===
|
||||||
|
// =====================
|
||||||
|
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
impl<'s> Tree<'s> {
|
impl<'s> Tree<'s> {
|
||||||
/// Apply the provided function to each [`Token`] or [`Tree`] that is a child of the node.
|
/// Apply the provided function to each [`Token`] or [`Tree`] that is a child of the node.
|
||||||
pub fn visit_items<F>(&self, f: F)
|
pub fn visit_items<F>(&self, f: F)
|
||||||
@ -1356,4 +1291,24 @@ impl<'s> Tree<'s> {
|
|||||||
}
|
}
|
||||||
self.variant.visit_item(&mut ItemFnVisitor { f });
|
self.variant.visit_item(&mut ItemFnVisitor { f });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply the provided function recursively to each [`Tree`] that is a descendant of the node.
|
||||||
|
pub fn visit_trees<F>(&self, f: F)
|
||||||
|
where F: for<'a> FnMut(&'a Tree<'s>) {
|
||||||
|
struct ItemFnVisitor<F> {
|
||||||
|
f: F,
|
||||||
|
}
|
||||||
|
impl<F> Visitor for ItemFnVisitor<F> {}
|
||||||
|
impl<'a, 's: 'a, F> ItemVisitor<'s, 'a> for ItemFnVisitor<F>
|
||||||
|
where F: FnMut(&'a Tree<'s>)
|
||||||
|
{
|
||||||
|
fn visit_item(&mut self, item: item::Ref<'s, 'a>) -> bool {
|
||||||
|
if let item::Ref::Tree(tree) = item {
|
||||||
|
(self.f)(tree);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.variant.visit_item(&mut ItemFnVisitor { f });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@ use crate::syntax::tree::*;
|
|||||||
// =============
|
// =============
|
||||||
|
|
||||||
/// A line of code.
|
/// A line of code.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Visitor, Reflect, Serialize, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Reflect, Serialize, Deserialize)]
|
||||||
pub struct Line<'s> {
|
pub struct Line<'s> {
|
||||||
/// Token ending the previous line, if any.
|
/// Token ending the previous line, if any.
|
||||||
pub newline: token::Newline<'s>,
|
pub newline: token::Newline<'s>,
|
||||||
@ -196,7 +197,8 @@ impl<'s> From<Prefix<'s>> for Tree<'s> {
|
|||||||
// ======================
|
// ======================
|
||||||
|
|
||||||
/// The content of a line in an operator block.
|
/// The content of a line in an operator block.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Visitor, Reflect, Serialize, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Reflect, Serialize, Deserialize)]
|
||||||
pub struct OperatorBlockExpression<'s> {
|
pub struct OperatorBlockExpression<'s> {
|
||||||
/// The operator at the beginning of the line.
|
/// The operator at the beginning of the line.
|
||||||
pub operator: OperatorOrError<'s>,
|
pub operator: OperatorOrError<'s>,
|
||||||
@ -234,7 +236,8 @@ impl<'s> span::Builder<'s> for OperatorBlockExpression<'s> {
|
|||||||
// === Operator block lines ====
|
// === Operator block lines ====
|
||||||
|
|
||||||
/// A line in an operator block.
|
/// A line in an operator block.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Visitor, Reflect, Serialize, Deserialize)]
|
#[cfg_attr(feature = "debug", derive(Visitor))]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Reflect, Serialize, Deserialize)]
|
||||||
pub struct OperatorLine<'s> {
|
pub struct OperatorLine<'s> {
|
||||||
/// Token ending the previous line, if any.
|
/// Token ending the previous line, if any.
|
||||||
pub newline: token::Newline<'s>,
|
pub newline: token::Newline<'s>,
|
||||||
|
@ -32,8 +32,8 @@ use syn::Variant;
|
|||||||
/// ======================
|
/// ======================
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
|
|
||||||
/// Implements [`TreeVisitable`], [`TreeVisitableMut`], [`SpanVisitable`], and [`SpanVisitableMut`].
|
/// Implements [`ItemVisitable`].
|
||||||
/// These traits are defined in the [`crate::ast`] module. Macros in this module hardcode the names
|
/// This trait is defined in the [`crate::ast`] module. Macros in this module hardcode the names
|
||||||
/// of the traits and are not implemented in a generic way because the current Rust implementation
|
/// of the traits and are not implemented in a generic way because the current Rust implementation
|
||||||
/// does not understand generic definition. See the [`crate::ast`] module to learn more about the
|
/// does not understand generic definition. See the [`crate::ast`] module to learn more about the
|
||||||
/// design and the Rust compiler issue.
|
/// design and the Rust compiler issue.
|
||||||
@ -42,8 +42,6 @@ pub fn derive_visitor(input: proc_macro::TokenStream) -> proc_macro::TokenStream
|
|||||||
let decl = syn::parse_macro_input!(input as DeriveInput);
|
let decl = syn::parse_macro_input!(input as DeriveInput);
|
||||||
let ident = &decl.ident;
|
let ident = &decl.ident;
|
||||||
let (impl_generics, ty_generics, _inherent_where_clause_opt) = &decl.generics.split_for_impl();
|
let (impl_generics, ty_generics, _inherent_where_clause_opt) = &decl.generics.split_for_impl();
|
||||||
let body = gen_body(quote!(TreeVisitable::visit), &decl.data, false);
|
|
||||||
let body_span = gen_body(quote!(SpanVisitable::visit_span), &decl.data, false);
|
|
||||||
let body_item = gen_body(quote!(ItemVisitable::visit_item), &decl.data, false);
|
let body_item = gen_body(quote!(ItemVisitable::visit_item), &decl.data, false);
|
||||||
|
|
||||||
let impl_generics_vec: Vec<_> = impl_generics.to_token_stream().into_iter().collect();
|
let impl_generics_vec: Vec<_> = impl_generics.to_token_stream().into_iter().collect();
|
||||||
@ -61,27 +59,9 @@ pub fn derive_visitor(input: proc_macro::TokenStream) -> proc_macro::TokenStream
|
|||||||
let impl_generics = quote!(<#impl_generics 'a>);
|
let impl_generics = quote!(<#impl_generics 'a>);
|
||||||
|
|
||||||
let output = quote! {
|
let output = quote! {
|
||||||
impl #impl_generics TreeVisitable #impl_generics for #ident #ty_generics {
|
|
||||||
fn visit<T: TreeVisitor #impl_generics>(&'a self, visitor:&mut T) {
|
|
||||||
visitor.before_visiting_children();
|
|
||||||
#body
|
|
||||||
visitor.after_visiting_children();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl #impl_generics SpanVisitable #impl_generics for #ident #ty_generics {
|
|
||||||
fn visit_span<T: SpanVisitor #impl_generics>(&'a self, visitor:&mut T) {
|
|
||||||
visitor.before_visiting_children();
|
|
||||||
#body_span
|
|
||||||
visitor.after_visiting_children();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl #impl_generics ItemVisitable #impl_generics for #ident #ty_generics {
|
impl #impl_generics ItemVisitable #impl_generics for #ident #ty_generics {
|
||||||
fn visit_item<T: ItemVisitor #impl_generics>(&'a self, visitor:&mut T) {
|
fn visit_item<T: ItemVisitor #impl_generics>(&'a self, visitor:&mut T) {
|
||||||
visitor.before_visiting_children();
|
|
||||||
#body_item
|
#body_item
|
||||||
visitor.after_visiting_children();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,6 @@ publish = true
|
|||||||
crate-type = ["rlib"]
|
crate-type = ["rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
enso-logging = { path = "../logging" }
|
|
||||||
enso-reflect = { path = "../reflect" }
|
enso-reflect = { path = "../reflect" }
|
||||||
enso-zst = { path = "../zst" }
|
enso-zst = { path = "../zst" }
|
||||||
boolinator = { workspace = true }
|
boolinator = { workspace = true }
|
||||||
|
@ -22,21 +22,3 @@ pub use derive_more::*;
|
|||||||
pub use enso_reflect::prelude::*;
|
pub use enso_reflect::prelude::*;
|
||||||
pub use serde::Deserialize;
|
pub use serde::Deserialize;
|
||||||
pub use serde::Serialize;
|
pub use serde::Serialize;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ===============
|
|
||||||
// === Logging ===
|
|
||||||
// ===============
|
|
||||||
|
|
||||||
pub use enso_logging::debug;
|
|
||||||
pub use enso_logging::debug_span;
|
|
||||||
pub use enso_logging::error;
|
|
||||||
pub use enso_logging::error_span;
|
|
||||||
pub use enso_logging::info;
|
|
||||||
pub use enso_logging::info_span;
|
|
||||||
pub use enso_logging::prelude::*;
|
|
||||||
pub use enso_logging::trace;
|
|
||||||
pub use enso_logging::trace_span;
|
|
||||||
pub use enso_logging::warn;
|
|
||||||
pub use enso_logging::warn_span;
|
|
||||||
|
Loading…
Reference in New Issue
Block a user