mirror of
https://github.com/enso-org/enso.git
synced 2024-08-17 08:50:52 +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",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "enso-macro-utils"
|
||||
version = "0.2.0"
|
||||
@ -1610,7 +1592,6 @@ dependencies = [
|
||||
"boolinator",
|
||||
"derivative",
|
||||
"derive_more",
|
||||
"enso-logging",
|
||||
"enso-macros",
|
||||
"enso-reflect",
|
||||
"enso-zst",
|
||||
|
@ -115,8 +115,6 @@ futures = { version = "0.3" }
|
||||
itertools = { version = "0.12.1" }
|
||||
lazy_static = { version = "1.4" }
|
||||
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" }
|
||||
convert_case = { version = "0.6.0" }
|
||||
rustybuzz = { version = "0.5.1" }
|
||||
@ -149,4 +147,3 @@ syn_1 = { package = "syn", version = "1.0", features = [
|
||||
quote = { version = "1.0.23" }
|
||||
semver = { version = "1.0.0", features = ["serde"] }
|
||||
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"
|
||||
license-file = "../../LICENSE"
|
||||
|
||||
[features]
|
||||
debug = ["dep:enso-parser-syntax-tree-visitor"]
|
||||
|
||||
[dependencies]
|
||||
enso-prelude = { path = "../prelude" }
|
||||
enso-reflect = { path = "../reflect" }
|
||||
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" }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
@ -10,7 +10,7 @@ repository = "https://github.com/enso-org/enso"
|
||||
license-file = "../../LICENSE"
|
||||
|
||||
[dependencies]
|
||||
enso-parser = { path = "../" }
|
||||
enso-parser = { path = "../", features = ["debug"] }
|
||||
enso-metamodel = { path = "../../metamodel", features = ["rust"] }
|
||||
enso-metamodel-lexpr = { path = "../../metamodel/lexpr" }
|
||||
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 ===
|
||||
#![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 errors = RefCell::new(vec![]);
|
||||
ast.map(|tree| {
|
||||
ast.visit_trees(|tree| {
|
||||
if let enso_parser::syntax::tree::Variant::Invalid(err) = &*tree.variant {
|
||||
let error = format!("{}: {}", err.error.message, tree.code());
|
||||
errors.borrow_mut().push((error, tree.span.clone()));
|
@ -1702,7 +1702,7 @@ impl Errors {
|
||||
let ast = parse(code);
|
||||
expect_tree_representing_code(code, &ast);
|
||||
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(_) => {
|
||||
errors.update(|e| Self { invalid_node: true, ..e });
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ serde = { workspace = true }
|
||||
[dev-dependencies]
|
||||
enso-metamodel = { path = "../../metamodel", features = ["rust"] }
|
||||
enso-metamodel-lexpr = { path = "../../metamodel/lexpr" }
|
||||
enso-parser = { path = "..", features = ["debug"] }
|
||||
lexpr = "0.2.6"
|
||||
pretty_assertions = "1.4"
|
||||
|
||||
|
@ -246,9 +246,7 @@ impl<'a, L: Location> Span<'a, L> {
|
||||
}
|
||||
Some(_) => break,
|
||||
None => {
|
||||
let unexpected_condition = "Internal error: Expected greater indent level.";
|
||||
self.warn(unexpected_condition);
|
||||
warn!("{unexpected_condition}");
|
||||
self.warn("Internal error: Expected greater indent level.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ fn extract_docs(_filename: &str, mut code: &str) -> Vec<String> {
|
||||
}
|
||||
let ast = enso_parser::Parser::new().run(code);
|
||||
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) => {
|
||||
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) {
|
||||
match meta_ {
|
||||
Ok(meta_) => meta = Some(meta_),
|
||||
Err(e) => error!("Ignoring invalid metadata: {e}."),
|
||||
Err(e) => eprintln!("Ignoring invalid metadata: {e}."),
|
||||
}
|
||||
code = code_;
|
||||
}
|
||||
|
@ -302,19 +302,16 @@ impl<'s> Resolver<'s> {
|
||||
if self.macros.len() > self.macro_scope_start() {
|
||||
let current_macro = self.macros.last_mut().unwrap();
|
||||
if let Some(subsegments) = current_macro.possible_next_segments.get(repr) {
|
||||
trace!("Entering next segment of the current macro.");
|
||||
let mut new_match_tree =
|
||||
Self::move_to_next_segment(&mut current_macro.matched_macro_def, subsegments);
|
||||
mem::swap(&mut new_match_tree, &mut current_macro.possible_next_segments);
|
||||
return Step::StartSegment(token);
|
||||
} 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);
|
||||
return Step::MacroStackPop(token.into());
|
||||
}
|
||||
}
|
||||
if let Some(segments) = root_macro_map.get(repr, context) {
|
||||
trace!("Starting a new nested macro resolution.");
|
||||
let mut matched_macro_def = default();
|
||||
let segments_start = self.segments.len();
|
||||
let new_macro = PartiallyMatchedMacro {
|
||||
@ -328,7 +325,6 @@ impl<'s> Resolver<'s> {
|
||||
self.macros.push(new_macro);
|
||||
Step::StartSegment(token)
|
||||
} else {
|
||||
trace!("Consuming token as current segment body.");
|
||||
Step::NormalToken(token.into())
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use crate::syntax::*;
|
||||
|
||||
use crate::span_builder;
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
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)*
|
||||
/// [`Tree`] variants definition. See its docs to learn more.
|
||||
#[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.
|
||||
#[tagged_enum(apply_attributes_to = "variants")]
|
||||
#[reflect(inline)]
|
||||
@ -383,7 +385,8 @@ with_ast_definition!(generate_ast_definition());
|
||||
// === Invalid ===
|
||||
|
||||
/// 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)]
|
||||
#[reflect(transparent)]
|
||||
#[serde(from = "crate::serialization::Error")]
|
||||
@ -417,7 +420,8 @@ impl<'s> span::Builder<'s> for Error {
|
||||
// === Argument blocks ===
|
||||
|
||||
/// 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> {
|
||||
/// The token beginning the line.
|
||||
pub newline: token::Newline<'s>,
|
||||
@ -435,7 +439,8 @@ impl<'s> span::Builder<'s> for ArgumentDefinitionLine<'s> {
|
||||
// === Text literals ===
|
||||
|
||||
/// 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> {
|
||||
/// The text content of the literal. If it is multiline, the offset information may contain
|
||||
/// part of the content, after trimming appropriately.
|
||||
@ -481,7 +486,8 @@ impl<'s> span::Builder<'s> for TextElement<'s> {
|
||||
// === Documentation ===
|
||||
|
||||
/// 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> {
|
||||
/// The comment-initiating token.
|
||||
pub open: token::TextStart<'s>,
|
||||
@ -522,7 +528,8 @@ impl<'s> span::Builder<'s> for DocComment<'s> {
|
||||
|
||||
// === 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)]
|
||||
pub struct FractionalDigits<'s> {
|
||||
/// The dot operator.
|
||||
@ -541,7 +548,8 @@ impl<'s> span::Builder<'s> for FractionalDigits<'s> {
|
||||
// === Functions ===
|
||||
|
||||
/// 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> {
|
||||
/// Opening parenthesis (outer).
|
||||
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.
|
||||
#[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> {
|
||||
/// The `=` token.
|
||||
pub equals: token::Operator<'s>,
|
||||
@ -591,7 +600,8 @@ impl<'s> span::Builder<'s> for ArgumentDefault<'s> {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
/// The `:` token.
|
||||
pub operator: token::Operator<'s>,
|
||||
@ -607,7 +617,8 @@ impl<'s> span::Builder<'s> for ArgumentType<'s> {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
/// The `->` operator.
|
||||
pub arrow: token::Operator<'s>,
|
||||
@ -626,7 +637,8 @@ impl<'s> span::Builder<'s> for ReturnSpecification<'s> {
|
||||
// === CaseOf ===
|
||||
|
||||
/// 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> {
|
||||
/// 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.
|
||||
@ -653,7 +665,8 @@ impl<'s> span::Builder<'s> for CaseLine<'s> {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
/// Documentation, if present.
|
||||
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>>;
|
||||
|
||||
/// 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)]
|
||||
pub struct MultipleOperatorError<'s> {
|
||||
pub operators: NonEmptyVec<token::Operator<'s>>,
|
||||
@ -731,7 +745,8 @@ impl<'s> NonEmptyOperatorSequence<'s> for OperatorOrError<'s> {
|
||||
// === MultiSegmentApp ===
|
||||
|
||||
/// 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)]
|
||||
pub struct MultiSegmentAppSegment<'s> {
|
||||
pub header: Token<'s>,
|
||||
@ -748,7 +763,8 @@ impl<'s> span::Builder<'s> for MultiSegmentAppSegment<'s> {
|
||||
// === Array and Tuple ===
|
||||
|
||||
/// 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> {
|
||||
/// The delimiting operator.
|
||||
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:
|
||||
/// https://github.com/rust-lang/rust/issues/96634.
|
||||
#[allow(missing_docs)]
|
||||
pub trait Visitor {
|
||||
fn before_visiting_children(&mut self) {}
|
||||
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;
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
pub trait Visitor {}
|
||||
|
||||
/// The visitor trait allowing for [`Item`] traversal.
|
||||
#[allow(missing_docs)]
|
||||
#[cfg(feature = "debug")]
|
||||
pub trait ItemVisitor<'s, 'a>: Visitor {
|
||||
fn visit_item(&mut self, ast: item::Ref<'s, 'a>) -> bool;
|
||||
}
|
||||
@ -1129,11 +1132,13 @@ macro_rules! define_visitor {
|
||||
$visitable:ident
|
||||
) => {
|
||||
/// The visitable trait. See documentation of [`define_visitor`] to learn more.
|
||||
#[cfg(feature = "debug")]
|
||||
#[allow(missing_docs)]
|
||||
pub trait $visitable<'s, 'a> {
|
||||
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> {
|
||||
fn $visit<V: $visitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
||||
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>
|
||||
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> {
|
||||
fn $visit<V: $visitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
||||
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> {
|
||||
fn $visit<V: $visitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
||||
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);
|
||||
|
||||
crate::with_token_definition!(define_visitor_for_tokens());
|
||||
|
||||
|
||||
// === Trait Implementations for Simple Leaf Types ===
|
||||
|
||||
macro_rules! spanless_leaf_impls {
|
||||
($ty:ty) => {
|
||||
impl<'s, 'a> TreeVisitable<'s, 'a> for $ty {}
|
||||
impl<'a, 's> SpanVisitable<'s, 'a> for $ty {}
|
||||
#[cfg(feature = "debug")]
|
||||
impl<'a, 's> ItemVisitable<'s, 'a> for $ty {}
|
||||
impl<'s> span::Builder<'s> for $ty {
|
||||
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>);
|
||||
|
||||
|
||||
// === 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 ===
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
impl<'s, 'a> ItemVisitable<'s, 'a> for Tree<'s> {
|
||||
fn visit_item<V: ItemVisitor<'s, 'a>>(&'a self, visitor: &mut V) {
|
||||
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>
|
||||
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.
|
||||
#[cfg(feature = "debug")]
|
||||
#[derive(Debug, Default)]
|
||||
#[allow(missing_docs)]
|
||||
struct CodePrinterVisitor {
|
||||
pub code: String,
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
impl Visitor for CodePrinterVisitor {}
|
||||
#[cfg(feature = "debug")]
|
||||
impl<'s, 'a> ItemVisitor<'s, 'a> for CodePrinterVisitor {
|
||||
fn visit_item(&mut self, item: item::Ref<'s, 'a>) -> bool {
|
||||
match item {
|
||||
@ -1291,6 +1249,7 @@ impl<'s, 'a> ItemVisitor<'s, 'a> for CodePrinterVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
impl<'s> Tree<'s> {
|
||||
/// Code generator of this AST.
|
||||
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 ===
|
||||
// =====================
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
impl<'s> Tree<'s> {
|
||||
/// Apply the provided function to each [`Token`] or [`Tree`] that is a child of the node.
|
||||
pub fn visit_items<F>(&self, f: F)
|
||||
@ -1356,4 +1291,24 @@ impl<'s> Tree<'s> {
|
||||
}
|
||||
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.
|
||||
#[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> {
|
||||
/// Token ending the previous line, if any.
|
||||
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.
|
||||
#[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> {
|
||||
/// The operator at the beginning of the line.
|
||||
pub operator: OperatorOrError<'s>,
|
||||
@ -234,7 +236,8 @@ impl<'s> span::Builder<'s> for OperatorBlockExpression<'s> {
|
||||
// === Operator block lines ====
|
||||
|
||||
/// 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> {
|
||||
/// Token ending the previous line, if any.
|
||||
pub newline: token::Newline<'s>,
|
||||
|
@ -32,8 +32,8 @@ use syn::Variant;
|
||||
/// ======================
|
||||
use quote::ToTokens;
|
||||
|
||||
/// Implements [`TreeVisitable`], [`TreeVisitableMut`], [`SpanVisitable`], and [`SpanVisitableMut`].
|
||||
/// These traits are defined in the [`crate::ast`] module. Macros in this module hardcode the names
|
||||
/// Implements [`ItemVisitable`].
|
||||
/// 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
|
||||
/// does not understand generic definition. See the [`crate::ast`] module to learn more about the
|
||||
/// 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 ident = &decl.ident;
|
||||
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 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 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 {
|
||||
fn visit_item<T: ItemVisitor #impl_generics>(&'a self, visitor:&mut T) {
|
||||
visitor.before_visiting_children();
|
||||
#body_item
|
||||
visitor.after_visiting_children();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -16,7 +16,6 @@ publish = true
|
||||
crate-type = ["rlib"]
|
||||
|
||||
[dependencies]
|
||||
enso-logging = { path = "../logging" }
|
||||
enso-reflect = { path = "../reflect" }
|
||||
enso-zst = { path = "../zst" }
|
||||
boolinator = { workspace = true }
|
||||
|
@ -22,21 +22,3 @@ pub use derive_more::*;
|
||||
pub use enso_reflect::prelude::*;
|
||||
pub use serde::Deserialize;
|
||||
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