From d959fb2d0cc4d8ad97eae86666876e22b2e50613 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 9 Jan 2022 00:14:39 -0600 Subject: [PATCH] add string_reader --- Cargo.lock | 4 + Cargo.toml | 1 + .../src/arguments/bool_argument_type.rs | 8 + azalea-brigadier/src/arguments/mod.rs | 1 + azalea-brigadier/src/builder/mod.rs | 0 azalea-brigadier/src/context/mod.rs | 0 azalea-brigadier/src/exceptions/mod.rs | 0 .../src/immutable_string_reader.rs | 10 + azalea-brigadier/src/lib.rs | 18 ++ azalea-brigadier/src/message.rs | 1 + azalea-brigadier/src/string_reader.rs | 277 ++++++++++++++++++ azalea-brigadier/src/suggestion/mod.rs | 0 azalea-brigadier/src/tree/mod.rs | 0 13 files changed, 320 insertions(+) create mode 100644 azalea-brigadier/src/arguments/mod.rs create mode 100644 azalea-brigadier/src/builder/mod.rs create mode 100644 azalea-brigadier/src/context/mod.rs create mode 100644 azalea-brigadier/src/exceptions/mod.rs create mode 100644 azalea-brigadier/src/suggestion/mod.rs create mode 100644 azalea-brigadier/src/tree/mod.rs diff --git a/Cargo.lock b/Cargo.lock index eca4d4fa..67259fef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,10 @@ dependencies = [ "uuid", ] +[[package]] +name = "azalea-brigadier" +version = "0.1.0" + [[package]] name = "azalea-chat" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index d22c326b..7f958207 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ members = [ "azalea-core", "azalea-auth", "azalea-nbt", + "azalea-brigadier", ] diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs index e69de29b..d4a33517 100644 --- a/azalea-brigadier/src/arguments/bool_argument_type.rs +++ b/azalea-brigadier/src/arguments/bool_argument_type.rs @@ -0,0 +1,8 @@ +struct BoolArgumentType { + // private static final Collection EXAMPLES = Arrays.asList("true", "false"); + const EXAMPLES: &'static [&'static str] = &["true", "false"]; +} + +impl ArgumentType for BoolArgumentType { + +} \ No newline at end of file diff --git a/azalea-brigadier/src/arguments/mod.rs b/azalea-brigadier/src/arguments/mod.rs new file mode 100644 index 00000000..18d01d88 --- /dev/null +++ b/azalea-brigadier/src/arguments/mod.rs @@ -0,0 +1 @@ +mod argument_type; diff --git a/azalea-brigadier/src/builder/mod.rs b/azalea-brigadier/src/builder/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/context/mod.rs b/azalea-brigadier/src/context/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/exceptions/mod.rs b/azalea-brigadier/src/exceptions/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/immutable_string_reader.rs b/azalea-brigadier/src/immutable_string_reader.rs index e69de29b..2e067ace 100644 --- a/azalea-brigadier/src/immutable_string_reader.rs +++ b/azalea-brigadier/src/immutable_string_reader.rs @@ -0,0 +1,10 @@ +pub trait ImmutableStringReader { + fn remaining_length(&self) -> usize; + fn total_length(&self) -> usize; + fn get_read(&self) -> &str; + fn remaining(&self) -> &str; + fn can_read_length(&self, length: usize) -> bool; + fn can_read(&self) -> bool; + fn peek(&self) -> char; + fn peek_offset(&self, offset: usize) -> char; +} diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index 1b4a90c9..d0966de3 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -1,3 +1,21 @@ +mod ambiguity_consumer; +mod arguments; +mod builder; +mod command; +mod command_dispatcher; +mod context; +mod exceptions; +mod immutable_string_reader; +mod literal_message; +mod message; +mod parse_results; +mod redirect_modifier; +mod result_consumer; +mod single_redirect_modifier; +mod string_reader; +mod suggestion; +mod tree; + #[cfg(test)] mod tests { #[test] diff --git a/azalea-brigadier/src/message.rs b/azalea-brigadier/src/message.rs index e69de29b..8b137891 100644 --- a/azalea-brigadier/src/message.rs +++ b/azalea-brigadier/src/message.rs @@ -0,0 +1 @@ + diff --git a/azalea-brigadier/src/string_reader.rs b/azalea-brigadier/src/string_reader.rs index e69de29b..376cc711 100644 --- a/azalea-brigadier/src/string_reader.rs +++ b/azalea-brigadier/src/string_reader.rs @@ -0,0 +1,277 @@ +use crate::immutable_string_reader::ImmutableStringReader; +use std::str::FromStr; + +#[derive(Clone)] +struct StringReader<'a> { + pub string: &'a str, + pub cursor: usize, +} + +const SYNTAX_ESCAPE: char = '\\'; +const SYNTAX_DOUBLE_QUOTE: char = '"'; +const SYNTAX_SINGLE_QUOTE: char = '\''; + +impl<'a> From<&'a str> for &StringReader<'a> { + fn from(string: &'a str) -> &StringReader<'a> { + &StringReader { string, cursor: 0 } + } +} + +impl ImmutableStringReader for StringReader<'_> { + fn remaining_length(&self) -> usize { + self.string.len() - self.cursor + } + + fn total_length(&self) -> usize { + self.string.len() + } + + fn get_read(&self) -> &str { + &self.string[self.cursor..] + } + + fn remaining(&self) -> &str { + &self.string[self.cursor..] + } + + fn can_read_length(&self, length: usize) -> bool { + self.cursor + length <= self.string.len() + } + + fn can_read(&self) -> bool { + self.can_read_length(1) + } + + fn peek(&self) -> char { + self.string.chars().nth(self.cursor).unwrap() + } + + fn peek_offset(&self, offset: usize) -> char { + self.string.chars().nth(self.cursor + offset).unwrap() + } +} + +impl StringReader<'_> { + fn read(&mut self) -> char { + let c = self.peek(); + self.cursor += 1; + c + } + + fn skip(&mut self) { + self.cursor += 1; + } + + fn is_allowed_number(c: char) -> bool { + c >= '0' && c <= '9' || c == '.' || c == '-' + } + + fn is_quoted_string_start(c: char) -> bool { + c == SYNTAX_DOUBLE_QUOTE || c == SYNTAX_SINGLE_QUOTE + } + + fn skip_whitespace(&mut self) { + while self.can_read() && self.peek().is_whitespace() { + self.skip(); + } + } + + fn read_int(&self) -> Result<(), CommandSyntaxException> { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + self.skip(); + } + let number = &self.string[start..self.cursor]; + if number.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_int() + .create_with_context(self)); + } + let result = i32::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_int() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn read_long(&self) -> Result<(), CommandSyntaxException> { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + self.skip(); + } + let number = &self.string[start..self.cursor]; + if number.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_long() + .create_with_context(self)); + } + let result = i64::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_long() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn read_double(&self) -> Result<(), CommandSyntaxException> { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + self.skip(); + } + let number = &self.string[start..self.cursor]; + if number.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_double() + .create_with_context(self)); + } + let result = f64::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_double() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn read_float(&self) -> Result<(), CommandSyntaxException> { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_number(self.peek()) { + self.skip(); + } + let number = &self.string[start..self.cursor]; + if number.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_float() + .create_with_context(self)); + } + let result = f32::from_str(number); + if result.is_err() { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_float() + .create_with_context(self, number)); + } + + Ok(()) + } + + fn is_allowed_in_unquoted_string(c: char) -> bool { + c >= '0' && c <= '9' + || c >= 'A' && c <= 'Z' + || c >= 'a' && c <= 'z' + || c == '_' + || c == '-' + || c == '.' + || c == '+' + } + + fn read_unquoted_string(&self) -> &str { + let start = self.cursor; + while self.can_read() && StringReader::<'_>::is_allowed_in_unquoted_string(self.peek()) { + self.skip(); + } + &self.string[start..self.cursor] + } + + fn read_quoted_string(&self) -> Result<&str, CommandSyntaxException> { + if !self.can_read() { + return ""; + } + let next = self.peek(); + if !StringReader::<'_>::is_quoted_string_start(next) { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_start_of_quote() + .create_with_context(self)); + } + self.skip(); + self.read_string_until(next) + } + + fn read_string_until(&self, terminator: char) -> Result { + let result = String::new(); + let mut escaped = false; + while self.can_read() { + let c = self.read(); + if escaped { + if c == terminator || c == SYNTAX_ESCAPE { + result.push(c); + escaped = false; + } else { + self.cursor -= 1; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_escape() + .create_with_context(self, c)); + } + } else if c == SYNTAX_ESCAPE { + escaped = true; + } else if c == terminator { + return Ok(result); + } else { + result.push(c); + } + } + + Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_end_of_quote() + .create_with_context(self)) + } + + fn read_string(&self) -> Result { + // if (!canRead()) { + // return ""; + // } + // final char next = peek(); + // if (isQuotedStringStart(next)) { + // skip(); + // return readStringUntil(next); + // } + // return readUnquotedString(); + if !self.can_read() { + return Ok(String::new()); + } + let next = self.peek(); + if StringReader::<'_>::is_quoted_string_start(next) { + self.skip(); + return self.read_string_until(next); + } + Ok(self.read_unquoted_string().to_string()) + } + + fn read_boolean(&self) -> Result { + let start = self.cursor; + let value = self.read_string()?; + if value.is_empty() { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_bool() + .create_with_context(self)); + } + + if value == "true" { + return Ok(true); + } else if value == "false" { + return Ok(false); + } else { + self.cursor = start; + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_invalid_bool() + .create_with_context(self, value)); + } + } + + fn expect(&self, c: char) -> Result<(), CommandSyntaxException> { + if !self.can_read() || self.peek() != c { + return Err(CommandSyntaxException::BUILT_IN_EXCEPTIONS + .reader_expected_symbol() + .create_with_context(self, c)); + } + self.skip(); + } diff --git a/azalea-brigadier/src/suggestion/mod.rs b/azalea-brigadier/src/suggestion/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs new file mode 100644 index 00000000..e69de29b