From 52740d38fc18580796288a4c44873e4ea20ca298 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Sat, 11 May 2024 22:33:52 +0300 Subject: [PATCH 01/26] begin work --- .../src/mcfunction/checker/index.ts | 11 ++- .../src/mcfunction/completer/argument.ts | 6 +- .../src/mcfunction/node/argument.ts | 35 +++++++-- .../src/mcfunction/parser/argument.ts | 75 +++++++++++++++++-- 4 files changed, 107 insertions(+), 20 deletions(-) diff --git a/packages/java-edition/src/mcfunction/checker/index.ts b/packages/java-edition/src/mcfunction/checker/index.ts index e760096a7..c76e15659 100644 --- a/packages/java-edition/src/mcfunction/checker/index.ts +++ b/packages/java-edition/src/mcfunction/checker/index.ts @@ -5,8 +5,11 @@ import * as mcf from '@spyglassmc/mcfunction' import * as nbt from '@spyglassmc/nbt' import { getTagValues } from '../../common/index.js' import { text_component } from '../../json/checker/data/text_component.js' -import type { EntitySelectorInvertableArgumentValueNode } from '../node/index.js' -import { BlockNode, EntityNode, ItemNode, ParticleNode } from '../node/index.js' +import type { + EntitySelectorInvertableArgumentValueNode, + ItemOldNode, +} from '../node/index.js' +import { BlockNode, EntityNode, ParticleNode } from '../node/index.js' export const command: core.Checker = (node, ctx) => { if (node.slash && node.parent && mcf.McfunctionNode.is(node.parent)) { @@ -161,7 +164,7 @@ const entity: core.SyncChecker = (node, ctx) => { nbt.checker.index('entity_type', types)(nbtValue, ctx) } -const item: core.SyncChecker = (node, ctx) => { +const item: core.SyncChecker = (node, ctx) => { if (!node.nbt) { return } @@ -339,6 +342,6 @@ export function register(meta: core.MetaRegistry) { meta.registerChecker('mcfunction:command', command) meta.registerChecker('mcfunction:block', block) meta.registerChecker('mcfunction:entity', entity) - meta.registerChecker('mcfunction:item', item) + meta.registerChecker('mcfunction:item', item) meta.registerChecker('mcfunction:particle', particle) } diff --git a/packages/java-edition/src/mcfunction/completer/argument.ts b/packages/java-edition/src/mcfunction/completer/argument.ts index 84aab0ae3..d4761fedc 100644 --- a/packages/java-edition/src/mcfunction/completer/argument.ts +++ b/packages/java-edition/src/mcfunction/completer/argument.ts @@ -39,13 +39,13 @@ import { import type { BlockStatesNode, EntitySelectorArgumentsNode, + ItemOldNode, } from '../node/index.js' import { BlockNode, CoordinateNode, EntitySelectorNode, IntRangeNode, - ItemNode, ObjectiveCriteriaNode, ParticleNode, ScoreHolderNode, @@ -243,7 +243,7 @@ const coordinate: Completer = (node, _ctx) => { return [CompletionItem.create('~', node)] } -const item: Completer = (node, ctx) => { +const item: Completer = (node, ctx) => { const ans: CompletionItem[] = [] if (Range.contains(node.id, ctx.offset, true)) { ans.push(...completer.resourceLocation(node.id, ctx)) @@ -434,7 +434,7 @@ export function register(meta: MetaRegistry) { selectorArguments, ) meta.registerCompleter('mcfunction:int_range', intRange) - meta.registerCompleter('mcfunction:item', item) + meta.registerCompleter('mcfunction:item', item) meta.registerCompleter( 'mcfunction:objective_criteria', objectiveCriteria, diff --git a/packages/java-edition/src/mcfunction/node/argument.ts b/packages/java-edition/src/mcfunction/node/argument.ts index 3d4bf35f6..7bc50f3c2 100644 --- a/packages/java-edition/src/mcfunction/node/argument.ts +++ b/packages/java-edition/src/mcfunction/node/argument.ts @@ -279,18 +279,21 @@ export interface FloatRangeNode extends core.AstNode { value: [number | undefined, number | undefined] } -export interface ItemNode extends core.AstNode { +export interface ItemOldNode extends core.AstNode { type: 'mcfunction:item' children: (core.ResourceLocationNode | nbt.NbtCompoundNode)[] id: core.ResourceLocationNode nbt?: nbt.NbtCompoundNode } -export namespace ItemNode { - export function is(node: core.AstNode | undefined): node is ItemNode { - return (node as ItemNode | undefined)?.type === 'mcfunction:item' +export namespace ItemOldNode { + export function is(node: core.AstNode | undefined): node is ItemOldNode { + return (node as ItemOldNode | undefined)?.type === 'mcfunction:item' } - export function mock(range: core.RangeLike, isPredicate: boolean): ItemNode { + export function mock( + range: core.RangeLike, + isPredicate: boolean, + ): ItemOldNode { const id = core.ResourceLocationNode.mock(range, { category: 'item', allowTag: isPredicate, @@ -304,6 +307,26 @@ export namespace ItemNode { } } +export interface ItemNewNode extends core.AstNode { + type: 'mcfunction:item' + children: (core.ResourceLocationNode | ComponentListNode)[] + id: core.ResourceLocationNode + components?: ComponentListNode +} + +export type ItemNode = ItemOldNode | ItemNewNode + +export interface ComponentListNode extends core.AstNode { + type: 'mcfunction:component_list' + children: core.PairNode[] +} + +export namespace ComponentListNode { + export function is(node: core.AstNode): node is ComponentListNode { + return (node as ComponentListNode).type === 'mcfunction:component_list' + } +} + export interface IntRangeNode extends core.AstNode { type: 'mcfunction:int_range' children: (core.IntegerNode | core.LiteralNode)[] @@ -372,7 +395,7 @@ export interface ParticleNode extends core.AstNode { | core.IntegerNode | core.ResourceLocationNode | BlockNode - | ItemNode + | ItemOldNode | VectorNode )[] id: core.ResourceLocationNode diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 461e766e0..a1906f51c 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -25,13 +25,16 @@ import type { EntitySelectorVariable, FloatRangeNode, IntRangeNode, + ItemNewNode, ItemNode, + ItemOldNode, MessageNode, ParticleNode, ScoreHolderNode, UuidNode, VectorNode, } from '../node/index.js' +import { ComponentListNode } from '../node/index.js' import { BlockStatesNode, CoordinateSystem, @@ -78,6 +81,11 @@ function shouldValidateLength(ctx: core.ParserContext) { return !release || ReleaseVersion.cmp(release, '1.18') < 0 } +function shouldUseOldItemStackFormat(ctx: core.ParserContext) { + const release = ctx.project['loadedVersion'] as ReleaseVersion | undefined + return release === undefined || ReleaseVersion.cmp(release, '1.20.5') < 0 +} + /** * @returns The parser for the specified argument tree node. All argument parsers used in the `mcfunction` package * fail on empty input. @@ -473,19 +481,21 @@ const greedyString: core.InfallibleParser = core.string({ unquotable: { blockList: new Set(['\n', '\r']) }, }) -function item(isPredicate: false): core.InfallibleParser -function item(isPredicate: true): core.InfallibleParser -function item(isPredicate: boolean): core.InfallibleParser { +function item_old(isPredicate: false): core.InfallibleParser +function item_old(isPredicate: true): core.InfallibleParser +function item_old( + isPredicate: boolean, +): core.InfallibleParser { return core.map< core.SequenceUtil, - ItemNode + ItemOldNode >( core.sequence([ core.resourceLocation({ category: 'item', allowTag: isPredicate }), core.optional(core.failOnEmpty(nbt.parser.compound)), ]), (res) => { - const ans: ItemNode = { + const ans: ItemOldNode = { type: 'mcfunction:item', range: res.range, children: res.children, @@ -496,8 +506,35 @@ function item(isPredicate: boolean): core.InfallibleParser { }, ) } -const itemStack: core.InfallibleParser = item(false) -const itemPredicate: core.InfallibleParser = item(true) + +function item_new(isPredicate: false): core.InfallibleParser +function item_new(isPredicate: true): core.InfallibleParser +function item_new( + isPredicate: boolean, +): core.InfallibleParser { + return core.map< + core.SequenceUtil, + ItemNewNode + >( + core.sequence([ + core.resourceLocation({ category: 'item', allowTag: isPredicate }), + core.optional(core.failOnEmpty(components)), + ]), + (res) => { + const ans: ItemNewNode = { + type: 'mcfunction:item', + range: res.range, + children: res.children, + id: res.children.find(core.ResourceLocationNode.is)!, + components: res.children.find(ComponentListNode.is), + } + return ans + }, + ) +} + +const itemStack: core.InfallibleParser = item_old(false) +const itemPredicate: core.InfallibleParser = item_old(true) const message: core.InfallibleParser = (src, ctx) => { const ans: MessageNode = { @@ -1640,3 +1677,27 @@ function vector( return ans } } + +function components(): core.InfallibleParser { + return core.map( + core.record({ + start: '[', + pair: { + key: core.resourceLocation({ category: 'item_component' }), + sep: '=', + value: nbt.parser.entry, + end: ',', + trailingEnd: true, + }, + end: ']', + }), + (res) => { + const ans: ComponentListNode = { + type: 'mcfunction:component_list', + range: res.range, + children: res.children, + } + return ans + }, + ) +} From dc6c238cef894ced1641db824bbfb1009b07e6bc Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Sat, 11 May 2024 22:40:58 +0300 Subject: [PATCH 02/26] potentially fix error --- packages/java-edition/src/mcfunction/parser/argument.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index a1906f51c..0ef9b0d85 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -518,7 +518,7 @@ function item_new( >( core.sequence([ core.resourceLocation({ category: 'item', allowTag: isPredicate }), - core.optional(core.failOnEmpty(components)), + core.optional(core.failOnEmpty(components())), ]), (res) => { const ans: ItemNewNode = { From bd7d1a69a9d720f87bca370394953cfb7790affd Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Sat, 11 May 2024 22:43:24 +0300 Subject: [PATCH 03/26] make ParticleNode refer to ItemNode --- packages/java-edition/src/mcfunction/node/argument.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/java-edition/src/mcfunction/node/argument.ts b/packages/java-edition/src/mcfunction/node/argument.ts index 7bc50f3c2..56a6982b5 100644 --- a/packages/java-edition/src/mcfunction/node/argument.ts +++ b/packages/java-edition/src/mcfunction/node/argument.ts @@ -395,7 +395,7 @@ export interface ParticleNode extends core.AstNode { | core.IntegerNode | core.ResourceLocationNode | BlockNode - | ItemOldNode + | ItemNode | VectorNode )[] id: core.ResourceLocationNode From 6c8fdff68b85995f755d9b2764bb8dbf6d0509f7 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Sun, 12 May 2024 20:07:50 +0300 Subject: [PATCH 04/26] Rudimentary item component syntax support --- .../src/mcfunction/checker/index.ts | 20 +++++---- .../src/mcfunction/completer/argument.ts | 17 +++++-- .../src/mcfunction/node/argument.ts | 44 ++++++++++++++----- .../src/mcfunction/parser/argument.ts | 31 +++++++++---- packages/mcfunction/src/completer/index.ts | 8 ++-- 5 files changed, 87 insertions(+), 33 deletions(-) diff --git a/packages/java-edition/src/mcfunction/checker/index.ts b/packages/java-edition/src/mcfunction/checker/index.ts index c76e15659..b0d4dd68e 100644 --- a/packages/java-edition/src/mcfunction/checker/index.ts +++ b/packages/java-edition/src/mcfunction/checker/index.ts @@ -7,9 +7,9 @@ import { getTagValues } from '../../common/index.js' import { text_component } from '../../json/checker/data/text_component.js' import type { EntitySelectorInvertableArgumentValueNode, - ItemOldNode, } from '../node/index.js' -import { BlockNode, EntityNode, ParticleNode } from '../node/index.js' +import { ItemOldNode } from '../node/index.js' +import { BlockNode, EntityNode, ItemNode, ParticleNode } from '../node/index.js' export const command: core.Checker = (node, ctx) => { if (node.slash && node.parent && mcf.McfunctionNode.is(node.parent)) { @@ -164,15 +164,19 @@ const entity: core.SyncChecker = (node, ctx) => { nbt.checker.index('entity_type', types)(nbtValue, ctx) } -const item: core.SyncChecker = (node, ctx) => { - if (!node.nbt) { +const item: core.SyncChecker = (node, ctx) => { + if (!ItemNode.hasUserData(node)) { return } - nbt.checker.index( - 'item', - core.ResourceLocationNode.toString(node.id, 'full'), - )(node.nbt, ctx) + if (ItemOldNode.is(node)) { + nbt.checker.index( + 'item', + core.ResourceLocationNode.toString(node.id, 'full'), + )(node.nbt!, ctx) + } else { + // TODO: Implement checking for item componentrs + } } const particle: core.SyncChecker = (node, ctx) => { diff --git a/packages/java-edition/src/mcfunction/completer/argument.ts b/packages/java-edition/src/mcfunction/completer/argument.ts index d4761fedc..a57dd6bc4 100644 --- a/packages/java-edition/src/mcfunction/completer/argument.ts +++ b/packages/java-edition/src/mcfunction/completer/argument.ts @@ -1,6 +1,7 @@ import type { Arrayable, Completer, + CompleterContext, MetaRegistry, RegistryCategory, WorldgenFileCategory, @@ -27,6 +28,7 @@ import * as json from '@spyglassmc/json' import { localeQuote, localize } from '@spyglassmc/locales' import type * as mcf from '@spyglassmc/mcfunction' import { getTagValues } from '../../common/index.js' +import { ReleaseVersion } from '../../dependency/common.js' import { ColorArgumentValues, EntityAnchorArgumentValues, @@ -46,6 +48,7 @@ import { CoordinateNode, EntitySelectorNode, IntRangeNode, + ItemNode, ObjectiveCriteriaNode, ParticleNode, ScoreHolderNode, @@ -53,9 +56,17 @@ import { } from '../node/index.js' import type { ArgumentTreeNode } from '../tree/index.js' +function getItemStackFormat(ctx: CompleterContext): 'old' | 'new' { + const release = ctx.config.env.gameVersion as ReleaseVersion | undefined + return (release === undefined || ReleaseVersion.cmp(release, '1.20.5') < 0) + ? 'old' + : 'new' +} + export const getMockNodes: mcf.completer.MockNodesGetter = ( rawTreeNode, range, + ctx, ): Arrayable => { const treeNode = rawTreeNode as ArgumentTreeNode @@ -111,11 +122,11 @@ export const getMockNodes: mcf.completer.MockNodesGetter = ( case 'minecraft:item_enchantment': return ResourceLocationNode.mock(range, { category: 'enchantment' }) case 'minecraft:item_predicate': - return ItemNode.mock(range, true) + return ItemNode.mock(range, true, getItemStackFormat(ctx)) case 'minecraft:item_slot': return LiteralNode.mock(range, { pool: ItemSlotArgumentValues }) case 'minecraft:item_stack': - return ItemNode.mock(range, false) + return ItemNode.mock(range, false, getItemStackFormat(ctx)) case 'minecraft:mob_effect': return ResourceLocationNode.mock(range, { category: 'mob_effect' }) case 'minecraft:objective': @@ -299,7 +310,7 @@ const particle: Completer = (node, ctx) => { VectorNode.mock(ctx.offset, { dimension: 3 }), ], falling_dust: [BlockNode.mock(ctx.offset, false)], - item: [ItemNode.mock(ctx.offset, false)], + item: [ItemNode.mock(ctx.offset, false, getItemStackFormat(ctx))], sculk_charge: [FloatNode.mock(ctx.offset)], shriek: [IntegerNode.mock(ctx.offset)], vibration: [ diff --git a/packages/java-edition/src/mcfunction/node/argument.ts b/packages/java-edition/src/mcfunction/node/argument.ts index 56a6982b5..b9cb4f105 100644 --- a/packages/java-edition/src/mcfunction/node/argument.ts +++ b/packages/java-edition/src/mcfunction/node/argument.ts @@ -289,15 +289,48 @@ export namespace ItemOldNode { export function is(node: core.AstNode | undefined): node is ItemOldNode { return (node as ItemOldNode | undefined)?.type === 'mcfunction:item' } +} + +export interface ItemNewNode extends core.AstNode { + type: 'mcfunction:item' + children: (core.ResourceLocationNode | ComponentListNode)[] + id: core.ResourceLocationNode + components?: ComponentListNode +} + +export namespace ItemNewNode { + export function is(node: core.AstNode | undefined): node is ItemNewNode { + return (node as ItemNewNode | undefined)?.type === 'mcfunction:item' + } +} + +export type ItemNode = ItemOldNode | ItemNewNode + +export namespace ItemNode { + export function is(node: core.AstNode | undefined): node is ItemNode { + return ( + ItemOldNode.is(node) || ItemNewNode.is(node) + ) + } + + export function hasUserData(node: ItemNode): boolean { + if (ItemOldNode.is(node)) { + return !!node.nbt + } else { + return !!node.components + } + } export function mock( range: core.RangeLike, isPredicate: boolean, - ): ItemOldNode { + format: 'old' | 'new', + ): ItemNode { const id = core.ResourceLocationNode.mock(range, { category: 'item', allowTag: isPredicate, }) + return { type: 'mcfunction:item', range: core.Range.get(range), @@ -307,15 +340,6 @@ export namespace ItemOldNode { } } -export interface ItemNewNode extends core.AstNode { - type: 'mcfunction:item' - children: (core.ResourceLocationNode | ComponentListNode)[] - id: core.ResourceLocationNode - components?: ComponentListNode -} - -export type ItemNode = ItemOldNode | ItemNewNode - export interface ComponentListNode extends core.AstNode { type: 'mcfunction:component_list' children: core.PairNode[] diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 0ef9b0d85..fccefdfa5 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -481,11 +481,11 @@ const greedyString: core.InfallibleParser = core.string({ unquotable: { blockList: new Set(['\n', '\r']) }, }) -function item_old(isPredicate: false): core.InfallibleParser -function item_old(isPredicate: true): core.InfallibleParser +function item_old(isPredicate: false): core.InfallibleParser +function item_old(isPredicate: true): core.InfallibleParser function item_old( isPredicate: boolean, -): core.InfallibleParser { +): core.InfallibleParser { return core.map< core.SequenceUtil, ItemOldNode @@ -507,11 +507,11 @@ function item_old( ) } -function item_new(isPredicate: false): core.InfallibleParser -function item_new(isPredicate: true): core.InfallibleParser +function item_new(isPredicate: false): core.InfallibleParser +function item_new(isPredicate: true): core.InfallibleParser function item_new( isPredicate: boolean, -): core.InfallibleParser { +): core.InfallibleParser { return core.map< core.SequenceUtil, ItemNewNode @@ -533,8 +533,16 @@ function item_new( ) } -const itemStack: core.InfallibleParser = item_old(false) -const itemPredicate: core.InfallibleParser = item_old(true) +const itemStack: core.InfallibleParser = (src, ctx) => { + return shouldUseOldItemStackFormat(ctx) + ? item_old(false)(src, ctx) + : item_new(false)(src, ctx) +} +const itemPredicate: core.InfallibleParser = (src, ctx) => { + return shouldUseOldItemStackFormat(ctx) + ? item_old(true)(src, ctx) + : item_new(true)(src, ctx) +} const message: core.InfallibleParser = (src, ctx) => { const ans: MessageNode = { @@ -1683,7 +1691,12 @@ function components(): core.InfallibleParser { core.record({ start: '[', pair: { - key: core.resourceLocation({ category: 'item_component' }), + // TODO: Create an item_component category + key: core.resourceLocation({ + pool: [], + allowUnknown: true, + namespacePathSep: '.', + }), sep: '=', value: nbt.parser.entry, end: ',', diff --git a/packages/mcfunction/src/completer/index.ts b/packages/mcfunction/src/completer/index.ts index c5dc0dd8c..fa4e4f542 100644 --- a/packages/mcfunction/src/completer/index.ts +++ b/packages/mcfunction/src/completer/index.ts @@ -14,6 +14,7 @@ import { export type MockNodesGetter = ( treeNode: ArgumentTreeNode, range: core.RangeLike, + ctx: core.CompleterContext, ) => core.Arrayable /** @@ -79,9 +80,10 @@ export function command( }) ), ...argumentTreeNodes.flatMap(([_name, treeNode]) => - core.Arrayable.toArray(getMockNodes(treeNode, ctx.offset)).flatMap( - (n) => core.completer.dispatch(n, ctx), - ) + core.Arrayable.toArray(getMockNodes(treeNode, ctx.offset, ctx)) + .flatMap( + (n) => core.completer.dispatch(n, ctx), + ) ), ] } From 3e0f112bb7989b0d4bdd272d6a07d2b228dfc5eb Mon Sep 17 00:00:00 2001 From: Misode Date: Sun, 12 May 2024 03:47:08 +0200 Subject: [PATCH 05/26] =?UTF-8?q?=E2=9C=A8=20Implement=20various=20command?= =?UTF-8?q?=20parsers=20and=20tree=20patch=20changes=20(#1128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/src/symbol/Symbol.ts | 4 ++ .../java-edition/src/dependency/common.ts | 7 +++- .../src/mcfunction/common/index.ts | 22 +++++++++++ .../src/mcfunction/completer/argument.ts | 10 +++++ .../src/mcfunction/parser/argument.ts | 10 +++++ .../src/mcfunction/tree/argument.ts | 27 +++++++++++++ .../java-edition/src/mcfunction/tree/patch.ts | 38 +++++++++++++++++++ 7 files changed, 117 insertions(+), 1 deletion(-) diff --git a/packages/core/src/symbol/Symbol.ts b/packages/core/src/symbol/Symbol.ts index aaf677852..291ede24b 100644 --- a/packages/core/src/symbol/Symbol.ts +++ b/packages/core/src/symbol/Symbol.ts @@ -119,6 +119,8 @@ export type TagFileCategory = (typeof TagFileCategories)[number] export const FileCategories = Object.freeze( [ 'advancement', + 'chat_type', + 'damage_type', 'dimension', 'dimension_type', 'function', @@ -127,6 +129,8 @@ export const FileCategories = Object.freeze( 'predicate', 'recipe', 'structure', + 'trim_material', + 'trim_pattern', ...TagFileCategories, ...WorldgenFileCategories, ] as const, diff --git a/packages/java-edition/src/dependency/common.ts b/packages/java-edition/src/dependency/common.ts index c0abecf03..becb74e46 100644 --- a/packages/java-edition/src/dependency/common.ts +++ b/packages/java-edition/src/dependency/common.ts @@ -28,7 +28,12 @@ export const PackVersionMap: Record = { 7: /^1\.17.*$/, 8: /^1\.18(\.1)?$/, 9: /^1\.18.*$/, - 10: /^1\.19.*$/, + 10: /^1\.19(\.[1-3])?$/, + 12: /^1\.19.*$/, + 15: /^1\.20(\.1)?$/, + 18: /^1\.20\.2$/, + 26: /^1\.20\.[3-4]$/, + 41: /^1\.20\.[5-6]$/, } export interface PackMcmeta { diff --git a/packages/java-edition/src/mcfunction/common/index.ts b/packages/java-edition/src/mcfunction/common/index.ts index 952288b32..a63852daf 100644 --- a/packages/java-edition/src/mcfunction/common/index.ts +++ b/packages/java-edition/src/mcfunction/common/index.ts @@ -66,3 +66,25 @@ export const SwizzleArgumentValues = [ 'zxy', 'zyx', ] + +export const HeightmapValues = [ + 'motion_blocking', + 'motion_blocking_no_leaves', + 'ocean_floor', + 'ocean_floor_wg', + 'world_surface', + 'world_surface_wg', +] + +export const RotationValues = [ + 'none', + 'clockwise_90', + '180', + 'counterclockwise_90', +] + +export const MirrorValues = [ + 'none', + 'left_right', + 'front_back', +] diff --git a/packages/java-edition/src/mcfunction/completer/argument.ts b/packages/java-edition/src/mcfunction/completer/argument.ts index a57dd6bc4..a3e250816 100644 --- a/packages/java-edition/src/mcfunction/completer/argument.ts +++ b/packages/java-edition/src/mcfunction/completer/argument.ts @@ -33,8 +33,11 @@ import { ColorArgumentValues, EntityAnchorArgumentValues, GamemodeArgumentValues, + HeightmapValues, ItemSlotArgumentValues, + MirrorValues, OperationArgumentValues, + RotationValues, ScoreboardSlotArgumentValues, SwizzleArgumentValues, } from '../common/index.js' @@ -109,6 +112,8 @@ export const getMockNodes: mcf.completer.MockNodesGetter = ( case 'minecraft:entity': case 'minecraft:game_profile': return EntitySelectorNode.mock(range) + case 'minecraft:heightmap': + return LiteralNode.mock(range, { pool: HeightmapValues }) case 'minecraft:entity_anchor': return LiteralNode.mock(range, { pool: EntityAnchorArgumentValues }) case 'minecraft:entity_summon': @@ -141,6 +146,7 @@ export const getMockNodes: mcf.completer.MockNodesGetter = ( case 'minecraft:particle': return ParticleNode.mock(range) case 'minecraft:resource': + case 'minecraft:resource_key': case 'minecraft:resource_or_tag': return ResourceLocationNode.mock(range, { category: ResourceLocation.shorten(treeNode.properties.registry) as @@ -163,6 +169,10 @@ export const getMockNodes: mcf.completer.MockNodesGetter = ( return LiteralNode.mock(range, { pool: SwizzleArgumentValues }) case 'minecraft:team': return SymbolNode.mock(range, { category: 'team' }) + case 'minecraft:template_mirror': + return LiteralNode.mock(range, { pool: MirrorValues }) + case 'minecraft:template_rotation': + return LiteralNode.mock(range, { pool: RotationValues }) case 'minecraft:vec2': return VectorNode.mock(range, { dimension: 2, integersOnly: true }) case 'minecraft:vec3': diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index fccefdfa5..70b20fe4f 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -9,8 +9,11 @@ import { ColorArgumentValues, EntityAnchorArgumentValues, GamemodeArgumentValues, + HeightmapValues, ItemSlotArgumentValues, + MirrorValues, OperationArgumentValues, + RotationValues, ScoreboardSlotArgumentValues, SwizzleArgumentValues, } from '../common/index.js' @@ -181,6 +184,8 @@ export const argument: mcf.ArgumentParserGetter = ( return wrap(core.literal(...GamemodeArgumentValues)) case 'minecraft:game_profile': return wrap(entity('multiple', 'players')) + case 'minecraft:heightmap': + return wrap(core.literal(...HeightmapValues)) case 'minecraft:int_range': return wrap(range('integer')) case 'minecraft:item_enchantment': @@ -229,6 +234,7 @@ export const argument: mcf.ArgumentParserGetter = ( case 'minecraft:particle': return wrap(particle) case 'minecraft:resource': + case 'minecraft:resource_key': case 'minecraft:resource_or_tag': return wrap( core.resourceLocation({ @@ -265,6 +271,10 @@ export const argument: mcf.ArgumentParserGetter = ( : undefined, ), ) + case 'minecraft:template_mirror': + return wrap(core.literal(...MirrorValues)) + case 'minecraft:template_rotation': + return wrap(core.literal(...RotationValues)) case 'minecraft:time': return wrap(time) case 'minecraft:uuid': diff --git a/packages/java-edition/src/mcfunction/tree/argument.ts b/packages/java-edition/src/mcfunction/tree/argument.ts index fd4bf7ef3..bc022c861 100644 --- a/packages/java-edition/src/mcfunction/tree/argument.ts +++ b/packages/java-edition/src/mcfunction/tree/argument.ts @@ -112,6 +112,11 @@ export interface MinecraftGameProfileArgumentTreeNode { parser: 'minecraft:game_profile' } +export interface MinecraftHeightmapArgumentTreeNode + extends mcf.ArgumentTreeNode +{ + parser: 'minecraft:heightmap' +} export interface MinecraftIntRangeArgumentTreeNode extends mcf.ArgumentTreeNode { @@ -184,6 +189,14 @@ export interface MinecraftResourceArgumentTreeNode registry: string } } +export interface MinecraftResourceKeyArgumentTreeNode + extends mcf.ArgumentTreeNode +{ + parser: 'minecraft:resource_key' + properties: { + registry: string + } +} export interface MinecraftResourceLocationArgumentTreeNode extends mcf.ArgumentTreeNode { @@ -222,6 +235,16 @@ export interface MinecraftSwizzleArgumentTreeNode extends mcf.ArgumentTreeNode { export interface MinecraftTeamArgumentTreeNode extends mcf.ArgumentTreeNode { parser: 'minecraft:team' } +export interface MinecraftTemplateMirrorArgumentTreeNode + extends mcf.ArgumentTreeNode +{ + parser: 'minecraft:template_mirror' +} +export interface MinecraftTemplateRotationArgumentTreeNode + extends mcf.ArgumentTreeNode +{ + parser: 'minecraft:template_rotation' +} export interface MinecraftTimeArgumentTreeNode extends mcf.ArgumentTreeNode { parser: 'minecraft:time' } @@ -262,6 +285,7 @@ export type ArgumentTreeNode = | MinecraftFunctionArgumentTreeNode | MinecraftGamemodeArgumentTreeNode | MinecraftGameProfileArgumentTreeNode + | MinecraftHeightmapArgumentTreeNode | MinecraftIntRangeArgumentTreeNode | MinecraftItemEnchantmentArgumentTreeNode | MinecraftItemPredicateArgumentTreeNode @@ -277,6 +301,7 @@ export type ArgumentTreeNode = | MinecraftOperationArgumentTreeNode | MinecraftParticleArgumentTreeNode | MinecraftResourceArgumentTreeNode + | MinecraftResourceKeyArgumentTreeNode | MinecraftResourceLocationArgumentTreeNode | MinecraftResourceOrTagArgumentTreeNode | MinecraftRotationArgumentTreeNode @@ -284,6 +309,8 @@ export type ArgumentTreeNode = | MinecraftScoreboardSlotArgumentTreeNode | MinecraftSwizzleArgumentTreeNode | MinecraftTeamArgumentTreeNode + | MinecraftTemplateMirrorArgumentTreeNode + | MinecraftTemplateRotationArgumentTreeNode | MinecraftTimeArgumentTreeNode | MinecraftUuidArgumentTreeNode | MinecraftVec2ArgumentTreeNode diff --git a/packages/java-edition/src/mcfunction/tree/patch.ts b/packages/java-edition/src/mcfunction/tree/patch.ts index 322bf93e3..580fb9b81 100644 --- a/packages/java-edition/src/mcfunction/tree/patch.ts +++ b/packages/java-edition/src/mcfunction/tree/patch.ts @@ -333,6 +333,13 @@ export function getPatch(release: ReleaseVersion): PartialRootTreeNode { help: { permission: 0, }, + ...(ReleaseVersion.cmp(release, '1.18') >= 0 + ? { + jfr: { + permission: 4, + }, + } + : {}), kick: { permission: 3, }, @@ -418,6 +425,30 @@ export function getPatch(release: ReleaseVersion): PartialRootTreeNode { 'pardon-ip': { permission: 3, }, + ...(ReleaseVersion.cmp(release, '1.17') >= 0 + ? { + perf: { + permission: 4, + }, + } + : {}), + ...(ReleaseVersion.cmp(release, '1.19') >= 0 + ? { + place: { + children: { + template: { + children: { + template: { + properties: { + category: 'structure' + } + } + } + } + } + }, + } + : {}), playsound: Sound, publish: { permission: 4, @@ -559,6 +590,13 @@ export function getPatch(release: ReleaseVersion): PartialRootTreeNode { tell: { permission: 0, }, + ...(ReleaseVersion.cmp(release, '1.20.2') >= 0 + ? { + tick: { + permission: 3, + }, + } + : {}), tm: { permission: 0, }, From 4e05d80072293d6c55a85f7ec41942ea695c58de Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Mon, 13 May 2024 15:06:21 +0300 Subject: [PATCH 06/26] fix `ItemOldNode.is` and `ItemNewNode.is` --- packages/java-edition/src/mcfunction/node/argument.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/java-edition/src/mcfunction/node/argument.ts b/packages/java-edition/src/mcfunction/node/argument.ts index b9cb4f105..22a853cbf 100644 --- a/packages/java-edition/src/mcfunction/node/argument.ts +++ b/packages/java-edition/src/mcfunction/node/argument.ts @@ -287,7 +287,8 @@ export interface ItemOldNode extends core.AstNode { } export namespace ItemOldNode { export function is(node: core.AstNode | undefined): node is ItemOldNode { - return (node as ItemOldNode | undefined)?.type === 'mcfunction:item' + return (node as ItemOldNode | undefined)?.type === 'mcfunction:item' && + 'nbt' in (node as ItemNode) } } @@ -300,7 +301,8 @@ export interface ItemNewNode extends core.AstNode { export namespace ItemNewNode { export function is(node: core.AstNode | undefined): node is ItemNewNode { - return (node as ItemNewNode | undefined)?.type === 'mcfunction:item' + return (node as ItemNewNode | undefined)?.type === 'mcfunction:item' && + 'components' in (node as ItemNode) } } From 797cfdf5667de764bbe5c25d7f50e0b01ddd0ac9 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Mon, 13 May 2024 15:08:37 +0300 Subject: [PATCH 07/26] report error when duplicate components are defined --- .../src/mcfunction/checker/index.ts | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/java-edition/src/mcfunction/checker/index.ts b/packages/java-edition/src/mcfunction/checker/index.ts index b0d4dd68e..b42ebe24a 100644 --- a/packages/java-edition/src/mcfunction/checker/index.ts +++ b/packages/java-edition/src/mcfunction/checker/index.ts @@ -175,7 +175,27 @@ const item: core.SyncChecker = (node, ctx) => { core.ResourceLocationNode.toString(node.id, 'full'), )(node.nbt!, ctx) } else { - // TODO: Implement checking for item componentrs + const present = new Set() + + for (const component of node.components!.children) { + const componentName = core.ResourceLocationNode.toString( + component.key!, + 'full', + ) + + if (present.has(componentName)) { + ctx.err.report( + localize( + 'mcfunction.checker.item.duplicate-component', + componentName, + ), + node.components!.range, + core.ErrorSeverity.Error, + ) + } else { + present.add(componentName) + } + } } } @@ -346,6 +366,6 @@ export function register(meta: core.MetaRegistry) { meta.registerChecker('mcfunction:command', command) meta.registerChecker('mcfunction:block', block) meta.registerChecker('mcfunction:entity', entity) - meta.registerChecker('mcfunction:item', item) + meta.registerChecker('mcfunction:item', item) meta.registerChecker('mcfunction:particle', particle) } From ea2a2cb631731c9083fc6c0ed6ac814c003cf60a Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Mon, 13 May 2024 15:34:34 +0300 Subject: [PATCH 08/26] make duplicate components error localize properly --- packages/java-edition/src/mcfunction/checker/index.ts | 2 +- packages/locales/src/locales/en.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/java-edition/src/mcfunction/checker/index.ts b/packages/java-edition/src/mcfunction/checker/index.ts index b42ebe24a..50dbbf034 100644 --- a/packages/java-edition/src/mcfunction/checker/index.ts +++ b/packages/java-edition/src/mcfunction/checker/index.ts @@ -186,7 +186,7 @@ const item: core.SyncChecker = (node, ctx) => { if (present.has(componentName)) { ctx.err.report( localize( - 'mcfunction.checker.item.duplicate-component', + 'duplicate-components', componentName, ), node.components!.range, diff --git a/packages/locales/src/locales/en.json b/packages/locales/src/locales/en.json index e255ca1f7..f75e51c38 100644 --- a/packages/locales/src/locales/en.json +++ b/packages/locales/src/locales/en.json @@ -33,6 +33,7 @@ "conjunction.or_3+_1": ", ", "conjunction.or_3+_2": ", or ", "datafix.error.command-replaceitem": "/replaceitem was removed in 20w46a (the second snapshot of 1.17) in favour of /item", + "duplicate-components": "Component %0% already defined", "duplicate-key": "Duplicate key %0%", "ending-quote": "an ending quote %0%", "entity": "an entity", From 6f975f355453452719a8d637e40cf0a0b085cf65 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Mon, 13 May 2024 15:36:03 +0300 Subject: [PATCH 09/26] make error for duplicate components consistent --- packages/locales/src/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/locales/src/locales/en.json b/packages/locales/src/locales/en.json index f75e51c38..501eb2071 100644 --- a/packages/locales/src/locales/en.json +++ b/packages/locales/src/locales/en.json @@ -33,7 +33,7 @@ "conjunction.or_3+_1": ", ", "conjunction.or_3+_2": ", or ", "datafix.error.command-replaceitem": "/replaceitem was removed in 20w46a (the second snapshot of 1.17) in favour of /item", - "duplicate-components": "Component %0% already defined", + "duplicate-components": "Duplicate component definitions for %0%", "duplicate-key": "Duplicate key %0%", "ending-quote": "an ending quote %0%", "entity": "an entity", From e51dad1fb464065704702d381ab56e5d361e4d06 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Mon, 13 May 2024 16:39:14 +0300 Subject: [PATCH 10/26] make error reporting for duplicates smarter --- .../src/mcfunction/checker/index.ts | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/packages/java-edition/src/mcfunction/checker/index.ts b/packages/java-edition/src/mcfunction/checker/index.ts index 50dbbf034..abd4be8ed 100644 --- a/packages/java-edition/src/mcfunction/checker/index.ts +++ b/packages/java-edition/src/mcfunction/checker/index.ts @@ -6,6 +6,7 @@ import * as nbt from '@spyglassmc/nbt' import { getTagValues } from '../../common/index.js' import { text_component } from '../../json/checker/data/text_component.js' import type { + ComponentListNode, EntitySelectorInvertableArgumentValueNode, } from '../node/index.js' import { ItemOldNode } from '../node/index.js' @@ -175,27 +176,35 @@ const item: core.SyncChecker = (node, ctx) => { core.ResourceLocationNode.toString(node.id, 'full'), )(node.nbt!, ctx) } else { - const present = new Set() + const groupedComponents = new Map< + string, + core.PairNode[] + >() - for (const component of node.components!.children) { + node.components!.children.forEach(component => { const componentName = core.ResourceLocationNode.toString( component.key!, 'full', ) - if (present.has(componentName)) { - ctx.err.report( - localize( - 'duplicate-components', - componentName, - ), - node.components!.range, - core.ErrorSeverity.Error, - ) - } else { - present.add(componentName) + if (!groupedComponents.has(componentName)) { + groupedComponents.set(componentName, []) } - } + + groupedComponents.get(componentName)!.push(component) + }) + + groupedComponents.forEach((components, componentName) => { + if (components.length > 1) { + components.forEach(component => { + ctx.err.report( + localize('duplicate-components', componentName), + component.key!.range, + core.ErrorSeverity.Error, + ) + }) + } + }) } } From 0b3af753ca4cd9cc43919e686f1b92a4789c3294 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Mon, 13 May 2024 16:42:06 +0300 Subject: [PATCH 11/26] rename `item_old` and `item_new` --- .../src/mcfunction/parser/argument.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 70b20fe4f..143162635 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -491,9 +491,9 @@ const greedyString: core.InfallibleParser = core.string({ unquotable: { blockList: new Set(['\n', '\r']) }, }) -function item_old(isPredicate: false): core.InfallibleParser -function item_old(isPredicate: true): core.InfallibleParser -function item_old( +function itemOld(isPredicate: false): core.InfallibleParser +function itemOld(isPredicate: true): core.InfallibleParser +function itemOld( isPredicate: boolean, ): core.InfallibleParser { return core.map< @@ -517,9 +517,9 @@ function item_old( ) } -function item_new(isPredicate: false): core.InfallibleParser -function item_new(isPredicate: true): core.InfallibleParser -function item_new( +function itemNew(isPredicate: false): core.InfallibleParser +function itemNew(isPredicate: true): core.InfallibleParser +function itemNew( isPredicate: boolean, ): core.InfallibleParser { return core.map< @@ -545,13 +545,13 @@ function item_new( const itemStack: core.InfallibleParser = (src, ctx) => { return shouldUseOldItemStackFormat(ctx) - ? item_old(false)(src, ctx) - : item_new(false)(src, ctx) + ? itemOld(false)(src, ctx) + : itemNew(false)(src, ctx) } const itemPredicate: core.InfallibleParser = (src, ctx) => { return shouldUseOldItemStackFormat(ctx) - ? item_old(true)(src, ctx) - : item_new(true)(src, ctx) + ? itemOld(true)(src, ctx) + : itemNew(true)(src, ctx) } const message: core.InfallibleParser = (src, ctx) => { From 27c395a195eb2c49fd4da7a513be3bbf1572e779 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Fri, 17 May 2024 17:29:31 +0300 Subject: [PATCH 12/26] (very buggy) new item predicate syntax --- packages/core/src/parser/list.ts | 12 +- .../src/mcfunction/completer/argument.ts | 2 +- .../src/mcfunction/node/argument.ts | 53 ++++- .../src/mcfunction/parser/argument.ts | 217 ++++++++++++++++-- 4 files changed, 259 insertions(+), 25 deletions(-) diff --git a/packages/core/src/parser/list.ts b/packages/core/src/parser/list.ts index c98cf53b2..bdb96350b 100644 --- a/packages/core/src/parser/list.ts +++ b/packages/core/src/parser/list.ts @@ -11,7 +11,7 @@ import { attempt } from './util.js' export interface Options { start: string value: Parser - sep: string + sep: string | string[] trailingSep: boolean end: string } @@ -41,7 +41,13 @@ export function list({ // Item sep of the last item. if (requiresValueSep && !hasValueSep) { - ctx.err.report(localize('expected', localeQuote(sep)), src) + if (sep instanceof Array) { + sep.forEach((s) => + ctx.err.report(localize('expected', localeQuote(s)), src) + ) + } else { + ctx.err.report(localize('expected', localeQuote(sep)), src) + } } // Value. @@ -56,7 +62,7 @@ export function list({ localize('expected', localize('parser.list.value')), Range.create( src, - () => src.skipUntilOrEnd(sep, end, '\r', '\n'), + () => src.skipUntilOrEnd(...sep, end, '\r', '\n'), ), ) } else { diff --git a/packages/java-edition/src/mcfunction/completer/argument.ts b/packages/java-edition/src/mcfunction/completer/argument.ts index a3e250816..16a416cda 100644 --- a/packages/java-edition/src/mcfunction/completer/argument.ts +++ b/packages/java-edition/src/mcfunction/completer/argument.ts @@ -60,7 +60,7 @@ import { import type { ArgumentTreeNode } from '../tree/index.js' function getItemStackFormat(ctx: CompleterContext): 'old' | 'new' { - const release = ctx.config.env.gameVersion as ReleaseVersion | undefined + const release = ctx.project['loadedVersion'] as ReleaseVersion | undefined return (release === undefined || ReleaseVersion.cmp(release, '1.20.5') < 0) ? 'old' : 'new' diff --git a/packages/java-edition/src/mcfunction/node/argument.ts b/packages/java-edition/src/mcfunction/node/argument.ts index 22a853cbf..e7fa7b700 100644 --- a/packages/java-edition/src/mcfunction/node/argument.ts +++ b/packages/java-edition/src/mcfunction/node/argument.ts @@ -294,9 +294,16 @@ export namespace ItemOldNode { export interface ItemNewNode extends core.AstNode { type: 'mcfunction:item' - children: (core.ResourceLocationNode | ComponentListNode)[] + children: ( + | core.LiteralNode + | core.ResourceLocationNode + | ComponentListNode + | ComponentPredicatesNode + )[] id: core.ResourceLocationNode components?: ComponentListNode + componentPredicates?: ComponentPredicatesNode + wildcard?: boolean } export namespace ItemNewNode { @@ -353,6 +360,50 @@ export namespace ComponentListNode { } } +export interface ComponentPredicatesNode extends core.AstNode { + type: 'mcfunction:component_predicates' + children: ComponentTestBaseNode[] +} + +export namespace ComponentPredicatesNode { + export function is(node: core.AstNode): node is ComponentPredicatesNode { + return (node as ComponentPredicatesNode).type === + 'mcfunction:component_predicates' + } +} + +export interface ComponentTestBaseNode extends core.AstNode { + negated: boolean +} + +export namespace ComponentTestBaseNode { + export function is(node: core.AstNode): node is ComponentTestBaseNode { + return (node as ComponentTestBaseNode).type.startsWith( + 'mcfunction:component_test', + ) + } +} + +export interface ComponentTestExactNode extends ComponentTestBaseNode { + type: 'mcfunction:component_test_exact' + children: [core.ResourceLocationNode, nbt.NbtNode] + component: core.ResourceLocationNode + value: nbt.NbtNode +} + +export interface ComponentTestExistsNode extends ComponentTestBaseNode { + type: 'mcfunction:component_test_exists' + children: [core.ResourceLocationNode] + component: core.ResourceLocationNode +} + +export interface ComponentTestSubpredicateNode extends ComponentTestBaseNode { + type: 'mcfunction:component_test_subpredicate' + children: [core.ResourceLocationNode, nbt.NbtNode] + component: core.ResourceLocationNode + subpredicate: nbt.NbtNode +} + export interface IntRangeNode extends core.AstNode { type: 'mcfunction:int_range' children: (core.IntegerNode | core.LiteralNode)[] diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 143162635..65ef19482 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -19,6 +19,9 @@ import { } from '../common/index.js' import type { BlockNode, + ComponentTestExactNode, + ComponentTestExistsNode, + ComponentTestSubpredicateNode, CoordinateNode, EntityNode, EntitySelectorAdvancementsArgumentCriteriaNode, @@ -37,7 +40,8 @@ import type { UuidNode, VectorNode, } from '../node/index.js' -import { ComponentListNode } from '../node/index.js' +import { ComponentPredicatesNode } from '../node/index.js' +import { ComponentListNode, ComponentTestBaseNode } from '../node/index.js' import { BlockStatesNode, CoordinateSystem, @@ -522,25 +526,57 @@ function itemNew(isPredicate: true): core.InfallibleParser function itemNew( isPredicate: boolean, ): core.InfallibleParser { - return core.map< - core.SequenceUtil, - ItemNewNode - >( - core.sequence([ - core.resourceLocation({ category: 'item', allowTag: isPredicate }), - core.optional(core.failOnEmpty(components())), - ]), - (res) => { - const ans: ItemNewNode = { - type: 'mcfunction:item', - range: res.range, - children: res.children, - id: res.children.find(core.ResourceLocationNode.is)!, - components: res.children.find(ComponentListNode.is), - } - return ans - }, - ) + if (isPredicate) { + return core.map< + core.SequenceUtil< + | core.LiteralNode + | core.ResourceLocationNode + | ComponentPredicatesNode + >, + ItemNewNode + >( + core.sequence([ + core.any([ + core.literal('*'), + core.resourceLocation({ category: 'item', allowTag: true }), + ]), + core.optional(core.failOnEmpty(componentTests())), + ]), + (res) => { + const ans: ItemNewNode = { + type: 'mcfunction:item', + range: res.range, + children: res.children, + id: res.children.find(core.ResourceLocationNode.is)!, + componentPredicates: res.children.find( + ComponentPredicatesNode.is, + ), + wildcard: res.children.find(core.LiteralNode.is) !== undefined, + } + return ans + }, + ) + } else { + return core.map< + core.SequenceUtil, + ItemNewNode + >( + core.sequence([ + core.resourceLocation({ category: 'item', allowTag: isPredicate }), + core.optional(core.failOnEmpty(components())), + ]), + (res) => { + const ans: ItemNewNode = { + type: 'mcfunction:item', + range: res.range, + children: res.children, + id: res.children.find(core.ResourceLocationNode.is)!, + components: res.children.find(ComponentListNode.is), + } + return ans + }, + ) + } } const itemStack: core.InfallibleParser = (src, ctx) => { @@ -1724,3 +1760,144 @@ function components(): core.InfallibleParser { }, ) } + +function componentTests(): core.InfallibleParser { + const exact = core.map< + core.SequenceUtil< + core.ResourceLocationNode | core.LiteralNode | nbt.NbtNode + >, + ComponentTestExactNode + >( + core.sequence([ + core.resourceLocation({ + pool: [], + allowUnknown: true, + namespacePathSep: '.', + }), + core.literal('='), + nbt.parser.entry, + ]), + (res) => { + const id = res.children.find(core.ResourceLocationNode.is)! + const value = res.children.find(nbt.NbtNode.is)! + const ans: ComponentTestExactNode = { + type: 'mcfunction:component_test_exact', + range: res.range, + children: [id, value], + component: id, + value, + negated: false, + } + + return ans + }, + ) + + const exists = core.map< + core.ResourceLocationNode, + ComponentTestExistsNode + >( + core.resourceLocation({ + pool: [], + allowUnknown: true, + namespacePathSep: '.', + }), + (res) => { + const ans: ComponentTestExistsNode = { + type: 'mcfunction:component_test_exists', + range: res.range, + children: [res], + component: res, + negated: false, + } + + return ans + }, + ) + + const subpredicate = core.map< + core.SequenceUtil< + core.ResourceLocationNode | core.LiteralNode | nbt.NbtNode + >, + ComponentTestSubpredicateNode + >( + core.sequence([ + core.resourceLocation({ + pool: [], + allowUnknown: true, + namespacePathSep: '.', + }), + core.literal('~'), + nbt.parser.entry, + ]), + (res) => { + const id = res.children.find(core.ResourceLocationNode.is)! + const subpredicate = res.children.find(nbt.NbtNode.is)! + const ans: ComponentTestSubpredicateNode = { + type: 'mcfunction:component_test_subpredicate', + range: res.range, + children: [id, subpredicate], + component: id, + subpredicate, + negated: false, + } + + return ans + }, + ) + + const not = core.map< + core.SequenceUtil< + | core.LiteralNode + | ComponentTestBaseNode + >, + ComponentTestBaseNode + >( + core.sequence([ + core.literal('!'), + core.any([exact, subpredicate, exists]), + ]), + (res) => { + const test = res.children.find(ComponentTestBaseNode.is)! + test.negated = true + return test + }, + ) + + const test = core.select([ + { + predicate: (src) => src.peek() === '!', + parser: not, + }, + { + parser: core.map< + | ComponentTestExactNode + | ComponentTestExistsNode + | ComponentTestSubpredicateNode, + ComponentTestBaseNode + >( + core.any([exact, subpredicate, exists]), + (res) => res, + ), + }, + ]) + + return core.map( + core.list({ + start: '[', + value: test, + sep: [',', '|'], + end: ']', + trailingSep: true, + }), + (res) => { + const ans: ComponentPredicatesNode = { + type: 'mcfunction:component_predicates', + range: res.range, + children: res.children.map((c) => c.value!), + } + + return ans + }, + ) +} From 46973664bb1c9cf1bb19c4040a96f1762a790a45 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Fri, 17 May 2024 18:39:11 +0300 Subject: [PATCH 13/26] undo changes to `list.ts` --- packages/core/src/parser/list.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/core/src/parser/list.ts b/packages/core/src/parser/list.ts index bdb96350b..c98cf53b2 100644 --- a/packages/core/src/parser/list.ts +++ b/packages/core/src/parser/list.ts @@ -11,7 +11,7 @@ import { attempt } from './util.js' export interface Options { start: string value: Parser - sep: string | string[] + sep: string trailingSep: boolean end: string } @@ -41,13 +41,7 @@ export function list({ // Item sep of the last item. if (requiresValueSep && !hasValueSep) { - if (sep instanceof Array) { - sep.forEach((s) => - ctx.err.report(localize('expected', localeQuote(s)), src) - ) - } else { - ctx.err.report(localize('expected', localeQuote(sep)), src) - } + ctx.err.report(localize('expected', localeQuote(sep)), src) } // Value. @@ -62,7 +56,7 @@ export function list({ localize('expected', localize('parser.list.value')), Range.create( src, - () => src.skipUntilOrEnd(...sep, end, '\r', '\n'), + () => src.skipUntilOrEnd(sep, end, '\r', '\n'), ), ) } else { From 11a5537f5cc9a85312afccb75b65352c0f7729a2 Mon Sep 17 00:00:00 2001 From: Misode Date: Thu, 30 May 2024 17:22:28 +0200 Subject: [PATCH 14/26] Implement a few of SPGoding's suggestions --- .../src/mcfunction/node/argument.ts | 32 ++++++++++++------- .../src/mcfunction/parser/argument.ts | 26 ++++++--------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/packages/java-edition/src/mcfunction/node/argument.ts b/packages/java-edition/src/mcfunction/node/argument.ts index 7160d324c..d0788d611 100644 --- a/packages/java-edition/src/mcfunction/node/argument.ts +++ b/packages/java-edition/src/mcfunction/node/argument.ts @@ -362,7 +362,7 @@ export namespace ComponentListNode { export interface ComponentPredicatesNode extends core.AstNode { type: 'mcfunction:component_predicates' - children: ComponentTestBaseNode[] + children: ComponentTestNode[] } export namespace ComponentPredicatesNode { @@ -376,14 +376,6 @@ export interface ComponentTestBaseNode extends core.AstNode { negated: boolean } -export namespace ComponentTestBaseNode { - export function is(node: core.AstNode): node is ComponentTestBaseNode { - return (node as ComponentTestBaseNode).type.startsWith( - 'mcfunction:component_test', - ) - } -} - export interface ComponentTestExactNode extends ComponentTestBaseNode { type: 'mcfunction:component_test_exact' children: [core.ResourceLocationNode, nbt.NbtNode] @@ -398,10 +390,26 @@ export interface ComponentTestExistsNode extends ComponentTestBaseNode { } export interface ComponentTestSubpredicateNode extends ComponentTestBaseNode { - type: 'mcfunction:component_test_subpredicate' + type: 'mcfunction:component_test_sub_predicate' children: [core.ResourceLocationNode, nbt.NbtNode] - component: core.ResourceLocationNode - subpredicate: nbt.NbtNode + subPredicateType: core.ResourceLocationNode + subPredicate: nbt.NbtNode +} + +export type ComponentTestNode = + | ComponentTestExactNode + | ComponentTestExistsNode + | ComponentTestSubpredicateNode + +export namespace ComponentTestNode { + export function is(node: core.AstNode): node is ComponentTestNode { + return (node as ComponentTestNode).type === + 'mcfunction:component_test_exact' || + (node as ComponentTestNode).type === + 'mcfunction:component_test_exists' || + (node as ComponentTestNode).type === + 'mcfunction:component_test_sub_predicate' + } } export interface IntRangeNode extends core.AstNode { diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 2e34b35c1..51506d6bd 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -44,7 +44,7 @@ import { BlockStatesNode, ComponentListNode, ComponentPredicatesNode, - ComponentTestBaseNode, + ComponentTestNode, CoordinateSystem, EntitySelectorArgumentsNode, EntitySelectorAtVariable, @@ -1856,11 +1856,11 @@ function componentTests(): core.InfallibleParser { const id = res.children.find(core.ResourceLocationNode.is)! const subpredicate = res.children.find(nbt.NbtNode.is)! const ans: ComponentTestSubpredicateNode = { - type: 'mcfunction:component_test_subpredicate', + type: 'mcfunction:component_test_sub_predicate', range: res.range, children: [id, subpredicate], - component: id, - subpredicate, + subPredicateType: id, + subPredicate: subpredicate, negated: false, } @@ -1871,16 +1871,16 @@ function componentTests(): core.InfallibleParser { const not = core.map< core.SequenceUtil< | core.LiteralNode - | ComponentTestBaseNode + | ComponentTestNode >, - ComponentTestBaseNode + ComponentTestNode >( core.sequence([ core.literal('!'), core.any([exact, subpredicate, exists]), ]), (res) => { - const test = res.children.find(ComponentTestBaseNode.is)! + const test = res.children.find(ComponentTestNode.is)! test.negated = true return test }, @@ -1892,15 +1892,7 @@ function componentTests(): core.InfallibleParser { parser: not, }, { - parser: core.map< - | ComponentTestExactNode - | ComponentTestExistsNode - | ComponentTestSubpredicateNode, - ComponentTestBaseNode - >( - core.any([exact, subpredicate, exists]), - (res) => res, - ), + parser: core.any([exact, subpredicate, exists]), }, ]) @@ -1916,7 +1908,7 @@ function componentTests(): core.InfallibleParser { const ans: ComponentPredicatesNode = { type: 'mcfunction:component_predicates', range: res.range, - children: res.children.map((c) => c.value!), + children: res.children.map(c => c.value!), } return ans From a5752fdd5717fc897d74c95a71a2f89b52cc138c Mon Sep 17 00:00:00 2001 From: Misode Date: Thu, 30 May 2024 18:15:49 +0200 Subject: [PATCH 15/26] Refactor nodes to separate item stack and predicate instead of old and new --- .../src/mcfunction/checker/index.ts | 27 ++- .../src/mcfunction/completer/argument.ts | 25 ++- .../src/mcfunction/node/argument.ts | 115 +++++----- .../src/mcfunction/parser/argument.ts | 199 ++++++++---------- packages/locales/src/locales/en.json | 2 +- 5 files changed, 172 insertions(+), 196 deletions(-) diff --git a/packages/java-edition/src/mcfunction/checker/index.ts b/packages/java-edition/src/mcfunction/checker/index.ts index 32c79cff1..6013ebb9f 100644 --- a/packages/java-edition/src/mcfunction/checker/index.ts +++ b/packages/java-edition/src/mcfunction/checker/index.ts @@ -8,8 +8,7 @@ import type { EntitySelectorInvertableArgumentValueNode } from '../node/index.js import { BlockNode, EntityNode, - ItemNode, - ItemOldNode, + ItemStackNode, ParticleNode, } from '../node/index.js' @@ -47,8 +46,8 @@ const rootCommand = ( block(node, ctx) } else if (EntityNode.is(node)) { entity(node, ctx) - } else if (ItemNode.is(node)) { - item(node, ctx) + } else if (ItemStackNode.is(node)) { + itemStack(node, ctx) } else if (ParticleNode.is(node)) { particle(node, ctx) } else if (json.JsonNode.is(node)) { @@ -166,17 +165,14 @@ const entity: core.SyncChecker = (node, ctx) => { nbt.checker.index('entity_type', types)(nbtValue, ctx) } -const item: core.SyncChecker = (node, ctx) => { - if (!ItemNode.hasUserData(node)) { - return - } - - if (ItemOldNode.is(node)) { +const itemStack: core.SyncChecker = (node, ctx) => { + if (node.nbt) { nbt.checker.index( 'item', core.ResourceLocationNode.toString(node.id, 'full'), - )(node.nbt!, ctx) - } else { + )(node.nbt, ctx) + } + if (node.components) { const groupedComponents = new Map< string, core.PairNode[] @@ -199,7 +195,10 @@ const item: core.SyncChecker = (node, ctx) => { if (components.length > 1) { components.forEach(component => { ctx.err.report( - localize('duplicate-components', componentName), + localize( + 'mcfunction.parser.duplicate-components', + componentName, + ), component.key!.range, core.ErrorSeverity.Error, ) @@ -376,6 +375,6 @@ export function register(meta: core.MetaRegistry) { meta.registerChecker('mcfunction:command', command) meta.registerChecker('mcfunction:block', block) meta.registerChecker('mcfunction:entity', entity) - meta.registerChecker('mcfunction:item', item) + meta.registerChecker('mcfunction:item_stack', itemStack) meta.registerChecker('mcfunction:particle', particle) } diff --git a/packages/java-edition/src/mcfunction/completer/argument.ts b/packages/java-edition/src/mcfunction/completer/argument.ts index 843f41c36..c348c90b6 100644 --- a/packages/java-edition/src/mcfunction/completer/argument.ts +++ b/packages/java-edition/src/mcfunction/completer/argument.ts @@ -44,14 +44,14 @@ import { import type { BlockStatesNode, EntitySelectorArgumentsNode, - ItemOldNode, } from '../node/index.js' import { BlockNode, CoordinateNode, EntitySelectorNode, IntRangeNode, - ItemNode, + ItemPredicateNode, + ItemStackNode, ObjectiveCriteriaNode, ParticleNode, ScoreHolderNode, @@ -127,13 +127,13 @@ export const getMockNodes: mcf.completer.MockNodesGetter = ( case 'minecraft:item_enchantment': return ResourceLocationNode.mock(range, { category: 'enchantment' }) case 'minecraft:item_predicate': - return ItemNode.mock(range, true, getItemStackFormat(ctx)) + return ItemPredicateNode.mock(range) case 'minecraft:item_slot': return LiteralNode.mock(range, { pool: getItemSlotArgumentValues(ctx), }) case 'minecraft:item_stack': - return ItemNode.mock(range, false, getItemStackFormat(ctx)) + return ItemStackNode.mock(range) case 'minecraft:mob_effect': return ResourceLocationNode.mock(range, { category: 'mob_effect' }) case 'minecraft:objective': @@ -266,7 +266,7 @@ const coordinate: Completer = (node, _ctx) => { return [CompletionItem.create('~', node)] } -const item: Completer = (node, ctx) => { +const itemStack: Completer = (node, ctx) => { const ans: CompletionItem[] = [] if (Range.contains(node.id, ctx.offset, true)) { ans.push(...completer.resourceLocation(node.id, ctx)) @@ -274,6 +274,17 @@ const item: Completer = (node, ctx) => { return ans } +const itemPredicate: Completer = (node, ctx) => { + const ans: CompletionItem[] = [] + if ( + Range.contains(node.id, ctx.offset, true) && + node.id.type === 'resource_location' + ) { + ans.push(...completer.resourceLocation(node.id, ctx)) + } + return ans +} + const objectiveCriteria: Completer = (node, ctx) => { const ans = ObjectiveCriteriaNode.SimpleValues.map((v) => CompletionItem.create(v, node) @@ -326,7 +337,7 @@ const particle: Completer = (node, ctx) => { VectorNode.mock(ctx.offset, { dimension: 3 }), ], falling_dust: [BlockNode.mock(ctx.offset, false)], - item: [ItemNode.mock(ctx.offset, false, getItemStackFormat(ctx))], + item: [ItemStackNode.mock(ctx.offset)], sculk_charge: [FloatNode.mock(ctx.offset)], shriek: [IntegerNode.mock(ctx.offset)], vibration: [ @@ -461,7 +472,7 @@ export function register(meta: MetaRegistry) { selectorArguments, ) meta.registerCompleter('mcfunction:int_range', intRange) - meta.registerCompleter('mcfunction:item', item) + meta.registerCompleter('mcfunction:item_stack', itemStack) meta.registerCompleter( 'mcfunction:objective_criteria', objectiveCriteria, diff --git a/packages/java-edition/src/mcfunction/node/argument.ts b/packages/java-edition/src/mcfunction/node/argument.ts index d0788d611..a1524b20d 100644 --- a/packages/java-edition/src/mcfunction/node/argument.ts +++ b/packages/java-edition/src/mcfunction/node/argument.ts @@ -279,69 +279,26 @@ export interface FloatRangeNode extends core.AstNode { value: [number | undefined, number | undefined] } -export interface ItemOldNode extends core.AstNode { - type: 'mcfunction:item' - children: (core.ResourceLocationNode | nbt.NbtCompoundNode)[] +export interface ItemStackNode extends core.AstNode { + type: 'mcfunction:item_stack' + children: + (core.ResourceLocationNode | ComponentListNode | nbt.NbtCompoundNode)[] id: core.ResourceLocationNode - nbt?: nbt.NbtCompoundNode -} -export namespace ItemOldNode { - export function is(node: core.AstNode | undefined): node is ItemOldNode { - return (node as ItemOldNode | undefined)?.type === 'mcfunction:item' && - 'nbt' in (node as ItemNode) - } -} - -export interface ItemNewNode extends core.AstNode { - type: 'mcfunction:item' - children: ( - | core.LiteralNode - | core.ResourceLocationNode - | ComponentListNode - | ComponentPredicatesNode - )[] - id: core.ResourceLocationNode - components?: ComponentListNode - componentPredicates?: ComponentPredicatesNode - wildcard?: boolean -} - -export namespace ItemNewNode { - export function is(node: core.AstNode | undefined): node is ItemNewNode { - return (node as ItemNewNode | undefined)?.type === 'mcfunction:item' && - 'components' in (node as ItemNode) - } + components?: ComponentListNode // since 1.20.5 + nbt?: nbt.NbtCompoundNode // until 1.20.5 } - -export type ItemNode = ItemOldNode | ItemNewNode - -export namespace ItemNode { - export function is(node: core.AstNode | undefined): node is ItemNode { - return ( - ItemOldNode.is(node) || ItemNewNode.is(node) - ) - } - - export function hasUserData(node: ItemNode): boolean { - if (ItemOldNode.is(node)) { - return !!node.nbt - } else { - return !!node.components - } +export namespace ItemStackNode { + export function is(node: core.AstNode | undefined): node is ItemStackNode { + return (node as ItemStackNode | undefined)?.type === + 'mcfunction:item_stack' } export function mock( range: core.RangeLike, - isPredicate: boolean, - format: 'old' | 'new', - ): ItemNode { - const id = core.ResourceLocationNode.mock(range, { - category: 'item', - allowTag: isPredicate, - }) - + ): ItemStackNode { + const id = core.ResourceLocationNode.mock(range, { category: 'item' }) return { - type: 'mcfunction:item', + type: 'mcfunction:item_stack', range: core.Range.get(range), children: [id], id, @@ -360,14 +317,50 @@ export namespace ComponentListNode { } } -export interface ComponentPredicatesNode extends core.AstNode { +export interface ItemPredicateNode extends core.AstNode { + type: 'mcfunction:item_predicate' + children: ( + | core.ResourceLocationNode + | core.LiteralNode + | ComponentTestsNode + | nbt.NbtCompoundNode + )[] + id: core.ResourceLocationNode | core.LiteralNode + tests?: ComponentTestsNode // since 1.20.5 + nbt?: nbt.NbtCompoundNode // until 1.20.5 +} +export namespace ItemPredicateNode { + export function is( + node: core.AstNode | undefined, + ): node is ItemPredicateNode { + return (node as ItemPredicateNode | undefined)?.type === + 'mcfunction:item_predicate' + } + + export function mock( + range: core.RangeLike, + ): ItemPredicateNode { + const id = core.ResourceLocationNode.mock(range, { + category: 'item', + allowTag: true, + }) + return { + type: 'mcfunction:item_predicate', + range: core.Range.get(range), + children: [id], + id, + } + } +} + +export interface ComponentTestsNode extends core.AstNode { type: 'mcfunction:component_predicates' children: ComponentTestNode[] } -export namespace ComponentPredicatesNode { - export function is(node: core.AstNode): node is ComponentPredicatesNode { - return (node as ComponentPredicatesNode).type === +export namespace ComponentTestsNode { + export function is(node: core.AstNode): node is ComponentTestsNode { + return (node as ComponentTestsNode).type === 'mcfunction:component_predicates' } } @@ -481,7 +474,7 @@ export interface ParticleNode extends core.AstNode { | core.FloatNode | core.IntegerNode | BlockNode - | ItemNode + | ItemStackNode | VectorNode // Since 1.20.5 | nbt.NbtCompoundNode diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 51506d6bd..8a17f387d 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -31,9 +31,8 @@ import type { EntitySelectorVariable, FloatRangeNode, IntRangeNode, - ItemNewNode, - ItemNode, - ItemOldNode, + ItemPredicateNode, + ItemStackNode, MessageNode, ParticleNode, ScoreHolderNode, @@ -43,8 +42,8 @@ import type { import { BlockStatesNode, ComponentListNode, - ComponentPredicatesNode, ComponentTestNode, + ComponentTestsNode, CoordinateSystem, EntitySelectorArgumentsNode, EntitySelectorAtVariable, @@ -91,7 +90,7 @@ function shouldValidateLength(ctx: core.ParserContext) { function shouldUseOldItemStackFormat(ctx: core.ParserContext) { const release = ctx.project['loadedVersion'] as ReleaseVersion | undefined - return release === undefined || ReleaseVersion.cmp(release, '1.20.5') < 0 + return !release || ReleaseVersion.cmp(release, '1.20.5') < 0 } /** @@ -498,99 +497,72 @@ const greedyString: core.InfallibleParser = core.string({ unquotable: { blockList: new Set(['\n', '\r']) }, }) -function itemOld(isPredicate: false): core.InfallibleParser -function itemOld(isPredicate: true): core.InfallibleParser -function itemOld( - isPredicate: boolean, -): core.InfallibleParser { +const itemStack: core.InfallibleParser = (src, ctx) => { + const oldFormat = shouldUseOldItemStackFormat(ctx) + // TODO return core.map< - core.SequenceUtil, - ItemOldNode + core.SequenceUtil< + core.ResourceLocationNode | ComponentListNode | nbt.NbtCompoundNode + >, + ItemStackNode >( core.sequence([ - core.resourceLocation({ category: 'item', allowTag: isPredicate }), - core.optional(core.failOnEmpty(nbt.parser.compound)), + core.resourceLocation({ category: 'item' }), + oldFormat + ? core.optional(core.failOnEmpty(nbt.parser.compound)) + : core.optional(core.failOnEmpty(components)), ]), (res) => { - const ans: ItemOldNode = { - type: 'mcfunction:item', + const ans: ItemStackNode = { + type: 'mcfunction:item_stack', range: res.range, children: res.children, id: res.children.find(core.ResourceLocationNode.is)!, + components: res.children.find(ComponentListNode.is), nbt: res.children.find(nbt.NbtCompoundNode.is), } return ans }, - ) + )(src, ctx) } -function itemNew(isPredicate: false): core.InfallibleParser -function itemNew(isPredicate: true): core.InfallibleParser -function itemNew( - isPredicate: boolean, -): core.InfallibleParser { - if (isPredicate) { - return core.map< - core.SequenceUtil< - | core.LiteralNode - | core.ResourceLocationNode - | ComponentPredicatesNode - >, - ItemNewNode - >( - core.sequence([ - core.any([ +const itemPredicate: core.InfallibleParser = (src, ctx) => { + const oldFormat = shouldUseOldItemStackFormat(ctx) + // TODO + return core.map< + core.SequenceUtil< + | core.LiteralNode + | core.ResourceLocationNode + | ComponentTestsNode + | nbt.NbtCompoundNode + >, + ItemPredicateNode + >( + core.sequence([ + oldFormat + ? core.resourceLocation({ category: 'item', allowTag: true }) + : core.any([ core.literal('*'), core.resourceLocation({ category: 'item', allowTag: true }), ]), - core.optional(core.failOnEmpty(componentTests())), - ]), - (res) => { - const ans: ItemNewNode = { - type: 'mcfunction:item', - range: res.range, - children: res.children, - id: res.children.find(core.ResourceLocationNode.is)!, - componentPredicates: res.children.find( - ComponentPredicatesNode.is, - ), - wildcard: res.children.find(core.LiteralNode.is) !== undefined, - } - return ans - }, - ) - } else { - return core.map< - core.SequenceUtil, - ItemNewNode - >( - core.sequence([ - core.resourceLocation({ category: 'item', allowTag: isPredicate }), - core.optional(core.failOnEmpty(components())), - ]), - (res) => { - const ans: ItemNewNode = { - type: 'mcfunction:item', - range: res.range, - children: res.children, - id: res.children.find(core.ResourceLocationNode.is)!, - components: res.children.find(ComponentListNode.is), - } - return ans - }, - ) - } -} - -const itemStack: core.InfallibleParser = (src, ctx) => { - return shouldUseOldItemStackFormat(ctx) - ? itemOld(false)(src, ctx) - : itemNew(false)(src, ctx) -} -const itemPredicate: core.InfallibleParser = (src, ctx) => { - return shouldUseOldItemStackFormat(ctx) - ? itemOld(true)(src, ctx) - : itemNew(true)(src, ctx) + oldFormat + ? core.optional(core.failOnEmpty(nbt.parser.compound)) + : core.optional(core.failOnEmpty(componentTests)), + ]), + (res) => { + const ans: ItemPredicateNode = { + type: 'mcfunction:item_predicate', + range: res.range, + children: res.children, + id: (res.children.find(core.ResourceLocationNode.is) || + res.children.find(core.LiteralNode.is))!, + tests: res.children.find( + ComponentTestsNode.is, + ), + } + return ans + }, + )(src, ctx) } const message: core.InfallibleParser = (src, ctx) => { @@ -1754,36 +1726,37 @@ function vector( } } -function components(): core.InfallibleParser { - return core.map( - core.record({ - start: '[', - pair: { - // TODO: Create an item_component category - key: core.resourceLocation({ - pool: [], - allowUnknown: true, - namespacePathSep: '.', - }), - sep: '=', - value: nbt.parser.entry, - end: ',', - trailingEnd: true, - }, - end: ']', - }), - (res) => { - const ans: ComponentListNode = { - type: 'mcfunction:component_list', - range: res.range, - children: res.children, - } - return ans +const components: core.InfallibleParser = core.map( + core.record({ + start: '[', + pair: { + // TODO: Create an item_component category + key: core.resourceLocation({ + pool: [], + allowUnknown: true, + namespacePathSep: '.', + }), + sep: '=', + value: nbt.parser.entry, + end: ',', + trailingEnd: true, }, - ) -} + end: ']', + }), + (res) => { + const ans: ComponentListNode = { + type: 'mcfunction:component_list', + range: res.range, + children: res.children, + } + return ans + }, +) -function componentTests(): core.InfallibleParser { +const componentTests: core.InfallibleParser = ( + src, + ctx, +) => { const exact = core.map< core.SequenceUtil< core.ResourceLocationNode | core.LiteralNode | nbt.NbtNode @@ -1900,12 +1873,12 @@ function componentTests(): core.InfallibleParser { core.list({ start: '[', value: test, - sep: ',', + sep: ',', // TODO: support | end: ']', - trailingSep: true, + trailingSep: false, }), (res) => { - const ans: ComponentPredicatesNode = { + const ans: ComponentTestsNode = { type: 'mcfunction:component_predicates', range: res.range, children: res.children.map(c => c.value!), @@ -1913,5 +1886,5 @@ function componentTests(): core.InfallibleParser { return ans }, - ) + )(src, ctx) } diff --git a/packages/locales/src/locales/en.json b/packages/locales/src/locales/en.json index 59447a7e3..ba3bcb799 100644 --- a/packages/locales/src/locales/en.json +++ b/packages/locales/src/locales/en.json @@ -33,7 +33,6 @@ "conjunction.or_3+_1": ", ", "conjunction.or_3+_2": ", or ", "datafix.error.command-replaceitem": "/replaceitem was removed in 20w46a (the second snapshot of 1.17) in favour of /item", - "duplicate-components": "Duplicate component definitions for %0%", "duplicate-key": "Duplicate key %0%", "ending-quote": "an ending quote %0%", "entity": "an entity", @@ -69,6 +68,7 @@ "long.between": "a long between %0% and %1%", "mcfunction.checker.command.data-modify-unapplicable-operation": "Operation %0% can only be used on %1%; the target path has type %2% instead", "mcfunction.completer.block.states.default-value": "Default: %0%", + "mcfunction.parser.duplicate-components": "Duplicate component definitions for %0%", "mcfunction.parser.entity-selector.arguments.not-applicable": "%0% is not applicable here", "mcfunction.parser.entity-selector.arguments.unknown": "Unknown entity selector argument %0%", "mcfunction.parser.entity-selector.entities-disallowed": "The selector contains non-player entities", From 05f101073108b242511bc354e464eea963355dab Mon Sep 17 00:00:00 2001 From: Misode Date: Thu, 30 May 2024 19:38:44 +0200 Subject: [PATCH 16/26] Remove unused function --- packages/java-edition/src/mcfunction/completer/argument.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/java-edition/src/mcfunction/completer/argument.ts b/packages/java-edition/src/mcfunction/completer/argument.ts index c348c90b6..f8dc27556 100644 --- a/packages/java-edition/src/mcfunction/completer/argument.ts +++ b/packages/java-edition/src/mcfunction/completer/argument.ts @@ -59,13 +59,6 @@ import { } from '../node/index.js' import type { ArgumentTreeNode } from '../tree/index.js' -function getItemStackFormat(ctx: CompleterContext): 'old' | 'new' { - const release = ctx.project['loadedVersion'] as ReleaseVersion | undefined - return (release === undefined || ReleaseVersion.cmp(release, '1.20.5') < 0) - ? 'old' - : 'new' -} - export const getMockNodes: mcf.completer.MockNodesGetter = ( rawTreeNode, ctx: CompleterContext, From 6ed3a5344139a62daa71abb82caf6a7c202a7f3f Mon Sep 17 00:00:00 2001 From: Misode Date: Thu, 30 May 2024 19:43:35 +0200 Subject: [PATCH 17/26] Update snapshots --- .../parser/argument/minecraftItemPredicate.spec.js | 8 ++++---- .../mcfunction/parser/argument/minecraftItemStack.spec.js | 6 +++--- .../mcfunction/parser/argument/minecraftParticle.spec.js | 2 +- packages/java-edition/src/mcfunction/parser/argument.ts | 1 + 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftItemPredicate.spec.js b/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftItemPredicate.spec.js index f99368c28..2f6667f79 100644 --- a/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftItemPredicate.spec.js +++ b/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftItemPredicate.spec.js @@ -1,6 +1,6 @@ exports['mcfunction argument minecraft:item_predicate Parse "#stick" 1'] = { "node": { - "type": "mcfunction:item", + "type": "mcfunction:item_predicate", "range": { "start": 0, "end": 6 @@ -22,7 +22,7 @@ exports['mcfunction argument minecraft:item_predicate Parse "#stick" 1'] = { exports['mcfunction argument minecraft:item_predicate Parse "#stick{foo:bar}" 1'] = { "node": { - "type": "mcfunction:item", + "type": "mcfunction:item_predicate", "range": { "start": 0, "end": 15 @@ -146,7 +146,7 @@ exports['mcfunction argument minecraft:item_predicate Parse "#stick{foo:bar}" 1' exports['mcfunction argument minecraft:item_predicate Parse "minecraft:stick" 1'] = { "node": { - "type": "mcfunction:item", + "type": "mcfunction:item_predicate", "range": { "start": 0, "end": 15 @@ -168,7 +168,7 @@ exports['mcfunction argument minecraft:item_predicate Parse "minecraft:stick" 1' exports['mcfunction argument minecraft:item_predicate Parse "stick" 1'] = { "node": { - "type": "mcfunction:item", + "type": "mcfunction:item_predicate", "range": { "start": 0, "end": 5 diff --git a/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftItemStack.spec.js b/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftItemStack.spec.js index f924e4c2d..3b4b0450c 100644 --- a/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftItemStack.spec.js +++ b/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftItemStack.spec.js @@ -1,6 +1,6 @@ exports['mcfunction argument minecraft:item_stack Parse "minecraft:stick" 1'] = { "node": { - "type": "mcfunction:item", + "type": "mcfunction:item_stack", "range": { "start": 0, "end": 15 @@ -22,7 +22,7 @@ exports['mcfunction argument minecraft:item_stack Parse "minecraft:stick" 1'] = exports['mcfunction argument minecraft:item_stack Parse "stick" 1'] = { "node": { - "type": "mcfunction:item", + "type": "mcfunction:item_stack", "range": { "start": 0, "end": 5 @@ -43,7 +43,7 @@ exports['mcfunction argument minecraft:item_stack Parse "stick" 1'] = { exports['mcfunction argument minecraft:item_stack Parse "stick{foo:bar}" 1'] = { "node": { - "type": "mcfunction:item", + "type": "mcfunction:item_stack", "range": { "start": 0, "end": 14 diff --git a/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftParticle.spec.js b/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftParticle.spec.js index 2ab998416..c15f0fe66 100644 --- a/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftParticle.spec.js +++ b/__snapshots__/packages/java-edition/test-out/mcfunction/parser/argument/minecraftParticle.spec.js @@ -338,7 +338,7 @@ exports['mcfunction argument minecraft:particle Parse "item carrot_on_a_stick" 1 ] }, { - "type": "mcfunction:item", + "type": "mcfunction:item_stack", "range": { "start": 5, "end": 22 diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 8a17f387d..897ce1247 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -559,6 +559,7 @@ const itemPredicate: core.InfallibleParser = (src, ctx) => { tests: res.children.find( ComponentTestsNode.is, ), + nbt: res.children.find(nbt.NbtCompoundNode.is), } return ans }, From 28be8e54d174d167af875ea62c00283817b35db6 Mon Sep 17 00:00:00 2001 From: Misode Date: Thu, 30 May 2024 20:15:56 +0200 Subject: [PATCH 18/26] Use correct symbol categories + change duplicate check --- packages/core/src/symbol/Symbol.ts | 2 ++ .../src/mcfunction/checker/index.ts | 9 ++--- .../src/mcfunction/parser/argument.ts | 33 ++++--------------- packages/locales/src/locales/en.json | 2 +- 4 files changed, 13 insertions(+), 33 deletions(-) diff --git a/packages/core/src/symbol/Symbol.ts b/packages/core/src/symbol/Symbol.ts index 291ede24b..fe2118b8c 100644 --- a/packages/core/src/symbol/Symbol.ts +++ b/packages/core/src/symbol/Symbol.ts @@ -27,6 +27,7 @@ export const RegistryCategories = Object.freeze( 'block_predicate_type', 'chunk_status', 'custom_stat', + 'data_component_type', 'enchantment', 'entity_type', 'float_provider_type', @@ -35,6 +36,7 @@ export const RegistryCategories = Object.freeze( 'height_provider_type', 'int_provider_type', 'item', + 'item_sub_predicate_type', 'loot_condition_type', 'loot_function_type', 'loot_nbt_provider_type', diff --git a/packages/java-edition/src/mcfunction/checker/index.ts b/packages/java-edition/src/mcfunction/checker/index.ts index 6013ebb9f..1a35e2845 100644 --- a/packages/java-edition/src/mcfunction/checker/index.ts +++ b/packages/java-edition/src/mcfunction/checker/index.ts @@ -191,16 +191,13 @@ const itemStack: core.SyncChecker = (node, ctx) => { groupedComponents.get(componentName)!.push(component) }) - groupedComponents.forEach((components, componentName) => { + groupedComponents.forEach((components) => { if (components.length > 1) { components.forEach(component => { ctx.err.report( - localize( - 'mcfunction.parser.duplicate-components', - componentName, - ), + localize('mcfunction.parser.duplicate-components'), component.key!.range, - core.ErrorSeverity.Error, + core.ErrorSeverity.Warning, ) }) } diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 897ce1247..9708dfb57 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -499,7 +499,6 @@ const greedyString: core.InfallibleParser = core.string({ const itemStack: core.InfallibleParser = (src, ctx) => { const oldFormat = shouldUseOldItemStackFormat(ctx) - // TODO return core.map< core.SequenceUtil< core.ResourceLocationNode | ComponentListNode | nbt.NbtCompoundNode @@ -528,7 +527,6 @@ const itemStack: core.InfallibleParser = (src, ctx) => { const itemPredicate: core.InfallibleParser = (src, ctx) => { const oldFormat = shouldUseOldItemStackFormat(ctx) - // TODO return core.map< core.SequenceUtil< | core.LiteralNode @@ -1731,12 +1729,7 @@ const components: core.InfallibleParser = core.map( core.record({ start: '[', pair: { - // TODO: Create an item_component category - key: core.resourceLocation({ - pool: [], - allowUnknown: true, - namespacePathSep: '.', - }), + key: core.resourceLocation({ category: 'data_component_type' }), sep: '=', value: nbt.parser.entry, end: ',', @@ -1765,11 +1758,7 @@ const componentTests: core.InfallibleParser = ( ComponentTestExactNode >( core.sequence([ - core.resourceLocation({ - pool: [], - allowUnknown: true, - namespacePathSep: '.', - }), + core.resourceLocation({ category: 'data_component_type' }), core.literal('='), nbt.parser.entry, ]), @@ -1793,11 +1782,7 @@ const componentTests: core.InfallibleParser = ( core.ResourceLocationNode, ComponentTestExistsNode >( - core.resourceLocation({ - pool: [], - allowUnknown: true, - namespacePathSep: '.', - }), + core.resourceLocation({ category: 'data_component_type' }), (res) => { const ans: ComponentTestExistsNode = { type: 'mcfunction:component_test_exists', @@ -1811,18 +1796,14 @@ const componentTests: core.InfallibleParser = ( }, ) - const subpredicate = core.map< + const subPredicate = core.map< core.SequenceUtil< core.ResourceLocationNode | core.LiteralNode | nbt.NbtNode >, ComponentTestSubpredicateNode >( core.sequence([ - core.resourceLocation({ - pool: [], - allowUnknown: true, - namespacePathSep: '.', - }), + core.resourceLocation({ category: 'item_sub_predicate_type' }), core.literal('~'), nbt.parser.entry, ]), @@ -1851,7 +1832,7 @@ const componentTests: core.InfallibleParser = ( >( core.sequence([ core.literal('!'), - core.any([exact, subpredicate, exists]), + core.any([exact, subPredicate, exists]), ]), (res) => { const test = res.children.find(ComponentTestNode.is)! @@ -1866,7 +1847,7 @@ const componentTests: core.InfallibleParser = ( parser: not, }, { - parser: core.any([exact, subpredicate, exists]), + parser: core.any([exact, subPredicate, exists]), }, ]) diff --git a/packages/locales/src/locales/en.json b/packages/locales/src/locales/en.json index ba3bcb799..b0733db30 100644 --- a/packages/locales/src/locales/en.json +++ b/packages/locales/src/locales/en.json @@ -68,7 +68,7 @@ "long.between": "a long between %0% and %1%", "mcfunction.checker.command.data-modify-unapplicable-operation": "Operation %0% can only be used on %1%; the target path has type %2% instead", "mcfunction.completer.block.states.default-value": "Default: %0%", - "mcfunction.parser.duplicate-components": "Duplicate component definitions for %0%", + "mcfunction.parser.duplicate-components": "Duplicate component", "mcfunction.parser.entity-selector.arguments.not-applicable": "%0% is not applicable here", "mcfunction.parser.entity-selector.arguments.unknown": "Unknown entity selector argument %0%", "mcfunction.parser.entity-selector.entities-disallowed": "The selector contains non-player entities", From 3d8f860ef7a3e93aaf4b53c5683f8bc77566b973 Mon Sep 17 00:00:00 2001 From: Misode Date: Thu, 30 May 2024 21:22:15 +0200 Subject: [PATCH 19/26] Start of completers --- .../src/mcfunction/completer/argument.ts | 69 +++++++++++++++++-- .../src/mcfunction/node/argument.ts | 4 +- .../src/mcfunction/parser/argument.ts | 4 +- 3 files changed, 68 insertions(+), 9 deletions(-) diff --git a/packages/java-edition/src/mcfunction/completer/argument.ts b/packages/java-edition/src/mcfunction/completer/argument.ts index f8dc27556..da32dca5b 100644 --- a/packages/java-edition/src/mcfunction/completer/argument.ts +++ b/packages/java-edition/src/mcfunction/completer/argument.ts @@ -27,6 +27,7 @@ import { import * as json from '@spyglassmc/json' import { localeQuote, localize } from '@spyglassmc/locales' import type * as mcf from '@spyglassmc/mcfunction' +import type * as nbt from '@spyglassmc/nbt' import { getTagValues } from '../../common/index.js' import { ReleaseVersion } from '../../dependency/common.js' import { @@ -43,6 +44,8 @@ import { } from '../common/index.js' import type { BlockStatesNode, + ComponentListNode, + ComponentTestsNode, EntitySelectorArgumentsNode, } from '../node/index.js' import { @@ -255,6 +258,38 @@ const blockStates: Completer = (node, ctx) => { })(node, ctx) } +const componentList: Completer = (node, ctx) => { + return completer.record< + ResourceLocationNode, + nbt.NbtNode, + ComponentListNode + >({ + key: ( + _record, + pair, + ctx, + range, + _insertValue, + _insertComma, + _existingKeys, + ) => { + const id = pair?.key ?? + ResourceLocationNode.mock(pair?.key ?? range, { + category: 'data_component_type', + }) + return completer.resourceLocation(id, ctx) + }, + value: () => { + return [] // TODO + }, + })(node, ctx) +} + +const componentTests: Completer = (node, ctx) => { + // TODO: improve this completer + return completer.dispatch(node, ctx) +} + const coordinate: Completer = (node, _ctx) => { return [CompletionItem.create('~', node)] } @@ -264,16 +299,28 @@ const itemStack: Completer = (node, ctx) => { if (Range.contains(node.id, ctx.offset, true)) { ans.push(...completer.resourceLocation(node.id, ctx)) } + if (node.components && Range.contains(node.components, ctx.offset, true)) { + ans.push(...componentList(node.components, ctx)) + } + if (node.nbt && Range.contains(node.nbt, ctx.offset, true)) { + // TODO + } return ans } const itemPredicate: Completer = (node, ctx) => { const ans: CompletionItem[] = [] - if ( - Range.contains(node.id, ctx.offset, true) && - node.id.type === 'resource_location' - ) { - ans.push(...completer.resourceLocation(node.id, ctx)) + if (Range.contains(node.id, ctx.offset, true)) { + ans.push(CompletionItem.create('*', node, { sortText: '##' })) + if (node.id.type === 'resource_location') { + ans.push(...completer.resourceLocation(node.id, ctx)) + } + } + if (node.tests && Range.contains(node.tests, ctx.offset, true)) { + ans.push(...componentTests(node.tests, ctx)) + } + if (node.nbt && Range.contains(node.nbt, ctx.offset, true)) { + // TODO } return ans } @@ -455,6 +502,14 @@ const vector: Completer = (node, _ctx) => { export function register(meta: MetaRegistry) { meta.registerCompleter('mcfunction:block', block) + meta.registerCompleter( + 'mcfunction:component_list', + componentList, + ) + meta.registerCompleter( + 'mcfunction:component_tests', + componentTests, + ) meta.registerCompleter('mcfunction:coordinate', coordinate) meta.registerCompleter( 'mcfunction:entity_selector', @@ -466,6 +521,10 @@ export function register(meta: MetaRegistry) { ) meta.registerCompleter('mcfunction:int_range', intRange) meta.registerCompleter('mcfunction:item_stack', itemStack) + meta.registerCompleter( + 'mcfunction:item_predicate', + itemPredicate, + ) meta.registerCompleter( 'mcfunction:objective_criteria', objectiveCriteria, diff --git a/packages/java-edition/src/mcfunction/node/argument.ts b/packages/java-edition/src/mcfunction/node/argument.ts index a1524b20d..86784227f 100644 --- a/packages/java-edition/src/mcfunction/node/argument.ts +++ b/packages/java-edition/src/mcfunction/node/argument.ts @@ -354,14 +354,14 @@ export namespace ItemPredicateNode { } export interface ComponentTestsNode extends core.AstNode { - type: 'mcfunction:component_predicates' + type: 'mcfunction:component_tests' children: ComponentTestNode[] } export namespace ComponentTestsNode { export function is(node: core.AstNode): node is ComponentTestsNode { return (node as ComponentTestsNode).type === - 'mcfunction:component_predicates' + 'mcfunction:component_tests' } } diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 9708dfb57..b36a1b014 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -540,8 +540,8 @@ const itemPredicate: core.InfallibleParser = (src, ctx) => { oldFormat ? core.resourceLocation({ category: 'item', allowTag: true }) : core.any([ - core.literal('*'), core.resourceLocation({ category: 'item', allowTag: true }), + core.literal('*'), ]), oldFormat ? core.optional(core.failOnEmpty(nbt.parser.compound)) @@ -1861,7 +1861,7 @@ const componentTests: core.InfallibleParser = ( }), (res) => { const ans: ComponentTestsNode = { - type: 'mcfunction:component_predicates', + type: 'mcfunction:component_tests', range: res.range, children: res.children.map(c => c.value!), } From a6d7d514e73f7bae90edc7634b14ebe157290633 Mon Sep 17 00:00:00 2001 From: Misode Date: Thu, 30 May 2024 22:52:54 +0200 Subject: [PATCH 20/26] Refactor component test parser --- packages/core/src/parser/resourceLocation.ts | 1 + .../src/mcfunction/completer/argument.ts | 2 +- .../src/mcfunction/parser/argument.ts | 185 ++++++------------ 3 files changed, 66 insertions(+), 122 deletions(-) diff --git a/packages/core/src/parser/resourceLocation.ts b/packages/core/src/parser/resourceLocation.ts index 38b25a637..b01c7d64b 100644 --- a/packages/core/src/parser/resourceLocation.ts +++ b/packages/core/src/parser/resourceLocation.ts @@ -14,6 +14,7 @@ const Terminators = new Set([ '\r', '\n', '=', + '~', ',', '"', "'", diff --git a/packages/java-edition/src/mcfunction/completer/argument.ts b/packages/java-edition/src/mcfunction/completer/argument.ts index da32dca5b..c4eb7bd47 100644 --- a/packages/java-edition/src/mcfunction/completer/argument.ts +++ b/packages/java-edition/src/mcfunction/completer/argument.ts @@ -287,7 +287,7 @@ const componentList: Completer = (node, ctx) => { const componentTests: Completer = (node, ctx) => { // TODO: improve this completer - return completer.dispatch(node, ctx) + return [] } const coordinate: Completer = (node, _ctx) => { diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index b36a1b014..cff69fed2 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -21,6 +21,7 @@ import type { BlockNode, ComponentTestExactNode, ComponentTestExistsNode, + ComponentTestNode, ComponentTestSubpredicateNode, CoordinateNode, EntityNode, @@ -42,7 +43,6 @@ import type { import { BlockStatesNode, ComponentListNode, - ComponentTestNode, ComponentTestsNode, CoordinateSystem, EntitySelectorArgumentsNode, @@ -1747,126 +1747,69 @@ const components: core.InfallibleParser = core.map( }, ) -const componentTests: core.InfallibleParser = ( - src, - ctx, -) => { - const exact = core.map< - core.SequenceUtil< - core.ResourceLocationNode | core.LiteralNode | nbt.NbtNode - >, - ComponentTestExactNode - >( - core.sequence([ - core.resourceLocation({ category: 'data_component_type' }), - core.literal('='), - nbt.parser.entry, - ]), - (res) => { - const id = res.children.find(core.ResourceLocationNode.is)! - const value = res.children.find(nbt.NbtNode.is)! - const ans: ComponentTestExactNode = { - type: 'mcfunction:component_test_exact', - range: res.range, - children: [id, value], - component: id, - value, - negated: false, - } - - return ans - }, - ) - - const exists = core.map< - core.ResourceLocationNode, - ComponentTestExistsNode - >( - core.resourceLocation({ category: 'data_component_type' }), - (res) => { - const ans: ComponentTestExistsNode = { - type: 'mcfunction:component_test_exists', - range: res.range, - children: [res], - component: res, - negated: false, - } - - return ans - }, - ) +const componentTest: core.InfallibleParser = (src, ctx) => { + const start = src.cursor + src.skipWhitespace() + const negated = src.trySkip('!') + src.skipWhitespace() - const subPredicate = core.map< - core.SequenceUtil< - core.ResourceLocationNode | core.LiteralNode | nbt.NbtNode - >, - ComponentTestSubpredicateNode - >( - core.sequence([ - core.resourceLocation({ category: 'item_sub_predicate_type' }), - core.literal('~'), - nbt.parser.entry, - ]), - (res) => { - const id = res.children.find(core.ResourceLocationNode.is)! - const subpredicate = res.children.find(nbt.NbtNode.is)! - const ans: ComponentTestSubpredicateNode = { - type: 'mcfunction:component_test_sub_predicate', - range: res.range, - children: [id, subpredicate], - subPredicateType: id, - subPredicate: subpredicate, - negated: false, - } - - return ans - }, - ) - - const not = core.map< - core.SequenceUtil< - | core.LiteralNode - | ComponentTestNode - >, - ComponentTestNode - >( - core.sequence([ - core.literal('!'), - core.any([exact, subPredicate, exists]), - ]), - (res) => { - const test = res.children.find(ComponentTestNode.is)! - test.negated = true - return test - }, + const resLoc = core.resourceLocation({ category: 'data_component_type' })( + src, + ctx, ) - - const test = core.select([ - { - predicate: (src) => src.peek() === '!', - parser: not, - }, - { - parser: core.any([exact, subPredicate, exists]), - }, - ]) - - return core.map( - core.list({ - start: '[', - value: test, - sep: ',', // TODO: support | - end: ']', - trailingSep: false, - }), - (res) => { - const ans: ComponentTestsNode = { - type: 'mcfunction:component_tests', - range: res.range, - children: res.children.map(c => c.value!), - } - - return ans - }, - )(src, ctx) + src.skipWhitespace() + + if (src.trySkip('=')) { + const value = nbt.parser.compound(src, ctx) + src.skipWhitespace() + const ans: ComponentTestExactNode = { + type: 'mcfunction:component_test_exact', + range: core.Range.create(start, src), + children: [resLoc, value], + component: resLoc, + value: value, + negated, + } + return ans + } + if (src.trySkip('~')) { + resLoc.options.category = 'item_sub_predicate_type' + const predicate = nbt.parser.compound(src, ctx) + src.skipWhitespace() + const ans: ComponentTestSubpredicateNode = { + type: 'mcfunction:component_test_sub_predicate', + range: core.Range.create(start, src), + children: [resLoc, predicate], + subPredicateType: resLoc, + subPredicate: predicate, + negated, + } + return ans + } + const ans: ComponentTestExistsNode = { + type: 'mcfunction:component_test_exists', + range: core.Range.create(start, src), + children: [resLoc], + component: resLoc, + negated, + } + return ans } + +const componentTests: core.InfallibleParser = core.map( + core.list({ + start: '[', + value: componentTest, + sep: ',', // TODO: support | + end: ']', + trailingSep: false, + }), + (res) => { + const ans: ComponentTestsNode = { + type: 'mcfunction:component_tests', + range: res.range, + children: res.children.filter(c => c.value).map(c => c.value!), + } + return ans + }, +) From 8612a2e3f8cd8fc9245f7239b2889d05fe983106 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Sat, 15 Jun 2024 12:18:50 +0300 Subject: [PATCH 21/26] fix equality component tests only expecting compounds --- packages/java-edition/src/mcfunction/parser/argument.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index cff69fed2..4dc161cf8 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -1747,7 +1747,7 @@ const components: core.InfallibleParser = core.map( }, ) -const componentTest: core.InfallibleParser = (src, ctx) => { +const componentTest: core.Parser = (src, ctx) => { const start = src.cursor src.skipWhitespace() const negated = src.trySkip('!') @@ -1760,7 +1760,12 @@ const componentTest: core.InfallibleParser = (src, ctx) => { src.skipWhitespace() if (src.trySkip('=')) { - const value = nbt.parser.compound(src, ctx) + const value = nbt.parser.entry(src, ctx) + + if (value == core.Failure) { + return core.Failure + } + src.skipWhitespace() const ans: ComponentTestExactNode = { type: 'mcfunction:component_test_exact', From 0b89a61eb0968898f4a7fc24b117b0c377e603d2 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Sat, 15 Jun 2024 15:44:19 +0300 Subject: [PATCH 22/26] finish item predicates --- packages/core/src/parser/resourceLocation.ts | 1 + .../src/mcfunction/completer/argument.ts | 1 + .../src/mcfunction/node/argument.ts | 29 ++++- .../src/mcfunction/parser/argument.ts | 108 +++++++++++++++--- 4 files changed, 122 insertions(+), 17 deletions(-) diff --git a/packages/core/src/parser/resourceLocation.ts b/packages/core/src/parser/resourceLocation.ts index b01c7d64b..fca59cf1a 100644 --- a/packages/core/src/parser/resourceLocation.ts +++ b/packages/core/src/parser/resourceLocation.ts @@ -25,6 +25,7 @@ const Terminators = new Set([ '(', ')', ';', + '|', ]) const LegalCharacters = new Set([ 'a', diff --git a/packages/java-edition/src/mcfunction/completer/argument.ts b/packages/java-edition/src/mcfunction/completer/argument.ts index c4eb7bd47..e0e4cdf40 100644 --- a/packages/java-edition/src/mcfunction/completer/argument.ts +++ b/packages/java-edition/src/mcfunction/completer/argument.ts @@ -45,6 +45,7 @@ import { import type { BlockStatesNode, ComponentListNode, + ComponentTestsAllOfNode, ComponentTestsNode, EntitySelectorArgumentsNode, } from '../node/index.js' diff --git a/packages/java-edition/src/mcfunction/node/argument.ts b/packages/java-edition/src/mcfunction/node/argument.ts index 86784227f..ceab00e94 100644 --- a/packages/java-edition/src/mcfunction/node/argument.ts +++ b/packages/java-edition/src/mcfunction/node/argument.ts @@ -355,13 +355,36 @@ export namespace ItemPredicateNode { export interface ComponentTestsNode extends core.AstNode { type: 'mcfunction:component_tests' - children: ComponentTestNode[] + children: ComponentTestsAnyOfNode[] } export namespace ComponentTestsNode { export function is(node: core.AstNode): node is ComponentTestsNode { - return (node as ComponentTestsNode).type === - 'mcfunction:component_tests' + return (node as ComponentTestsNode).type === 'mcfunction:component_tests' + } +} + +export interface ComponentTestsAnyOfNode extends core.AstNode { + type: 'mcfunction:component_tests_any_of' + children: ComponentTestsAllOfNode[] +} + +export namespace ComponentTestsAnyOfNode { + export function is(node: core.AstNode): node is ComponentTestsAnyOfNode { + return (node as ComponentTestsAnyOfNode).type === + 'mcfunction:component_tests_any_of' + } +} + +export interface ComponentTestsAllOfNode extends core.AstNode { + type: 'mcfunction:component_tests_all_of' + children: ComponentTestNode[] +} + +export namespace ComponentTestsAllOfNode { + export function is(node: core.AstNode): node is ComponentTestsAllOfNode { + return (node as ComponentTestsAllOfNode).type === + 'mcfunction:component_tests_all_of' } } diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 4dc161cf8..22fcb07d2 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -23,6 +23,7 @@ import type { ComponentTestExistsNode, ComponentTestNode, ComponentTestSubpredicateNode, + ComponentTestsAllOfNode, CoordinateNode, EntityNode, EntitySelectorAdvancementsArgumentCriteriaNode, @@ -38,12 +39,12 @@ import type { ParticleNode, ScoreHolderNode, UuidNode, - VectorNode, -} from '../node/index.js' + VectorNode} from '../node/index.js'; import { BlockStatesNode, ComponentListNode, ComponentTestsNode, + ComponentTestsAnyOfNode, CoordinateSystem, EntitySelectorArgumentsNode, EntitySelectorAtVariable, @@ -1761,8 +1762,10 @@ const componentTest: core.Parser = (src, ctx) => { if (src.trySkip('=')) { const value = nbt.parser.entry(src, ctx) - + if (value == core.Failure) { + ctx.err.report(localize('expected', localize('nbt.node')), src) + src.skipUntilOrEnd(',', '|', ']') return core.Failure } @@ -1779,7 +1782,14 @@ const componentTest: core.Parser = (src, ctx) => { } if (src.trySkip('~')) { resLoc.options.category = 'item_sub_predicate_type' - const predicate = nbt.parser.compound(src, ctx) + const predicate = nbt.parser.entry(src, ctx) + + if (predicate == core.Failure) { + ctx.err.report(localize('expected', localize('nbt.node')), src) + src.skipUntilOrEnd(',', '|', ']') + return core.Failure + } + src.skipWhitespace() const ans: ComponentTestSubpredicateNode = { type: 'mcfunction:component_test_sub_predicate', @@ -1801,20 +1811,90 @@ const componentTest: core.Parser = (src, ctx) => { return ans } +const componentTestsAllOf: core.InfallibleParser = (src, ctx) => { + const parser: core.InfallibleParser = (src, ctx) => { + const children = [] + const start = src.cursor + + while (src.canRead()) { + src.skipWhitespace() + const testNode = componentTest(src, ctx) + + if (testNode == core.Failure) { + continue + } + + children.push(testNode) + src.skipWhitespace() + + if (src.peek() === ',') { + src.skip() + } else if (src.peek() === '|' || src.peek() === ']') { + break + } else { + ctx.err.report(localize('expected', localeQuote(']')), src) + src.skipUntilOrEnd(',', '|', ']') + } + } + + const ans: ComponentTestsAllOfNode = { + type: 'mcfunction:component_tests_all_of', + range: core.Range.create(start, src), + children, + } + + return ans + }; + + return parser(src, ctx) + +} + +const componentTestsAnyOf: core.InfallibleParser = (src, ctx) => { + const parser: core.InfallibleParser = (src, ctx) => { + const children = [] + const start = src.cursor + + while (src.canRead()) { + src.skipWhitespace() + const allOfNode = componentTestsAllOf(src, ctx) + children.push(allOfNode) + src.skipWhitespace() + + if (src.peek() === '|') { + src.skip() + } else if (src.peek() === ']') { + break + } else { + ctx.err.report(localize('expected', localeQuote(']')), src) + src.skipUntilOrEnd('|', ']') + } + } + + const ans: ComponentTestsAnyOfNode = { + type: 'mcfunction:component_tests_any_of', + range: core.Range.create(start, src), + children, + } + + return ans + }; + + return parser(src, ctx) +} + const componentTests: core.InfallibleParser = core.map( - core.list({ - start: '[', - value: componentTest, - sep: ',', // TODO: support | - end: ']', - trailingSep: false, - }), + core.sequence([ + core.literal('['), + core.optional(core.failOnEmpty(componentTestsAnyOf)), + core.literal(']'), + ]), (res) => { const ans: ComponentTestsNode = { type: 'mcfunction:component_tests', range: res.range, - children: res.children.filter(c => c.value).map(c => c.value!), + children: res.children.filter(ComponentTestsAnyOfNode.is).map(c => c), } return ans - }, -) + } +); From e6f6a5da3079fbe960493b80a76a165c4c9a664e Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Sat, 15 Jun 2024 16:53:36 +0300 Subject: [PATCH 23/26] fix weird merge --- .../src/mcfunction/checker/index.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/java-edition/src/mcfunction/checker/index.ts b/packages/java-edition/src/mcfunction/checker/index.ts index 330c2dc9f..c82ee0ad6 100644 --- a/packages/java-edition/src/mcfunction/checker/index.ts +++ b/packages/java-edition/src/mcfunction/checker/index.ts @@ -9,14 +9,10 @@ import type { EntitySelectorInvertableArgumentValueNode } from '../node/index.js import { BlockNode, EntityNode, -<<<<<<< HEAD ItemStackNode, -======= - ItemNode, JsonNode, NbtNode, NbtResourceNode, ->>>>>>> upstream/main ParticleNode, } from '../node/index.js' @@ -88,7 +84,7 @@ const entity: core.SyncChecker = (node, ctx) => { const itemStack: core.SyncChecker = (node, ctx) => { if (node.nbt) { nbt.checker.index( - 'item', + 'minecraft:item', core.ResourceLocationNode.toString(node.id, 'full'), )(node.nbt, ctx) } @@ -98,7 +94,6 @@ const itemStack: core.SyncChecker = (node, ctx) => { core.PairNode[] >() -<<<<<<< HEAD node.components!.children.forEach(component => { const componentName = core.ResourceLocationNode.toString( component.key!, @@ -124,12 +119,6 @@ const itemStack: core.SyncChecker = (node, ctx) => { } }) } -======= - nbt.checker.index( - 'minecraft:item', - core.ResourceLocationNode.toString(node.id, 'full'), - )(node.nbt, ctx) ->>>>>>> upstream/main } const jsonChecker: core.SyncChecker = (node, ctx) => { @@ -255,11 +244,7 @@ export function register(meta: core.MetaRegistry) { meta.registerChecker('mcfunction:command', command) meta.registerChecker('mcfunction:block', block) meta.registerChecker('mcfunction:entity', entity) -<<<<<<< HEAD meta.registerChecker('mcfunction:item_stack', itemStack) -======= - meta.registerChecker('mcfunction:item', item) meta.registerChecker('mcfunction:json', jsonChecker) ->>>>>>> upstream/main meta.registerChecker('mcfunction:particle', particle) } From f0077d7f3e2970d08836b6ce5a8e6ff430d7a305 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Sat, 15 Jun 2024 16:58:54 +0300 Subject: [PATCH 24/26] fix linting --- .../src/mcfunction/parser/argument.ts | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index d1721d073..acd55c260 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -23,8 +23,8 @@ import type { ComponentTestExactNode, ComponentTestExistsNode, ComponentTestNode, - ComponentTestSubpredicateNode, ComponentTestsAllOfNode, + ComponentTestSubpredicateNode, CoordinateNode, EntityNode, EntitySelectorAdvancementsArgumentCriteriaNode, @@ -43,12 +43,13 @@ import type { ParticleNode, ScoreHolderNode, UuidNode, - VectorNode} from '../node/index.js'; + VectorNode, +} from '../node/index.js' import { BlockStatesNode, ComponentListNode, - ComponentTestsNode, ComponentTestsAnyOfNode, + ComponentTestsNode, CoordinateSystem, EntitySelectorArgumentsNode, EntitySelectorAtVariable, @@ -1907,8 +1908,14 @@ const componentTest: core.Parser = (src, ctx) => { return ans } -const componentTestsAllOf: core.InfallibleParser = (src, ctx) => { - const parser: core.InfallibleParser = (src, ctx) => { +const componentTestsAllOf: core.InfallibleParser = ( + src, + ctx, +) => { + const parser: core.InfallibleParser = ( + src, + ctx, + ) => { const children = [] const start = src.cursor @@ -1922,7 +1929,7 @@ const componentTestsAllOf: core.InfallibleParser = (src children.push(testNode) src.skipWhitespace() - + if (src.peek() === ',') { src.skip() } else if (src.peek() === '|' || src.peek() === ']') { @@ -1940,14 +1947,19 @@ const componentTestsAllOf: core.InfallibleParser = (src } return ans - }; + } return parser(src, ctx) - } -const componentTestsAnyOf: core.InfallibleParser = (src, ctx) => { - const parser: core.InfallibleParser = (src, ctx) => { +const componentTestsAnyOf: core.InfallibleParser = ( + src, + ctx, +) => { + const parser: core.InfallibleParser = ( + src, + ctx, + ) => { const children = [] const start = src.cursor @@ -1974,7 +1986,7 @@ const componentTestsAnyOf: core.InfallibleParser = (src } return ans - }; + } return parser(src, ctx) } @@ -1992,5 +2004,5 @@ const componentTests: core.InfallibleParser = core.map( children: res.children.filter(ComponentTestsAnyOfNode.is).map(c => c), } return ans - } -); + }, +) From 5cfdfa1fb9b8d9c393409a2c1252e7ad42f7b458 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Sat, 15 Jun 2024 18:24:49 +0300 Subject: [PATCH 25/26] return component test nodes to being infallible --- .../src/mcfunction/node/argument.ts | 8 ++++---- .../src/mcfunction/parser/argument.ts | 18 +++++++----------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/java-edition/src/mcfunction/node/argument.ts b/packages/java-edition/src/mcfunction/node/argument.ts index 3c8c424aa..04372396f 100644 --- a/packages/java-edition/src/mcfunction/node/argument.ts +++ b/packages/java-edition/src/mcfunction/node/argument.ts @@ -408,9 +408,9 @@ export interface ComponentTestBaseNode extends core.AstNode { export interface ComponentTestExactNode extends ComponentTestBaseNode { type: 'mcfunction:component_test_exact' - children: [core.ResourceLocationNode, nbt.NbtNode] + children: (core.ResourceLocationNode | nbt.NbtNode)[] component: core.ResourceLocationNode - value: nbt.NbtNode + value?: nbt.NbtNode } export interface ComponentTestExistsNode extends ComponentTestBaseNode { @@ -421,9 +421,9 @@ export interface ComponentTestExistsNode extends ComponentTestBaseNode { export interface ComponentTestSubpredicateNode extends ComponentTestBaseNode { type: 'mcfunction:component_test_sub_predicate' - children: [core.ResourceLocationNode, nbt.NbtNode] + children: (core.ResourceLocationNode | nbt.NbtNode)[] subPredicateType: core.ResourceLocationNode - subPredicate: nbt.NbtNode + subPredicate?: nbt.NbtNode } export type ComponentTestNode = diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index acd55c260..9e1d281f7 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -1845,7 +1845,7 @@ const components: core.InfallibleParser = core.map( }, ) -const componentTest: core.Parser = (src, ctx) => { +const componentTest: core.InfallibleParser = (src, ctx) => { const start = src.cursor src.skipWhitespace() const negated = src.trySkip('!') @@ -1858,19 +1858,19 @@ const componentTest: core.Parser = (src, ctx) => { src.skipWhitespace() if (src.trySkip('=')) { - const value = nbt.parser.entry(src, ctx) + let value: core.Result | undefined = nbt.parser.entry(src, ctx); if (value == core.Failure) { ctx.err.report(localize('expected', localize('nbt.node')), src) src.skipUntilOrEnd(',', '|', ']') - return core.Failure + value = undefined } src.skipWhitespace() const ans: ComponentTestExactNode = { type: 'mcfunction:component_test_exact', range: core.Range.create(start, src), - children: [resLoc, value], + children: [resLoc, ...(value ? [value] : [])], component: resLoc, value: value, negated, @@ -1879,19 +1879,19 @@ const componentTest: core.Parser = (src, ctx) => { } if (src.trySkip('~')) { resLoc.options.category = 'item_sub_predicate_type' - const predicate = nbt.parser.entry(src, ctx) + let predicate: core.Result | undefined = nbt.parser.entry(src, ctx) if (predicate == core.Failure) { ctx.err.report(localize('expected', localize('nbt.node')), src) src.skipUntilOrEnd(',', '|', ']') - return core.Failure + predicate = undefined } src.skipWhitespace() const ans: ComponentTestSubpredicateNode = { type: 'mcfunction:component_test_sub_predicate', range: core.Range.create(start, src), - children: [resLoc, predicate], + children: [resLoc, ...(predicate ? [predicate] : [])], subPredicateType: resLoc, subPredicate: predicate, negated, @@ -1923,10 +1923,6 @@ const componentTestsAllOf: core.InfallibleParser = ( src.skipWhitespace() const testNode = componentTest(src, ctx) - if (testNode == core.Failure) { - continue - } - children.push(testNode) src.skipWhitespace() From 3be2227e0d43091fb78c169f39c99d15c9820b02 Mon Sep 17 00:00:00 2001 From: Trivaxy Date: Sat, 15 Jun 2024 18:26:39 +0300 Subject: [PATCH 26/26] ... fix lint. i forgot to make this automatic --- .../java-edition/src/mcfunction/parser/argument.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/java-edition/src/mcfunction/parser/argument.ts b/packages/java-edition/src/mcfunction/parser/argument.ts index 9e1d281f7..4fcb15a47 100644 --- a/packages/java-edition/src/mcfunction/parser/argument.ts +++ b/packages/java-edition/src/mcfunction/parser/argument.ts @@ -1858,7 +1858,10 @@ const componentTest: core.InfallibleParser = (src, ctx) => { src.skipWhitespace() if (src.trySkip('=')) { - let value: core.Result | undefined = nbt.parser.entry(src, ctx); + let value: core.Result | undefined = nbt.parser.entry( + src, + ctx, + ) if (value == core.Failure) { ctx.err.report(localize('expected', localize('nbt.node')), src) @@ -1879,7 +1882,10 @@ const componentTest: core.InfallibleParser = (src, ctx) => { } if (src.trySkip('~')) { resLoc.options.category = 'item_sub_predicate_type' - let predicate: core.Result | undefined = nbt.parser.entry(src, ctx) + let predicate: core.Result | undefined = nbt.parser.entry( + src, + ctx, + ) if (predicate == core.Failure) { ctx.err.report(localize('expected', localize('nbt.node')), src)