Skip to content

Commit

Permalink
feat: support item components
Browse files Browse the repository at this point in the history
  • Loading branch information
vberlier committed Feb 28, 2024
1 parent 504d92f commit 150d936
Show file tree
Hide file tree
Showing 16 changed files with 455 additions and 35 deletions.
18 changes: 17 additions & 1 deletion mecha/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"AstJsonObject",
"AstNbt",
"AstNbtValue",
"AstNbtBool",
"AstNbtList",
"AstNbtCompoundKey",
"AstNbtCompoundEntry",
Expand All @@ -53,6 +54,7 @@
"AstResourceLocation",
"AstBlockState",
"AstBlock",
"AstItemComponent",
"AstItem",
"AstItemSlot",
"AstRange",
Expand Down Expand Up @@ -722,7 +724,7 @@ def from_value(cls, value: Any) -> "AstNbt":
if isinstance(value, (Numeric, String)):
return AstNbtValue(value=value)
elif isinstance(value, bool):
return AstNbtValue(value=Byte(1 if value else 0))
return AstNbtBool(value=Byte(1 if value else 0))
elif isinstance(value, int):
return AstNbtValue(value=Int(value))
elif isinstance(value, float):
Expand Down Expand Up @@ -771,6 +773,11 @@ def evaluate(self) -> Any:
return self.value


@dataclass(frozen=True, slots=True)
class AstNbtBool(AstNbtValue):
"""Ast nbt bool node."""


@dataclass(frozen=True, slots=True)
class AstNbtList(AstNbt):
"""Ast nbt list node."""
Expand Down Expand Up @@ -897,11 +904,20 @@ class AstBlock(AstNode):
data_tags: Optional[AstNbt] = None


@dataclass(frozen=True, slots=True)
class AstItemComponent(AstNode):
"""Ast item component node."""

key: AstResourceLocation = required_field()
value: AstNbt = required_field()


@dataclass(frozen=True, slots=True)
class AstItem(AstNode):
"""Ast item node."""

identifier: AstResourceLocation = required_field()
components: AstChildren[AstItemComponent] = AstChildren()
data_tags: Optional[AstNbt] = None


