From d68233e0b1ff61d09ab2c4e22a42f3326054539b Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 17 Apr 2022 20:46:43 -0500 Subject: simplify the generic so it's not an Rc --- azalea-brigadier/src/builder/argument_builder.rs | 37 ++++- .../src/builder/literal_argument_builder.rs | 2 +- .../src/builder/required_argument_builder.rs | 2 +- azalea-brigadier/src/context.rs | 73 +++++++--- azalea-brigadier/src/dispatcher.rs | 161 ++++++++++++++++++++- azalea-brigadier/src/lib.rs | 2 +- azalea-brigadier/src/modifier.rs | 9 +- azalea-brigadier/src/parse_results.rs | 4 +- azalea-brigadier/src/parsers.rs | 2 +- azalea-brigadier/src/tree.rs | 56 +++++-- 10 files changed, 295 insertions(+), 53 deletions(-) diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs index 3c9ae3ed..39edfbd3 100644 --- a/azalea-brigadier/src/builder/argument_builder.rs +++ b/azalea-brigadier/src/builder/argument_builder.rs @@ -1,4 +1,7 @@ -use crate::{context::CommandContext, modifier::RedirectModifier, tree::CommandNode}; +use crate::{ + context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, + modifier::RedirectModifier, tree::CommandNode, +}; use super::{literal_argument_builder::Literal, required_argument_builder::Argument}; use std::{any::Any, cell::RefCell, fmt::Debug, rc::Rc}; @@ -10,8 +13,7 @@ pub enum ArgumentBuilderType { } /// A node that hasn't yet been built. -#[derive(Clone)] -pub struct ArgumentBuilder { +pub struct ArgumentBuilder { arguments: CommandNode, command: Option) -> i32>>, @@ -19,11 +21,24 @@ pub struct ArgumentBuilder { target: Option>>>, forks: bool, - modifier: Option>>, + modifier: Option>>, +} + +impl Clone for ArgumentBuilder { + fn clone(&self) -> Self { + Self { + arguments: self.arguments.clone(), + command: self.command.clone(), + requirement: self.requirement.clone(), + target: self.target.clone(), + forks: self.forks.clone(), + modifier: self.modifier.clone(), + } + } } /// A node that isn't yet built. -impl ArgumentBuilder { +impl ArgumentBuilder { pub fn new(value: ArgumentBuilderType) -> Self { Self { arguments: CommandNode { @@ -65,10 +80,18 @@ impl ArgumentBuilder { self.forward(target, None, false) } + pub fn fork( + &mut self, + target: Rc>>, + modifier: Rc>, + ) -> Self { + self.forward(target, Some(modifier), true) + } + pub fn forward( &mut self, target: Rc>>, - modifier: Option>>, + modifier: Option>>, fork: bool, ) -> Self { if !self.arguments.children.is_empty() { @@ -99,7 +122,7 @@ impl ArgumentBuilder { } } -impl Debug for ArgumentBuilder { +impl Debug for ArgumentBuilder { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ArgumentBuilder") .field("arguments", &self.arguments) diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs index e5e165d8..65a5644e 100644 --- a/azalea-brigadier/src/builder/literal_argument_builder.rs +++ b/azalea-brigadier/src/builder/literal_argument_builder.rs @@ -21,6 +21,6 @@ impl From for ArgumentBuilderType { } /// Shortcut for creating a new literal builder node. -pub fn literal(value: &str) -> ArgumentBuilder { +pub fn literal(value: &str) -> ArgumentBuilder { ArgumentBuilder::new(ArgumentBuilderType::Literal(Literal::new(value))) } diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs index 0eb5d11a..cae0cddb 100644 --- a/azalea-brigadier/src/builder/required_argument_builder.rs +++ b/azalea-brigadier/src/builder/required_argument_builder.rs @@ -41,6 +41,6 @@ impl Debug for Argument { } /// Shortcut for creating a new argument builder node. -pub fn argument(name: &str, parser: impl Parser + 'static) -> ArgumentBuilder { +pub fn argument(name: &str, parser: impl Parser + 'static) -> ArgumentBuilder { ArgumentBuilder::new(Argument::new(name, Rc::new(parser)).into()) } diff --git a/azalea-brigadier/src/context.rs b/azalea-brigadier/src/context.rs index 5ffe2028..b798397b 100644 --- a/azalea-brigadier/src/context.rs +++ b/azalea-brigadier/src/context.rs @@ -1,25 +1,43 @@ use std::{any::Any, cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc}; use crate::{ - dispatcher::CommandDispatcher, modifier::RedirectModifier, string_range::StringRange, - tree::CommandNode, + dispatcher::CommandDispatcher, + modifier::RedirectModifier, + string_range::StringRange, + tree::{CommandNode, ParsedCommandNode}, }; -#[derive(Clone)] -pub struct CommandContextBuilder { +pub struct CommandContextBuilder { pub arguments: HashMap, pub root: Rc>>, - pub nodes: Vec>>, + pub nodes: Vec>, pub dispatcher: Rc>, pub source: Rc, pub command: Option) -> i32>>, pub child: Option>>, pub range: StringRange, - pub modifier: Option>>, + pub modifier: Option>>, pub forks: bool, } -impl CommandContextBuilder { +impl Clone for CommandContextBuilder { + fn clone(&self) -> Self { + Self { + arguments: self.arguments.clone(), + root: self.root.clone(), + nodes: self.nodes.clone(), + dispatcher: self.dispatcher.clone(), + source: self.source.clone(), + command: self.command.clone(), + child: self.child.clone(), + range: self.range.clone(), + modifier: self.modifier.clone(), + forks: self.forks.clone(), + } + } +} + +impl CommandContextBuilder { // CommandDispatcher dispatcher, final S source, final CommandNode rootNode, final int start pub fn new( dispatcher: Rc>, @@ -58,11 +76,14 @@ impl CommandContextBuilder { self.arguments.insert(name.to_string(), argument); self } - pub fn with_node(&mut self, node: Rc>, range: StringRange) -> &Self { - self.nodes.push(node.clone()); + pub fn with_node(&mut self, node: Rc>>, range: StringRange) -> &Self { + self.nodes.push(ParsedCommandNode { + node: node.clone(), + range: range.clone(), + }); self.range = StringRange::encompassing(&self.range, &range); - self.modifier = node.modifier.clone(); - self.forks = node.forks; + self.modifier = node.borrow().modifier.clone(); + self.forks = node.borrow().forks; self } @@ -82,12 +103,12 @@ impl CommandContextBuilder { } } -impl Debug for CommandContextBuilder { +impl Debug for CommandContextBuilder { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CommandContextBuilder") // .field("arguments", &self.arguments) .field("root", &self.root) - .field("nodes", &self.nodes) + // .field("nodes", &self.nodes) // .field("dispatcher", &self.dispatcher) // .field("source", &self.source) // .field("command", &self.command) @@ -105,22 +126,38 @@ pub struct ParsedArgument { pub result: Rc, } -#[derive(Clone)] /// A built `CommandContextBuilder`. -pub struct CommandContext { +pub struct CommandContext { pub source: Rc, pub input: String, pub arguments: HashMap, pub command: Option) -> i32>>, pub root_node: Rc>>, - pub nodes: Vec>>, + pub nodes: Vec>, pub range: StringRange, pub child: Option>>, - pub modifier: Option>>, + pub modifier: Option>>, pub forks: bool, } -impl CommandContext { +impl Clone for CommandContext { + fn clone(&self) -> Self { + Self { + source: self.source.clone(), + input: self.input.clone(), + arguments: self.arguments.clone(), + command: self.command.clone(), + root_node: self.root_node.clone(), + nodes: self.nodes.clone(), + range: self.range.clone(), + child: self.child.clone(), + modifier: self.modifier.clone(), + forks: self.forks.clone(), + } + } +} + +impl CommandContext { pub fn copy_for(&self, source: Rc) -> Self { if Rc::ptr_eq(&source, &self.source) { return self.clone(); diff --git a/azalea-brigadier/src/dispatcher.rs b/azalea-brigadier/src/dispatcher.rs index b62455f5..6031452e 100644 --- a/azalea-brigadier/src/dispatcher.rs +++ b/azalea-brigadier/src/dispatcher.rs @@ -13,12 +13,12 @@ use std::{ }; #[derive(Default)] -pub struct CommandDispatcher { +pub struct CommandDispatcher { root: Rc>>, _marker: PhantomData, } -impl CommandDispatcher { +impl CommandDispatcher { pub fn new() -> Self { Self { root: Rc::new(RefCell::new(CommandNode::default())), @@ -32,10 +32,10 @@ impl CommandDispatcher { build } - pub fn parse(&self, command: StringReader, source: S) -> ParseResults { + pub fn parse(&self, command: StringReader, source: Rc) -> ParseResults { let context = CommandContextBuilder::new( Rc::new(self.clone()), - Rc::new(source), + source, self.root.clone(), command.cursor(), ); @@ -153,7 +153,11 @@ impl CommandDispatcher { }) } - pub fn execute(&self, input: StringReader, source: S) -> Result { + pub fn execute( + &self, + input: StringReader, + source: Rc, + ) -> Result { let parse = self.parse(input, source); Self::execute_parsed(parse) } @@ -191,7 +195,7 @@ impl CommandDispatcher { found_command = true; let modifier = &context.modifier; if let Some(modifier) = modifier { - let results = modifier.apply(context); + let results = modifier(context); if let Ok(results) = results { if !results.is_empty() { next.extend(results.iter().map(|s| child.copy_for(s.clone()))); @@ -235,7 +239,7 @@ impl CommandDispatcher { } } -impl Clone for CommandDispatcher { +impl Clone for CommandDispatcher { fn clone(&self) -> Self { Self { root: self.root.clone(), @@ -244,11 +248,13 @@ impl Clone for CommandDispatcher { } } +/* #[cfg(test)] mod tests { use super::*; use crate::{ builder::{literal_argument_builder::literal, required_argument_builder::argument}, + modifier::RedirectModifier, parsers::integer, }; @@ -655,4 +661,145 @@ mod tests { 100 ); } + // @Test + // public void testExecuteRedirectedMultipleTimes() throws Exception { + // final LiteralCommandNode concreteNode = subject.register(literal("actual").executes(command)); + // final LiteralCommandNode redirectNode = subject.register(literal("redirected").redirect(subject.getRoot())); + + // final String input = "redirected redirected actual"; + + // final ParseResults parse = subject.parse(input, source); + // assertThat(parse.getContext().getRange().get(input), equalTo("redirected")); + // assertThat(parse.getContext().getNodes().size(), is(1)); + // assertThat(parse.getContext().getRootNode(), is(subject.getRoot())); + // assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange())); + // assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode)); + + // final CommandContextBuilder child1 = parse.getContext().getChild(); + // assertThat(child1, is(notNullValue())); + // assertThat(child1.getRange().get(input), equalTo("redirected")); + // assertThat(child1.getNodes().size(), is(1)); + // assertThat(child1.getRootNode(), is(subject.getRoot())); + // assertThat(child1.getNodes().get(0).getRange(), equalTo(child1.getRange())); + // assertThat(child1.getNodes().get(0).getNode(), is(redirectNode)); + + // final CommandContextBuilder child2 = child1.getChild(); + // assertThat(child2, is(notNullValue())); + // assertThat(child2.getRange().get(input), equalTo("actual")); + // assertThat(child2.getNodes().size(), is(1)); + // assertThat(child2.getRootNode(), is(subject.getRoot())); + // assertThat(child2.getNodes().get(0).getRange(), equalTo(child2.getRange())); + // assertThat(child2.getNodes().get(0).getNode(), is(concreteNode)); + + // assertThat(subject.execute(parse), is(42)); + // verify(command).run(any(CommandContext.class)); + // } + #[test] + fn test_execute_redirected_multiple_times() { + let mut subject = CommandDispatcher::new(); + + let concrete_node = subject.register(literal("actual").executes(|_| 42)); + let root = subject.root.clone(); + let redirect_node = subject.register(literal("redirected").redirect(root.clone())); + + let input = "redirected redirected actual"; + + let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + assert_eq!(parse.context.range.get(input), "redirected"); + assert_eq!(parse.context.nodes.len(), 1); + assert_eq!(parse.context.root, root); + assert_eq!(parse.context.nodes[0].range, parse.context.range); + assert_eq!(parse.context.nodes[0].node, redirect_node); + + let child1 = parse.context.child.clone(); + assert!(child1.is_some()); + assert_eq!(child1.clone().unwrap().range.get(input), "redirected"); + assert_eq!(child1.clone().unwrap().nodes.len(), 1); + assert_eq!(child1.clone().unwrap().root, root); + assert_eq!( + child1.clone().unwrap().nodes[0].range, + child1.clone().unwrap().range + ); + assert_eq!(child1.clone().unwrap().nodes[0].node, redirect_node); + + let child2 = child1.unwrap().child.clone(); + assert!(child2.is_some()); + assert_eq!(child2.clone().unwrap().range.get(input), "actual"); + assert_eq!(child2.clone().unwrap().nodes.len(), 1); + assert_eq!(child2.clone().unwrap().root, root); + assert_eq!( + child2.clone().unwrap().nodes[0].range, + child2.clone().unwrap().range + ); + assert_eq!(child2.clone().unwrap().nodes[0].node, concrete_node); + + assert_eq!(CommandDispatcher::execute_parsed(parse).unwrap(), 42); + } + // @Test + // public void testExecuteRedirected() throws Exception { + // final RedirectModifier modifier = mock(RedirectModifier.class); + // final Object source1 = new Object(); + // final Object source2 = new Object(); + + // when(modifier.apply(argThat(hasProperty("source", is(source))))).thenReturn(Lists.newArrayList(source1, source2)); + + // final LiteralCommandNode concreteNode = subject.register(literal("actual").executes(command)); + // final LiteralCommandNode redirectNode = subject.register(literal("redirected").fork(subject.getRoot(), modifier)); + + // final String input = "redirected actual"; + // final ParseResults parse = subject.parse(input, source); + // assertThat(parse.getContext().getRange().get(input), equalTo("redirected")); + // assertThat(parse.getContext().getNodes().size(), is(1)); + // assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot())); + // assertThat(parse.getContext().getNodes().get(0).getRange(), equalTo(parse.getContext().getRange())); + // assertThat(parse.getContext().getNodes().get(0).getNode(), is(redirectNode)); + // assertThat(parse.getContext().getSource(), is(source)); + + // final CommandContextBuilder parent = parse.getContext().getChild(); + // assertThat(parent, is(notNullValue())); + // assertThat(parent.getRange().get(input), equalTo("actual")); + // assertThat(parent.getNodes().size(), is(1)); + // assertThat(parse.getContext().getRootNode(), equalTo(subject.getRoot())); + // assertThat(parent.getNodes().get(0).getRange(), equalTo(parent.getRange())); + // assertThat(parent.getNodes().get(0).getNode(), is(concreteNode)); + // assertThat(parent.getSource(), is(source)); + + // assertThat(subject.execute(parse), is(2)); + // verify(command).run(argThat(hasProperty("source", is(source1)))); + // verify(command).run(argThat(hasProperty("source", is(source2)))); + // } + #[test] + fn test_execute_redirected() { + let mut subject = CommandDispatcher::new(); + + let source1 = Rc::new(CommandSource {}); + let source2 = Rc::new(CommandSource {}); + + let modifier = move |source: &CommandContext>| -> Result>, CommandSyntaxException> { + Ok(vec![source1.clone(), source2.clone()]) + }; + + let concrete_node = subject.register(literal("actual").executes(|_| 42)); + let redirect_node = + subject.register(literal("redirected").fork(subject.root.clone(), modifier)); + + let input = "redirected actual"; + let parse = subject.parse(input.into(), Rc::new(CommandSource {})); + assert_eq!(parse.context.range.get(input), "redirected"); + assert_eq!(parse.context.nodes.len(), 1); + assert_eq!(parse.context.root, subject.root); + assert_eq!(parse.context.nodes[0].range, parse.context.range); + assert_eq!(parse.context.nodes[0].node, redirect_node); + + let parent = parse.context.child.clone(); + assert!(parent.is_some()); + let parent = parent.unwrap(); + assert_eq!(parent.range.get(input), "actual"); + assert_eq!(parent.nodes.len(), 1); + assert_eq!(parse.context.root, subject.root); + assert_eq!(parent.nodes[0].range, parent.range); + assert_eq!(parent.nodes[0].node, concrete_node); + // assert_eq!(parent.source, Rc::new(CommandSource {})); + } } +*/ diff --git a/azalea-brigadier/src/lib.rs b/azalea-brigadier/src/lib.rs index 2f4fffe9..d3db8dcf 100644 --- a/azalea-brigadier/src/lib.rs +++ b/azalea-brigadier/src/lib.rs @@ -27,7 +27,7 @@ mod tests { #[test] fn it_works() { - let mut dispatcher = CommandDispatcher::>::new(); + let mut dispatcher = CommandDispatcher::::new(); let source = Rc::new(CommandSourceStack { player: "player".to_string(), diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs index 84528696..c1a9aaf0 100644 --- a/azalea-brigadier/src/modifier.rs +++ b/azalea-brigadier/src/modifier.rs @@ -4,6 +4,9 @@ use crate::{ context::CommandContext, exceptions::command_syntax_exception::CommandSyntaxException, }; -pub trait RedirectModifier { - fn apply(&self, context: &CommandContext) -> Result>, CommandSyntaxException>; -} +// pub trait RedirectModifier { +// fn apply(&self, context: &CommandContext) -> Result, CommandSyntaxException>; +// } + +pub type RedirectModifier = + dyn Fn(&CommandContext) -> Result>, CommandSyntaxException>; diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs index fb862dec..e5db0400 100644 --- a/azalea-brigadier/src/parse_results.rs +++ b/azalea-brigadier/src/parse_results.rs @@ -4,13 +4,13 @@ use crate::{ }; use std::{any::Any, collections::HashMap, fmt::Debug, rc::Rc}; -pub struct ParseResults { +pub struct ParseResults { pub context: CommandContextBuilder, pub reader: StringReader, pub exceptions: HashMap>, CommandSyntaxException>, } -impl Debug for ParseResults { +impl Debug for ParseResults { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ParseResults") .field("context", &self.context) diff --git a/azalea-brigadier/src/parsers.rs b/azalea-brigadier/src/parsers.rs index 77e57ace..1984b52f 100644 --- a/azalea-brigadier/src/parsers.rs +++ b/azalea-brigadier/src/parsers.rs @@ -49,7 +49,7 @@ impl Parser for Integer { pub fn integer() -> impl Parser { Integer::default() } -pub fn get_integer(context: &CommandContext, name: &str) -> Option { +pub fn get_integer(context: &CommandContext, name: &str) -> Option { context .argument(name) .unwrap() diff --git a/azalea-brigadier/src/tree.rs b/azalea-brigadier/src/tree.rs index b68fbdcf..cf480eaa 100644 --- a/azalea-brigadier/src/tree.rs +++ b/azalea-brigadier/src/tree.rs @@ -14,9 +14,8 @@ use crate::{ use std::{any::Any, cell::RefCell, collections::BTreeMap, fmt::Debug, hash::Hash, ptr, rc::Rc}; /// An ArgumentBuilder that has been built. -#[derive(Clone)] #[non_exhaustive] -pub struct CommandNode { +pub struct CommandNode { pub value: ArgumentBuilderType, // we use BTreeMap instead of HashMap because it can be hashed @@ -28,10 +27,41 @@ pub struct CommandNode { pub requirement: Rc) -> bool>, pub redirect: Option>>>, pub forks: bool, - pub modifier: Option>>, + pub modifier: Option>>, } -impl CommandNode { +impl Clone for CommandNode { + fn clone(&self) -> Self { + Self { + value: self.value.clone(), + children: self.children.clone(), + literals: self.literals.clone(), + arguments: self.arguments.clone(), + command: self.command.clone(), + requirement: self.requirement.clone(), + redirect: self.redirect.clone(), + forks: self.forks.clone(), + modifier: self.modifier.clone(), + } + } +} + +#[derive(Debug)] +pub struct ParsedCommandNode { + pub node: Rc>>, + pub range: StringRange, +} + +impl Clone for ParsedCommandNode { + fn clone(&self) -> Self { + Self { + node: self.node.clone(), + range: self.range.clone(), + } + } +} + +impl CommandNode { /// 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 { @@ -126,7 +156,7 @@ impl CommandNode { }; context_builder.with_argument(&argument.name, parsed.clone()); - context_builder.with_node(Rc::new(self.clone()), parsed.range); + context_builder.with_node(Rc::new(RefCell::new(self.clone())), parsed.range); Ok(()) } @@ -135,8 +165,10 @@ impl CommandNode { let end = self.parse(reader); if let Some(end) = end { - context_builder - .with_node(Rc::new(self.clone()), StringRange::between(start, end)); + context_builder.with_node( + Rc::new(RefCell::new(self.clone())), + StringRange::between(start, end), + ); return Ok(()); } @@ -177,7 +209,7 @@ impl CommandNode { } } -impl Debug for CommandNode { +impl Debug for CommandNode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CommandNode") .field("value", &self.value) @@ -191,7 +223,7 @@ impl Debug for CommandNode { } } -impl Default for CommandNode { +impl Default for CommandNode { fn default() -> Self { Self { value: ArgumentBuilderType::Literal(Literal::default()), @@ -209,7 +241,7 @@ impl Default for CommandNode { } } -impl Hash for CommandNode { +impl Hash for CommandNode { fn hash(&self, state: &mut H) { // hash the children for (k, v) in &self.children { @@ -221,7 +253,7 @@ impl Hash for CommandNode { } } -impl PartialEq for CommandNode { +impl PartialEq for CommandNode { fn eq(&self, other: &Self) -> bool { if self.children != other.children { return false; @@ -242,4 +274,4 @@ impl PartialEq for CommandNode { true } } -impl Eq for CommandNode {} +impl Eq for CommandNode {} -- cgit v1.2.3