df_ls_syntax_analysis 0.3.0-rc.1

A language server for Dwarf Fortress RAW files
Documentation
use super::TokenArgument;
use df_ls_diagnostics::{hash_map, DMExtraInfo, DiagnosticsInfo};
use df_ls_lexical_analysis::{Node, TreeCursor};

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Token {
    pub node: Option<Node>,
    pub(crate) name: Option<TokenName>,
    pub(crate) arguments: Vec<Argument>,
    pub current_argument_index: usize,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TokenName {
    pub node: Node,
    pub value: String,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Argument {
    pub node: Node,
    pub value: TokenArgument,
}

impl Token {
    /// Get the Token Name
    pub fn get_token_name(&self) -> Result<&TokenName, ()> {
        match &self.name {
            Some(v) => Ok(v),
            None => Err(()),
        }
    }

    /// Get the argument on a specific index
    pub fn get_argument(&self, index: usize) -> Result<&Argument, ()> {
        match self.arguments.get(index) {
            Some(v) => Ok(v),
            None => Err(()),
        }
    }

    /// Get current argument
    pub fn get_current_arg(&self) -> Result<&Argument, ()> {
        match self.arguments.get(self.current_argument_index) {
            Some(v) => Ok(v),
            None => Err(()),
        }
    }

    /// Get current argument as an `Option`
    pub fn get_current_arg_opt(&self) -> Option<&Argument> {
        self.arguments.get(self.current_argument_index)
    }

    /// Get previous argument as an `Option`
    pub fn get_previous_arg_opt(&self) -> Option<&Argument> {
        if self.current_argument_index < 1 {
            return None;
        }
        self.arguments.get(self.current_argument_index - 1)
    }

    /// Get the last argument as an `Option`
    /// If the token has no arguments, this will be `None`.
    pub fn get_last_arg_opt(&self) -> Option<&Argument> {
        self.arguments.last()
    }

    /// Get current argument and make sure it exists
    /// If it does not exists, add diagnostics error
    pub fn checked_get_token_name(
        &self,
        source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<TokenName, ()> {
        self.check_token_name(source, diagnostics, add_diagnostics_on_err)?;
        self.get_token_name().cloned()
    }

    /// Get current argument and make sure it exists
    /// If it does not exists, add diagnostics error
    pub fn checked_get_current_arg(
        &self,
        _source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<Argument, ()> {
        match self.arguments.get(self.current_argument_index) {
            Some(arg) => Ok(arg.clone()),
            None => {
                if add_diagnostics_on_err {
                    diagnostics.add_message(
                        DMExtraInfo {
                            range: self.node.as_ref().unwrap().get_range(),
                            message_template_data: hash_map! {
                                "expected_parameters_num" => (self.current_argument_index+1).to_string(),
                                "found_parameters_num" => self.arguments.len().to_string(),
                            },
                        },
                        "wrong_arg_number",
                    );
                }
                Err(())
            }
        }
    }

    /// Consume current argument and set index to next argument
    pub fn consume_argument(&mut self) {
        self.current_argument_index += 1;
    }

    /// Make sure the current argument exists
    /// If it does not exists, add a diagnostics error
    pub fn check_as_required_argument(
        &self,
        _source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<(), ()> {
        if self.get_current_arg_opt().is_some() {
            Ok(())
        } else if add_diagnostics_on_err {
            diagnostics.add_message(
                DMExtraInfo {
                    range: self.node.as_ref().unwrap().get_range(),
                    message_template_data: hash_map! {
                        "expected_parameters_num" => format!("at least {}", self.current_argument_index+1),
                        "found_parameters_num" => self.arguments.len().to_string(),
                    },
                },
                "wrong_arg_number",
            );
            Err(())
        } else {
            Err(())
        }
    }

    /// Make sure all arguments in the token are consumed
    /// and there are no extras.
    pub fn check_all_arg_consumed(
        &self,
        _source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<(), ()> {
        if self.current_argument_index == self.arguments.len() {
            Ok(())
        } else if add_diagnostics_on_err {
            log::error!("err in check_all_arg_consumed");
            diagnostics.add_message(
                DMExtraInfo {
                    range: self.node.as_ref().unwrap().get_range(),
                    message_template_data: hash_map! {
                        "expected_parameters_num" => self.current_argument_index.to_string(),
                        "found_parameters_num" => self.arguments.len().to_string(),
                    },
                },
                "wrong_arg_number",
            );
            Err(())
        } else {
            Err(())
        }
    }

    /// Check if the token name exists
    pub fn check_token_name(
        &self,
        _source: &str,
        diagnostics: &mut DiagnosticsInfo,
        add_diagnostics_on_err: bool,
    ) -> Result<(), ()> {
        // Check if token name is set
        let token_name = if let Some(token_name) = &self.name {
            token_name
        } else {
            if add_diagnostics_on_err {
                diagnostics.add_message(
                    DMExtraInfo::new(self.node.as_ref().unwrap().get_range()),
                    "missing_token_name",
                );
            }
            return Err(());
        };

        // Check if token name is empty
        if token_name.value.is_empty() {
            // Incorrect
            if add_diagnostics_on_err {
                diagnostics.add_message(
                    DMExtraInfo::new(self.node.as_ref().unwrap().get_range()),
                    "missing_token_name",
                );
            }
            return Err(());
        }
        // Correct
        Ok(())
    }

    pub fn consume_token(cursor: &mut TreeCursor) -> Result<(), ()> {
        match cursor.goto_next_sibling() {
            true => Ok(()),
            false => Err(()),
        }
    }
}

impl From<TokenName> for Argument {
    fn from(item: TokenName) -> Self {
        Argument {
            node: item.node,
            value: TokenArgument::TVReference(item.value),
        }
    }
}

impl From<&TokenName> for Argument {
    fn from(item: &TokenName) -> Self {
        Argument {
            node: item.node.clone(),
            value: TokenArgument::TVReference(item.value.clone()),
        }
    }
}