df_ls_syntax_analysis 0.3.0-rc.1

A language server for Dwarf Fortress RAW files
Documentation
use super::super::{Argument, Token, TokenArgument, TryFromArgument, TryFromArgumentGroup};
use super::TokenDeserialize;
use df_ls_diagnostics::{hash_map, DMExtraInfo, DiagnosticsInfo};

// Prevent having to copy post following code with little changes
// for each of the integer types
macro_rules! token_de_integer {
    ( $x:ty ) => {
        // Deserialize a token with following pattern: `[REF:INT]`
        crate::token_deserialize_unary_token!($x);
    };
}

// Big numbers like i128 should not be used.
// token_de_integer!(i128);
token_de_integer!(i64);
token_de_integer!(i32);
token_de_integer!(i16);
token_de_integer!(i8);

// Big numbers like u128 should not be used.
// token_de_integer!(u128);
// Can not cast i64 to u64 without loss
// token_de_integer!(u64);
token_de_integer!(u32);
token_de_integer!(u16);
token_de_integer!(u8);

// ------------------------- Convert a group of arguments to Self -----------------------

// Prevent having to copy post following code with little changes
// for each of the integer types
macro_rules! try_from_argument_group {
    ( $x:ty ) => {
        impl TryFromArgumentGroup for $x {
            fn try_from_argument_group(
                token: &mut Token,
                source: &str,
                diagnostics: &mut DiagnosticsInfo,
                add_diagnostics_on_err: bool,
            ) -> Result<Self, ()> {
                token.check_as_required_argument(source, diagnostics, add_diagnostics_on_err)?;
                let arg = token.get_current_arg_opt();
                let result =
                    Self::try_from_argument(arg, source, diagnostics, add_diagnostics_on_err);
                token.consume_argument();
                result
            }
        }
    };
}

// try_from_argument_group!(i128);
try_from_argument_group!(i64);
try_from_argument_group!(i32);
try_from_argument_group!(i16);
try_from_argument_group!(i8);

// try_from_argument_group!(u128);
// Can not cast i64 to u64 without loss
// try_from_argument_group!(u64);
try_from_argument_group!(u32);
try_from_argument_group!(u16);
try_from_argument_group!(u8);

// -------------------------Convert one argument to Self -----------------------

// Prevent having to copy post following code with little changes
// for each of the integer types
macro_rules! token_argument_into_integer {
    ( $x:ty, $t:literal ) => {
        impl TryFromArgument for $x {
            fn try_from_argument(
                arg_opt: Option<&Argument>,
                _source: &str,
                diagnostics: &mut DiagnosticsInfo,
                add_diagnostics_on_err: bool,
            ) -> Result<Self, ()> {
                if let Some(arg) = arg_opt {
                    match arg.value {
                        TokenArgument::TVInteger(i64_value) => {
                            if i64_value > (<$x>::MAX as i64) {
                                if add_diagnostics_on_err {
                                    diagnostics.add_message(
                                        DMExtraInfo {
                                            range: arg.node.get_range(),
                                            message_template_data: hash_map! {
                                                "max_value" => <$x>::MAX.to_string(),
                                            },
                                        },
                                        "too_large_int",
                                    );
                                }
                                return Err(());
                            }
                            if i64_value < (<$x>::MIN as i64) {
                                if add_diagnostics_on_err {
                                    diagnostics.add_message(
                                        DMExtraInfo {
                                            range: arg.node.get_range(),
                                            message_template_data: hash_map! {
                                                "min_value" => <$x>::MIN.to_string(),
                                            },
                                        },
                                        "too_small_int",
                                    );
                                }
                                return Err(());
                            }
                            Ok(i64_value as $x)
                        }
                        _ => {
                            if add_diagnostics_on_err {
                                diagnostics.add_message(
                                    DMExtraInfo {
                                        range: arg.node.get_range(),
                                        message_template_data: hash_map! {
                                            "expected_parameters" => Self::expected_argument_types(),
                                            "found_parameters" => arg.value.argument_to_token_type_name(),
                                        },
                                    },
                                    "wrong_arg_type",
                                );
                            }
                            Err(())
                        }
                    }
                } else {
                    Err(())
                }
            }

            fn expected_argument_types() -> String {
                format!("Integer ({})", $t)
            }
        }
    };
}

