implement remaining execute operators

This commit is contained in:
Moritz Hölting 2024-04-09 21:25:19 +02:00
parent a818325ce9
commit 2bac397096
3 changed files with 720 additions and 32 deletions

View File

@ -21,7 +21,18 @@ pub enum KeywordKind {
Function,
If,
Else,
Align,
Anchored,
As,
AsAt,
At,
Facing,
In,
On,
Positioned,
Rotated,
Store,
Summon,
Group,
Run,
Lua,
@ -67,7 +78,18 @@ impl KeywordKind {
Self::Function => "fn",
Self::If => "if",
Self::Else => "else",
Self::Align => "align",
Self::Anchored => "anchored",
Self::As => "as",
Self::AsAt => "asat",
Self::At => "at",
Self::Facing => "facing",
Self::In => "in",
Self::On => "on",
Self::Positioned => "positioned",
Self::Rotated => "rotated",
Self::Store => "store",
Self::Summon => "summon",
Self::Group => "group",
Self::Run => "run",
Self::Lua => "lua",
@ -78,7 +100,22 @@ impl KeywordKind {
/// Whether the keyword starts an execute block.
#[must_use]
pub fn starts_execute_block(&self) -> bool {
matches!(self, Self::If | Self::As)
matches!(
self,
Self::If
| Self::Align
| Self::Anchored
| Self::As
| Self::AsAt
| Self::At
| Self::Facing
| Self::In
| Self::On
| Self::Positioned
| Self::Rotated
| Self::Store
| Self::Summon
)
}
}

View File

@ -15,7 +15,7 @@ use crate::{
},
syntax::{
error::{Error, SyntaxKind, UnexpectedSyntax},
parser::{Parser, Reading},
parser::{DelimitedTree, Parser, Reading},
syntax_tree::condition::ParenthesizedCondition,
},
};
@ -25,7 +25,8 @@ use super::Block;
/// Syntax Synopsis:
/// ```ebnf
/// ExecuteBlock:
/// ExecuteBlockHead ExecuteBlockTail
/// (ExecuteBlockHead ExecuteBlockTail)
/// | (Conditional Block Else)
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@ -49,23 +50,57 @@ impl SourceElement for ExecuteBlock {
}
}
/// Syntax Synopsis:
/// ```ebnf
/// ExecuteBlockHead:
/// Conditional
/// | As
/// ;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner, From)]
#[allow(missing_docs)]
pub enum ExecuteBlockHead {
Conditional(Conditional),
Align(Align),
Anchored(Anchored),
As(As),
AsAt(AsAt),
At(At),
Facing(Facing),
In(In),
On(On),
Positioned(Positioned),
Rotated(Rotated),
Store(Store),
Summon(Summon),
}
impl SourceElement for ExecuteBlockHead {
fn span(&self) -> Span {
match self {
Self::Conditional(conditional) => conditional.span(),
Self::Align(align) => align.span(),
Self::Anchored(anchored) => anchored.span(),
Self::As(as_) => as_.span(),
Self::AsAt(as_at) => as_at.span(),
Self::At(at) => at.span(),
Self::Facing(facing) => facing.span(),
Self::In(in_) => in_.span(),
Self::On(on) => on.span(),
Self::Positioned(positioned) => positioned.span(),
Self::Rotated(rotated) => rotated.span(),
Self::Store(store) => store.span(),
Self::Summon(summon) => summon.span(),
}
}
}
/// Syntax Synopsis:
/// ```ebnf
/// ExecuteBlockTail:
/// ExecuteBlock
/// | Block
/// ;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, EnumAsInner, From)]
#[allow(missing_docs)]
@ -188,8 +223,487 @@ impl SourceElement for As {
impl As {
/// Dissolves the [`As`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, StringLiteral, Punctuation) {
(self.as_keyword, self.as_selector, self.close_paren)
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.as_keyword,
self.open_paren,
self.as_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// Align:
/// 'align' '(' StringLiteral ')'
/// ;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct Align {
/// The `align` keyword.
#[get = "pub"]
align_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the align statement.
#[get = "pub"]
align_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for Align {
fn span(&self) -> Span {
self.align_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the align statement is invalid.")
}
}
impl Align {
/// Dissolves the [`Align`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.align_keyword,
self.open_paren,
self.align_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// Anchored:
/// 'anchored' '(' StringLiteral ')'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct Anchored {
/// The `anchored` keyword.
#[get = "pub"]
anchored_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the anchored statement.
#[get = "pub"]
anchored_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for Anchored {
fn span(&self) -> Span {
self.anchored_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the anchored statement is invalid.")
}
}
impl Anchored {
/// Dissolves the [`Anchored`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.anchored_keyword,
self.open_paren,
self.anchored_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// AsAt:
/// 'asat' '(' StringLiteral ')'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct AsAt {
/// The `asat` keyword.
#[get = "pub"]
asat_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the asat statement.
#[get = "pub"]
asat_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for AsAt {
fn span(&self) -> Span {
self.asat_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the asat statement is invalid.")
}
}
impl AsAt {
/// Dissolves the [`AsAt`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.asat_keyword,
self.open_paren,
self.asat_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// At:
/// 'at' '(' StringLiteral ')'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct At {
/// The `at` keyword.
#[get = "pub"]
at_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the at statement.
#[get = "pub"]
at_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for At {
fn span(&self) -> Span {
self.at_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the at statement is invalid.")
}
}
impl At {
/// Dissolves the [`At`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.at_keyword,
self.open_paren,
self.at_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// Facing:
/// 'facing' '(' StringLiteral ')'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct Facing {
/// The `facing` keyword.
#[get = "pub"]
facing_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the facing statement.
#[get = "pub"]
facing_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for Facing {
fn span(&self) -> Span {
self.facing_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the facing statement is invalid.")
}
}
impl Facing {
/// Dissolves the [`Facing`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.facing_keyword,
self.open_paren,
self.facing_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// In:
/// 'in' '(' StringLiteral ')'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct In {
/// The `in` keyword.
#[get = "pub"]
in_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the in statement.
#[get = "pub"]
in_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for In {
fn span(&self) -> Span {
self.in_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the in statement is invalid.")
}
}
impl In {
/// Dissolves the [`In`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.in_keyword,
self.open_paren,
self.in_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// On:
/// 'on' '(' StringLiteral ')'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct On {
/// The `on` keyword.
#[get = "pub"]
on_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the on statement.
#[get = "pub"]
on_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for On {
fn span(&self) -> Span {
self.on_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the on statement is invalid.")
}
}
impl On {
/// Dissolves the [`On`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.on_keyword,
self.open_paren,
self.on_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// Positioned:
/// 'positioned' '(' StringLiteral ')'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct Positioned {
/// The `positioned` keyword.
#[get = "pub"]
positioned_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the positioned statement.
#[get = "pub"]
positioned_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for Positioned {
fn span(&self) -> Span {
self.positioned_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the positioned statement is invalid.")
}
}
impl Positioned {
/// Dissolves the [`Positioned`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.positioned_keyword,
self.open_paren,
self.positioned_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// Rotated:
/// 'rotated' '(' StringLiteral ')'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct Rotated {
/// The `rotated` keyword.
#[get = "pub"]
rotated_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the rotated statement.
#[get = "pub"]
rotated_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for Rotated {
fn span(&self) -> Span {
self.rotated_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the rotated statement is invalid.")
}
}
impl Rotated {
/// Dissolves the [`Rotated`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.rotated_keyword,
self.open_paren,
self.rotated_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// Store:
/// 'store' '(' StringLiteral ')'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct Store {
/// The `store` keyword.
#[get = "pub"]
store_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the store statement.
#[get = "pub"]
store_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for Store {
fn span(&self) -> Span {
self.store_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the store statement is invalid.")
}
}
impl Store {
/// Dissolves the [`Store`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.store_keyword,
self.open_paren,
self.store_selector,
self.close_paren,
)
}
}
/// Syntax Synopsis:
/// ```ebnf
/// Summon:
/// 'summon' '(' StringLiteral ')'
/// ;
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Getters)]
pub struct Summon {
/// The `summon` keyword.
#[get = "pub"]
summon_keyword: Keyword,
/// The open parenthesis.
#[get = "pub"]
open_paren: Punctuation,
/// The selector of the summon statement.
#[get = "pub"]
summon_selector: StringLiteral,
/// The close parenthesis.
#[get = "pub"]
close_paren: Punctuation,
}
impl SourceElement for Summon {
fn span(&self) -> Span {
self.summon_keyword
.span()
.join(&self.close_paren.span())
.expect("The span of the summon statement is invalid.")
}
}
impl Summon {
/// Dissolves the [`Summon`] into its components.
#[must_use]
pub fn dissolve(self) -> (Keyword, Punctuation, StringLiteral, Punctuation) {
(
self.summon_keyword,
self.open_paren,
self.summon_selector,
self.close_paren,
)
}
}
@ -250,13 +764,11 @@ impl<'a> Parser<'a> {
}
}
Reading::Atomic(Token::Keyword(as_keyword))
if as_keyword.keyword == KeywordKind::As =>
{
Reading::Atomic(Token::Keyword(keyword)) if keyword.keyword.starts_execute_block() => {
// eat the as keyword
self.forward();
let as_selector = match self.stop_at_significant() {
let argument = match self.stop_at_significant() {
Reading::IntoDelimited(punc) if punc.punctuation == '(' => self.step_into(
Delimiter::Parenthesis,
|parser| parser.parse_string_literal(handler),
@ -276,15 +788,9 @@ impl<'a> Parser<'a> {
let tail = self.parse_execute_block_tail(handler)?;
Some(ExecuteBlock::HeadTail(
ExecuteBlockHead::As(As {
as_keyword,
open_paren: as_selector.open,
as_selector: as_selector.tree?,
close_paren: as_selector.close,
}),
tail,
))
let head = head_from_keyword(keyword, argument)?;
Some(ExecuteBlock::HeadTail(head, tail))
}
// unexpected
@ -339,3 +845,96 @@ impl<'a> Parser<'a> {
}
}
}
fn head_from_keyword(
keyword: Keyword,
argument: DelimitedTree<StringLiteral>,
) -> Option<ExecuteBlockHead> {
Some(match keyword.keyword {
KeywordKind::Align => Align {
align_keyword: keyword,
open_paren: argument.open,
align_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::Anchored => Anchored {
anchored_keyword: keyword,
open_paren: argument.open,
anchored_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::As => As {
as_keyword: keyword,
open_paren: argument.open,
as_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::AsAt => AsAt {
asat_keyword: keyword,
open_paren: argument.open,
asat_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::At => At {
at_keyword: keyword,
open_paren: argument.open,
at_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::Facing => Facing {
facing_keyword: keyword,
open_paren: argument.open,
facing_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::In => In {
in_keyword: keyword,
open_paren: argument.open,
in_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::On => On {
on_keyword: keyword,
open_paren: argument.open,
on_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::Positioned => Positioned {
positioned_keyword: keyword,
open_paren: argument.open,
positioned_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::Rotated => Rotated {
rotated_keyword: keyword,
open_paren: argument.open,
rotated_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::Store => Store {
store_keyword: keyword,
open_paren: argument.open,
store_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
KeywordKind::Summon => Summon {
summon_keyword: keyword,
open_paren: argument.open,
summon_selector: argument.tree?,
close_paren: argument.close,
}
.into(),
_ => unreachable!("The keyword is not a valid execute block head."),
})
}

View File

@ -341,21 +341,8 @@ impl Transpiler {
self.transpile_execute_block_internal(execute_block, handler)
}
}?;
let combined = match head {
ExecuteBlockHead::Conditional(cond) => {
if let Some(tail) = tail {
self.transpile_conditional(cond, tail, None, handler)?
} else {
None
}
}
ExecuteBlockHead::As(as_) => {
let selector = as_.as_selector().str_content();
tail.map(|tail| Execute::As(selector.to_string(), Box::new(tail)))
}
};
Ok(combined)
self.combine_execute_head_tail(head, tail, handler)
}
ExecuteBlock::IfElse(cond, block, el) => {
let statements = block.statements();
@ -440,4 +427,69 @@ impl Transpiler {
el,
)))
}
fn combine_execute_head_tail(
&mut self,
head: &ExecuteBlockHead,
tail: Option<Execute>,
handler: &impl Handler<TranspileError>,
) -> TranspileResult<Option<Execute>> {
Ok(match head {
ExecuteBlockHead::Conditional(cond) => {
if let Some(tail) = tail {
self.transpile_conditional(cond, tail, None, handler)?
} else {
None
}
}
ExecuteBlockHead::As(as_) => {
let selector = as_.as_selector().str_content();
tail.map(|tail| Execute::As(selector.to_string(), Box::new(tail)))
}
ExecuteBlockHead::At(at) => {
let selector = at.at_selector().str_content();
tail.map(|tail| Execute::At(selector.to_string(), Box::new(tail)))
}
ExecuteBlockHead::Align(align) => {
let align = align.align_selector().str_content();
tail.map(|tail| Execute::Align(align.to_string(), Box::new(tail)))
}
ExecuteBlockHead::Anchored(anchored) => {
let anchor = anchored.anchored_selector().str_content();
tail.map(|tail| Execute::Anchored(anchor.to_string(), Box::new(tail)))
}
ExecuteBlockHead::In(in_) => {
let dimension = in_.in_selector().str_content();
tail.map(|tail| Execute::In(dimension.to_string(), Box::new(tail)))
}
ExecuteBlockHead::Positioned(positioned) => {
let position = positioned.positioned_selector().str_content();
tail.map(|tail| Execute::Positioned(position.to_string(), Box::new(tail)))
}
ExecuteBlockHead::Rotated(rotated) => {
let rotation = rotated.rotated_selector().str_content();
tail.map(|tail| Execute::Rotated(rotation.to_string(), Box::new(tail)))
}
ExecuteBlockHead::Facing(facing) => {
let facing = facing.facing_selector().str_content();
tail.map(|tail| Execute::Facing(facing.to_string(), Box::new(tail)))
}
ExecuteBlockHead::AsAt(as_at) => {
let selector = as_at.asat_selector().str_content();
tail.map(|tail| Execute::AsAt(selector.to_string(), Box::new(tail)))
}
ExecuteBlockHead::On(on) => {
let dimension = on.on_selector().str_content();
tail.map(|tail| Execute::On(dimension.to_string(), Box::new(tail)))
}
ExecuteBlockHead::Store(store) => {
let store = store.store_selector().str_content();
tail.map(|tail| Execute::Store(store.to_string(), Box::new(tail)))
}
ExecuteBlockHead::Summon(summon) => {
let entity = summon.summon_selector().str_content();
tail.map(|tail| Execute::Summon(entity.to_string(), Box::new(tail)))
}
})
}
}