From 6abe437c706f9e6058e6db0cb126a3d9c4d90cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Sat, 21 Sep 2024 22:45:05 +0200 Subject: [PATCH] implement tag declaration --- CHANGELOG.md | 1 + grammar.md | 7 +- src/lexical/token.rs | 6 ++ src/syntax/syntax_tree/declaration.rs | 115 +++++++++++++++++++++++++- src/syntax/syntax_tree/mod.rs | 1 + src/transpile/transpiler.rs | 17 +++- 6 files changed, 144 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e9034f..c3f5203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - lua blocks - imports - group +- Tags ### Changed diff --git a/grammar.md b/grammar.md index fcacd1a..38f6694 100644 --- a/grammar.md +++ b/grammar.md @@ -14,7 +14,7 @@ Namespace: 'namespace' StringLiteral; ### Declaration ```ebnf -Declaration: FunctionDeclaration | Import; +Declaration: FunctionDeclaration | Import | TagDeclaration; ``` ### Import @@ -22,6 +22,11 @@ Declaration: FunctionDeclaration | Import; Import: 'from' StringLiteral 'import' Identifier; ``` +### TagDeclaration +```ebnf +TagDeclaration: 'tag' StringLiteral ('of' StringLiteral)? 'replace'? '[' (StringLiteral (',' StringLiteral)*)? ']'; +``` + ### FunctionDeclaration ```ebnf Function: diff --git a/src/lexical/token.rs b/src/lexical/token.rs index dee23ec..253bec0 100644 --- a/src/lexical/token.rs +++ b/src/lexical/token.rs @@ -41,6 +41,9 @@ pub enum KeywordKind { Namespace, From, Import, + Tag, + Of, + Replace, } impl Display for KeywordKind { @@ -101,6 +104,9 @@ impl KeywordKind { Self::Namespace => "namespace", Self::From => "from", Self::Import => "import", + Self::Tag => "tag", + Self::Of => "of", + Self::Replace => "replace", } } diff --git a/src/syntax/syntax_tree/declaration.rs b/src/syntax/syntax_tree/declaration.rs index 431eff7..72531c1 100644 --- a/src/syntax/syntax_tree/declaration.rs +++ b/src/syntax/syntax_tree/declaration.rs @@ -20,7 +20,7 @@ use crate::{ }, }; -use super::{statement::Block, ConnectedList}; +use super::{statement::Block, ConnectedList, DelimitedList}; /// Syntax Synopsis: /// @@ -35,6 +35,7 @@ use super::{statement::Block, ConnectedList}; pub enum Declaration { Function(Function), Import(Import), + Tag(Tag), } impl SourceElement for Declaration { @@ -42,6 +43,7 @@ impl SourceElement for Declaration { match self { Self::Function(function) => function.span(), Self::Import(import) => import.span(), + Self::Tag(tag) => tag.span(), } } } @@ -226,6 +228,79 @@ impl SourceElement for Import { } } +/// Syntax Synopsis: +/// +/// ``` ebnf +/// TagDeclaration: +/// 'tag' StringLiteral ('of' StringLiteral)? 'replace'? '[' (StringLiteral (',' StringLiteral)*)? ']' +/// ; +/// ``` +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)] +pub struct Tag { + #[get = "pub"] + tag_keyword: Keyword, + #[get = "pub"] + name: StringLiteral, + #[get = "pub"] + of_type: Option<(Keyword, StringLiteral)>, + #[get = "pub"] + replace: Option, + #[get = "pub"] + entries: DelimitedList, +} + +impl Tag { + #[must_use] + #[allow(clippy::type_complexity)] + pub fn dissolve( + self, + ) -> ( + Keyword, + StringLiteral, + Option<(Keyword, StringLiteral)>, + Option, + DelimitedList, + ) { + ( + self.tag_keyword, + self.name, + self.of_type, + self.replace, + self.entries, + ) + } + + #[cfg(feature = "shulkerbox")] + #[must_use] + pub fn tag_type(&self) -> shulkerbox::datapack::tag::TagType { + use shulkerbox::datapack::tag::TagType; + + self.of_type + .as_ref() + .map_or(TagType::Functions, |(_, tag_type)| { + match tag_type.str_content().as_ref() { + "function" => TagType::Functions, + "block" => TagType::Blocks, + "entity_type" => TagType::Entities, + "fluid" => TagType::Fluids, + "game_event" => TagType::GameEvents, + "item" => TagType::Items, + other => TagType::Others(other.to_string()), + } + }) + } +} + +impl SourceElement for Tag { + fn span(&self) -> Span { + self.tag_keyword + .span() + .join(&self.entries.close.span) + .unwrap() + } +} + impl<'a> Parser<'a> { /// Parses an annotation. /// @@ -384,6 +459,44 @@ impl<'a> Parser<'a> { } } + Reading::Atomic(Token::Keyword(tag_keyword)) + if tag_keyword.keyword == KeywordKind::Tag => + { + // eat the tag keyword + self.forward(); + + // parse the name + let name = self.parse_string_literal(handler)?; + + let of_type = self + .try_parse(|parser| { + let of_keyword = parser.parse_keyword(KeywordKind::Of, &VoidHandler)?; + let of_type = parser.parse_string_literal(handler)?; + + Ok((of_keyword, of_type)) + }) + .ok(); + + let replace = self + .try_parse(|parser| parser.parse_keyword(KeywordKind::Replace, &VoidHandler)) + .ok(); + + let entries = self.parse_enclosed_list( + Delimiter::Bracket, + ',', + |parser| parser.parse_string_literal(handler), + handler, + )?; + + Ok(Declaration::Tag(Tag { + tag_keyword, + name, + of_type, + replace, + entries, + })) + } + unexpected => { // make progress self.forward(); diff --git a/src/syntax/syntax_tree/mod.rs b/src/syntax/syntax_tree/mod.rs index 7d892e5..df20696 100644 --- a/src/syntax/syntax_tree/mod.rs +++ b/src/syntax/syntax_tree/mod.rs @@ -49,6 +49,7 @@ pub struct ConnectedList { /// Represents a syntax tree node with a pattern of having [`ConnectedList`] delimited by a pair of /// punctuation like such `(a, b, c)`. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DelimitedList { /// The open punctuation of the list. diff --git a/src/transpile/transpiler.rs b/src/transpile/transpiler.rs index 8711503..388b5fc 100644 --- a/src/transpile/transpiler.rs +++ b/src/transpile/transpiler.rs @@ -84,7 +84,6 @@ impl Transpiler { let mut always_transpile_functions = Vec::new(); - // #[allow(clippy::significant_drop_in_scrutinee)] { let functions = self.functions.read().unwrap(); for (_, data) in functions.iter() { @@ -183,6 +182,22 @@ impl Transpiler { } } } + Declaration::Tag(tag) => { + let namespace = self + .datapack + .namespace_mut(&namespace.namespace_name().str_content()); + let sb_tag = namespace.tag_mut(&tag.name().str_content(), tag.tag_type()); + + if let Some(list) = &tag.entries().list { + for value in list.elements() { + sb_tag.add_value(value.str_content().as_ref().into()); + } + } + + if tag.replace().is_some() { + sb_tag.set_replace(true); + } + } }; }