suggest similarly named functions if invoked function does not exist
This commit is contained in:
parent
6422737cf3
commit
3332511290
|
@ -27,10 +27,12 @@ colored = "2.1.0"
|
||||||
derive_more = { version = "0.99.17", default-features = false, features = ["deref", "from", "deref_mut"] }
|
derive_more = { version = "0.99.17", default-features = false, features = ["deref", "from", "deref_mut"] }
|
||||||
enum-as-inner = "0.6.0"
|
enum-as-inner = "0.6.0"
|
||||||
getset = "0.1.2"
|
getset = "0.1.2"
|
||||||
|
itertools = "0.13.0"
|
||||||
mlua = { version = "0.9.7", features = ["lua54", "vendored"], optional = true }
|
mlua = { version = "0.9.7", features = ["lua54", "vendored"], optional = true }
|
||||||
path-absolutize = "3.1.1"
|
path-absolutize = "3.1.1"
|
||||||
serde = { version = "1.0.197", features = ["derive", "rc"], optional = true }
|
serde = { version = "1.0.197", features = ["derive", "rc"], optional = true }
|
||||||
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", default-features = false, optional = true, rev = "a2d20dab8ea97bbd873edafb23afaad34292457f" }
|
shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", default-features = false, optional = true, rev = "a2d20dab8ea97bbd873edafb23afaad34292457f" }
|
||||||
|
strsim = "0.11.1"
|
||||||
strum = { version = "0.26.2", features = ["derive"] }
|
strum = { version = "0.26.2", features = ["derive"] }
|
||||||
strum_macros = "0.26.2"
|
strum_macros = "0.26.2"
|
||||||
thiserror = "1.0.58"
|
thiserror = "1.0.58"
|
||||||
|
|
|
@ -29,6 +29,9 @@ use shulkerbox::{datapack::Datapack, virtual_fs::VFolder};
|
||||||
|
|
||||||
use crate::lexical::token_stream::TokenStream;
|
use crate::lexical::token_stream::TokenStream;
|
||||||
|
|
||||||
|
/// The version of the `ShulkerScript` language.
|
||||||
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
/// Converts the given source code to tokens and returns a token stream.
|
/// Converts the given source code to tokens and returns a token stream.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
//! Errors that can occur during transpilation.
|
//! Errors that can occur during transpilation.
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::{collections::HashMap, fmt::Display};
|
||||||
|
|
||||||
|
use getset::Getters;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
base::{
|
base::{
|
||||||
|
@ -10,6 +13,8 @@ use crate::{
|
||||||
syntax::syntax_tree::expression::Expression,
|
syntax::syntax_tree::expression::Expression,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::transpiler::FunctionData;
|
||||||
|
|
||||||
/// Errors that can occur during transpilation.
|
/// Errors that can occur during transpilation.
|
||||||
#[allow(clippy::module_name_repetitions, missing_docs)]
|
#[allow(clippy::module_name_repetitions, missing_docs)]
|
||||||
#[derive(Debug, thiserror::Error, Clone)]
|
#[derive(Debug, thiserror::Error, Clone)]
|
||||||
|
@ -28,9 +33,41 @@ pub enum TranspileError {
|
||||||
pub type TranspileResult<T> = Result<T, TranspileError>;
|
pub type TranspileResult<T> = Result<T, TranspileError>;
|
||||||
|
|
||||||
/// An error that occurs when a function declaration is missing.
|
/// An error that occurs when a function declaration is missing.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
|
||||||
pub struct MissingFunctionDeclaration {
|
pub struct MissingFunctionDeclaration {
|
||||||
pub span: Span,
|
#[get = "pub"]
|
||||||
|
span: Span,
|
||||||
|
#[get = "pub"]
|
||||||
|
alternatives: Vec<FunctionData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MissingFunctionDeclaration {
|
||||||
|
pub(super) fn from_context(
|
||||||
|
identifier_span: Span,
|
||||||
|
functions: &HashMap<(String, String), FunctionData>,
|
||||||
|
) -> Self {
|
||||||
|
let own_name = identifier_span.str();
|
||||||
|
let own_program_identifier = identifier_span.source_file().identifier();
|
||||||
|
let alternatives = functions
|
||||||
|
.iter()
|
||||||
|
.filter_map(|((program_identifier, function_name), data)| {
|
||||||
|
let normalized_distance = strsim::normalized_levenshtein(own_name, function_name);
|
||||||
|
(program_identifier == own_program_identifier
|
||||||
|
&& (normalized_distance > 0.8
|
||||||
|
|| strsim::levenshtein(own_name, function_name) < 3))
|
||||||
|
.then_some((normalized_distance, data))
|
||||||
|
})
|
||||||
|
.sorted_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal))
|
||||||
|
.map(|(_, data)| data)
|
||||||
|
.take(8)
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
alternatives,
|
||||||
|
span: identifier_span,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for MissingFunctionDeclaration {
|
impl Display for MissingFunctionDeclaration {
|
||||||
|
@ -41,10 +78,23 @@ impl Display for MissingFunctionDeclaration {
|
||||||
);
|
);
|
||||||
write!(f, "{}", Message::new(Severity::Error, message))?;
|
write!(f, "{}", Message::new(Severity::Error, message))?;
|
||||||
|
|
||||||
|
let help_message = if self.alternatives.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let mut message = String::from("did you mean ");
|
||||||
|
for (i, alternative) in self.alternatives.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
message.push_str(", ");
|
||||||
|
}
|
||||||
|
message.push_str(&format!("`{}`", alternative.identifier_span.str()));
|
||||||
|
}
|
||||||
|
Some(message + "?")
|
||||||
|
};
|
||||||
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"\n{}",
|
"\n{}",
|
||||||
SourceCodeDisplay::new(&self.span, Option::<u8>::None)
|
SourceCodeDisplay::new(&self.span, help_message.as_ref())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,13 +35,13 @@ pub struct Transpiler {
|
||||||
aliases: RwLock<HashMap<(String, String), (String, String)>>,
|
aliases: RwLock<HashMap<(String, String), (String, String)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
struct FunctionData {
|
pub(super) struct FunctionData {
|
||||||
namespace: String,
|
pub(super) namespace: String,
|
||||||
identifier_span: Span,
|
pub(super) identifier_span: Span,
|
||||||
statements: Vec<Statement>,
|
pub(super) statements: Vec<Statement>,
|
||||||
public: bool,
|
pub(super) public: bool,
|
||||||
annotations: HashMap<String, Option<String>>,
|
pub(super) annotations: HashMap<String, Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transpiler {
|
impl Transpiler {
|
||||||
|
@ -224,9 +224,10 @@ impl Transpiler {
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
let error = TranspileError::MissingFunctionDeclaration(
|
let error = TranspileError::MissingFunctionDeclaration(
|
||||||
MissingFunctionDeclaration {
|
MissingFunctionDeclaration::from_context(
|
||||||
span: identifier_span.clone(),
|
identifier_span.clone(),
|
||||||
},
|
&functions,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
handler.receive(error.clone());
|
handler.receive(error.clone());
|
||||||
error
|
error
|
||||||
|
@ -244,10 +245,12 @@ impl Transpiler {
|
||||||
.and_then(|q| functions.get(&q).filter(|f| f.public))
|
.and_then(|q| functions.get(&q).filter(|f| f.public))
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
let error =
|
let error = TranspileError::MissingFunctionDeclaration(
|
||||||
TranspileError::MissingFunctionDeclaration(MissingFunctionDeclaration {
|
MissingFunctionDeclaration::from_context(
|
||||||
span: identifier_span.clone(),
|
identifier_span.clone(),
|
||||||
});
|
&functions,
|
||||||
|
),
|
||||||
|
);
|
||||||
handler.receive(error.clone());
|
handler.receive(error.clone());
|
||||||
error
|
error
|
||||||
})?;
|
})?;
|
||||||
|
@ -297,10 +300,12 @@ impl Transpiler {
|
||||||
.get(&program_query)
|
.get(&program_query)
|
||||||
.or_else(|| alias_query.and_then(|q| locations.get(&q).filter(|(_, p)| *p)))
|
.or_else(|| alias_query.and_then(|q| locations.get(&q).filter(|(_, p)| *p)))
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
let error =
|
let error = TranspileError::MissingFunctionDeclaration(
|
||||||
TranspileError::MissingFunctionDeclaration(MissingFunctionDeclaration {
|
MissingFunctionDeclaration::from_context(
|
||||||
span: identifier_span.clone(),
|
identifier_span.clone(),
|
||||||
});
|
&self.functions.read().unwrap(),
|
||||||
|
),
|
||||||
|
);
|
||||||
handler.receive(error.clone());
|
handler.receive(error.clone());
|
||||||
error
|
error
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue