list alternative identifiers using oxford_join
This commit is contained in:
		
							parent
							
								
									1b85a2f654
								
							
						
					
					
						commit
						218f488e76
					
				| 
						 | 
					@ -31,6 +31,7 @@ enum-as-inner = "0.6.0"
 | 
				
			||||||
getset = "0.1.2"
 | 
					getset = "0.1.2"
 | 
				
			||||||
itertools = "0.14.0"
 | 
					itertools = "0.14.0"
 | 
				
			||||||
mlua = { version = "0.11.3", features = ["lua54", "vendored"], optional = true }
 | 
					mlua = { version = "0.11.3", features = ["lua54", "vendored"], optional = true }
 | 
				
			||||||
 | 
					oxford_join = { version = "0.7.1", optional = true }
 | 
				
			||||||
pathdiff = "0.2.3"
 | 
					pathdiff = "0.2.3"
 | 
				
			||||||
serde = { version = "1.0.217", features = ["derive"], optional = true }
 | 
					serde = { version = "1.0.217", features = ["derive"], optional = true }
 | 
				
			||||||
serde_json = { version = "1.0.138", optional = true }
 | 
					serde_json = { version = "1.0.138", optional = true }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,17 +11,12 @@ use crate::{
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    lexical::token::StringLiteral,
 | 
					    lexical::token::StringLiteral,
 | 
				
			||||||
    syntax::syntax_tree::expression::Expression,
 | 
					    syntax::syntax_tree::expression::Expression,
 | 
				
			||||||
    transpile::error::{
 | 
					    transpile::error::{AssignmentError, IllegalIndexing, MismatchedTypes, UnknownIdentifier},
 | 
				
			||||||
        AssignmentError, IllegalIndexing, MismatchedTypes, MissingFunctionDeclaration,
 | 
					 | 
				
			||||||
        UnknownIdentifier,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Hash, thiserror::Error)]
 | 
				
			||||||
#[allow(missing_docs)]
 | 
					#[allow(missing_docs)]
 | 
				
			||||||
pub enum Error {
 | 
					pub enum Error {
 | 
				
			||||||
    #[error(transparent)]
 | 
					 | 
				