Expand Down
82 changes: 51 additions & 31 deletions mecha/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@
"ResourceLocationParser",
"NoTagConstraint",
"BlockParser",
"BlockStatesParser",
"BracketedKeyValuePairsParser",
"ItemParser",
"NoBlockStatesConstraint",
"NoDataTagsConstraint",
"BasicLiteralParser",
"RangeParser",
Expand Down Expand Up @@ -110,6 +109,7 @@
AstGreedy,
AstHeightmap,
AstItem,
AstItemComponent,
AstItemParticleParameters,
AstItemSlot,
AstJson,
Expand All @@ -126,6 +126,7 @@
AstMessage,
AstMessageText,
AstNbt,
AstNbtBool,
AstNbtByteArray,
AstNbtCompound,
AstNbtCompoundEntry,
Expand Down Expand Up @@ -246,7 +247,8 @@ def get_default_parsers() -> Dict[str, Parser]:
resource_location_parser=delegate("resource_location_or_tag"),
block_states_parser=AdjacentConstraint(
parser=MultilineParser(
BlockStatesParser(
BracketedKeyValuePairsParser(
node_type=AstBlockState,
key_parser=StringParser(type="phrase"),
value_parser=delegate("phrase"),
)
Expand All @@ -259,7 +261,8 @@ def get_default_parsers() -> Dict[str, Parser]:
resource_location_parser=delegate("resource_location"),
block_states_parser=AdjacentConstraint(
parser=MultilineParser(
BlockStatesParser(
BracketedKeyValuePairsParser(
node_type=AstBlockState,
key_parser=StringParser(type="phrase"),
value_parser=delegate("phrase"),
)
Expand All @@ -270,11 +273,31 @@ def get_default_parsers() -> Dict[str, Parser]:
),
"item_predicate": ItemParser(
resource_location_parser=delegate("resource_location_or_tag"),
components_parser=AdjacentConstraint(
parser=MultilineParser(
BracketedKeyValuePairsParser(
node_type=AstItemComponent,
key_parser=delegate("resource_location"),
value_parser=delegate("nbt"),
)
),
hint=r"\[",
),
data_tags_parser=delegate("adjacent_nbt_compound"),
),
"item_slot": BasicLiteralParser(AstItemSlot),
"item_stack": ItemParser(
resource_location_parser=delegate("resource_location"),
components_parser=AdjacentConstraint(
parser=MultilineParser(
BracketedKeyValuePairsParser(
node_type=AstItemComponent,
key_parser=delegate("resource_location"),
value_parser=delegate("nbt"),
)
),
hint=r"\[",
),
data_tags_parser=delegate("adjacent_nbt_compound"),
),
"message": parse_message,
Expand Down Expand Up @@ -1089,10 +1112,10 @@ class NbtParser:
}
)

literal_aliases: Dict[str, Any] = field(
literal_aliases: Dict[str, AstNbtValue] = field(
default_factory=lambda: { # type: ignore
"true": Byte(1),
"false": Byte(0),
"true": AstNbtBool(value=Byte(1)),
"false": AstNbtBool(value=Byte(0)),
}
)

Expand Down Expand Up @@ -1204,8 +1227,8 @@ def __call__(self, stream: TokenStream) -> AstNbt:
elif string:
alias = string.value.lower()

if alias in self.literal_aliases:
value = self.literal_aliases[alias]
if node := self.literal_aliases.get(alias):
return set_location(node, stream.current)
else:
value = String(string.value) # type: ignore

Expand Down Expand Up @@ -1353,14 +1376,15 @@ def __call__(self, stream: TokenStream) -> AstBlock:


@dataclass
class BlockStatesParser:
"""Parser for minecraft block state."""
class BracketedKeyValuePairsParser:
"""Parser for bracketed key-value pairs."""

node_type: Type[AstNode]
key_parser: Parser
value_parser: Parser

def __call__(self, stream: TokenStream) -> AstChildren[AstBlockState]:
block_states: List[AstBlockState] = []
def __call__(self, stream: TokenStream) -> AstChildren[AstNode]:
pairs: List[AstNode] = []

with stream.syntax(
bracket=r"\[|\]",
Expand All @@ -1374,49 +1398,45 @@ def __call__(self, stream: TokenStream) -> AstChildren[AstBlockState]:
stream.expect("equal")
value_node = self.value_parser(stream)

entry_node = AstBlockState(key=key_node, value=value_node)
entry_node = self.node_type(key=key_node, value=value_node) # type: ignore
entry_node = set_location(entry_node, key_node, value_node)
block_states.append(entry_node)
pairs.append(entry_node)

if not stream.get("comma"):
stream.expect(("bracket", "]"))
break

return AstChildren(block_states)
return AstChildren(pairs)


@dataclass
class ItemParser:
"""Parser for minecraft items."""

resource_location_parser: Parser
components_parser: Parser
data_tags_parser: Parser

def __call__(self, stream: TokenStream) -> AstItem:
identifier = self.resource_location_parser(stream)
location = identifier.location
end_location = identifier.end_location

if components := self.components_parser(stream):
end_location = stream.current.end_location
else:
components = AstChildren[AstItemComponent]()

data_tags = self.data_tags_parser(stream)

node = AstItem(identifier=identifier, data_tags=data_tags)
node = AstItem(
identifier=identifier,
components=components,
data_tags=data_tags,
)
return set_location(node, location, data_tags if data_tags else end_location)


@dataclass
class NoBlockStatesConstraint:
"""Constraint that disallows block states."""

parser: Parser

def __call__(self, stream: TokenStream) -> Any:
if isinstance(node := self.parser(stream), AstBlock):
if node.block_states:
exc = InvalidSyntax("Specifying block states not allowed.")
raise node.emit_error(exc)
return node


@dataclass
class NoDataTagsConstraint:
"""Constraint that disallows data tags."""
Expand Down
12 changes: 12 additions & 0 deletions mecha/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
AstCommand,
AstCoordinate,
AstItem,
AstItemComponent,
AstJson,
AstLiteral,
AstMacroLine,
AstMacroLineText,
AstMacroLineVariable,
AstMessage,
AstNbt,
AstNbtBool,
AstNbtPath,
AstNbtPathKey,
AstNbtPathSubscript,
Expand Down Expand Up @@ -234,6 +236,10 @@ def json(self, node: AstJson, result: List[str]):
def nbt(self, node: AstNbt, result: List[str]):
result.append(node.evaluate().snbt(compact=self.formatting.nbt_compact))

@rule(AstNbtBool)
def nbt_bool(self, node: AstNbtBool, result: List[str]):
result.append("true" if node.value else "false")

@rule(AstResourceLocation)
def resource_location(self, node: AstResourceLocation, result: List[str]):
result.append(node.get_value())
Expand All @@ -250,9 +256,15 @@ def block(self, node: AstBlock, result: List[str]):
if node.data_tags:
yield node.data_tags

@rule(AstItemComponent)
def item_component(self, node: AstItem, result: List[str]):
yield from self.key_value(node, "=", result)

@rule(AstItem)
def item(self, node: AstItem, result: List[str]):
yield node.identifier
if node.components:
yield from self.collection(node.components, "[]", result)
if node.data_tags:
yield node.data_tags

Expand Down
7 changes: 7 additions & 0 deletions tests/resources/command_examples.mcfunction
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,10 @@ execute store result score @s foo run return run data get entity @s foodSaturati
gamerule maxCommandForkCount 9999
scoreboard objectives modify foo displayautoupdate true
scoreboard objectives modify foo numberformat styled {"bold": true}
give @s wooden_pickaxe[damage=23]
give @s wooden_pickaxe[damage=23]{foo: 123}
give @s netherite_hoe[damage=5,repair_cost=2]
give @s stick{foo:'bar'}
give @s stick[custom_data={foo:'bar'}]
clear @s diamond_pickaxe[damage=0]
give @s diamond_sword[enchantment_glint_override=true]
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ stick
is_tag: False
namespace: None
path: 'stick'
components:
<empty>
data_tags: None
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ minecraft:stick
is_tag: False
namespace: 'minecraft'
path: 'stick'
components:
<empty>
data_tags: None
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ minecraft:item_predicate 0 2
is_tag: True
namespace: None
path: 'stick'
components:
<empty>
data_tags: None
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ minecraft:item_predicate 0 3
is_tag: True
namespace: None
path: 'stick'
components:
<empty>
data_tags:
<class 'mecha.ast.AstNbtCompound'>
location: SourceLocation(pos=6, lineno=1, colno=7)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ stick
is_tag: False
namespace: None
path: 'stick'
components:
<empty>
data_tags: None
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ minecraft:stick
is_tag: False
namespace: 'minecraft'
path: 'stick'
components:
<empty>
data_tags: None
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ stick{foo: "bar"}
is_tag: False
namespace: None
path: 'stick'
components:
<empty>
data_tags:
<class 'mecha.ast.AstNbtCompound'>
location: SourceLocation(pos=5, lineno=1, colno=6)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ item minecraft:apple
is_tag: False
namespace: 'minecraft'
path: 'apple'
components:
<empty>
data_tags: None
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ item minecraft:apple{foo: "bar"}
is_tag: False
namespace: 'minecraft'
path: 'apple'
components:
<empty>
data_tags:
<class 'mecha.ast.AstNbtCompound'>
location: SourceLocation(pos=20, lineno=1, colno=21)
Expand Down
Loading

0 comments on commit 150d936

Please sign in to comment.