// token_argument_into_integer!(i128);
token_argument_into_integer!(i64, "i64");
token_argument_into_integer!(i32, "i32");
token_argument_into_integer!(i16, "i16");
token_argument_into_integer!(i8, "i8");

// token_argument_into_integer!(u128);
// Can not cast i64 to u64 without loss
// token_argument_into_integer!(u64);
token_argument_into_integer!(u32, "u32");
token_argument_into_integer!(u16, "u16");
token_argument_into_integer!(u8, "u8");

// -------------------------Convert from TokenArgument -----------------------

macro_rules! token_argument_from_integer {
    ( $x:ty ) => {
        impl From<$x> for TokenArgument {
            fn from(item: $x) -> TokenArgument {
                TokenArgument::TVInteger(item as i64)
            }
        }

        impl From<Option<$x>> for TokenArgument {
            fn from(item: Option<$x>) -> TokenArgument {
                match item {
                    Some(v) => TokenArgument::TVInteger(v as i64),
                    None => TokenArgument::TVEmpty,
                }
            }
        }
    };
}

// token_argument_from_integer!(i128);
token_argument_from_integer!(i64);
token_argument_from_integer!(i32);
token_argument_from_integer!(i16);
token_argument_from_integer!(i8);

// token_argument_from_integer!(u128);
// Can not cast i64 to u64 without loss
// token_argument_from_integer!(u64);
token_argument_from_integer!(u32);
token_argument_from_integer!(u16);
token_argument_from_integer!(u8);

// ---------------------------- TESTS --------------------------
#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_tree_structure;
    use crate::test_utils::SyntaxTestBuilder;
    use df_ls_diagnostics::{Position, Range};
    use df_ls_lexical_analysis::test_utils::LexerTestBuilder;

    #[test]
    fn test_integer_correct() {
        let test_builder = SyntaxTestBuilder::from_lexer_test_builder(
            LexerTestBuilder::test_source(
                "header
                [REF:127]
                [REF:-127]
                [REF:-20]
                [REF:1555]
                [REF:4294967295]",
            )
            .add_test_lexer_diagnostics_codes(vec![])
            .add_test_lexer_diagnostics_ranges(vec![]),
        )
        .add_test_syntax_diagnostics_codes(vec![])
        .add_test_syntax_diagnostics_ranges(vec![]);

        test_tree_structure!(
            test_builder,
            [
                i8 => 127i8,
                (i8,) => (-127i8,),
                i16 => -20i16,
                u16 => 1555u16,
                u32 => 4294967295u32,
            ]
        );
    }

    #[test]
    fn test_integer_overflow() {
        let test_builder = SyntaxTestBuilder::from_lexer_test_builder(
            LexerTestBuilder::test_source(
                "header
                [REF:130]
                [REF:-130]
                [REF:-32769]
                [REF:-1]
                [REF:4294967296]",
            )
            .add_test_lexer_diagnostics_codes(vec![])
            .add_test_lexer_diagnostics_ranges(vec![]),
        )
        .add_test_syntax_diagnostics_codes(vec![
            "too_large_int",
            "too_small_int",
            "too_small_int",
            "too_small_int",
            "too_large_int",
        ])
        .add_test_syntax_diagnostics_ranges(vec![
            Range {
                start: Position {
                    line: 1,
                    character: 21,
                },
                end: Position {
                    line: 1,
                    character: 24,
                },
            },
            Range {
                start: Position {
                    line: 2,
                    character: 21,
                },
                end: Position {
                    line: 2,
                    character: 25,
                },
            },
            Range {
                start: Position {
                    line: 3,
                    character: 21,
                },
                end: Position {
                    line: 3,
                    character: 27,
                },
            },
            Range {
                start: Position {
                    line: 4,
                    character: 21,
                },
                end: Position {
                    line: 4,
                    character: 23,
                },
            },
            Range {
                start: Position {
                    line: 5,
                    character: 21,
                },
                end: Position {
                    line: 5,
                    character: 31,
                },
            },
        ]);

        test_tree_structure!(
            test_builder,
            [
                i8 =!,
                (i8,) =!,
                i16 =!,
                u16 =!,
                u32 =!,
            ]
        );
    }
}