			||||||
    MissingFunctionDeclaration(#[from] MissingFunctionDeclaration),
 | 
					 | 
				
			||||||
    #[error(transparent)]
 | 
					    #[error(transparent)]
 | 
				
			||||||
    UnexpectedExpression(#[from] UnexpectedExpression),
 | 
					    UnexpectedExpression(#[from] UnexpectedExpression),
 | 
				
			||||||
    #[error(transparent)]
 | 
					    #[error(transparent)]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
use std::fmt::Display;
 | 
					use std::fmt::Display;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use getset::Getters;
 | 
					use getset::Getters;
 | 
				
			||||||
 | 
					use oxford_join::OxfordJoin as _;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    base::{
 | 
					    base::{
 | 
				
			||||||
| 
						 | 
					@ -12,14 +13,12 @@ use crate::{
 | 
				
			||||||
    semantic::error::{ConflictingFunctionNames, InvalidFunctionArguments, UnexpectedExpression},
 | 
					    semantic::error::{ConflictingFunctionNames, InvalidFunctionArguments, UnexpectedExpression},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::{expression::ExpectedType, FunctionData};
 | 
					use super::expression::ExpectedType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// 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, PartialEq, Eq)]
 | 
					#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
 | 
				
			||||||
pub enum TranspileError {
 | 
					pub enum TranspileError {
 | 
				
			||||||
    #[error(transparent)]
 | 
					 | 
				
			||||||
    MissingFunctionDeclaration(#[from] MissingFunctionDeclaration),
 | 
					 | 
				
			||||||
    #[error(transparent)]
 | 
					    #[error(transparent)]
 | 
				
			||||||
    UnexpectedExpression(#[from] UnexpectedExpression),
 | 
					    UnexpectedExpression(#[from] UnexpectedExpression),
 | 
				
			||||||
    #[error("Lua code evaluation is disabled.")]
 | 
					    #[error("Lua code evaluation is disabled.")]
 | 
				
			||||||
| 
						 | 
					@ -53,101 +52,6 @@ pub enum TranspileError {
 | 
				
			||||||
/// The result of a transpilation operation.
 | 
					/// The result of a transpilation operation.
 | 
				
			||||||
pub type TranspileResult<T> = Result<T, TranspileError>;
 | 
					pub type TranspileResult<T> = Result<T, TranspileError>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// An error that occurs when a function declaration is missing.
 | 
					 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Getters)]
 | 
					 | 
				
			||||||
pub struct MissingFunctionDeclaration {
 | 
					 | 
				
			||||||
    /// The span of the identifier that is missing.
 | 
					 | 
				
			||||||
    #[get = "pub"]
 | 
					 | 
				
			||||||
    span: Span,
 | 
					 | 
				
			||||||
    /// Possible alternatives for the missing function declaration.
 | 
					 | 
				
			||||||
    #[get = "pub"]
 | 
					 | 
				
			||||||
    alternatives: Vec<FunctionData>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl MissingFunctionDeclaration {
 | 
					 | 
				
			||||||
    #[cfg(feature = "shulkerbox")]
 | 
					 | 
				
			||||||
    pub(super) fn from_scope(
 | 
					 | 
				
			||||||
        identifier_span: Span,
 | 
					 | 
				
			||||||
        scope: &std::sync::Arc<super::variables::Scope>,
 | 
					 | 
				
			||||||
    ) -> Self {
 | 
					 | 
				
			||||||
        use itertools::Itertools as _;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let own_name = identifier_span.str();
 | 
					 | 
				
			||||||
        let alternatives = scope
 | 
					 | 
				
			||||||
            .get_all_variables()
 | 
					 | 
				
			||||||
            .iter()
 | 
					 | 
				
			||||||
            .filter_map(|(name, value)| {
 | 
					 | 
				
			||||||
                let super::variables::VariableData::Function {
 | 
					 | 
				
			||||||
                    function_data: data,
 | 
					 | 
				
			||||||
                    ..
 | 
					 | 
				
			||||||
                } = value.as_ref()
 | 
					 | 
				
			||||||
                else {
 | 
					 | 
				
			||||||
                    return None;
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let normalized_distance = strsim::normalized_damerau_levenshtein(own_name, name);
 | 
					 | 
				
			||||||
                (normalized_distance > 0.8 || strsim::damerau_levenshtein(own_name, 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 {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
					 | 
				
			||||||
        use std::fmt::Write as _;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let message = format!(
 | 
					 | 
				
			||||||
            "no matching function declaration found for invocation of function `{}`",
 | 
					 | 
				
			||||||
            self.span.str()
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
        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(", ");
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                let _ = write!(message, "`{}`", alternative.identifier_span.str());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Some(message + "?")
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        write!(
 | 
					 | 
				
			||||||
            f,
 | 
					 | 
				
			||||||
            "\n{}",
 | 
					 | 
				
			||||||
            SourceCodeDisplay::new(&self.span, help_message.as_ref())
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl std::error::Error for MissingFunctionDeclaration {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl std::hash::Hash for MissingFunctionDeclaration {
 | 
					 | 
				
			||||||
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 | 
					 | 
				
			||||||
        self.span.hash(state);
 | 
					 | 
				
			||||||
        for alternative in &self.alternatives {
 | 
					 | 
				
			||||||
            alternative.identifier_span.hash(state);
 | 
					 | 
				
			||||||
            alternative.namespace.hash(state);
 | 
					 | 
				
			||||||
            alternative.parameters.hash(state);
 | 
					 | 
				
			||||||
            alternative.public.hash(state);
 | 
					 | 
				
			||||||
            alternative.statements.hash(state);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// An error that occurs when a function declaration is missing.
 | 
					/// An error that occurs when a function declaration is missing.
 | 
				
			||||||
#[allow(clippy::module_name_repetitions)]
 | 
					#[allow(clippy::module_name_repetitions)]
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq)]
 | 
				
			||||||
| 
						 | 
					@ -341,8 +245,6 @@ impl UnknownIdentifier {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Display for UnknownIdentifier {
 | 
					impl Display for UnknownIdentifier {
 | 
				
			||||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
					    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
				
			||||||
        use std::fmt::Write as _;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        write!(
 | 
					        write!(
 | 
				
			||||||
            f,
 | 
					            f,
 | 
				
			||||||
            "{}",
 | 
					            "{}",
 | 
				
			||||||
| 
						 | 
					@ -355,14 +257,15 @@ impl Display for UnknownIdentifier {
 | 
				
			||||||
        let help_message = if self.alternatives.is_empty() {
 | 
					        let help_message = if self.alternatives.is_empty() {
 | 
				
			||||||
            None
 | 
					            None
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            let mut message = String::from("did you mean ");
 | 
					            let message = String::from("did you mean ");
 | 
				
			||||||
            for (i, alternative) in self.alternatives.iter().enumerate() {
 | 
					            let inner = self
 | 
				
			||||||
                if i > 0 {
 | 
					                .alternatives
 | 
				
			||||||
                    message.push_str(", ");
 | 
					                .iter()
 | 
				
			||||||
                }
 | 
					                .map(|s| format!("`{s}`"))
 | 
				
			||||||
                let _ = write!(message, "`{alternative}`");
 | 
					                .collect::<Vec<_>>();
 | 
				
			||||||
            }
 | 
					            let inner = inner.oxford_or();
 | 
				
			||||||
            Some(message + "?")
 | 
					
 | 
				
			||||||
 | 
					            Some(message + &inner + "?")
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        write!(
 | 
					        write!(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,7 @@ use crate::{
 | 
				
			||||||
        statement::Statement,
 | 
					        statement::Statement,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    transpile::{
 | 
					    transpile::{
 | 
				
			||||||
        error::{IllegalAnnotationContent, MissingFunctionDeclaration},
 | 
					        error::IllegalAnnotationContent,
 | 
				
			||||||
        util::{MacroString, MacroStringPart},
 | 
					        util::{MacroString, MacroStringPart},
 | 
				
			||||||
        variables::FunctionVariableDataType,
 | 
					        variables::FunctionVariableDataType,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					@ -57,9 +57,10 @@ impl Transpiler {
 | 
				
			||||||
        let function = scope.get_variable(identifier_span.str());
 | 
					        let function = scope.get_variable(identifier_span.str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let function_data = function.ok_or_else(|| {
 | 
					        let function_data = function.ok_or_else(|| {
 | 
				
			||||||
            let err = TranspileError::MissingFunctionDeclaration(
 | 
					            let err = TranspileError::UnknownIdentifier(UnknownIdentifier::from_scope(
 | 
				
			||||||
                MissingFunctionDeclaration::from_scope(identifier_span.clone(), scope),
 | 
					                identifier_span.clone(),
 | 
				
			||||||
            );
 | 
					                scope,
 | 
				
			||||||
 | 
					            ));
 | 
				
			||||||
            handler.receive(Box::new(err.clone()));
 | 
					            handler.receive(Box::new(err.clone()));
 | 
				
			||||||
            err
 | 
					            err
 | 
				
			||||||
        })?;
 | 
					        })?;
 | 
				
			||||||
| 
						 | 
					@ -91,14 +92,8 @@ impl Transpiler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let function_location = function_path
 | 
					                let function_location = function_path
 | 
				
			||||||
                    .get()
 | 
					                    .get()
 | 
				
			||||||
                    .ok_or_else(|| {
 | 
					                    .expect("set in prepare_transpile_function if not previously set")
 | 
				
			||||||
                        let err = TranspileError::MissingFunctionDeclaration(
 | 
					                    .to_string();
 | 
				
			||||||
                            MissingFunctionDeclaration::from_scope(identifier_span.clone(), scope),
 | 
					 | 
				
			||||||
                        );
 | 
					 | 
				
			||||||
                        handler.receive(Box::new(err.clone()));
 | 
					 | 
				
			||||||
                        err
 | 
					 | 
				
			||||||
                    })
 | 
					 | 
				
			||||||
                    .map(String::to_owned)?;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                let args = self.transpile_function_arguments(
 | 
					                let args = self.transpile_function_arguments(
 | 
				
			||||||
                    function_data,
 | 
					                    function_data,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue