aboutsummaryrefslogtreecommitdiff
path: root/azalea-brigadier/src/tree.rs
diff options
context:
space:
mode:
authormat <github@matdoes.dev>2022-04-17 14:02:13 -0500
committermat <github@matdoes.dev>2022-04-17 14:02:13 -0500
commita72a47ced76065caf739898954cd18edbc39174b (patch)
tree5526c7663f253bbd7c8318b9d98413f1f2074852 /azalea-brigadier/src/tree.rs
parent4ff67d4917ce333232189e86aee09f2d82451fc6 (diff)
downloadazalea-drasl-a72a47ced76065caf739898954cd18edbc39174b.tar.xz
Rewrite brigadier
Diffstat (limited to 'azalea-brigadier/src/tree.rs')
-rw-r--r--azalea-brigadier/src/tree.rs269
1 files changed, 269 insertions, 0 deletions
diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs
new file mode 100644
index 00000000..2f023697
--- /dev/null
+++ b/azalea-brigadier/src/tree.rs
@@ -0,0 +1,269 @@
+use crate::{
+ builder::{
+ argument_builder::ArgumentBuilderType, literal_argument_builder::Literal,
+ required_argument_builder::Argument,
+ },
+ context::{CommandContext, CommandContextBuilder, ParsedArgument},
+ exceptions::{
+ builtin_exceptions::BuiltInExceptions, command_syntax_exception::CommandSyntaxException,
+ },
+ modifier::RedirectModifier,
+ string_range::StringRange,
+ string_reader::StringReader,
+};
+use std::{
+ any::Any,
+ cell::RefCell,
+ collections::{BTreeMap, HashMap},
+ fmt::Debug,
+ hash::Hash,
+ ptr,
+ rc::Rc,
+};
+
+/// An ArgumentBuilder that has been built.
+#[derive(Clone)]
+#[non_exhaustive]
+pub struct CommandNode<S: Any + Clone> {
+ pub value: ArgumentBuilderType,
+
+ // we use BTreeMap instead of HashMap because it can be hashed
+ pub children: BTreeMap<String, Rc<RefCell<CommandNode<S>>>>,
+ pub literals: BTreeMap<String, Rc<RefCell<CommandNode<S>>>>,
+ pub arguments: BTreeMap<String, Rc<RefCell<CommandNode<S>>>>,
+
+ pub command: Option<Rc<dyn Fn(&CommandContext<S>) -> i32>>,
+ pub requirement: Rc<dyn Fn(Rc<S>) -> bool>,
+ pub redirect: Option<Rc<RefCell<CommandNode<S>>>>,
+ pub forks: bool,
+ pub modifier: Option<Rc<dyn RedirectModifier<S>>>,
+}
+
+impl<S: Any + Clone> CommandNode<S> {
+ // pub fn new()
+ // TODO: precalculate `literals` and `arguments` and include them in CommandNode
+ fn literals(&self) -> &BTreeMap<String, Rc<RefCell<CommandNode<S>>>> {
+ &self.literals
+ }
+ fn arguments(&self) -> &BTreeMap<String, Rc<RefCell<CommandNode<S>>>> {
+ &self.arguments
+ }
+
+ /// Gets the literal, or panics. You should use match if you're not certain about the type.
+ pub fn literal(&self) -> &Literal {
+ match self.value {
+ ArgumentBuilderType::Literal(ref literal) => literal,
+ _ => panic!("CommandNode::literal() called on non-literal node"),
+ }
+ }
+ /// Gets the argument, or panics. You should use match if you're not certain about the type.
+ pub fn argument(&self) -> &Argument {
+ match self.value {
+ ArgumentBuilderType::Argument(ref argument) => argument,
+ _ => panic!("CommandNode::argument() called on non-argument node"),
+ }
+ }
+
+ pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec<Rc<RefCell<CommandNode<S>>>> {
+ let literals = self.literals();
+
+ println!("get relevant nodes {:?} literals={:?}", self, literals);
+
+ if literals.len() > 0 {
+ let cursor = input.cursor();
+ while input.can_read() && input.peek() != ' ' {
+ input.skip();
+ }
+ let text: String = input
+ .string()
+ .chars()
+ .skip(cursor)
+ .take(input.cursor() - cursor)
+ .collect();
+ input.cursor = cursor;
+ let literal = literals.get(&text);
+ if let Some(literal) = literal {
+ return vec![literal.clone()];
+ } else {
+ return self
+ .arguments()
+ .values()
+ .map(|argument| argument.clone())
+ .collect();
+ }
+ } else {
+ return self
+ .arguments()
+ .values()
+ .map(|argument| argument.clone())
+ .collect();
+ }
+ }
+
+ pub fn can_use(&self, source: Rc<S>) -> bool {
+ (self.requirement)(source)
+ }
+
+ pub fn add_child(&mut self, node: &Rc<RefCell<CommandNode<S>>>) {
+ let child = self.children.get(node.borrow().name());
+ if let Some(child) = child {
+ // We've found something to merge onto
+ if let Some(command) = &node.borrow().command {
+ child.borrow_mut().command = Some(command.clone());
+ }
+ for grandchild in node.borrow().children.values() {
+ child.borrow_mut().add_child(grandchild);
+ }
+ } else {
+ self.children
+ .insert(node.borrow().name().to_string(), node.clone());
+ match &node.borrow().value {
+ ArgumentBuilderType::Literal(literal) => {
+ self.literals.insert(literal.value.clone(), node.clone());
+ }
+ ArgumentBuilderType::Argument(argument) => {
+ self.arguments.insert(argument.name.clone(), node.clone());
+ }
+ }
+ }
+ }
+
+ pub fn name(&self) -> &str {
+ match &self.value {
+ ArgumentBuilderType::Argument(argument) => &argument.name,
+ ArgumentBuilderType::Literal(literal) => &literal.value,
+ }
+ }
+
+ pub fn parse_with_context(
+ &self,
+ reader: &mut StringReader,
+ context_builder: &mut CommandContextBuilder<S>,
+ ) -> Result<(), CommandSyntaxException> {
+ match self.value {
+ ArgumentBuilderType::Argument(ref argument) => {
+ let start = reader.cursor();
+ // TODO: handle this better
+ let result = argument
+ .parse(reader)
+ .expect("Couldn't get result for some reason");
+ let parsed = ParsedArgument {
+ range: StringRange::between(start, reader.cursor()),
+ result: result,
+ };
+
+ context_builder.with_argument(&argument.name, parsed.clone());
+ context_builder.with_node(Rc::new(self.clone()), parsed.range);
+
+ Ok(())
+ }
+ ArgumentBuilderType::Literal(ref literal) => {
+ let start = reader.cursor();
+ let end = self.parse(reader);
+
+ if let Some(end) = end {
+ context_builder
+ .with_node(Rc::new(self.clone()), StringRange::between(start, end));
+ return Ok(());
+ }
+
+ Err(BuiltInExceptions::LiteralIncorrect {
+ expected: literal.value.clone(),
+ }
+ .create_with_context(reader))
+ }
+ }
+ }
+
+ fn parse(&self, reader: &mut StringReader) -> Option<usize> {
+ match self.value {
+ ArgumentBuilderType::Argument(ref argument) => {
+ panic!("Can't parse argument.")
+ }
+ ArgumentBuilderType::Literal(ref literal) => {
+ let start = reader.cursor();
+ if reader.can_read_length(literal.value.len()) {
+ let end = start + literal.value.len();
+ if reader
+ .string()
+ .get(start..end)
+ .expect("Couldn't slice reader correctly?")
+ == literal.value
+ {
+ reader.cursor = end;
+ if !reader.can_read() || reader.peek() == ' ' {
+ return Some(end);
+ } else {
+ reader.cursor = start;
+ }
+ }
+ }
+ }
+ }
+ None
+ }
+}
+
+impl<S: Any + Clone> Debug for CommandNode<S> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("CommandNode")
+ .field("value", &self.value)
+ .field("children", &self.children)
+ .field("command", &self.command.is_some())
+ // .field("requirement", &self.requirement)
+ .field("redirect", &self.redirect)
+ .field("forks", &self.forks)
+ // .field("modifier", &self.modifier)
+ .finish()
+ }
+}
+
+impl<S: Any + Clone> Default for CommandNode<S> {
+ fn default() -> Self {
+ println!("making default node");
+ Self {
+ value: ArgumentBuilderType::Literal(Literal::default()),
+
+ children: BTreeMap::new(),
+ literals: BTreeMap::new(),
+ arguments: BTreeMap::new(),
+
+ command: None,
+ requirement: Rc::new(|_| true),
+ redirect: None,
+ forks: false,
+ modifier: None,
+ }
+ }
+}
+
+impl<S: Any + Clone> Hash for CommandNode<S> {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ // hash the children
+ for (k, v) in &self.children {
+ k.hash(state);
+ v.borrow().hash(state);
+ }
+ // i hope this works because if doesn't then that'll be a problem
+ ptr::hash(&self.command, state);
+ }
+}
+
+impl<S: Any + Clone> PartialEq for CommandNode<S> {
+ fn eq(&self, other: &Self) -> bool {
+ if self.children != other.children {
+ return false;
+ }
+ if let Some(selfexecutes) = &self.command {
+ if let Some(otherexecutes) = &other.command {
+ if !Rc::ptr_eq(selfexecutes, otherexecutes) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ true
+ }
+}
+impl<S: Any + Clone> Eq for CommandNode<S> {}