Compare commits

...

18 Commits

Author SHA1 Message Date
barsdeveloper
5d847ab8f6 Several fixes 2024-07-18 17:35:29 +02:00
barsdeveloper
8a2cd6c26e Fixing still 2024-07-17 21:38:10 +02:00
barsdeveloper
e0d8990e6a Decoding code fixes 2024-07-16 21:36:13 +02:00
barsdeveloper
98ebdd78b2 Better typing for grammars 2024-06-06 23:16:21 +02:00
barsdeveloper
ad4ba2c46d WIP 2024-06-06 20:10:17 +02:00
barsdeveloper
6d99db5fd1 CustomProperties creating fixed 2024-06-05 16:30:14 +02:00
barsdeveloper
beccfe522a Fix Function reference 2024-06-05 15:58:55 +02:00
barsdeveloper
ad8305ca52 Remove serialization bits 2024-06-05 00:28:28 +02:00
barsdeveloper
6ca966e176 Entities tests fixed 2024-06-04 22:54:16 +02:00
barsdeveloper
e16822760f Simple entities serialization fixed 2024-06-04 14:40:47 +02:00
barsdeveloper
c05e6d3cc9 More entities fixed 2024-06-04 01:00:41 +02:00
barsdeveloper
1a8636bb5d More tests and fixed 2024-06-03 12:42:42 +02:00
barsdeveloper
8fed17b20f Tests for various entity classes and update entity class implementations 2024-06-03 00:11:30 +02:00
barsdeveloper
5314228b33 Fix format text entity 2024-06-01 16:27:17 +02:00
barsdeveloper
8258572e56 ArrayEntity parsing fixed 2024-05-31 18:39:58 +02:00
barsdeveloper
ecc71b76d1 WIP 2024-05-31 15:09:48 +02:00
barsdeveloper
1c2778fbf8 Still WIP 2024-05-28 16:44:39 +02:00
barsdeveloper
70b4cabb97 Merge remote-tracking branch 'origin/master' into refactoring-entities 2024-05-21 15:52:34 +02:00
103 changed files with 5265 additions and 6218 deletions

4536
dist/ueblueprint.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -413,7 +413,7 @@ export default class Blueprint extends IElement {
this.entity = this.entity.mergeWith(element.entity)
const additionalSerialization = atob(element.entity.ExportedNodes)
this.template.getPasteInputObject().pasted(additionalSerialization)
.forEach(node => node.entity.isExported = true)
.forEach(node => node.entity._exported = true)
continue
}
const name = element.entity.getObjectName()

View File

@@ -1,7 +1,4 @@
import ComputedType from "./entity/ComputedType.js"
import Configuration from "./Configuration.js"
import MirroredEntity from "./entity/MirroredEntity.js"
import Union from "./entity/Union.js"
export default class Utility {
@@ -184,97 +181,6 @@ export default class Utility {
return false
}
/**
* @param {Attribute} a
* @param {Attribute} b
*/
static equals(a, b) {
while (a instanceof MirroredEntity) {
a = a.get()
}
while (b instanceof MirroredEntity) {
b = b.get()
}
// Here we cannot check both instanceof IEntity because this would introduce a circular include dependency
if (/** @type {IEntity?} */(a)?.equals && /** @type {IEntity?} */(b)?.equals) {
return /** @type {IEntity} */(a).equals(/** @type {IEntity} */(b))
}
a = Utility.sanitize(a)
b = Utility.sanitize(b)
if (a?.constructor === BigInt && b?.constructor === Number) {
b = BigInt(b)
} else if (a?.constructor === Number && b?.constructor === BigInt) {
a = BigInt(a)
}
if (a === b) {
return true
}
if (a instanceof Array && b instanceof Array) {
return a.length === b.length && a.every((value, i) => Utility.equals(value, b[i]))
}
return false
}
/**
* @template {Attribute | AttributeTypeDescription} T
* @param {T} value
* @returns {AttributeConstructor<T>}
*/
static getType(value) {
if (value === null) {
return null
}
if (value?.constructor === Object && /** @type {AttributeInformation} */(value)?.type instanceof Function) {
return /** @type {AttributeInformation} */(value).type
}
return /** @type {AttributeConstructor<any>} */(value?.constructor)
}
/**
* @template {Attribute} V
* @template {AttributeConstructor<V>} C
* @param {C} type
* @returns {value is InstanceType<C>}
*/
static isValueOfType(value, type, acceptNull = false) {
if (type instanceof MirroredEntity) {
type = type.getTargetType()
}
return (acceptNull && value === null) || value instanceof type || value?.constructor === type
}
/** @param {Attribute} value */
static sanitize(value, targetType = /** @type {AttributeTypeDescription } */(value?.constructor)) {
if (targetType instanceof Array) {
targetType = targetType[0]
}
if (targetType instanceof ComputedType) {
return value // The type is computed, can't say anything about it
}
if (targetType instanceof Union) {
let type = targetType.values.find(t => Utility.isValueOfType(value, t, false))
if (!type) {
type = targetType.values[0]
}
targetType = type
}
if (targetType instanceof MirroredEntity) {
if (value instanceof MirroredEntity) {
return value
}
return Utility.sanitize(value, targetType.getTargetType())
}
if (targetType && !Utility.isValueOfType(value, targetType, true)) {
value = targetType === BigInt
? BigInt(/** @type {Number} */(value))
: new /** @type {EntityConstructor} */(targetType)(value)
}
if (value instanceof Boolean || value instanceof Number || value instanceof String) {
value = /** @type {TerminalAttribute} */(value.valueOf()) // Get the relative primitive value
}
return value
}
/**
* @param {Number} x
* @param {Number} y
@@ -334,11 +240,14 @@ export default class Utility {
}
/** @param {String} value */
static escapeString(value) {
return value
.replaceAll(new RegExp(`(${Configuration.stringEscapedCharacters.source})`, "g"), '\\$1')
.replaceAll("\n", "\\n") // Replace newline with \n
.replaceAll("\t", "\\t") // Replace tab with \t
static escapeString(value, inline = true) {
let result = value.replaceAll(new RegExp(`(${Configuration.stringEscapedCharacters.source})`, "g"), '\\$1')
if (inline) {
result = result
.replaceAll("\n", "\\n") // Replace newline with \n
.replaceAll("\t", "\\t") // Replace tab with \t
}
return result
}
/** @param {String} value */

View File

@@ -24,7 +24,7 @@ export default function nodeColor(entity) {
}
switch (entity.getClass()) {
case Configuration.paths.callFunction:
return entity.bIsPureFunc
return entity.bIsPureFunc?.valueOf()
? Configuration.nodeColors.green
: Configuration.nodeColors.blue
case Configuration.paths.niagaraNodeFunctionCall:
@@ -74,7 +74,7 @@ export default function nodeColor(entity) {
return Configuration.nodeColors.intenseGreen
}
}
if (entity.bIsPureFunc) {
if (entity.bIsPureFunc?.valueOf()) {
return Configuration.nodeColors.green
}
return Configuration.nodeColors.blue

View File

@@ -20,7 +20,7 @@ export default function nodeTemplateClass(nodeEntity) {
|| nodeEntity.getClass() === Configuration.paths.callArrayFunction
) {
const memberParent = nodeEntity.FunctionReference?.MemberParent?.path ?? ""
const memberName = nodeEntity.FunctionReference?.MemberName
const memberName = nodeEntity.FunctionReference?.MemberName?.valueOf()
if (
memberName && (
memberParent === Configuration.paths.kismetMathLibrary
@@ -96,13 +96,15 @@ export default function nodeTemplateClass(nodeEntity) {
}
return MetasoundNodeTemplate
case Configuration.paths.niagaraNodeOp:
if ([
"Boolean::LogicEq",
"Boolean::LogicNEq",
"Numeric::Abs",
"Numeric::Add",
"Numeric::Mul",
].includes(nodeEntity.OpName)) {
if (
[
"Boolean::LogicEq",
"Boolean::LogicNEq",
"Numeric::Abs",
"Numeric::Add",
"Numeric::Mul",
].includes(nodeEntity.OpName?.valueOf())
) {
return VariableOperationNodeTemplate
}
break

View File

@@ -1,5 +1,9 @@
import Configuration from "../Configuration.js"
import Utility from "../Utility.js"
import BooleanEntity from "../entity/BooleanEntity.js"
import LinearColorEntity from "../entity/LinearColorEntity.js"
import MirroredEntity from "../entity/MirroredEntity.js"
import VectorEntity from "../entity/VectorEntity.js"
const sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/
const keyNameValue = {
@@ -56,18 +60,18 @@ export default function nodeTitle(entity) {
switch (entity.getType()) {
case Configuration.paths.asyncAction:
if (entity.ProxyFactoryFunctionName) {
return Utility.formatStringName(entity.ProxyFactoryFunctionName)
return Utility.formatStringName(entity.ProxyFactoryFunctionName?.valueOf())
}
case Configuration.paths.actorBoundEvent:
case Configuration.paths.componentBoundEvent:
return `${Utility.formatStringName(entity.DelegatePropertyName)} (${entity.ComponentPropertyName ?? "Unknown"})`
return `${Utility.formatStringName(entity.DelegatePropertyName?.valueOf())} (${entity.ComponentPropertyName?.valueOf() ?? "Unknown"})`
case Configuration.paths.callDelegate:
return `Call ${entity.DelegateReference?.MemberName ?? "None"}`
return `Call ${entity.DelegateReference?.MemberName?.valueOf() ?? "None"}`
case Configuration.paths.createDelegate:
return "Create Event"
case Configuration.paths.customEvent:
if (entity.CustomFunctionName) {
return entity.CustomFunctionName
return entity.CustomFunctionName?.valueOf()
}
case Configuration.paths.dynamicCast:
if (!entity.TargetType) {
@@ -77,7 +81,7 @@ export default function nodeTitle(entity) {
case Configuration.paths.enumLiteral:
return `Literal enum ${entity.Enum?.getName()}`
case Configuration.paths.event:
return `Event ${(entity.EventReference?.MemberName ?? "").replace(/^Receive/, "")}`
return `Event ${(entity.EventReference?.MemberName?.valueOf() ?? "").replace(/^Receive/, "")}`
case Configuration.paths.executionSequence:
return "Sequence"
case Configuration.paths.forEachElementInEnum:
@@ -85,9 +89,9 @@ export default function nodeTitle(entity) {
case Configuration.paths.forEachLoopWithBreak:
return "For Each Loop with Break"
case Configuration.paths.functionEntry:
return entity.FunctionReference?.MemberName === "UserConstructionScript"
return entity.FunctionReference?.MemberName?.valueOf() === "UserConstructionScript"
? "Construction Script"
: entity.FunctionReference?.MemberName
: entity.FunctionReference?.MemberName?.valueOf()
case Configuration.paths.functionResult:
return "Return Node"
case Configuration.paths.ifThenElse:
@@ -99,35 +103,29 @@ export default function nodeTitle(entity) {
case Configuration.paths.materialExpressionComponentMask: {
const materialObject = entity.getMaterialSubobject()
return `Mask ( ${Configuration.rgba
.filter(k => /** @type {MirroredEntity<any>} */(materialObject[k]).get() === true)
.filter(k => /** @type {MirroredEntity<typeof BooleanEntity>} */(materialObject[k]).getter().value === true)
.map(v => v + " ")
.join("")})`
}
case Configuration.paths.materialExpressionConstant:
input ??= [entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Value")?.DefaultValue]
input ??= [entity.getCustomproperties().find(pinEntity => pinEntity.PinName.valueOf() == "Value")?.DefaultValue]
case Configuration.paths.materialExpressionConstant2Vector:
input ??= [
entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "X")?.DefaultValue,
entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Y")?.DefaultValue,
entity.getCustomproperties().find(pinEntity => pinEntity.PinName?.valueOf() == "X")?.DefaultValue,
entity.getCustomproperties().find(pinEntity => pinEntity.PinName?.valueOf() == "Y")?.DefaultValue,
]
case Configuration.paths.materialExpressionConstant3Vector:
if (!input) {
/** @type {VectorEntity} */
const vector = entity.getCustomproperties()
.find(pinEntity => pinEntity.PinName == "Constant")
?.DefaultValue
input = [vector.X, vector.Y, vector.Z]
}
case Configuration.paths.materialExpressionConstant4Vector:
if (!input) {
/** @type {LinearColorEntity} */
const vector = entity.getCustomproperties()
.find(pinEntity => pinEntity.PinName == "Constant")
.find(pinEntity => pinEntity.PinName?.valueOf() == "Constant")
?.DefaultValue
input = [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf())
input = vector instanceof VectorEntity ? [vector.X, vector.Y, vector.Z].map(v => v.valueOf())
: vector instanceof LinearColorEntity ? [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf())
: /** @type {Number[]} */([])
}
if (input.length > 0) {
return input.map(v => Utility.printExponential(v)).reduce((acc, cur) => acc + "," + cur)
return input.map(v => Utility.printExponential(v)).join(",")
}
break
case Configuration.paths.materialExpressionFunctionInput: {
@@ -165,7 +163,7 @@ export default function nodeTitle(entity) {
return "Output"
case Configuration.paths.spawnActorFromClass:
let className = entity.getCustomproperties()
.find(pinEntity => pinEntity.PinName == "ReturnValue")
.find(pinEntity => pinEntity.PinName.valueOf() == "ReturnValue")
?.PinType
?.PinSubCategoryObject
?.getName()
@@ -232,7 +230,7 @@ export default function nodeTitle(entity) {
return Utility.formatStringName(settingsObject.BlueprintElementType.getName())
}
if (settingsObject.Operation) {
const match = settingsObject.Name.match(/PCGMetadata(\w+)Settings_\d+/)
const match = settingsObject.Name?.valueOf().match(/PCGMetadata(\w+)Settings_\d+/)
if (match) {
return Utility.formatStringName(match[1] + ": " + settingsObject.Operation)
}
@@ -242,9 +240,9 @@ export default function nodeTitle(entity) {
return settingsSubgraphObject.Graph.getName()
}
}
let memberName = entity.FunctionReference?.MemberName
let memberName = entity.FunctionReference?.MemberName?.valueOf()
if (memberName) {
const memberParent = entity.FunctionReference.MemberParent?.path ?? ""
const memberParent = entity.FunctionReference.MemberParent?.path?.valueOf() ?? ""
switch (memberName) {
case "AddKey":
let result = memberParent.match(sequencerScriptingNameRegex)
@@ -379,7 +377,7 @@ export default function nodeTitle(entity) {
return Utility.formatStringName(memberName)
}
if (entity.OpName) {
switch (entity.OpName) {
switch (entity.OpName.valueOf()) {
case "Boolean::LogicAnd": return "Logic AND"
case "Boolean::LogicEq": return "=="
case "Boolean::LogicNEq": return "!="
@@ -392,10 +390,10 @@ export default function nodeTitle(entity) {
case "Numeric::DistancePos": return "Distance"
case "Numeric::Mul": return String.fromCharCode(0x2a2f)
}
return Utility.formatStringName(entity.OpName).replaceAll("::", " ")
return Utility.formatStringName(entity.OpName.valueOf()).replaceAll("::", " ")
}
if (entity.FunctionDisplayName) {
return Utility.formatStringName(entity.FunctionDisplayName)
return Utility.formatStringName(entity.FunctionDisplayName.valueOf())
}
if (entity.ObjectRef) {
return entity.ObjectRef.getName()

View File

@@ -1,10 +1,13 @@
import Configuration from "../Configuration.js"
import ArrayEntity from "../entity/ArrayEntity.js"
import GuidEntity from "../entity/GuidEntity.js"
import NaturalNumberEntity from "../entity/NaturalNumberEntity.js"
import PinEntity from "../entity/PinEntity.js"
import StringEntity from "../entity/StringEntity.js"
/** @param {PinEntity} pinEntity */
const indexFromUpperCaseLetterName = pinEntity =>
pinEntity.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0)
pinEntity.PinName?.valueOf().match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0)
/** @param {ObjectEntity} entity */
export default function nodeVariadic(entity) {
@@ -19,7 +22,7 @@ export default function nodeVariadic(entity) {
switch (type) {
case Configuration.paths.commutativeAssociativeBinaryOperator:
case Configuration.paths.promotableOperator:
name = entity.FunctionReference?.MemberName
name = entity.FunctionReference?.MemberName?.valueOf()
switch (name) {
default:
if (
@@ -50,7 +53,7 @@ export default function nodeVariadic(entity) {
pinIndexFromEntity ??= indexFromUpperCaseLetterName
pinNameFromIndex ??= (index, min = -1, max = -1) => {
const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1)
entity.NumAdditionalInputs = pinEntities().length - 1
entity.NumAdditionalInputs = new NaturalNumberEntity(pinEntities().length - 1)
return result
}
break
@@ -58,7 +61,7 @@ export default function nodeVariadic(entity) {
break
case Configuration.paths.multiGate:
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1])
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName?.valueOf().match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1])
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) =>
`Out ${index >= 0 ? index : min > 0 ? "Out 0" : max + 1}`
break
@@ -74,26 +77,26 @@ export default function nodeVariadic(entity) {
// break
case Configuration.paths.switchInteger:
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*(\d+)\s*$/)?.[1])
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName?.valueOf().match(/^\s*(\d+)\s*$/)?.[1])
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => (index < 0 ? max + 1 : index).toString()
break
case Configuration.paths.switchGameplayTag:
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => {
const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`
entity.PinNames ??= []
entity.PinNames.push(result)
delete entity.PinTags[entity.PinTags.length - 1]
entity.PinTags[entity.PinTags.length] = null
entity.PinNames ??= new ArrayEntity()
entity.PinNames.valueOf().push(new StringEntity(result))
delete entity.PinTags.valueOf()[entity.PinTags.length - 1]
entity.PinTags.valueOf()[entity.PinTags.length] = null
return result
}
case Configuration.paths.switchName:
case Configuration.paths.switchString:
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1])
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.valueOf().match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1])
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => {
const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`
entity.PinNames ??= []
entity.PinNames.push(result)
entity.PinNames ??= new ArrayEntity()
entity.PinNames.valueOf().push(new StringEntity(result))
return result
}
break
@@ -138,8 +141,8 @@ export default function nodeVariadic(entity) {
}
)
const newPin = new PinEntity(modelPin)
newPin.PinId = GuidEntity.generateGuid()
newPin.PinName = pinNameFromIndex(index, min, max, newPin)
newPin.PinId = new GuidEntity()
newPin.PinName = new StringEntity(pinNameFromIndex(index, min, max, newPin))
newPin.PinToolTip = undefined
entity.getCustomproperties(true).push(newPin)
return newPin

View File

@@ -54,15 +54,15 @@ const pinColorMaterial = css`120, 120, 120`
/** @param {PinEntity} entity */
export default function pinColor(entity) {
if (entity.PinType.PinCategory == "mask") {
if (entity.PinType.PinCategory?.valueOf() == "mask") {
const result = colors[entity.PinType.PinSubCategory]
if (result) {
return result
}
} else if (entity.PinType.PinCategory == "optional") {
} else if (entity.PinType.PinCategory?.valueOf() == "optional") {
return pinColorMaterial
}
return colors[entity.getType()]
?? colors[entity.PinType.PinCategory.toLowerCase()]
?? colors[entity.PinType.PinCategory?.valueOf().toLowerCase()]
?? colors["default"]
}

View File

@@ -38,10 +38,10 @@ const inputPinTemplates = {
/** @param {PinEntity} entity */
export default function pinTemplate(entity) {
if (entity.PinType.ContainerType?.toString() === "Array") {
if (entity.PinType.ContainerType?.valueOf() === "Array") {
return PinTemplate
}
if (entity.PinType.bIsReference && !entity.PinType.bIsConst) {
if (entity.PinType.bIsReference?.valueOf() && !entity.PinType.bIsConst?.valueOf()) {
return inputPinTemplates["MUTABLE_REFERENCE"]
}
if (entity.getType() === "exec") {

View File

@@ -4,12 +4,12 @@ import Utility from "../Utility.js"
export default function pinTitle(entity) {
let result = entity.PinFriendlyName
? entity.PinFriendlyName.toString()
: Utility.formatStringName(entity.PinName ?? "")
: Utility.formatStringName(entity.PinName?.valueOf() ?? "")
let match
if (
entity.PinToolTip
// Match up until the first \n excluded or last character
&& (match = entity.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/))
&& (match = entity.PinToolTip?.valueOf().match(/\s*(.+?(?=\n)|.+\S)\s*/))
) {
if (match[1].toLowerCase() === result.toLowerCase()) {
return match[1] // In case they match, then keep the case of the PinToolTip

View File

@@ -2,11 +2,10 @@ import Configuration from "../Configuration.js"
import Utility from "../Utility.js"
import nodeTemplateClass from "../decoding/nodeTemplate.js"
import nodeTitle from "../decoding/nodeTitle.js"
import IdentifierEntity from "../entity/IdentifierEntity.js"
import ObjectEntity from "../entity/ObjectEntity.js"
import PinEntity from "../entity/PinEntity.js"
import PinReferenceEntity from "../entity/PinReferenceEntity.js"
import SerializerFactory from "../serialization/SerializerFactory.js"
import SymbolEntity from "../entity/SymbolEntity.js"
import NodeTemplate from "../template/node/NodeTemplate.js"
import ISelectableDraggableElement from "./ISelectableDraggableElement.js"
@@ -28,7 +27,7 @@ export default class NodeElement extends ISelectableDraggableElement {
advancedPinDisplay: {
type: String,
attribute: "data-advanced-display",
converter: IdentifierEntity.attributeConverter,
converter: SymbolEntity.attributeConverter,
reflect: true,
},
enabledState: {
@@ -86,7 +85,7 @@ export default class NodeElement extends ISelectableDraggableElement {
/** @param {String} str */
static fromSerializedObject(str) {
str = str.trim()
let entity = SerializerFactory.getSerializer(ObjectEntity).read(str)
let entity = ObjectEntity.grammar.parse(str)
return NodeElement.newObject(/** @type {ObjectEntity} */(entity))
}
@@ -103,10 +102,13 @@ export default class NodeElement extends ISelectableDraggableElement {
#redirectLinksAfterRename(name) {
for (let sourcePinElement of this.getPinElements()) {
for (let targetPinReference of sourcePinElement.getLinks()) {
this.blueprint.getPin(targetPinReference).redirectLink(sourcePinElement, new PinReferenceEntity({
objectName: name,
pinGuid: sourcePinElement.entity.PinId,
}))
this.blueprint.getPin(targetPinReference).redirectLink(
sourcePinElement,
new PinReferenceEntity(
name,
sourcePinElement.entity.PinId,
)
)
}
}
}
@@ -117,7 +119,7 @@ export default class NodeElement extends ISelectableDraggableElement {
this.advancedPinDisplay = entity.AdvancedPinDisplay?.toString()
this.enabledState = entity.EnabledState
this.nodeDisplayName = nodeTitle(entity)
this.pureFunction = entity.bIsPureFunc
this.pureFunction = entity.bIsPureFunc?.valueOf()
this.dragLinkObjects = []
super.initialize(entity, template)
this.#pins = this.template.createPinElements()
@@ -223,7 +225,7 @@ export default class NodeElement extends ISelectableDraggableElement {
}
setShowAdvancedPinDisplay(value) {
this.entity.AdvancedPinDisplay = new IdentifierEntity(value ? "Shown" : "Hidden")
this.entity.AdvancedPinDisplay = new SymbolEntity(value ? "Shown" : "Hidden")
this.advancedPinDisplay = this.entity.AdvancedPinDisplay
}

View File

@@ -1,9 +1,11 @@
import Utility from "../Utility.js"
import pinTemplate from "../decoding/pinTemplate.js"
import ArrayEntity from "../entity/ArrayEntity.js"
import GuidEntity from "../entity/GuidEntity.js"
import LinearColorEntity from "../entity/LinearColorEntity.js"
import PinEntity from "../entity/PinEntity.js"
import PinReferenceEntity from "../entity/PinReferenceEntity.js"
import StringEntity from "../entity/StringEntity.js"
import PinTemplate from "../template/pin/PinTemplate.js"
import ElementFactory from "./ElementFactory.js"
import IElement from "./IElement.js"
@@ -89,9 +91,9 @@ export default class PinElement extends IElement {
nodeElement = undefined
) {
this.nodeElement = nodeElement
this.advancedView = entity.bAdvancedView
this.advancedView = entity.bAdvancedView?.valueOf()
this.isLinked = false
this.connectable = !entity.bNotConnectable
this.connectable = !entity.bNotConnectable?.valueOf()
super.initialize(entity, template)
this.pinType = this.entity.getType()
this.defaultValue = this.entity.getDefaultValue()
@@ -106,7 +108,7 @@ export default class PinElement extends IElement {
createPinReference() {
return new PinReferenceEntity({
objectName: this.nodeElement.getNodeName(),
objectName: new StringEntity(this.nodeElement.getNodeName()),
pinGuid: this.getPinId(),
})
}
@@ -118,7 +120,7 @@ export default class PinElement extends IElement {
/** @returns {String} */
getPinName() {
return this.entity.PinName
return this.entity.PinName?.valueOf() ?? ""
}
getPinDisplayName() {
@@ -147,7 +149,7 @@ export default class PinElement extends IElement {
}
getLinks() {
return this.entity.LinkedTo ?? []
return this.entity.LinkedTo?.valueOf() ?? []
}
getDefaultValue(maybeCreate = false) {
@@ -165,21 +167,23 @@ export default class PinElement extends IElement {
/** @param {IElement[]} nodesWhitelist */
sanitizeLinks(nodesWhitelist = []) {
this.entity.LinkedTo = this.entity.LinkedTo?.filter(pinReference => {
let pin = this.blueprint.getPin(pinReference)
if (pin) {
if (nodesWhitelist.length && !nodesWhitelist.includes(pin.nodeElement)) {
return false
this.entity.LinkedTo = new ArrayEntity(
this.entity.LinkedTo?.valueOf().filter(pinReference => {
let pin = this.blueprint.getPin(pinReference)
if (pin) {
if (nodesWhitelist.length && !nodesWhitelist.includes(pin.nodeElement)) {
return false
}
let link = this.blueprint.getLink(this, pin)
if (!link) {
link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link"))
.newObject(this, pin)
this.blueprint.addGraphElement(link)
}
}
let link = this.blueprint.getLink(this, pin)
if (!link) {
link = /** @type {LinkElementConstructor} */(ElementFactory.getConstructor("ueb-link"))
.newObject(this, pin)
this.blueprint.addGraphElement(link)
}
}
return pin
})
return pin
})
)
this.isLinked = this.entity.isLinked()
}

View File

@@ -0,0 +1,37 @@
import P from "parsernostrum"
import IEntity from "./IEntity.js"
export default class AlternativesEntity extends IEntity {
/** @type {(typeof IEntity)[]} */
static alternatives = []
static className() {
let result = super.className()
if (this.alternatives.length) {
result += " (accepting: " + this.alternatives.map(v => v.className()).join(", ") + ")"
}
return result
}
static createGrammar() {
const grammars = this.alternatives.map(entity => entity.grammar)
if (grammars.includes(this.unknownEntityGrammar)) {
return this.unknownEntityGrammar
}
return P.alt(...grammars)
}
/**
* @template {(typeof IEntity)[]} Types
* @param {Types} types
*/
static accepting(...types) {
const result = /** @type {typeof AlternativesEntity & { alternatives: Types }} */(
this.asUniqueClass()
)
result.alternatives = types
result.grammar = result.createGrammar()
return result
}
}

86
js/entity/ArrayEntity.js Normal file
View File

@@ -0,0 +1,86 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import IEntity from "./IEntity.js"
/** @template {IEntity} T */
export default class ArrayEntity extends IEntity {
/** @type {typeof IEntity} */
static type
static grammar = /** @type {P<ArrayEntity<IEntity>>} */(
this.createGrammar()
)
get length() {
return this.values.length
}
/** @param {T[]} values */
constructor(values = []) {
super()
this.values = values
}
/** @returns {P<ArrayEntity<IEntity>>} */
static createGrammar(elementGrammar = this.type?.grammar ?? P.lazy(() => this.unknownEntityGrammar)) {
return this.inlined
? elementGrammar
: P.seq(
P.reg(/\(\s*/),
elementGrammar.sepBy(Grammar.commaSeparation).opt(),
P.reg(/\s*(,\s*)?\)/, 1),
).map(([_0, values, trailing]) => {
values = values instanceof Array ? values : []
const result = new this(values)
result.trailing = trailing !== undefined
return result
}).label(`ArrayEntity of ${this.type?.className() ?? "unknown values"}`)
}
/**
* @template {typeof IEntity} T
* @param {T} type
*/
static of(type) {
const result = /** @type {typeof ArrayEntity<ExtractType<T>> & {type: T, grammar: P<ArrayEntity<ExtractType<T>>> }} */(
this.asUniqueClass()
)
result.type = type
result.grammar = /** @type {P<ArrayEntity>} */(result.createGrammar())
return result
}
/** @param {IEntity} other */
equals(other) {
if (!(other instanceof ArrayEntity) || this.values.length !== other.values.length) {
return false
}
for (let i = 0; i < this.values.length; ++i) {
if (!this.values[i].equals(other.values[i])) {
return false
}
}
return true
}
valueOf() {
return this.values
}
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
if (Self.inlined) {
return super.toString.bind(this.values, insideString, indentation, Self, printKey, wrap)()
}
let result = this.values.map(v => v?.toString(insideString)).join(Self.attributeSeparator)
if (this.trailing) {
result += Self.attributeSeparator
}
return `(${result})`
}
}

View File

@@ -1,117 +0,0 @@
/**
* @template T
* @typedef {{
* type?: AttributeTypeDescription,
* default?: T,
* nullable?: Boolean,
* ignored?: Boolean,
* serialized?: Boolean,
* expected?: Boolean,
* inlined?: Boolean,
* quoted?: Boolean,
* silent?: Boolean,
* uninitialized?: Boolean,
* predicate?: (value: T) => Boolean,
* }} AttributeInfoSource
*/
/** @template T */
export default class AttributeInfo {
/** @typedef {keyof AttributeInfo<number>} AttributeKey */
static #default = {
nullable: false,
ignored: false, // Never serialize or deserialize
serialized: false, // Value is written and read as string
expected: false, // Must be there
inlined: false, // The key is a subobject or array and printed as inlined (A.B=123, A(0)=123)
quoted: false, // Key is serialized with quotes
silent: false, // Do not serialize if default
uninitialized: false, // Do not initialize with default
}
/** @param {AttributeInfoSource<T>} source */
constructor(source) {
this.type = source.type ?? source.default?.constructor
this.default = source.default
this.nullable = source.nullable ?? source.default === null
this.ignored = source.ignored
this.serialized = source.serialized
this.expected = source.expected
this.inlined = source.inlined
this.quoted = source.quoted
this.silent = source.silent
this.uninitialized = source.uninitialized
this.predicate = source.predicate
if (this.type === Array && this.default instanceof Array && this.default.length > 0) {
this.type = this.default
.map(v => v.constructor)
.reduce((acc, cur) => acc.includes(cur) ? acc : (acc.push(cur), acc), [])
}
}
/**
* @template {AttributeTypeDescription} D
* @param {D} type
* @returns {AttributeInfo<DescribedType<type>>}
*/
static createType(type) {
return new AttributeInfo({ type })
}
/**
* @template V
* @param {V} value
*/
static createValue(value) {
return new AttributeInfo({ default: value })
}
/**
* @param {IEntity | Object} source
* @param {String} attribute
* @param {AttributeKey} key
*/
static hasAttribute(source, attribute, key, type = /** @type {EntityConstructor} */(source.constructor)) {
const entity = /** @type {IEntity} */(source)
const result = entity.attributes[attribute]?.[key]
return /** @type {result} */(
result
?? type?.attributes?.[attribute]?.[key]
?? AttributeInfo.#default[key]
)
}
/**
* @template {IEntity | Object} S
* @template {EntityConstructor} C
* @template {keyof C["attributes"]} A
* @template {keyof C["attributes"][attribute]} K
* @param {S} source
* @param {A} attribute
* @param {K} key
* @param {C} type
* @returns {C["attributes"][attribute][key]}
*/
static getAttribute(source, attribute, key, type = /** @type {C} */(source.constructor)) {
let result = source["attributes"]?.[attribute]?.[key]
// Remember null is a valid asignment value for some attributes
if (result !== undefined) {
return result
}
result = /** @type {C["attributes"]} */(type?.attributes)?.[attribute]?.[key]
if (result !== undefined) {
return result
}
result = /** @type {C["attributes"][attribute]} */(AttributeInfo.#default)[key]
if (result !== undefined) {
return result
}
}
/** @param {AttributeKey} key */
get(key) {
return this[key] ?? AttributeInfo.#default[key]
}
}

View File

@@ -63,8 +63,8 @@ export default class BlueprintEntity extends ObjectEntity {
this.ScriptVariables = entity.ScriptVariables
}
let scriptVariables = Utility.mergeArrays(
this.ScriptVariables,
entity.ScriptVariables,
this.ScriptVariables.valueOf(),
entity.ScriptVariables.valueOf(),
(l, r) => l.OriginalChangeId.value == r.OriginalChangeId.value
)
if (scriptVariables.length === this.ScriptVariables.length) {

42
js/entity/BooleanEntity.js Executable file
View File

@@ -0,0 +1,42 @@
import P from "parsernostrum"
import IEntity from "./IEntity.js"
export default class BooleanEntity extends IEntity {
static grammar = /** @type {P<BooleanEntity>} */(
P.regArray(/(true)|(True)|(false)|(False)/)
.map(v => {
const result = (v[1] ?? v[2]) ? new this(true) : new this(false)
result.uppercase = (v[2] ?? v[4]) !== undefined
return result
})
.label("BooleanEntity")
)
#uppercase = true
get uppercase() {
return this.#uppercase
}
set uppercase(value) {
this.#uppercase = value
}
constructor(value = false) {
super()
this.value = value
}
valueOf() {
return this.value
}
toString() {
return this.value
? this.#uppercase
? "True"
: "true"
: this.#uppercase
? "False"
: "false"
}
}

View File

@@ -1,23 +1,19 @@
import Parsernostrum from "parsernostrum"
import AttributeInfo from "./AttributeInfo.js"
import P from "parsernostrum"
import IntegerEntity from "./IntegerEntity.js"
export default class ByteEntity extends IntegerEntity {
static attributes = {
...super.attributes,
value: new AttributeInfo({
...super.attributes.value,
predicate: v => v % 1 == 0 && v >= 0 && v < 1 << 8,
}),
}
static grammar = this.createGrammar()
static grammar = /** @type {P<ByteEntity>} */(
P.numberByte.map(v => new this(v))
)
static createGrammar() {
return Parsernostrum.numberByte.map(v => new this(v))
get value() {
return super.value
}
constructor(values = 0) {
super(values)
set value(value) {
value = Math.trunc(value)
if (value >= 0 && value < 1 << 8) {
super.value = value
}
}
}

View File

@@ -1,28 +1,15 @@
import Parsernostrum from "parsernostrum"
import AttributeInfo from "./AttributeInfo.js"
import P from "parsernostrum"
import IEntity from "./IEntity.js"
export default class ColorChannelEntity extends IEntity {
static attributes = {
...super.attributes,
value: AttributeInfo.createValue(0),
}
static grammar = this.createGrammar()
static grammar = /** @type {P<ColorChannelEntity>} */(
P.number.map(v => new this(v))
)
static createGrammar() {
return Parsernostrum.number.map(value => new this(value))
}
constructor(values = 0) {
if (values.constructor !== Object) {
// @ts-expect-error
values = {
value: values,
}
}
super(values)
/** @type {Number} */ this.value
constructor(value = 0) {
super()
this.value = value
}
valueOf() {

View File

@@ -1,14 +0,0 @@
export default class ComputedType {
#f
/** @param {Function} f */
constructor(f) {
this.#f = f
}
/** @param {IEntity} entity */
compute(entity) {
return this.#f(entity)
}
}

View File

@@ -0,0 +1,23 @@
import IEntity from "./IEntity.js"
export default class ComputedTypeEntity extends IEntity {
static grammar = undefined
/** @type {(entity: IEntity) => typeof IEntity} */
static f
/**
* @template {typeof ComputedTypeEntity.f} T
* @param {T} producer
*/
static from(producer) {
const result = /** @type {(typeof ComputedTypeEntity) & { f: T }} */(this.asUniqueClass())
result.f = producer
return result
}
/** @param {IEntity} entity */
compute(entity) {
return /** @type {typeof ComputedTypeEntity} */(this.Self()).f(entity)
}
}

View File

@@ -1,12 +1,10 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import EnumEntity from "./EnumEntity.js"
export default class EnumDisplayValueEntity extends EnumEntity {
static grammar = this.createGrammar()
static createGrammar() {
return Parsernostrum.reg(Grammar.Regex.InsideString).map(v => new this(v))
}
static grammar = /** @type {P<EnumDisplayValueEntity>} */(
P.reg(Grammar.Regex.InsideString).map(v => new this(v))
)
}

View File

@@ -1,11 +1,10 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import SymbolEntity from "./SymbolEntity.js"
export default class EnumEntity extends SymbolEntity {
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.symbol.map(v => new this(v))
}
static grammar = /** @type {P<EnumEntity>} */(
Grammar.symbol.map(v => new this(v))
)
}

View File

@@ -1,56 +1,43 @@
import Parsernostrum from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
import P from "parsernostrum"
import InvariantTextEntity from "./InvariantTextEntity.js"
import LocalizedTextEntity from "./LocalizedTextEntity.js"
import Union from "./Union.js"
import StringEntity from "./StringEntity.js"
import IEntity from "./IEntity.js"
export default class FormatTextEntity extends IEntity {
static attributes = {
...super.attributes,
value: new AttributeInfo({
type: [new Union(String, LocalizedTextEntity, InvariantTextEntity, FormatTextEntity)],
default: [],
}),
lookbehind: /** @type {AttributeInfo<Union<String[]>>} */(new AttributeInfo({
...super.attributes.lookbehind,
default: new Union("LOCGEN_FORMAT_NAMED", "LOCGEN_FORMAT_ORDERED"),
})),
}
static grammar = this.createGrammar()
static createGrammar() {
return Parsernostrum.seq(
Parsernostrum.reg(
// Resulting regex: /(LOCGEN_FORMAT_NAMED|LOCGEN_FORMAT_ORDERED)\s*/
new RegExp(`(${this.attributes.lookbehind.default.values.reduce((acc, cur) => acc + "|" + cur)})\\s*`),
1
),
Grammar.grammarFor(this.attributes.value)
static attributeSeparator = ", "
static lookbehind = ["LOCGEN_FORMAT_NAMED", "LOCGEN_FORMAT_ORDERED"]
static grammar = /** @type {P<FormatTextEntity>} */(
P.lazy(() => P.seq(
// Resulting regex: /(LOCGEN_FORMAT_NAMED|LOCGEN_FORMAT_ORDERED)\s*/
P.reg(new RegExp(String.raw`(${this.lookbehind.join("|")})\s*\(\s*`), 1),
P.alt(
...[StringEntity, LocalizedTextEntity, InvariantTextEntity, FormatTextEntity].map(type => type.grammar)
).sepBy(P.reg(/\s*\,\s*/)),
P.reg(/\s*\)/)
)
.map(([lookbehind, values]) => {
const result = new this({
value: values,
lookbehind,
})
const result = new this(values)
result.lookbehind = lookbehind
return result
})
}
}))
.label("FormatTextEntity")
)
/** @param {(StringEntity | LocalizedTextEntity | InvariantTextEntity | FormatTextEntity)[]} values */
constructor(values) {
super(values)
/** @type {(String | LocalizedTextEntity | InvariantTextEntity | FormatTextEntity)[]} */ this.value
super()
this.values = values
}
toString() {
const pattern = this.value?.[0]?.toString() // The pattern is always the first element of the array
valueOf() {
const pattern = this.values?.[0]?.valueOf() // The pattern is always the first element of the array
if (!pattern) {
return ""
}
const values = this.value.slice(1).map(v => v.toString())
return this.lookbehind == "LOCGEN_FORMAT_NAMED"
const values = this.values.slice(1).map(v => v?.valueOf())
let result = this.lookbehind == "LOCGEN_FORMAT_NAMED"
? pattern.replaceAll(/\{([a-zA-Z]\w*)\}/g, (substring, arg) => {
const argLocation = values.indexOf(arg) + 1
return argLocation > 0 && argLocation < values.length
@@ -65,5 +52,20 @@ export default class FormatTextEntity extends IEntity {
: substring
})
: ""
return result
}
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
const separator = Self.attributeSeparator
return this.lookbehind + "("
+ this.values.map(v => v.toString(insideString)).join(separator)
+ (Self.trailing ? separator : "")
+ ")"
}
}

View File

@@ -1,27 +1,26 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import GuidEntity from "./GuidEntity.js"
import IEntity from "./IEntity.js"
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
import StringEntity from "./StringEntity.js"
export default class FunctionReferenceEntity extends IEntity {
static attributes = {
...super.attributes,
MemberParent: AttributeInfo.createType(ObjectReferenceEntity),
MemberName: AttributeInfo.createType(String),
MemberGuid: AttributeInfo.createType(GuidEntity),
}
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.createEntityGrammar(this)
MemberParent: ObjectReferenceEntity,
MemberName: StringEntity,
MemberGuid: GuidEntity,
}
static grammar = /** @type {P<FunctionReferenceEntity>} */(
Grammar.createEntityGrammar(this, Grammar.commaSeparation, false, 0)
)
constructor(values) {
super(values)
/** @type {ObjectReferenceEntity} */ this.MemberParent
/** @type {String} */ this.MemberName
/** @type {GuidEntity} */ this.MemberGuid
/** @type {InstanceType<typeof FunctionReferenceEntity.attributes.MemberParent>} */ this.MemberParent
/** @type {InstanceType<typeof FunctionReferenceEntity.attributes.MemberName>} */ this.MemberName
/** @type {InstanceType<typeof FunctionReferenceEntity.attributes.MemberGuid>} */ this.MemberGuid
}
}

View File

@@ -1,6 +1,5 @@
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
import P from "parsernostrum"
var crypto
if (typeof window === "undefined") {
@@ -11,39 +10,23 @@ if (typeof window === "undefined") {
export default class GuidEntity extends IEntity {
static attributes = {
...super.attributes,
value: AttributeInfo.createValue(""),
}
static grammar = this.createGrammar()
static grammar = /** @type {P<GuidEntity>} */(
P.reg(/[0-9A-F]{32}/i).map(v => new this(v)).label("GuidEntity")
)
static createGrammar() {
return Grammar.guid.map(v => new this(v))
}
static generateGuid(random = true) {
static generateGuid() {
let values = new Uint32Array(4)
if (random === true) {
crypto.getRandomValues(values)
}
crypto.getRandomValues(values)
let guid = ""
values.forEach(n => {
guid += ("0".repeat(8) + n.toString(16).toUpperCase()).slice(-8)
})
return new GuidEntity({ value: guid })
return guid
}
constructor(values) {
if (!values) {
values = GuidEntity.generateGuid().value
}
if (values.constructor !== Object) {
values = {
value: values,
}
}
super(values)
/** @type {String} */ this.value
constructor(value = GuidEntity.generateGuid()) {
super()
this.value = value
}
valueOf() {

View File

@@ -1,169 +1,204 @@
import Configuration from "../Configuration.js"
import P from "parsernostrum"
import Utility from "../Utility.js"
import Serializable from "../serialization/Serializable.js"
import SerializerFactory from "../serialization/SerializerFactory.js"
import AttributeInfo from "./AttributeInfo.js"
import ComputedType from "./ComputedType.js"
import MirroredEntity from "./MirroredEntity.js"
import Union from "./Union.js"
/** @abstract */
export default class IEntity extends Serializable {
export default class IEntity {
/** @type {{ [attribute: String]: AttributeInfo }} */
static attributes = {
attributes: new AttributeInfo({
ignored: true,
}),
lookbehind: new AttributeInfo({
default: /** @type {String | Union<String[]>} */(""),
ignored: true,
uninitialized: true,
}),
/** @type {(v: String) => String} */
static same = v => v
/** @type {(entity: IEntity, serialized: String) => String} */
static notWrapped = (entity, serialized) => serialized
/** @type {(entity: IEntity, serialized: String) => String} */
static defaultWrapped = (entity, serialized) => `${entity.#lookbehind}(${serialized})`
static wrap = this.defaultWrapped
static attributeSeparator = ","
static keySeparator = "="
/** @type {(k: String) => String} */
static printKey = k => k
/** @type {P<IEntity>} */
static grammar = P.lazy(() => this.unknownEntity)
/** @type {P<IEntity>} */
static unknownEntityGrammar
static unknownEntity
/** @type {{ [key: String]: typeof IEntity }} */
static attributes = {}
/** @type {String | String[]} */
static lookbehind = ""
/** @type {(type: typeof IEntity) => InstanceType<typeof IEntity>} */
static default
static nullable = false
static ignored = false // Never serialize or deserialize
static serialized = false // Value is written and read as string
static expected = false // Must be there
static inlined = false // The key is a subobject or array and printed as inlined (A.B=123, A(0)=123)
static quoted = false // Key is serialized with quotes
static silent = false // Do not serialize if default
static trailing = false // Add attribute separator after the last attribute when serializing
#trailing = this.Self().trailing
get trailing() {
return this.#trailing
}
set trailing(value) {
this.#trailing = value
}
#lookbehind = /** @type {String} */(this.Self().lookbehind)
get lookbehind() {
return this.#lookbehind.trim()
}
set lookbehind(value) {
this.#lookbehind = value
}
/** @type {String[]} */
#_keys
get _keys() {
return this.#_keys
#keys
get keys() {
return this.#keys ?? Object.keys(this)
}
set _keys(keys) {
this.#_keys = keys
set keys(value) {
this.#keys = [... new Set(value)]
}
constructor(values = {}, suppressWarns = false) {
super()
const Self = /** @type {typeof IEntity} */(this.constructor)
/** @type {AttributeDeclarations?} */ this.attributes
/** @type {String} */ this.lookbehind
const valuesKeys = Object.keys(values)
const attributesKeys = values.attributes
? Utility.mergeArrays(Object.keys(values.attributes), Object.keys(Self.attributes))
: Object.keys(Self.attributes)
const allAttributesKeys = Utility.mergeArrays(valuesKeys, attributesKeys)
for (const key of allAttributesKeys) {
let value = values[key]
if (!suppressWarns && !(key in values)) {
if (!(key in Self.attributes) && !key.startsWith(Configuration.subObjectAttributeNamePrefix)) {
const typeName = value instanceof Array ? `[${value[0]?.constructor.name}]` : value.constructor.name
console.warn(
`UEBlueprint: Attribute ${key} (of type ${typeName}) in the serialized data is not defined in ${Self.name}.attributes`
)
constructor(values = {}) {
const attributes = this.Self().attributes
const keys = Utility.mergeArrays(
Object.keys(values),
Object.entries(attributes).filter(([k, v]) => v.default !== undefined).map(([k, v]) => k)
)
for (const key of keys) {
if (values[key] !== undefined) {
if (values[key].constructor === Object) {
// It is part of a nested key (words separated by ".")
values[key] = new (
attributes[key] !== undefined ? attributes[key] : IEntity.unknownEntity
)(values[key])
}
}
if (!(key in Self.attributes)) {
// Remember attributeName can come from the values and be not defined in the attributes.
// In that case just assign it and skip the rest.
this[key] = value
this[key] = values[key]
continue
}
Self.attributes.lookbehind
const predicate = AttributeInfo.getAttribute(values, key, "predicate", Self)
const assignAttribute = !predicate
? v => this[key] = v
: v => {
Object.defineProperties(this, {
["#" + key]: {
writable: true,
enumerable: false,
},
[key]: {
enumerable: true,
get() {
return this["#" + key]
},
set(v) {
if (!predicate(v)) {
console.warn(
`UEBlueprint: Tried to assign attribute ${key} to ${Self.name} not satisfying the predicate`
)
return
}
this["#" + key] = v
}
},
})
this[key] = v
}
let defaultValue = AttributeInfo.getAttribute(values, key, "default", Self)
if (defaultValue instanceof Function) {
defaultValue = defaultValue(this)
}
let defaultType = AttributeInfo.getAttribute(values, key, "type", Self)
if (defaultType instanceof ComputedType) {
defaultType = defaultType.compute(this)
}
if (defaultType instanceof Array) {
defaultType = Array
}
if (defaultType === undefined) {
defaultType = Utility.getType(defaultValue)
}
if (value !== undefined) {
// Remember value can still be null
if (
value?.constructor === String
&& AttributeInfo.getAttribute(values, key, "serialized", Self)
&& defaultType !== String
) {
try {
value = SerializerFactory
.getSerializer(defaultType)
.read(/** @type {String} */(value))
} catch (e) {
assignAttribute(value)
continue
}
}
assignAttribute(Utility.sanitize(value, /** @type {AttributeConstructor<Attribute>} */(defaultType)))
continue // We have a value, need nothing more
}
if (defaultValue !== undefined && !AttributeInfo.getAttribute(values, key, "uninitialized", Self)) {
assignAttribute(defaultValue)
const attribute = attributes[key]
if (attribute.default !== undefined) {
this[key] = attribute.default(attribute)
continue
}
}
}
/** @param {AttributeTypeDescription} attributeType */
static defaultValueProviderFromType(attributeType) {
if (attributeType === Boolean) {
return false
} else if (attributeType === Number) {
return 0
} else if (attributeType === BigInt) {
return 0n
} else if (attributeType === String) {
return ""
} else if (attributeType === Array || attributeType instanceof Array) {
return () => []
} else if (attributeType instanceof Union) {
return this.defaultValueProviderFromType(attributeType.values[0])
} else if (attributeType instanceof MirroredEntity) {
return () => new MirroredEntity(attributeType.type, attributeType.getter)
} else if (attributeType instanceof ComputedType) {
return undefined
} else {
return () => new /** @type {AnyConstructor<Attribute>} */(attributeType)()
static className() {
let self = this
while (!self.name) {
self = Object.getPrototypeOf(self)
}
return self.name
}
/**
* @template {new (...args: any) => any} C
* @param {C} type
* @returns {value is InstanceType<C>}
* @protected
* @template {typeof IEntity} T
* @this {T}
* @returns {T}
*/
static isValueOfType(value, type) {
return value != null && (value instanceof type || value.constructor === type)
static asUniqueClass() {
if (this.name.length) {
// @ts-expect-error
return class extends this { }
}
return this
}
static defineAttributes(object, attributes) {
Object.defineProperty(object, "attributes", {
writable: true,
configurable: false,
})
object.attributes = attributes
/**
* @template {typeof IEntity} T
* @this {T}
* @param {String} value
*/
static withLookbehind(value) {
const result = this.asUniqueClass()
result.lookbehind = value
return result
}
/**
* @template {typeof IEntity} T
* @this {T}
*/
static withDefault(value = /** @type {(type: T) => (InstanceType<T> | NullEntity)} */(type => new type())) {
const result = this.asUniqueClass()
result.default = value
return result
}
/**
* @template {typeof IEntity} T
* @this {T}
*/
static flagNullable(value = true) {
const result = this.asUniqueClass()
result.nullable = value
return result
}
/**
* @template {typeof IEntity} T
* @this {T}
*/
static flagSerialized(value = true) {
const result = this.asUniqueClass()
result.serialized = value
return result
}
/**
* @template {typeof IEntity} T
* @this {T}
*/
static flagInlined(value = true) {
const result = this.asUniqueClass()
result.inlined = value
return result
}
/**
* @template {typeof IEntity} T
* @this {T}
*/
static flagQuoted(value = true) {
const result = this.asUniqueClass()
result.quoted = value
return result
}
/**
* @template {typeof IEntity} T
* @this {T}
*/
static flagSilent(value = true) {
const result = this.asUniqueClass()
result.silent = value
return result
}
/**
* @template {typeof IEntity} T
* @this {InstanceType<T>}
*/
Self() {
return /** @type {T} */(this.constructor)
}
/** @param {String} key */
showProperty(key) {
/** @type {IEntity} */
let value = this[key]
const Self = this.Self()
if (Self.silent && Self.default !== undefined) {
if (Self["#default"] === undefined) {
Self["#default"] = Self.default(Self)
}
const defaultValue = Self["#default"]
return !value.equals(defaultValue)
}
return true
}
/**
@@ -193,41 +228,96 @@ export default class IEntity extends Serializable {
return this["#" + attribute]
},
set(v) {
if (v == this["#" + attribute]) {
return
if (v != this["#" + attribute]) {
callback(v)
this["#" + attribute] = v
}
callback(v)
this["#" + attribute] = v
}
},
},
})
}
}
getLookbehind() {
let lookbehind = this.lookbehind ?? AttributeInfo.getAttribute(this, "lookbehind", "default")
lookbehind = lookbehind instanceof Union ? lookbehind.values[0] : lookbehind
return lookbehind
}
unexpectedKeys() {
return Object.keys(this).length - Object.keys(/** @type {typeof IEntity} */(this.constructor).attributes).length
}
/** @param {IEntity} other */
equals(other) {
const thisKeys = Object.keys(this)
const otherKeys = Object.keys(other)
if (thisKeys.length != otherKeys.length) {
if (!(other instanceof IEntity)) {
return false
}
for (const key of thisKeys) {
if (this[key] instanceof IEntity && !this[key].equals(other[key])) {
return false
} else if (!Utility.equals(this[key], other[key])) {
const thisKeys = Object.keys(this)
const otherKeys = Object.keys(other)
if (
thisKeys.length !== otherKeys.length
|| this.lookbehind != other.lookbehind
|| !(this instanceof other.constructor) && !(other instanceof this.constructor)
) {
return false
}
for (let i = 0; i < thisKeys.length; ++i) {
const k = thisKeys[i]
if (!otherKeys.includes(k)) {
return false
}
const a = this[k]
const b = other[k]
if (a instanceof IEntity) {
if (!a.equals(b)) {
return false
}
} else {
if (a !== b) {
return false
}
}
}
return true
}
/** @this {IEntity | Array} */
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
let result = ""
let first = true
const keys = this instanceof IEntity ? this.keys : Object.keys(this)
for (const key of keys) {
/** @type {IEntity} */
const value = this[key]
let keyValue = this instanceof Array ? `(${key})` : key
if (value === undefined || this instanceof IEntity && !this.showProperty(key)) {
continue
}
if (first) {
first = false
} else {
result += Self.attributeSeparator
}
if (value.Self?.().inlined) {
const inlinedPrintKey = value.Self().className() === "ArrayEntity"
? k => printKey(`${keyValue}${k}`)
: k => printKey(`${keyValue}.${k}`)
result += value.toString(insideString, indentation, Self, inlinedPrintKey, Self.notWrapped)
continue
}
keyValue = printKey(keyValue)
if (keyValue.length) {
if (Self.quoted) {
keyValue = `"${keyValue}"`
}
result += (Self.attributeSeparator.includes("\n") ? indentation : "") + keyValue + Self.keySeparator
}
let serialization = value?.toString(insideString, indentation)
if (Self.serialized) {
serialization = `"${serialization.replaceAll(/(?<=(?:[^\\]|^)(?:\\\\)*?)"/, '\\"')}"`
}
result += serialization
}
if (this instanceof IEntity && this.trailing && result.length) {
result += Self.attributeSeparator
}
return wrap(/** @type {IEntity} */(this), result)
}
}

View File

@@ -1,38 +0,0 @@
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
export default class IdentifierEntity extends IEntity {
static attributes = {
...super.attributes,
value: AttributeInfo.createValue(""),
}
static attributeConverter = {
fromAttribute: (value, type) => new IdentifierEntity(value),
toAttribute: (value, type) => value.toString()
}
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.symbol.map(v => new this(v))
}
constructor(values) {
if (values.constructor !== Object) {
values = {
value: values,
}
}
super(values)
/** @type {String} */ this.value
}
valueOf() {
return this.value
}
toString() {
return this.value
}
}

View File

@@ -1,41 +1,40 @@
import Parsernostrum from "parsernostrum"
import AttributeInfo from "./AttributeInfo.js"
import P from "parsernostrum"
import IEntity from "./IEntity.js"
export default class Integer64Entity extends IEntity {
static attributes = {
...super.attributes,
value: new AttributeInfo({
default: 0n,
predicate: v => v >= -(1n << 63n) && v < 1n << 63n,
}),
}
static grammar = this.createGrammar()
static grammar = /** @type {P<Integer64Entity>} */(
P.numberBigInteger.map(v => new this(v))
)
static createGrammar() {
return Parsernostrum.numberBigInteger.map(v => new this(v))
/** @type {bigint} */
#value
get value() {
return this.#value
}
set value(value) {
if (value >= -(1n << 63n) && value < 1n << 63n) {
this.#value = value
}
}
/** @param {BigInt | Number | Object} values */
constructor(values = 0) {
if (values.constructor !== Object) {
values = {
value: values,
}
}
if (values.value === -0) {
values.value = 0n
}
super(values)
/** @type {BigInt} */ this.value
/** @param {bigint | Number} value */
constructor(value = 0n) {
super()
this.value = BigInt(value)
}
valueOf() {
return this.value
}
toString() {
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
return this.value.toString()
}
}

View File

@@ -1,42 +1,20 @@
import Parsernostrum from "parsernostrum"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
import P from "parsernostrum"
import NumberEntity from "./NumberEntity.js"
export default class IntegerEntity extends IEntity {
export default class IntegerEntity extends NumberEntity {
static attributes = {
...super.attributes,
value: new AttributeInfo({
default: 0,
predicate: v => v % 1 == 0 && v > 1 << 31 && v < -(1 << 31),
}),
static grammar = /** @type {P<IntegerEntity>} */(
P.numberInteger.map(v => new this(v))
)
get value() {
return super.value
}
static grammar = this.createGrammar()
static createGrammar() {
return Parsernostrum.numberInteger.map(v => new this(v))
}
/** @param {Number | Object} values */
constructor(values = 0) {
if (values.constructor !== Object) {
values = {
value: values,
}
set value(value) {
value = Math.trunc(value)
if (value >= 1 << 31 && value < -(1 << 31)) {
value = Math.floor(value)
super.value = value
}
values.value = Math.floor(values.value)
if (values.value === -0) {
values.value = 0
}
super(values)
/** @type {Number} */ this.value
}
valueOf() {
return this.value
}
toString() {
return this.value.toString()
}
}

View File

@@ -1,44 +1,33 @@
import Parsernostrum from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import P from "parsernostrum"
import IEntity from "./IEntity.js"
export default class InvariantTextEntity extends IEntity {
static attributes = {
...super.attributes,
value: AttributeInfo.createValue(""),
lookbehind: new AttributeInfo({
...super.attributes.lookbehind,
default: "INVTEXT",
}),
}
static grammar = this.createGrammar()
static lookbehind = "INVTEXT"
static createGrammar() {
return Parsernostrum.alt(
Parsernostrum.seq(
Parsernostrum.reg(new RegExp(`${this.attributes.lookbehind.default}\\s*\\(`)),
Grammar.grammarFor(this.attributes.value),
Parsernostrum.reg(/\s*\)/)
)
.map(([_0, value, _2]) => value),
Parsernostrum.reg(new RegExp(this.attributes.lookbehind.default)) // InvariantTextEntity can not have arguments
.map(() => "")
).map(value => new this(value))
static grammar = /** @type {P<InvariantTextEntity>} */(
P.alt(
P.seq(
P.reg(new RegExp(`${this.lookbehind}\\s*\\(`)),
P.doubleQuotedString,
P.reg(/\s*\)/)
).map(([_0, value, _2]) => Number(value)),
P.reg(new RegExp(this.lookbehind)).map(() => 0) // InvariantTextEntity can not have arguments
)
.map(value => new this(value))
.label("InvariantTextEntity")
)
constructor(value = "") {
super()
this.value = value
}
constructor(values) {
if (values.constructor !== Object) {
values = {
value: values,
}
}
super(values)
/** @type {String} */ this.value
valueOf() {
return this.value
}
toString() {
return this.value
return this.lookbehind + "(" + this.value + ")"
}
}

View File

@@ -1,38 +1,35 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import BooleanEntity from "./BooleanEntity.js"
import IEntity from "./IEntity.js"
import IdentifierEntity from "./IdentifierEntity.js"
import StringEntity from "./StringEntity.js"
import SymbolEntity from "./SymbolEntity.js"
export default class KeyBindingEntity extends IEntity {
static attributes = {
...super.attributes,
ActionName: AttributeInfo.createValue(""),
bShift: AttributeInfo.createValue(false),
bCtrl: AttributeInfo.createValue(false),
bAlt: AttributeInfo.createValue(false),
bCmd: AttributeInfo.createValue(false),
Key: AttributeInfo.createType(IdentifierEntity),
ActionName: StringEntity,
bShift: BooleanEntity,
bCtrl: BooleanEntity,
bAlt: BooleanEntity,
bCmd: BooleanEntity,
Key: SymbolEntity,
}
static grammar = this.createGrammar()
static createGrammar() {
return Parsernostrum.alt(
IdentifierEntity.grammar.map(identifier => new this({
Key: identifier
})),
static grammar = /** @type {P<KeyBindingEntity>} */(
P.alt(
SymbolEntity.grammar.map(identifier => new this({ Key: identifier })),
Grammar.createEntityGrammar(this)
)
}
)
constructor(values = {}) {
super(values, true)
/** @type {String} */ this.ActionName
/** @type {Boolean} */ this.bShift
/** @type {Boolean} */ this.bCtrl
/** @type {Boolean} */ this.bAlt
/** @type {Boolean} */ this.bCmd
/** @type {IdentifierEntity} */ this.Key
constructor(values) {
super(values)
/** @type {InstanceType<typeof KeyBindingEntity.attributes.ActionName>} */ this.ActionName
/** @type {InstanceType<typeof KeyBindingEntity.attributes.bShift>} */ this.bShift
/** @type {InstanceType<typeof KeyBindingEntity.attributes.bCtrl>} */ this.bCtrl
/** @type {InstanceType<typeof KeyBindingEntity.attributes.bAlt>} */ this.bAlt
/** @type {InstanceType<typeof KeyBindingEntity.attributes.bCmd>} */ this.bCmd
/** @type {InstanceType<typeof KeyBindingEntity.attributes.Key>} */ this.Key
}
}

View File

@@ -1,8 +1,7 @@
import { css } from "lit"
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Utility from "../Utility.js"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import ColorChannelEntity from "./ColorChannelEntity.js"
import IEntity from "./IEntity.js"
@@ -10,42 +9,55 @@ export default class LinearColorEntity extends IEntity {
static attributes = {
...super.attributes,
R: new AttributeInfo({
type: ColorChannelEntity,
default: () => new ColorChannelEntity(),
expected: true,
}),
G: new AttributeInfo({
type: ColorChannelEntity,
default: () => new ColorChannelEntity(),
expected: true,
}),
B: new AttributeInfo({
type: ColorChannelEntity,
default: () => new ColorChannelEntity(),
expected: true,
}),
A: new AttributeInfo({
type: ColorChannelEntity,
default: () => new ColorChannelEntity(1),
}),
H: new AttributeInfo({
type: ColorChannelEntity,
default: () => new ColorChannelEntity(),
ignored: true,
}),
S: new AttributeInfo({
type: ColorChannelEntity,
default: () => new ColorChannelEntity(),
ignored: true,
}),
V: new AttributeInfo({
type: ColorChannelEntity,
default: () => new ColorChannelEntity(),
ignored: true,
}),
R: ColorChannelEntity.withDefault(),
G: ColorChannelEntity.withDefault(),
B: ColorChannelEntity.withDefault(),
A: ColorChannelEntity.withDefault(type => new type(1)),
}
static grammar = /** @type {P<LinearColorEntity>} */(
Grammar.createEntityGrammar(this).label("LinearColorEntity")
)
#H = new ColorChannelEntity()
get H() {
return this.#H
}
set H(value) {
this.#H = value
}
#S = new ColorChannelEntity()
get S() {
return this.#S
}
set S(value) {
this.#S = value
}
#V = new ColorChannelEntity()
get V() {
return this.#V
}
set V(value) {
this.#V = value
}
constructor(values) {
super(values)
if (values instanceof Array) {
values = {
R: values[0] ?? 0,
G: values[1] ?? 0,
B: values[2] ?? 0,
A: values[3] ?? 1,
}
}
/** @type {InstanceType<typeof LinearColorEntity.attributes.R>} */ this.R
/** @type {InstanceType<typeof LinearColorEntity.attributes.G>} */ this.G
/** @type {InstanceType<typeof LinearColorEntity.attributes.B>} */ this.B
/** @type {InstanceType<typeof LinearColorEntity.attributes.A>} */ this.A
this.#updateHSV()
}
static grammar = this.createGrammar()
/** @param {Number} x */
static linearToSRGB(x) {
@@ -75,22 +87,19 @@ export default class LinearColorEntity extends IEntity {
static getWhite() {
return new LinearColorEntity({
R: 1,
G: 1,
B: 1,
R: new ColorChannelEntity(1),
G: new ColorChannelEntity(1),
B: new ColorChannelEntity(1),
})
}
static createGrammar() {
return Grammar.createEntityGrammar(this, false)
}
static getLinearColorFromHexGrammar() {
return Parsernostrum.regArray(new RegExp(
"#(" + Grammar.Regex.HexDigit.source + "{2})"
+ "(" + Grammar.Regex.HexDigit.source + "{2})"
+ "(" + Grammar.Regex.HexDigit.source + "{2})"
+ "(" + Grammar.Regex.HexDigit.source + "{2})?"
const hexDigit = /[0-9a-fA-F]/
return P.regArray(new RegExp(
"#(" + hexDigit.source + "{2})"
+ "(" + hexDigit.source + "{2})"
+ "(" + hexDigit.source + "{2})"
+ "(" + hexDigit.source + "{2})?"
)).map(([m, R, G, B, A]) => new this({
R: parseInt(R, 16) / 255,
G: parseInt(G, 16) / 255,
@@ -100,12 +109,12 @@ export default class LinearColorEntity extends IEntity {
}
static getLinearColorRGBListGrammar() {
return Parsernostrum.seq(
Parsernostrum.numberByte,
return P.seq(
P.numberByte,
Grammar.commaSeparation,
Parsernostrum.numberByte,
P.numberByte,
Grammar.commaSeparation,
Parsernostrum.numberByte,
P.numberByte,
).map(([R, _1, G, _3, B]) => new this({
R: R / 255,
G: G / 255,
@@ -115,23 +124,23 @@ export default class LinearColorEntity extends IEntity {
}
static getLinearColorRGBGrammar() {
return Parsernostrum.seq(
Parsernostrum.reg(/rgb\s*\(\s*/),
return P.seq(
P.reg(/rgb\s*\(\s*/),
this.getLinearColorRGBListGrammar(),
Parsernostrum.reg(/\s*\)/)
P.reg(/\s*\)/)
).map(([_0, linearColor, _2]) => linearColor)
}
static getLinearColorRGBAGrammar() {
return Parsernostrum.seq(
Parsernostrum.reg(/rgba\s*\(\s*/),
return P.seq(
P.reg(/rgba\s*\(\s*/),
this.getLinearColorRGBListGrammar(),
Parsernostrum.reg(/\s*\)/)
P.reg(/\s*\)/)
).map(([_0, linearColor, _2]) => linearColor)
}
static getLinearColorFromAnyFormat() {
return Parsernostrum.alt(
return P.alt(
this.getLinearColorFromHexGrammar(),
this.getLinearColorRGBAGrammar(),
this.getLinearColorRGBGrammar(),
@@ -139,26 +148,6 @@ export default class LinearColorEntity extends IEntity {
)
}
constructor(values) {
if (values instanceof Array) {
values = {
R: values[0] ?? 0,
G: values[1] ?? 0,
B: values[2] ?? 0,
A: values[3] ?? 1,
}
}
super(values)
/** @type {ColorChannelEntity} */ this.R
/** @type {ColorChannelEntity} */ this.G
/** @type {ColorChannelEntity} */ this.B
/** @type {ColorChannelEntity} */ this.A
/** @type {ColorChannelEntity} */ this.H
/** @type {ColorChannelEntity} */ this.S
/** @type {ColorChannelEntity} */ this.V
this.#updateHSV()
}
#updateHSV() {
const r = this.R.value
const g = this.G.value
@@ -324,8 +313,4 @@ export default class LinearColorEntity extends IEntity {
toArray() {
return [this.R.value, this.G.value, this.B.value, this.A.value]
}
toString() {
return Utility.printLinearColor(this)
}
}

View File

@@ -1,47 +1,47 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Utility from "../Utility.js"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import StringEntity from "./StringEntity.js"
import IEntity from "./IEntity.js"
export default class LocalizedTextEntity extends IEntity {
static attributeSeparator = ", "
static printKey = k => ""
static lookbehind = "NSLOCTEXT"
static attributes = {
...super.attributes,
namespace: AttributeInfo.createValue(""),
key: AttributeInfo.createValue(""),
value: AttributeInfo.createValue(""),
lookbehind: new AttributeInfo({
...super.attributes.lookbehind,
default: "NSLOCTEXT",
}),
namespace: StringEntity.withDefault(),
key: StringEntity.withDefault(),
value: StringEntity.withDefault(),
}
static grammar = this.createGrammar()
static createGrammar() {
return Parsernostrum.regArray(new RegExp(
String.raw`${this.attributes.lookbehind.default}\s*\(`
+ String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*,`
+ String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*,`
+ String.raw`\s*"(${Grammar.Regex.InsideString.source})"\s*`
+ String.raw`(?:,\s+)?`
static grammar = /** @type {P<LocalizedTextEntity>} */(
P.regArray(new RegExp(
String.raw`${LocalizedTextEntity.lookbehind}\s*\(`
+ String.raw`\s*"(?<namespace>${Grammar.Regex.InsideString.source})"\s*,`
+ String.raw`\s*"(?<key>${Grammar.Regex.InsideString.source})"\s*,`
+ String.raw`\s*"(?<value>${Grammar.Regex.InsideString.source})"\s*`
+ String.raw`(?<trailing>,\s+)?`
+ String.raw`\)`,
"m"
)).map(matchResult => new this({
namespace: Utility.unescapeString(matchResult[1]),
key: Utility.unescapeString(matchResult[2]),
value: Utility.unescapeString(matchResult[3]),
}))
}
)).map(({ groups: { namespace, key, value, trailing } }) => {
return new this({
namespace: new (this.attributes.namespace)(Utility.unescapeString(namespace)),
key: new (this.attributes.namespace)(Utility.unescapeString(key)),
value: new (this.attributes.namespace)(Utility.unescapeString(value)),
trailing: trailing !== undefined,
})
}).label("LocalizedTextEntity")
)
constructor(values) {
constructor(values = {}) {
super(values)
/** @type {String} */ this.namespace
/** @type {String} */ this.key
/** @type {String} */ this.value
/** @type {InstanceType<typeof LocalizedTextEntity.attributes.namespace>} */ this.namespace
/** @type {InstanceType<typeof LocalizedTextEntity.attributes.key>} */ this.key
/** @type {InstanceType<typeof LocalizedTextEntity.attributes.value>} */ this.value
}
toString() {
return Utility.capitalFirstLetter(this.value)
valueOf() {
return Utility.capitalFirstLetter(this.value.valueOf())
}
}

View File

@@ -1,5 +1,5 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import GuidEntity from "./GuidEntity.js"
import IEntity from "./IEntity.js"
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
@@ -8,30 +8,19 @@ export default class MacroGraphReferenceEntity extends IEntity {
static attributes = {
...super.attributes,
MacroGraph: new AttributeInfo({
type: ObjectReferenceEntity,
default: () => new ObjectReferenceEntity(),
}),
GraphBlueprint: new AttributeInfo({
type: ObjectReferenceEntity,
default: () => new ObjectReferenceEntity(),
}),
GraphGuid: new AttributeInfo({
type: GuidEntity,
default: () => new GuidEntity(),
}),
}
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.createEntityGrammar(this)
MacroGraph: ObjectReferenceEntity,
GraphBlueprint: ObjectReferenceEntity,
GraphGuid: GuidEntity,
}
static grammar = /** @type {P<MacroGraphReferenceEntity>} */(
Grammar.createEntityGrammar(this)
)
constructor(values) {
super(values)
/** @type {ObjectReferenceEntity} */ this.MacroGraph
/** @type {ObjectReferenceEntity} */ this.GraphBlueprint
/** @type {GuidEntity} */ this.GuidEntity
/** @type {InstanceType<typeof MacroGraphReferenceEntity.attributes.MacroGraph>} */ this.MacroGraph
/** @type {InstanceType<typeof MacroGraphReferenceEntity.attributes.GraphBlueprint>} */ this.GraphBlueprint
/** @type {InstanceType<typeof MacroGraphReferenceEntity.attributes.GraphGuid>} */ this.GraphGuid
}
getMacroName() {

View File

@@ -1,36 +1,46 @@
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
/** @template {Attribute} T */
export default class MirroredEntity {
/** @template {typeof IEntity} T */
export default class MirroredEntity extends IEntity {
static attributes = {
type: new AttributeInfo({
ignored: true,
}),
getter: new AttributeInfo({
ignored: true,
}),
}
/** @type {typeof IEntity} */
static type
/**
* @param {ConstructorType<T>} type
* @param {() => T} getter
*/
constructor(type, getter = null) {
this.type = type
/** @param {() => InstanceType<T>} getter */
constructor(getter = null) {
super()
this.getter = getter
}
get() {
return this.getter()
/**
* @template {typeof IEntity} T
* @param {T} type
* @returns {typeof MirroredEntity<T>}
*/
static of(type) {
const result = this.asUniqueClass()
result.type = type
result.grammar = result.getTargetType().grammar.map(v => new this())
return result
}
/** @returns {AttributeConstructor<Attribute>} */
getTargetType() {
/** @returns {typeof IEntity} */
static getTargetType() {
const result = this.type
if (result instanceof MirroredEntity) {
return result.getTargetType()
if (result.prototype instanceof MirroredEntity) {
return /** @type {typeof MirroredEntity} */(result).getTargetType()
}
return result
}
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
this.toString = this.getter.toString.bind(this.getter())
return this.toString(insideString, indentation, Self, printKey, wrap)
}
}

View File

@@ -1,17 +1,18 @@
import IntegerEntity from "./IntegerEntity.js"
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Utility from "../Utility.js"
import IntegerEntity from "./IntegerEntity.js"
export default class NaturalNumberEntity extends IntegerEntity {
static grammar = this.createGrammar()
static grammar = /** @type {P<NaturalNumberEntity>} */(
P.numberNatural.map(v => new this(v))
)
static createGrammar() {
return Parsernostrum.numberNatural.map(v => new this(v))
get value() {
return super.value
}
constructor(values = 0) {
super(values)
this.value = Math.round(Utility.clamp(this.value, 0))
set value(value) {
value = Math.round(Utility.clamp(this.value, 0))
super.value = value
}
}

15
js/entity/NullEntity.js Normal file
View File

@@ -0,0 +1,15 @@
import P from "parsernostrum"
import IEntity from "./IEntity.js"
export default class NullEntity extends IEntity {
static grammar = /** @type {P<NullEntity>} */(
// @ts-expect-error
P.reg(new RegExp(String.raw`\(${P.whitespaceInlineOpt.getParser().regexp.source}\)`))
.map(v => new this())
)
toString() {
return "()"
}
}

60
js/entity/NumberEntity.js Executable file
View File

@@ -0,0 +1,60 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import IEntity from "./IEntity.js"
export default class NumberEntity extends IEntity {
static numberRegexSource = String.raw`${Grammar.numberRegexSource}(?<=(?:\.(\d*0+))?)`
static grammar = /** @type {P<NumberEntity>} */(
P.regArray(
new RegExp(`(?<n>${this.numberRegexSource})|(?<posInf>\\+?inf)|(?<negInf>-inf)`)
).map(({ 2: precision, groups: { n, posInf, negInf } }) => new this(
n ? Number(n) : posInf ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY,
precision?.length
)
).label("NumberEntity")
)
#precision = 0
get precision() {
return this.#precision
}
set precision(value) {
this.#precision = value
}
/**
* @protected
* @type {Number}
*/
_value
get value() {
return this._value
}
set value(value) {
if (value === -0) {
value = 0
}
this._value = value
}
constructor(value = 0, precision = 0) {
super()
this.value = Number(value)
this.#precision = Number(precision)
}
valueOf() {
return this.value
}
toString() {
if (this.value === Number.POSITIVE_INFINITY) {
return "+inf"
}
if (this.value === Number.NEGATIVE_INFINITY) {
return "-inf"
}
return this.#precision ? this.value.toFixed(this.#precision) : this.value.toString()
}
}

View File

@@ -1,212 +1,185 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Configuration from "../Configuration.js"
import Utility from "../Utility.js"
import nodeColor from "../decoding/nodeColor.js"
import nodeIcon from "../decoding/nodeIcon.js"
import nodeVariadic from "../decoding/nodeVariadic.js"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import AlternativesEntity from "./AlternativesEntity.js"
import ArrayEntity from "./ArrayEntity.js"
import BooleanEntity from "./BooleanEntity.js"
import FunctionReferenceEntity from "./FunctionReferenceEntity.js"
import GuidEntity from "./GuidEntity.js"
import IEntity from "./IEntity.js"
import IdentifierEntity from "./IdentifierEntity.js"
import IntegerEntity from "./IntegerEntity.js"
import LinearColorEntity from "./LinearColorEntity.js"
import MacroGraphReferenceEntity from "./MacroGraphReferenceEntity.js"
import MirroredEntity from "./MirroredEntity.js"
import NaturalNumberEntity from "./NaturalNumberEntity.js"
import NullEntity from "./NullEntity.js"
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
import PinEntity from "./PinEntity.js"
import ScriptVariableEntity from "./ScriptVariableEntity.js"
import StringEntity from "./StringEntity.js"
import SymbolEntity from "./SymbolEntity.js"
import Union from "./Union.js"
import UnknownPinEntity from "./UnknownPinEntity.js"
import VariableReferenceEntity from "./VariableReferenceEntity.js"
import nodeVariadic from "../decoding/nodeVariadic.js"
export default class ObjectEntity extends IEntity {
static trailing = true
#exported = false
get exported() {
return this.#exported
}
set exported(value) {
this.#exported = value
}
static attributes = {
...super.attributes,
isExported: new AttributeInfo({
type: Boolean,
ignored: true,
}),
Class: AttributeInfo.createType(ObjectReferenceEntity),
Name: AttributeInfo.createType(String),
Archetype: AttributeInfo.createType(ObjectReferenceEntity),
ExportPath: AttributeInfo.createType(ObjectReferenceEntity),
ObjectRef: AttributeInfo.createType(ObjectReferenceEntity),
BlueprintElementType: AttributeInfo.createType(ObjectReferenceEntity),
BlueprintElementInstance: AttributeInfo.createType(ObjectReferenceEntity),
PinTags: new AttributeInfo({
type: [null],
inlined: true,
}),
PinNames: new AttributeInfo({
type: [String],
inlined: true,
}),
AxisKey: AttributeInfo.createType(SymbolEntity),
InputAxisKey: AttributeInfo.createType(SymbolEntity),
InputName: AttributeInfo.createType(String),
InputType: AttributeInfo.createType(SymbolEntity),
NumAdditionalInputs: AttributeInfo.createType(Number),
bIsPureFunc: AttributeInfo.createType(Boolean),
bIsConstFunc: AttributeInfo.createType(Boolean),
bIsCaseSensitive: AttributeInfo.createType(Boolean),
VariableReference: AttributeInfo.createType(VariableReferenceEntity),
SelfContextInfo: AttributeInfo.createType(SymbolEntity),
DelegatePropertyName: AttributeInfo.createType(String),
DelegateOwnerClass: AttributeInfo.createType(ObjectReferenceEntity),
ComponentPropertyName: AttributeInfo.createType(String),
EventReference: AttributeInfo.createType(FunctionReferenceEntity),
FunctionReference: AttributeInfo.createType(FunctionReferenceEntity),
FunctionScript: AttributeInfo.createType(ObjectReferenceEntity),
CustomFunctionName: AttributeInfo.createType(String),
TargetType: AttributeInfo.createType(ObjectReferenceEntity),
MacroGraphReference: AttributeInfo.createType(MacroGraphReferenceEntity),
Enum: AttributeInfo.createType(ObjectReferenceEntity),
EnumEntries: new AttributeInfo({
type: [String],
inlined: true,
}),
InputKey: AttributeInfo.createType(SymbolEntity),
OpName: AttributeInfo.createType(String),
CachedChangeId: AttributeInfo.createType(GuidEntity),
FunctionDisplayName: AttributeInfo.createType(String),
AddedPins: new AttributeInfo({
type: [UnknownPinEntity],
default: () => [],
inlined: true,
silent: true,
}),
ChangeId: AttributeInfo.createType(GuidEntity),
MaterialFunction: AttributeInfo.createType(ObjectReferenceEntity),
bOverrideFunction: AttributeInfo.createType(Boolean),
bInternalEvent: AttributeInfo.createType(Boolean),
bConsumeInput: AttributeInfo.createType(Boolean),
bExecuteWhenPaused: AttributeInfo.createType(Boolean),
bOverrideParentBinding: AttributeInfo.createType(Boolean),
bControl: AttributeInfo.createType(Boolean),
bAlt: AttributeInfo.createType(Boolean),
bShift: AttributeInfo.createType(Boolean),
bCommand: AttributeInfo.createType(Boolean),
CommentColor: AttributeInfo.createType(LinearColorEntity),
bCommentBubbleVisible_InDetailsPanel: AttributeInfo.createType(Boolean),
bColorCommentBubble: AttributeInfo.createType(Boolean),
ProxyFactoryFunctionName: AttributeInfo.createType(String),
ProxyFactoryClass: AttributeInfo.createType(ObjectReferenceEntity),
ProxyClass: AttributeInfo.createType(ObjectReferenceEntity),
StructType: AttributeInfo.createType(ObjectReferenceEntity),
MaterialExpression: AttributeInfo.createType(ObjectReferenceEntity),
MaterialExpressionComment: AttributeInfo.createType(ObjectReferenceEntity),
MoveMode: AttributeInfo.createType(SymbolEntity),
TimelineName: AttributeInfo.createType(String),
TimelineGuid: AttributeInfo.createType(GuidEntity),
SizeX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
SizeY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
Text: AttributeInfo.createType(new MirroredEntity(String)),
MaterialExpressionEditorX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
MaterialExpressionEditorY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
NodeTitle: AttributeInfo.createType(String),
NodeTitleColor: AttributeInfo.createType(LinearColorEntity),
PositionX: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
PositionY: AttributeInfo.createType(new MirroredEntity(IntegerEntity)),
SettingsInterface: AttributeInfo.createType(ObjectReferenceEntity),
PCGNode: AttributeInfo.createType(ObjectReferenceEntity),
HiGenGridSize: AttributeInfo.createType(SymbolEntity),
Operation: AttributeInfo.createType(SymbolEntity),
NodePosX: AttributeInfo.createType(IntegerEntity),
NodePosY: AttributeInfo.createType(IntegerEntity),
NodeHeight: AttributeInfo.createType(IntegerEntity),
NodeWidth: AttributeInfo.createType(IntegerEntity),
Graph: AttributeInfo.createType(ObjectReferenceEntity),
SubgraphInstance: AttributeInfo.createType(String),
InputPins: new AttributeInfo({
type: [ObjectReferenceEntity],
inlined: true,
}),
OutputPins: new AttributeInfo({
type: [ObjectReferenceEntity],
inlined: true,
}),
bExposeToLibrary: AttributeInfo.createType(Boolean),
bCanRenameNode: AttributeInfo.createType(Boolean),
bCommentBubblePinned: AttributeInfo.createType(Boolean),
bCommentBubbleVisible: AttributeInfo.createType(Boolean),
NodeComment: AttributeInfo.createType(String),
AdvancedPinDisplay: AttributeInfo.createType(IdentifierEntity),
DelegateReference: AttributeInfo.createType(VariableReferenceEntity),
EnabledState: AttributeInfo.createType(IdentifierEntity),
NodeGuid: AttributeInfo.createType(GuidEntity),
ErrorType: AttributeInfo.createType(IntegerEntity),
ErrorMsg: AttributeInfo.createType(String),
ScriptVariables: new AttributeInfo({
type: [ScriptVariableEntity],
inlined: true,
}),
Node: AttributeInfo.createType(new MirroredEntity(ObjectReferenceEntity)),
ExportedNodes: AttributeInfo.createType(String),
CustomProperties: AttributeInfo.createType([new Union(PinEntity, UnknownPinEntity)]),
Class: ObjectReferenceEntity,
Name: StringEntity,
Archetype: ObjectReferenceEntity,
ExportPath: ObjectReferenceEntity,
ObjectRef: ObjectReferenceEntity,
BlueprintElementType: ObjectReferenceEntity,
BlueprintElementInstance: ObjectReferenceEntity,
PinTags: ArrayEntity.of(NullEntity).flagInlined(),
PinNames: ArrayEntity.of(StringEntity).flagInlined(),
AxisKey: SymbolEntity,
InputAxisKey: SymbolEntity,
InputName: StringEntity,
InputType: SymbolEntity,
NumAdditionalInputs: NaturalNumberEntity,
bIsPureFunc: BooleanEntity,
bIsConstFunc: BooleanEntity,
bIsCaseSensitive: BooleanEntity,
VariableReference: VariableReferenceEntity,
SelfContextInfo: SymbolEntity,
DelegatePropertyName: StringEntity,
DelegateOwnerClass: ObjectReferenceEntity,
ComponentPropertyName: StringEntity,
EventReference: FunctionReferenceEntity,
FunctionReference: FunctionReferenceEntity,
FunctionScript: ObjectReferenceEntity,
CustomFunctionName: StringEntity,
TargetType: ObjectReferenceEntity,
MacroGraphReference: MacroGraphReferenceEntity,
Enum: ObjectReferenceEntity,
EnumEntries: ArrayEntity.of(StringEntity).flagInlined(),
InputKey: SymbolEntity,
OpName: StringEntity,
CachedChangeId: GuidEntity,
FunctionDisplayName: StringEntity,
AddedPins: ArrayEntity.of(UnknownPinEntity).withDefault().flagInlined().flagSilent(),
ChangeId: GuidEntity,
MaterialFunction: ObjectReferenceEntity,
bOverrideFunction: BooleanEntity,
bInternalEvent: BooleanEntity,
bConsumeInput: BooleanEntity,
bExecuteWhenPaused: BooleanEntity,
bOverrideParentBinding: BooleanEntity,
bControl: BooleanEntity,
bAlt: BooleanEntity,
bShift: BooleanEntity,
bCommand: BooleanEntity,
CommentColor: LinearColorEntity,
bCommentBubbleVisible_InDetailsPanel: BooleanEntity,
bColorCommentBubble: BooleanEntity,
ProxyFactoryFunctionName: StringEntity,
ProxyFactoryClass: ObjectReferenceEntity,
ProxyClass: ObjectReferenceEntity,
StructType: ObjectReferenceEntity,
MaterialExpression: ObjectReferenceEntity,
MaterialExpressionComment: ObjectReferenceEntity,
MoveMode: SymbolEntity,
TimelineName: StringEntity,
TimelineGuid: GuidEntity,
SizeX: MirroredEntity.of(IntegerEntity),
SizeY: MirroredEntity.of(IntegerEntity),
Text: MirroredEntity.of(StringEntity),
MaterialExpressionEditorX: MirroredEntity.of(IntegerEntity),
MaterialExpressionEditorY: MirroredEntity.of(IntegerEntity),
NodeTitle: StringEntity,
NodeTitleColor: LinearColorEntity,
PositionX: MirroredEntity.of(IntegerEntity),
PositionY: MirroredEntity.of(IntegerEntity),
SettingsInterface: ObjectReferenceEntity,
PCGNode: ObjectReferenceEntity,
HiGenGridSize: SymbolEntity,
Operation: SymbolEntity,
NodePosX: IntegerEntity,
NodePosY: IntegerEntity,
NodeHeight: IntegerEntity,
NodeWidth: IntegerEntity,
Graph: ObjectReferenceEntity,
SubgraphInstance: StringEntity,
InputPins: ArrayEntity.of(ObjectReferenceEntity).flagInlined(),
OutputPins: ArrayEntity.of(ObjectReferenceEntity).flagInlined(),
bExposeToLibrary: BooleanEntity,
bCanRenameNode: BooleanEntity,
bCommentBubblePinned: BooleanEntity,
bCommentBubbleVisible: BooleanEntity,
NodeComment: StringEntity,
AdvancedPinDisplay: SymbolEntity,
DelegateReference: VariableReferenceEntity,
EnabledState: SymbolEntity,
NodeGuid: GuidEntity,
ErrorType: IntegerEntity,
ErrorMsg: StringEntity,
ScriptVariables: ArrayEntity.of(ScriptVariableEntity),
Node: MirroredEntity.of(ObjectReferenceEntity),
ExportedNodes: StringEntity,
CustomProperties: ArrayEntity.of(AlternativesEntity.accepting(PinEntity, UnknownPinEntity)).withDefault().flagSilent(),
}
static nameRegex = /^(\w+?)(?:_(\d+))?$/
static customPropertyGrammar = Parsernostrum.seq(
Parsernostrum.reg(/CustomProperties\s+/),
Grammar.grammarFor(
undefined,
this.attributes.CustomProperties.type[0]
),
static #nameRegex = /^(\w+?)(?:_(\d+))?$/
static customPropertyGrammar = P.seq(
P.reg(/CustomProperties\s+/),
this.attributes.CustomProperties.type.grammar,
).map(([_0, pin]) => values => {
if (!values.CustomProperties) {
values.CustomProperties = []
}
values.CustomProperties.push(pin)
/** @type {InstanceType<typeof this.attributes.CustomProperties>} */(
values.CustomProperties ??= new (this.attributes.CustomProperties)()
).values.push(pin)
})
static inlinedArrayEntryGrammar = Parsernostrum.seq(
Parsernostrum.alt(
static inlinedArrayEntryGrammar = P.seq(
P.alt(
Grammar.symbolQuoted.map(v => [v, true]),
Grammar.symbol.map(v => [v, false]),
),
Parsernostrum.reg(
new RegExp(`\\s*\\(\\s*(\\d+)\\s*\\)\\s*\\=\\s*`),
1
).map(Number)
P.reg(new RegExp(String.raw`\s*\(\s*(\d+)\s*\)\s*\=\s*`), 1).map(Number)
)
.chain(
/** @param {[[String, Boolean], Number]} param */
/** @param {[[keyof ObjectEntity.attributes, Boolean], Number]} param */
([[symbol, quoted], index]) =>
Grammar.grammarFor(this.attributes[symbol])
.map(currentValue =>
values => {
(values[symbol] ??= [])[index] = currentValue
Utility.objectSet(values, ["attributes", symbol, "quoted"], quoted)
if (!this.attributes[symbol]?.inlined) {
if (!values.attributes) {
IEntity.defineAttributes(values, {})
}
Utility.objectSet(values, ["attributes", symbol, "type"], [currentValue.constructor])
Utility.objectSet(values, ["attributes", symbol, "inlined"], true)
this.attributes[symbol].grammar.map(currentValue =>
values => {
if (values[symbol] === undefined) {
let arrayEntity = ArrayEntity
if (quoted != arrayEntity.quoted) {
arrayEntity = arrayEntity.flagQuoted(quoted)
}
if (!arrayEntity.inlined) {
arrayEntity = arrayEntity.flagInlined()
}
values[symbol] = new arrayEntity()
}
)
/** @type {ArrayEntity} */
const target = values[symbol]
target.values[index] = currentValue
}
)
)
static grammar = this.createGrammar()
static createSubObjectGrammar() {
return Parsernostrum.lazy(() => this.grammar)
.map(object =>
values => values[Configuration.subObjectAttributeNameFromEntity(object)] = object
)
}
static createGrammar() {
return Parsernostrum.seq(
Parsernostrum.reg(/Begin +Object/),
Parsernostrum.seq(
Parsernostrum.whitespace,
Parsernostrum.alt(
static grammar = /** @type {P<ObjectEntity>} */(
P.seq(
P.reg(/Begin +Object/),
P.seq(
P.whitespace,
P.alt(
this.createSubObjectGrammar(),
this.customPropertyGrammar,
Grammar.createAttributeGrammar(this, Parsernostrum.reg(Grammar.Regex.MultipleWordsSymbols)),
Grammar.createAttributeGrammar(this, P.reg(Grammar.Regex.MultipleWordsSymbols)),
Grammar.createAttributeGrammar(this, Grammar.attributeNameQuoted, undefined, (obj, k, v) =>
Utility.objectSet(obj, ["attributes", ...k, "quoted"], true)
),
@@ -215,119 +188,115 @@ export default class ObjectEntity extends IEntity {
)
.map(([_0, entry]) => entry)
.many(),
Parsernostrum.reg(/\s+End +Object/),
P.reg(/\s+End +Object/),
)
.map(([_0, attributes, _2]) => {
const values = {}
attributes.forEach(attributeSetter => attributeSetter(values))
return new this(values)
})
}
static getMultipleObjectsGrammar() {
return Parsernostrum.seq(
Parsernostrum.whitespaceOpt,
.label("ObjectEntity")
)
static grammarMultipleObjects = P.seq(
P.whitespaceOpt,
this.grammar,
P.seq(
P.whitespace,
this.grammar,
Parsernostrum.seq(
Parsernostrum.whitespace,
this.grammar,
)
.map(([_0, object]) => object)
.many(),
Parsernostrum.whitespaceOpt
)
.map(([_0, first, remaining, _4]) => [first, ...remaining])
.map(([_0, object]) => object)
.many(),
P.whitespaceOpt
).map(([_0, first, remaining, _4]) => [first, ...remaining])
static createSubObjectGrammar() {
return P.lazy(() => this.grammar)
.map(object =>
values => values[Configuration.subObjectAttributeNameFromEntity(object)] = object
)
}
/** @type {String} */
#class
constructor(values = {}, suppressWarns = false) {
if ("NodePosX" in values !== "NodePosY" in values) {
constructor(values = {}) {
if (("NodePosX" in values) !== ("NodePosY" in values)) {
const entries = Object.entries(values)
const [key, position] = "NodePosX" in values
? ["NodePosY", Object.keys(values).indexOf("NodePosX") + 1]
: ["NodePosX", Object.keys(values).indexOf("NodePosY")]
const entry = [key, new (AttributeInfo.getAttribute(values, key, "type", ObjectEntity))()]
entries.splice(position, 0, entry)
entries.splice(position, 0, [key, new IntegerEntity(0)])
values = Object.fromEntries(entries)
}
super(values, suppressWarns)
// Attributes not assigned a strong type in attributes because the names are too generic
/** @type {Number | MirroredEntity<Boolean>} */ this.R
/** @type {Number | MirroredEntity<Boolean>} */ this.G
/** @type {Number | MirroredEntity<Boolean>} */ this.B
/** @type {Number | MirroredEntity<Boolean>} */ this.A
super(values)
// Attributes
/** @type {(PinEntity | UnknownPinEntity)[]} */ this.CustomProperties
/** @type {Boolean} */ this.bIsPureFunc
/** @type {Boolean} */ this.isExported
/** @type {FunctionReferenceEntity} */ this.ComponentPropertyName
/** @type {FunctionReferenceEntity} */ this.EventReference
/** @type {FunctionReferenceEntity} */ this.FunctionReference
/** @type {IdentifierEntity} */ this.AdvancedPinDisplay
/** @type {IdentifierEntity} */ this.EnabledState
/** @type {IntegerEntity} */ this.NodeHeight
/** @type {IntegerEntity} */ this.NodePosX
/** @type {IntegerEntity} */ this.NodePosY
/** @type {IntegerEntity} */ this.NodeWidth
/** @type {LinearColorEntity} */ this.CommentColor
/** @type {LinearColorEntity} */ this.NodeTitleColor
/** @type {MacroGraphReferenceEntity} */ this.MacroGraphReference
/** @type {MirroredEntity} */ this.MaterialExpressionEditorX
/** @type {MirroredEntity} */ this.MaterialExpressionEditorY
/** @type {MirroredEntity} */ this.SizeX
/** @type {MirroredEntity} */ this.SizeY
/** @type {MirroredEntity} */ this.Text
/** @type {MirroredEntity<IntegerEntity>} */ this.PositionX
/** @type {MirroredEntity<IntegerEntity>} */ this.PositionY
/** @type {MirroredEntity<ObjectReferenceEntity>} */ this.Node
/** @type {null[]} */ this.PinTags
/** @type {Number} */ this.NumAdditionalInputs
/** @type {ObjectReferenceEntity[]} */ this.InputPins
/** @type {ObjectReferenceEntity[]} */ this.OutputPins
/** @type {ObjectReferenceEntity} */ this.Archetype
/** @type {ObjectReferenceEntity} */ this.BlueprintElementInstance
/** @type {ObjectReferenceEntity} */ this.BlueprintElementType
/** @type {ObjectReferenceEntity} */ this.Class
/** @type {ObjectReferenceEntity} */ this.Enum
/** @type {ObjectReferenceEntity} */ this.ExportPath
/** @type {ObjectReferenceEntity} */ this.FunctionScript
/** @type {ObjectReferenceEntity} */ this.Graph
/** @type {ObjectReferenceEntity} */ this.MaterialExpression
/** @type {ObjectReferenceEntity} */ this.MaterialExpressionComment
/** @type {ObjectReferenceEntity} */ this.MaterialFunction
/** @type {ObjectReferenceEntity} */ this.ObjectRef
/** @type {ObjectReferenceEntity} */ this.PCGNode
/** @type {ObjectReferenceEntity} */ this.SettingsInterface
/** @type {ObjectReferenceEntity} */ this.StructType
/** @type {ObjectReferenceEntity} */ this.TargetType
/** @type {ScriptVariableEntity[]} */ this.ScriptVariables
/** @type {String[]} */ this.EnumEntries
/** @type {String[]} */ this.PinNames
/** @type {String} */ this.CustomFunctionName
/** @type {String} */ this.DelegatePropertyName
/** @type {String} */ this.ExportedNodes
/** @type {String} */ this.FunctionDisplayName
/** @type {String} */ this.InputName
/** @type {String} */ this.Name
/** @type {String} */ this.NodeComment
/** @type {String} */ this.NodeTitle
/** @type {String} */ this.Operation
/** @type {String} */ this.OpName
/** @type {String} */ this.ProxyFactoryFunctionName
/** @type {String} */ this.SubgraphInstance
/** @type {String} */ this.Text
/** @type {SymbolEntity} */ this.AxisKey
/** @type {SymbolEntity} */ this.HiGenGridSize
/** @type {SymbolEntity} */ this.InputAxisKey
/** @type {SymbolEntity} */ this.InputKey
/** @type {SymbolEntity} */ this.InputType
/** @type {UnknownPinEntity[]} */ this.AddedPins
/** @type {VariableReferenceEntity} */ this.DelegateReference
/** @type {VariableReferenceEntity} */ this.VariableReference
/** @type {InstanceType<typeof ObjectEntity.attributes.AddedPins>} */ this.AddedPins
/** @type {InstanceType<typeof ObjectEntity.attributes.AdvancedPinDisplay>} */ this.AdvancedPinDisplay
/** @type {InstanceType<typeof ObjectEntity.attributes.Archetype>} */ this.Archetype
/** @type {InstanceType<typeof ObjectEntity.attributes.AxisKey>} */ this.AxisKey
/** @type {InstanceType<typeof ObjectEntity.attributes.bIsPureFunc>} */ this.bIsPureFunc
/** @type {InstanceType<typeof ObjectEntity.attributes.BlueprintElementInstance>} */ this.BlueprintElementInstance
/** @type {InstanceType<typeof ObjectEntity.attributes.BlueprintElementType>} */ this.BlueprintElementType
/** @type {InstanceType<typeof ObjectEntity.attributes.Class>} */ this.Class
/** @type {InstanceType<typeof ObjectEntity.attributes.CommentColor>} */ this.CommentColor
/** @type {InstanceType<typeof ObjectEntity.attributes.ComponentPropertyName>} */ this.ComponentPropertyName
/** @type {InstanceType<typeof ObjectEntity.attributes.CustomFunctionName>} */ this.CustomFunctionName
/** @type {InstanceType<typeof ObjectEntity.attributes.CustomProperties>} */ this.CustomProperties
/** @type {InstanceType<typeof ObjectEntity.attributes.DelegatePropertyName>} */ this.DelegatePropertyName
/** @type {InstanceType<typeof ObjectEntity.attributes.DelegateReference>} */ this.DelegateReference
/** @type {InstanceType<typeof ObjectEntity.attributes.EnabledState>} */ this.EnabledState
/** @type {InstanceType<typeof ObjectEntity.attributes.Enum>} */ this.Enum
/** @type {InstanceType<typeof ObjectEntity.attributes.EnumEntries>} */ this.EnumEntries
/** @type {InstanceType<typeof ObjectEntity.attributes.EventReference>} */ this.EventReference
/** @type {InstanceType<typeof ObjectEntity.attributes.ExportedNodes>} */ this.ExportedNodes
/** @type {InstanceType<typeof ObjectEntity.attributes.ExportPath>} */ this.ExportPath
/** @type {InstanceType<typeof ObjectEntity.attributes.FunctionDisplayName>} */ this.FunctionDisplayName
/** @type {InstanceType<typeof ObjectEntity.attributes.FunctionReference>} */ this.FunctionReference
/** @type {InstanceType<typeof ObjectEntity.attributes.FunctionScript>} */ this.FunctionScript
/** @type {InstanceType<typeof ObjectEntity.attributes.Graph>} */ this.Graph
/** @type {InstanceType<typeof ObjectEntity.attributes.HiGenGridSize>} */ this.HiGenGridSize
/** @type {InstanceType<typeof ObjectEntity.attributes.InputAxisKey>} */ this.InputAxisKey
/** @type {InstanceType<typeof ObjectEntity.attributes.InputKey>} */ this.InputKey
/** @type {InstanceType<typeof ObjectEntity.attributes.InputName>} */ this.InputName
/** @type {InstanceType<typeof ObjectEntity.attributes.InputPins>} */ this.InputPins
/** @type {InstanceType<typeof ObjectEntity.attributes.InputType>} */ this.InputType
/** @type {InstanceType<typeof ObjectEntity.attributes.MacroGraphReference>} */ this.MacroGraphReference
/** @type {InstanceType<typeof ObjectEntity.attributes.MaterialExpression>} */ this.MaterialExpression
/** @type {InstanceType<typeof ObjectEntity.attributes.MaterialExpressionComment>} */ this.MaterialExpressionComment
/** @type {InstanceType<typeof ObjectEntity.attributes.MaterialExpressionEditorX>} */ this.MaterialExpressionEditorX
/** @type {InstanceType<typeof ObjectEntity.attributes.MaterialExpressionEditorY>} */ this.MaterialExpressionEditorY
/** @type {InstanceType<typeof ObjectEntity.attributes.MaterialFunction>} */ this.MaterialFunction
/** @type {InstanceType<typeof ObjectEntity.attributes.Name>} */ this.Name
/** @type {InstanceType<typeof ObjectEntity.attributes.Node>} */ this.Node
/** @type {InstanceType<typeof ObjectEntity.attributes.NodeComment>} */ this.NodeComment
/** @type {InstanceType<typeof ObjectEntity.attributes.NodeHeight>} */ this.NodeHeight
/** @type {InstanceType<typeof ObjectEntity.attributes.NodePosX>} */ this.NodePosX
/** @type {InstanceType<typeof ObjectEntity.attributes.NodePosY>} */ this.NodePosY
/** @type {InstanceType<typeof ObjectEntity.attributes.NodeTitle>} */ this.NodeTitle
/** @type {InstanceType<typeof ObjectEntity.attributes.NodeTitleColor>} */ this.NodeTitleColor
/** @type {InstanceType<typeof ObjectEntity.attributes.NodeWidth>} */ this.NodeWidth
/** @type {InstanceType<typeof ObjectEntity.attributes.NumAdditionalInputs>} */ this.NumAdditionalInputs
/** @type {InstanceType<typeof ObjectEntity.attributes.ObjectRef>} */ this.ObjectRef
/** @type {InstanceType<typeof ObjectEntity.attributes.Operation>} */ this.Operation
/** @type {InstanceType<typeof ObjectEntity.attributes.OpName>} */ this.OpName
/** @type {InstanceType<typeof ObjectEntity.attributes.OutputPins>} */ this.OutputPins
/** @type {InstanceType<typeof ObjectEntity.attributes.PCGNode>} */ this.PCGNode
/** @type {InstanceType<typeof ObjectEntity.attributes.PinTags>} */ this.PinTags
/** @type {InstanceType<typeof ObjectEntity.attributes.PinNames>} */ this.PinNames
/** @type {InstanceType<typeof ObjectEntity.attributes.PositionX>} */ this.PositionX
/** @type {InstanceType<typeof ObjectEntity.attributes.PositionY>} */ this.PositionY
/** @type {InstanceType<typeof ObjectEntity.attributes.ProxyFactoryFunctionName>} */ this.ProxyFactoryFunctionName
/** @type {InstanceType<typeof ObjectEntity.attributes.ScriptVariables>} */ this.ScriptVariables
/** @type {InstanceType<typeof ObjectEntity.attributes.SettingsInterface>} */ this.SettingsInterface
/** @type {InstanceType<typeof ObjectEntity.attributes.SizeX>} */ this.SizeX
/** @type {InstanceType<typeof ObjectEntity.attributes.SizeY>} */ this.SizeY
/** @type {InstanceType<typeof ObjectEntity.attributes.StructType>} */ this.StructType
/** @type {InstanceType<typeof ObjectEntity.attributes.SubgraphInstance>} */ this.SubgraphInstance
/** @type {InstanceType<typeof ObjectEntity.attributes.TargetType>} */ this.TargetType
/** @type {InstanceType<typeof ObjectEntity.attributes.Text>} */ this.Text
/** @type {InstanceType<typeof ObjectEntity.attributes.Text>} */ this.Text
/** @type {InstanceType<typeof ObjectEntity.attributes.VariableReference>} */ this.VariableReference
// Legacy nodes pins
if (this["Pins"] instanceof Array) {
@@ -337,7 +306,7 @@ export default class ObjectEntity extends IEntity {
const pinObject = this[Configuration.subObjectAttributeNameFromReference(objectReference, true)]
if (pinObject) {
const pinEntity = PinEntity.fromLegacyObject(pinObject)
pinEntity.LinkedTo = []
pinEntity.LinkedTo = new (PinEntity.attributes.LinkedTo)()
this.getCustomproperties(true).push(pinEntity)
Utility.objectSet(this, ["attributes", "CustomProperties", "ignored"], true)
}
@@ -356,22 +325,14 @@ export default class ObjectEntity extends IEntity {
if (this.getType() === Configuration.paths.materialExpressionComponentMask) {
// The following attributes are too generic therefore not assigned a MirroredEntity
const rgbaPins = Configuration.rgba.map(pinName =>
this.getPinEntities().find(pin => pin.PinName === pinName && (pin.recomputesNodeTitleOnChange = true))
this.getPinEntities().find(pin => pin.PinName.toString() === pinName && (pin.recomputesNodeTitleOnChange = true))
)
const attribute = {}
obj.R = new MirroredEntity(Boolean, () => rgbaPins[0].DefaultValue)
obj.G = new MirroredEntity(Boolean, () => rgbaPins[1].DefaultValue)
obj.B = new MirroredEntity(Boolean, () => rgbaPins[2].DefaultValue)
obj.A = new MirroredEntity(Boolean, () => rgbaPins[3].DefaultValue)
Utility.objectSet(obj, ["attributes", "R", "default"], false)
Utility.objectSet(obj, ["attributes", "R", "silent"], true)
Utility.objectSet(obj, ["attributes", "G", "default"], false)
Utility.objectSet(obj, ["attributes", "G", "silent"], true)
Utility.objectSet(obj, ["attributes", "B", "default"], false)
Utility.objectSet(obj, ["attributes", "B", "silent"], true)
Utility.objectSet(obj, ["attributes", "A", "default"], false)
Utility.objectSet(obj, ["attributes", "A", "silent"], true)
obj._keys = [...Configuration.rgba, ...Object.keys(obj).filter(k => !Configuration.rgba.includes(k))]
const silentBool = MirroredEntity.of(BooleanEntity).withDefault().flagSilent()
obj["R"] = new silentBool(() => rgbaPins[0].DefaultValue)
obj["G"] = new silentBool(() => rgbaPins[1].DefaultValue)
obj["B"] = new silentBool(() => rgbaPins[2].DefaultValue)
obj["A"] = new silentBool(() => rgbaPins[3].DefaultValue)
obj.keys = [...Configuration.rgba, ...super.keys.filter(k => !Configuration.rgba.includes(k))]
}
}
/** @type {ObjectEntity} */
@@ -383,15 +344,15 @@ export default class ObjectEntity extends IEntity {
/** @param {ObjectEntity} obj */
obj => {
if (obj.Node !== undefined) {
const nodeRef = obj.Node.get()
const nodeRef = obj.Node.getter()
if (
nodeRef.type === this.PCGNode.type
&& nodeRef.path === `${this.Name}.${this.PCGNode.path}`
) {
obj.Node.getter = () => new ObjectReferenceEntity({
type: this.PCGNode.type,
path: `${this.Name}.${this.PCGNode.path}`,
})
obj.Node.getter = () => new ObjectReferenceEntity(
this.PCGNode.type,
`${this.Name}.${this.PCGNode.path}`,
)
}
}
}
@@ -400,7 +361,7 @@ export default class ObjectEntity extends IEntity {
}
let inputIndex = 0
let outputIndex = 0
this.CustomProperties?.forEach((pinEntity, i) => {
this.getCustomproperties().forEach((pinEntity, i) => {
pinEntity.objectEntity = this
pinEntity.pinIndex = pinEntity.isInput()
? inputIndex++
@@ -441,12 +402,12 @@ export default class ObjectEntity extends IEntity {
if (dropCounter) {
return this.getNameAndCounter()[0]
}
return this.Name
return this.Name.valueOf()
}
/** @returns {[String, Number]} */
getNameAndCounter() {
const result = this.getObjectName().match(ObjectEntity.nameRegex)
const result = this.getObjectName().match(ObjectEntity.#nameRegex)
let name = ""
let counter = null
return result
@@ -509,10 +470,7 @@ export default class ObjectEntity extends IEntity {
}
getCustomproperties(canCreate = false) {
if (canCreate && !this.CustomProperties) {
this.CustomProperties = []
}
return this.CustomProperties ?? []
return this.CustomProperties.values
}
/** @returns {PinEntity[]} */
@@ -629,7 +587,7 @@ export default class ObjectEntity extends IEntity {
}
getDelegatePin() {
return this.getCustomproperties().find(pin => pin.PinType.PinCategory === "delegate")
return this.getCustomproperties().find(pin => pin.PinType.PinCategory.toString() === "delegate")
}
nodeColor() {
@@ -643,4 +601,46 @@ export default class ObjectEntity extends IEntity {
additionalPinInserter() {
return nodeVariadic(this)
}
/** @param {String} key */
showProperty(key) {
switch (key) {
case "Class":
case "Name":
case "Archetype":
case "ExportPath":
case "CustomProperties":
// Serielized separately, check doWrite()
return false
}
return super.showProperty(key)
}
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
const moreIndentation = indentation + Configuration.indentation
let result = indentation + "Begin Object"
+ (this.Class?.type || this.Class?.path ? ` Class=${this.Class.toString(insideString)}` : "")
+ (this.Name ? ` Name=${this.Name.toString(insideString)}` : "")
+ (this.Archetype ? ` Archetype=${this.Archetype.toString(insideString)}` : "")
+ (this.ExportPath?.type || this.ExportPath?.path ? ` ExportPath=${this.ExportPath.toString(insideString)}` : "")
+ "\n"
+ super.toString(insideString, moreIndentation, Self, printKey, wrap)
+ (!this.CustomProperties.Self().ignored
? this.getCustomproperties().map(pin =>
moreIndentation
+ printKey("CustomProperties ")
+ pin.toString(insideString)
+ this.Self().attributeSeparator
).join("")
: ""
)
+ indentation + "End Object"
return result
}
}

View File

@@ -1,80 +1,105 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Utility from "../Utility.js"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
export default class ObjectReferenceEntity extends IEntity {
static attributes = {
...super.attributes,
type: new AttributeInfo({
default: "",
serialized: true,
}),
path: new AttributeInfo({
default: "",
serialized: true,
}),
_full: new AttributeInfo({
ignored: true,
}),
}
static quoted = Parsernostrum.regArray(new RegExp(
static #quotedParser = P.regArray(new RegExp(
`'"(${Grammar.Regex.InsideString.source})"'`
+ "|"
+ `'(${Grammar.Regex.InsideSingleQuotedString.source})'`
)).map(([_0, a, b]) => a ?? b)
static path = this.quoted.getParser().parser.regexp.source + "|" + Grammar.Regex.Path.source
static typeReference = Parsernostrum.reg(
static typeReference = P.reg(
// @ts-expect-error
new RegExp(Grammar.Regex.Path.source + "|" + Grammar.symbol.getParser().regexp.source)
)
static fullReferenceGrammar = Parsernostrum.regArray(
static fullReferenceGrammar = P.regArray(
new RegExp(
// @ts-expect-error
"(" + this.typeReference.getParser().regexp.source + ")"
+ "(?:" + this.quoted.getParser().parser.regexp.source + ")"
// @ts-expect-error
+ "(?:" + this.#quotedParser.getParser().parser.regexp.source + ")"
)
).map(([_full, type, ...path]) => new this({ type, path: path.find(v => v), _full }))
static fullReferenceSerializedGrammar = Parsernostrum.regArray(
).map(([full, type, ...path]) => new this(type, path.find(v => v), full))
static fullReferenceSerializedGrammar = P.regArray(
new RegExp(
'"(' + Grammar.Regex.InsideString.source + "?)"
+ "(?:'(" + Grammar.Regex.InsideSingleQuotedString.source + `?)')?"`
)
).map(([_full, type, path]) => new this({ type, path, _full }))
static typeReferenceGrammar = this.typeReference.map(v => new this({ type: v, path: "", _full: v }))
static grammar = this.createGrammar()
constructor(values = {}) {
if (values.constructor === String) {
values = {
path: values
}
}
super(values)
if (!values._full || values._full.length === 0) {
this._full = `"${this.type + (this.path ? (`'${this.path}'`) : "")}"`
}
/** @type {String} */ this.type
/** @type {String} */ this.path
}
static createGrammar() {
return Parsernostrum.alt(
).map(([full, type, path]) => new this(type, path, full))
static typeReferenceGrammar = this.typeReference.map(v => new this(v, "", v))
static grammar = /** @type {P<ObjectReferenceEntity>} */(
P.alt(
this.fullReferenceSerializedGrammar,
this.fullReferenceGrammar,
this.typeReferenceGrammar,
)
).label("ObjectReferenceEntity")
)
#type
get type() {
return this.#type
}
set type(value) {
this.#type = value
}
#path
get path() {
return this.#path
}
set path(value) {
this.#path = value
}
#fullEscaped
/** @type {String} */
#full
get full() {
return this.#full
}
set full(value) {
this.#full = value
}
constructor(type = "None", path = "", full = null) {
super()
this.#type = type
this.#path = path
this.#full = full ?? `"${this.type + (this.path ? (`'${this.path}'`) : "")}"`
}
static createNoneInstance() {
return new ObjectReferenceEntity({ type: "None", path: "" })
return new ObjectReferenceEntity("None")
}
getName(dropCounter = false) {
return Utility.getNameFromPath(this.path.replace(/_C$/, ""), dropCounter)
}
toString() {
return this._full
/** @param {IEntity} other */
equals(other) {
if (!(other instanceof ObjectReferenceEntity)) {
return false
}
return this.type == other.type && this.path == other.path
}
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
if (insideString) {
if (this.#fullEscaped === undefined) {
this.#fullEscaped = Utility.escapeString(this.#full, false)
}
return this.#fullEscaped
}
return this.full
}
}

View File

@@ -1,36 +0,0 @@
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
export default class PathSymbolEntity extends IEntity {
static attributes = {
...super.attributes,
value: new AttributeInfo({
default: "",
}),
}
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.symbol.map(v => new this(v))
}
constructor(values) {
if (values.constructor !== Object) {
values = {
value: values,
}
}
super(values)
/** @type {String} */ this.value
}
valueOf() {
return this.value
}
toString() {
return this.value
}
}

View File

@@ -1,10 +1,13 @@
import P from "parsernostrum"
import Configuration from "../Configuration.js"
import pinColor from "../decoding/pinColor.js"
import pinTitle from "../decoding/pinTitle.js"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import AlternativesEntity from "./AlternativesEntity.js"
import ArrayEntity from "./ArrayEntity.js"
import BooleanEntity from "./BooleanEntity.js"
import ByteEntity from "./ByteEntity.js"
import ComputedType from "./ComputedType.js"
import ComputedTypeEntity from "./ComputedTypeEntity.js"
import EnumDisplayValueEntity from "./EnumDisplayValueEntity.js"
import EnumEntity from "./EnumEntity.js"
import FormatTextEntity from "./FormatTextEntity.js"
@@ -15,6 +18,7 @@ import IntegerEntity from "./IntegerEntity.js"
import InvariantTextEntity from "./InvariantTextEntity.js"
import LinearColorEntity from "./LinearColorEntity.js"
import LocalizedTextEntity from "./LocalizedTextEntity.js"
import NumberEntity from "./NumberEntity.js"
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
import PinReferenceEntity from "./PinReferenceEntity.js"
import PinTypeEntity from "./PinTypeEntity.js"
@@ -24,29 +28,30 @@ import SimpleSerializationRotatorEntity from "./SimpleSerializationRotatorEntity
import SimpleSerializationVector2DEntity from "./SimpleSerializationVector2DEntity.js"
import SimpleSerializationVector4DEntity from "./SimpleSerializationVector4DEntity.js"
import SimpleSerializationVectorEntity from "./SimpleSerializationVectorEntity.js"
import Union from "./Union.js"
import StringEntity from "./StringEntity.js"
import Vector2DEntity from "./Vector2DEntity.js"
import Vector4DEntity from "./Vector4DEntity.js"
import VectorEntity from "./VectorEntity.js"
/** @template {TerminalAttribute} T */
/** @template {IEntity} T */
export default class PinEntity extends IEntity {
static lookbehind = "Pin"
static #typeEntityMap = {
[Configuration.paths.linearColor]: LinearColorEntity,
[Configuration.paths.rotator]: RotatorEntity,
[Configuration.paths.vector]: VectorEntity,
[Configuration.paths.vector2D]: Vector2DEntity,
[Configuration.paths.vector4f]: Vector4DEntity,
"bool": Boolean,
"bool": BooleanEntity,
"byte": ByteEntity,
"enum": EnumEntity,
"exec": String,
"exec": StringEntity,
"int": IntegerEntity,
"int64": Integer64Entity,
"name": String,
"real": Number,
"string": String,
"name": StringEntity,
"real": NumberEntity,
"string": StringEntity,
}
static #alternativeTypeEntityMap = {
"enum": EnumDisplayValueEntity,
@@ -58,52 +63,38 @@ export default class PinEntity extends IEntity {
[Configuration.paths.vector4f]: SimpleSerializationVector4DEntity,
}
static attributes = {
...super.attributes,
lookbehind: new AttributeInfo({
default: "Pin",
ignored: true,
}),
objectEntity: new AttributeInfo({
ignored: true,
}),
pinIndex: new AttributeInfo({
type: Number,
ignored: true,
}),
PinId: new AttributeInfo({
type: GuidEntity,
default: () => new GuidEntity()
}),
PinName: AttributeInfo.createValue(""),
PinFriendlyName: AttributeInfo.createType(new Union(LocalizedTextEntity, FormatTextEntity, InvariantTextEntity, String)),
PinToolTip: AttributeInfo.createType(String),
Direction: AttributeInfo.createType(String),
PinType: new AttributeInfo({
type: PinTypeEntity,
default: () => new PinTypeEntity(),
inlined: true,
}),
LinkedTo: AttributeInfo.createType([PinReferenceEntity]),
SubPins: AttributeInfo.createType([PinReferenceEntity]),
ParentPin: AttributeInfo.createType(PinReferenceEntity),
DefaultValue: new AttributeInfo({
type: new ComputedType(
PinId: GuidEntity.withDefault(),
PinName: StringEntity.withDefault(),
PinFriendlyName: AlternativesEntity.accepting(
LocalizedTextEntity,
FormatTextEntity,
InvariantTextEntity,
StringEntity
),
PinToolTip: StringEntity,
Direction: StringEntity,
PinType: PinTypeEntity.withDefault().flagInlined(),
LinkedTo: ArrayEntity.of(PinReferenceEntity).withDefault().flagSilent(),
SubPins: ArrayEntity.of(PinReferenceEntity),
ParentPin: PinReferenceEntity,
DefaultValue:
ComputedTypeEntity.from(
/** @param {PinEntity} pinEntity */
pinEntity => pinEntity.getEntityType(true) ?? String
),
serialized: true,
}),
AutogeneratedDefaultValue: AttributeInfo.createType(String),
DefaultObject: AttributeInfo.createType(ObjectReferenceEntity),
PersistentGuid: AttributeInfo.createType(GuidEntity),
bHidden: AttributeInfo.createValue(false),
bNotConnectable: AttributeInfo.createValue(false),
bDefaultValueIsReadOnly: AttributeInfo.createValue(false),
bDefaultValueIsIgnored: AttributeInfo.createValue(false),
bAdvancedView: AttributeInfo.createValue(false),
bOrphanedPin: AttributeInfo.createValue(false),
pinEntity => pinEntity.getEntityType(true) ?? StringEntity
).flagSerialized(),
AutogeneratedDefaultValue: StringEntity,
DefaultObject: ObjectReferenceEntity,
PersistentGuid: GuidEntity,
bHidden: BooleanEntity.withDefault(),
bNotConnectable: BooleanEntity.withDefault(),
bDefaultValueIsReadOnly: BooleanEntity.withDefault(),
bDefaultValueIsIgnored: BooleanEntity.withDefault(),
bAdvancedView: BooleanEntity.withDefault(),
bOrphanedPin: BooleanEntity.withDefault(),
}
static grammar = this.createGrammar()
static grammar = /** @type {P<PinEntity>} */(
Grammar.createEntityGrammar(this)
)
#recomputesNodeTitleOnChange = false
set recomputesNodeTitleOnChange(value) {
@@ -113,42 +104,52 @@ export default class PinEntity extends IEntity {
return this.#recomputesNodeTitleOnChange
}
static createGrammar() {
return Grammar.createEntityGrammar(this)
#objectEntity
get objectEntity() {
return this.#objectEntity
}
set objectEntity(value) {
this.#objectEntity = value
}
constructor(values = {}, suppressWarns = false) {
super(values, suppressWarns)
/** @type {ObjectEntity} */ this.objectEntity
/** @type {Number} */ this.pinIndex
/** @type {GuidEntity} */ this.PinId
/** @type {String} */ this.PinName
/** @type {LocalizedTextEntity | String} */ this.PinFriendlyName
/** @type {String} */ this.PinToolTip
/** @type {String} */ this.Direction
/** @type {PinTypeEntity} */ this.PinType
/** @type {PinReferenceEntity[]} */ this.LinkedTo
#pinIndex
get pinIndex() {
return this.#pinIndex
}
set pinIndex(value) {
this.#pinIndex = value
}
constructor(values = {}) {
super(values)
/** @type {InstanceType<typeof PinEntity.attributes.PinId>} */ this.PinId
/** @type {InstanceType<typeof PinEntity.attributes.PinName>} */ this.PinName
/** @type {InstanceType<typeof PinEntity.attributes.PinFriendlyName>} */ this.PinFriendlyName
/** @type {InstanceType<typeof PinEntity.attributes.PinToolTip>} */ this.PinToolTip
/** @type {InstanceType<typeof PinEntity.attributes.Direction>} */ this.Direction
/** @type {InstanceType<typeof PinEntity.attributes.PinType>} */ this.PinType
/** @type {InstanceType<typeof PinEntity.attributes.LinkedTo>} */ this.LinkedTo
/** @type {T} */ this.DefaultValue
/** @type {String} */ this.AutogeneratedDefaultValue
/** @type {ObjectReferenceEntity} */ this.DefaultObject
/** @type {GuidEntity} */ this.PersistentGuid
/** @type {Boolean} */ this.bHidden
/** @type {Boolean} */ this.bNotConnectable
/** @type {Boolean} */ this.bDefaultValueIsReadOnly
/** @type {Boolean} */ this.bDefaultValueIsIgnored
/** @type {Boolean} */ this.bAdvancedView
/** @type {Boolean} */ this.bOrphanedPin
/** @type {InstanceType<typeof PinEntity.attributes.AutogeneratedDefaultValue>} */ this.AutogeneratedDefaultValue
/** @type {InstanceType<typeof PinEntity.attributes.DefaultObject>} */ this.DefaultObject
/** @type {InstanceType<typeof PinEntity.attributes.PersistentGuid>} */ this.PersistentGuid
/** @type {InstanceType<typeof PinEntity.attributes.bHidden>} */ this.bHidden
/** @type {InstanceType<typeof PinEntity.attributes.bNotConnectable>} */ this.bNotConnectable
/** @type {InstanceType<typeof PinEntity.attributes.bDefaultValueIsReadOnly>} */ this.bDefaultValueIsReadOnly
/** @type {InstanceType<typeof PinEntity.attributes.bDefaultValueIsIgnored>} */ this.bDefaultValueIsIgnored
/** @type {InstanceType<typeof PinEntity.attributes.bAdvancedView>} */ this.bAdvancedView
/** @type {InstanceType<typeof PinEntity.attributes.bOrphanedPin>} */ this.bOrphanedPin
}
/** @param {ObjectEntity} objectEntity */
static fromLegacyObject(objectEntity) {
return new PinEntity(objectEntity, true)
return new PinEntity(objectEntity)
}
getType() {
const category = this.PinType.PinCategory.toLocaleLowerCase()
const category = this.PinType.PinCategory?.valueOf().toLocaleLowerCase()
if (category === "struct" || category === "class" || category === "object" || category === "type") {
return this.PinType.PinSubCategoryObject.path
return this.PinType.PinSubCategoryObject?.path
}
if (this.isEnum()) {
return "enum"
@@ -161,7 +162,7 @@ export default class PinEntity extends IEntity {
if (pinObjectReference) {
/** @type {ObjectEntity} */
const pinObject = pcgSuboject[Configuration.subObjectAttributeNameFromReference(pinObjectReference, true)]
let allowedTypes = pinObject.Properties?.AllowedTypes?.toString() ?? ""
let allowedTypes = pinObject.Properties?.AllowedTypes?.valueOf() ?? ""
if (allowedTypes == "") {
allowedTypes = this.PinType.PinCategory ?? ""
if (allowedTypes == "") {
@@ -170,8 +171,8 @@ export default class PinEntity extends IEntity {
}
if (allowedTypes) {
if (
pinObject.Properties.bAllowMultipleData !== false
&& pinObject.Properties.bAllowMultipleConnections !== false
pinObject.Properties.bAllowMultipleData?.valueOf() !== false
&& pinObject.Properties.bAllowMultipleConnections?.valueOf() !== false
) {
allowedTypes += "[]"
}
@@ -180,7 +181,8 @@ export default class PinEntity extends IEntity {
}
}
if (category === "optional") {
switch (this.PinType.PinSubCategory) {
const subCategory = this.PinType.PinSubCategory?.valueOf()
switch (subCategory) {
case "red":
return "real"
case "rg":
@@ -190,12 +192,13 @@ export default class PinEntity extends IEntity {
case "rgba":
return Configuration.paths.linearColor
default:
return this.PinType.PinSubCategory
return subCategory
}
}
return category
}
/** @returns {typeof IEntity} */
getEntityType(alternative = false) {
const typeString = this.getType()
const entity = PinEntity.#typeEntityMap[typeString]
@@ -211,22 +214,11 @@ export default class PinEntity extends IEntity {
/** @param {PinEntity} other */
copyTypeFrom(other) {
this.PinType.PinCategory = other.PinType.PinCategory
this.PinType.PinSubCategory = other.PinType.PinSubCategory
this.PinType.PinSubCategoryObject = other.PinType.PinSubCategoryObject
this.PinType.PinSubCategoryMemberReference = other.PinType.PinSubCategoryMemberReference
this.PinType.PinValueType = other.PinType.PinValueType
this.PinType.ContainerType = other.PinType.ContainerType
this.PinType.bIsReference = other.PinType.bIsReference
this.PinType.bIsConst = other.PinType.bIsConst
this.PinType.bIsWeakPointer = other.PinType.bIsWeakPointer
this.PinType.bIsUObjectWrapper = other.PinType.bIsUObjectWrapper
this.PinType.bSerializeAsSinglePrecisionFloat = other.PinType.bSerializeAsSinglePrecisionFloat
this.PinType = other.PinType
}
getDefaultValue(maybeCreate = false) {
if (this.DefaultValue === undefined && maybeCreate) {
// @ts-expect-error
this.DefaultValue = new (this.getEntityType(true))()
}
return this.DefaultValue
@@ -240,19 +232,19 @@ export default class PinEntity extends IEntity {
}
isExecution() {
return this.PinType.PinCategory === "exec"
return this.PinType.PinCategory.toString() === "exec"
}
isHidden() {
return this.bHidden
return this.bHidden?.valueOf()
}
isInput() {
return !this.bHidden && this.Direction != "EGPD_Output"
return !this.isHidden() && this.Direction.valueOf() != "EGPD_Output"
}
isOutput() {
return !this.bHidden && this.Direction == "EGPD_Output"
return !this.isHidden() && this.Direction.valueOf() == "EGPD_Output"
}
isLinked() {
@@ -265,15 +257,12 @@ export default class PinEntity extends IEntity {
* @returns true if it was not already linked to the tarket
*/
linkTo(targetObjectName, targetPinEntity) {
const linkFound = this.LinkedTo?.some(pinReferenceEntity =>
const linkFound = this.LinkedTo.values?.some(pinReferenceEntity =>
pinReferenceEntity.objectName.toString() == targetObjectName
&& pinReferenceEntity.pinGuid.valueOf() == targetPinEntity.PinId.valueOf()
)
if (!linkFound) {
(this.LinkedTo ??= []).push(new PinReferenceEntity({
objectName: targetObjectName,
pinGuid: targetPinEntity.PinId,
}))
this.LinkedTo.values.push(new PinReferenceEntity(targetObjectName, targetPinEntity.PinId,))
return true
}
return false // Already linked
@@ -285,12 +274,12 @@ export default class PinEntity extends IEntity {
* @returns true if it was linked to the target
*/
unlinkFrom(targetObjectName, targetPinEntity) {
const indexElement = this.LinkedTo?.findIndex(pinReferenceEntity => {
const indexElement = this.LinkedTo.values?.findIndex(pinReferenceEntity => {
return pinReferenceEntity.objectName.toString() == targetObjectName
&& pinReferenceEntity.pinGuid.valueOf() == targetPinEntity.PinId.valueOf()
})
if (indexElement >= 0) {
this.LinkedTo.splice(indexElement, 1)
this.LinkedTo.values.splice(indexElement, 1)
if (this.LinkedTo.length === 0 && PinEntity.attributes.LinkedTo.default === undefined) {
this.LinkedTo = undefined
}

View File

@@ -1,34 +1,31 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import GuidEntity from "./GuidEntity.js"
import IEntity from "./IEntity.js"
import PathSymbolEntity from "./PathSymbolEntity.js"
import AttributeInfo from "./AttributeInfo.js"
import SymbolEntity from "./SymbolEntity.js"
export default class PinReferenceEntity extends IEntity {
static attributes = {
...super.attributes,
objectName: AttributeInfo.createType(PathSymbolEntity),
pinGuid: AttributeInfo.createType(GuidEntity),
}
static grammar = this.createGrammar()
static createGrammar() {
return Parsernostrum.seq(
PathSymbolEntity.grammar,
Parsernostrum.whitespace,
static grammar = /** @type {P<PinReferenceEntity>} */(
P.seq(
SymbolEntity.grammar,
P.whitespace,
GuidEntity.grammar
).map(
([objectName, _1, pinGuid]) => new this({
objectName: objectName,
pinGuid: pinGuid,
})
)
.map(([objectName, _1, pinGuid]) => new this(objectName, pinGuid))
.label("PinReferenceEntity")
)
/**
* @param {SymbolEntity} objectName
* @param {GuidEntity} pinGuid
*/
constructor(objectName = null, pinGuid = null) {
super()
this.objectName = objectName
this.pinGuid = pinGuid
}
constructor(values) {
super(values)
/** @type {PathSymbolEntity} */ this.objectName
/** @type {GuidEntity} */ this.pinGuid
toString() {
return this.objectName.toString() + " " + this.pinGuid.toString()
}
}

View File

@@ -1,69 +1,52 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import BooleanEntity from "./BooleanEntity.js"
import FunctionReferenceEntity from "./FunctionReferenceEntity.js"
import IEntity from "./IEntity.js"
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
import PathSymbolEntity from "./PathSymbolEntity.js"
import StringEntity from "./StringEntity.js"
import SymbolEntity from "./SymbolEntity.js"
export default class PinTypeEntity extends IEntity {
static attributes = {
...super.attributes,
PinCategory: AttributeInfo.createValue(""),
PinSubCategory: AttributeInfo.createValue(""),
PinSubCategoryObject: new AttributeInfo({
type: ObjectReferenceEntity,
default: () => ObjectReferenceEntity.createNoneInstance(),
}),
PinSubCategoryMemberReference: new AttributeInfo({
type: FunctionReferenceEntity,
default: null,
}),
PinValueType: new AttributeInfo({
type: PinTypeEntity,
default: null,
}),
ContainerType: AttributeInfo.createType(PathSymbolEntity),
bIsReference: AttributeInfo.createValue(false),
bIsConst: AttributeInfo.createValue(false),
bIsWeakPointer: AttributeInfo.createValue(false),
bIsUObjectWrapper: AttributeInfo.createValue(false),
bSerializeAsSinglePrecisionFloat: AttributeInfo.createValue(false),
PinCategory: StringEntity.withDefault(),
PinSubCategory: StringEntity.withDefault(),
PinSubCategoryObject: ObjectReferenceEntity.withDefault(),
PinSubCategoryMemberReference: FunctionReferenceEntity.withDefault(),
ContainerType: SymbolEntity,
bIsReference: BooleanEntity.withDefault(),
bIsConst: BooleanEntity.withDefault(),
bIsWeakPointer: BooleanEntity.withDefault(),
bIsUObjectWrapper: BooleanEntity.withDefault(),
bSerializeAsSinglePrecisionFloat: BooleanEntity.withDefault(),
}
static grammar = this.createGrammar()
static grammar = /** @type {P<PinTypeEntity>} */(
Grammar.createEntityGrammar(this).label("PinTypeEntity")
)
static createGrammar() {
return Grammar.createEntityGrammar(this)
}
constructor(values = {}, suppressWarns = false) {
super(values, suppressWarns)
/** @type {String} */ this.PinCategory
/** @type {String} */ this.PinSubCategory
/** @type {ObjectReferenceEntity} */ this.PinSubCategoryObject
/** @type {FunctionReferenceEntity} */ this.PinSubCategoryMemberReference
/** @type {PinTypeEntity} */ this.PinValueType
/** @type {PathSymbolEntity} */ this.ContainerType
/** @type {Boolean} */ this.bIsReference
/** @type {Boolean} */ this.bIsConst
/** @type {Boolean} */ this.bIsWeakPointer
/** @type {Boolean} */ this.bIsUObjectWrapper
/** @type {Boolean} */ this.bIsUObjectWrapper
/** @type {Boolean} */ this.bSerializeAsSinglePrecisionFloat
constructor(values = {}) {
super(values)
/** @type {InstanceType<typeof PinTypeEntity.attributes.PinCategory>} */ this.PinCategory
/** @type {InstanceType<typeof PinTypeEntity.attributes.PinSubCategory>} */ this.PinSubCategory
/** @type {InstanceType<typeof PinTypeEntity.attributes.PinSubCategoryObject>} */ this.PinSubCategoryObject
/** @type {InstanceType<typeof PinTypeEntity.attributes.PinSubCategoryMemberReference>} */ this.PinSubCategoryMemberReference
/** @type {InstanceType<typeof PinTypeEntity.attributes.ContainerType>} */ this.ContainerType
/** @type {InstanceType<typeof PinTypeEntity.attributes.bIsReference>} */ this.bIsReference
/** @type {InstanceType<typeof PinTypeEntity.attributes.bIsConst>} */ this.bIsConst
/** @type {InstanceType<typeof PinTypeEntity.attributes.bIsWeakPointer>} */ this.bIsWeakPointer
/** @type {InstanceType<typeof PinTypeEntity.attributes.bIsUObjectWrapper>} */ this.bIsUObjectWrapper
/** @type {InstanceType<typeof PinTypeEntity.attributes.bIsUObjectWrapper>} */ this.bIsUObjectWrapper
/** @type {InstanceType<typeof PinTypeEntity.attributes.bSerializeAsSinglePrecisionFloat>} */ this.bSerializeAsSinglePrecisionFloat
}
/** @param {PinTypeEntity} other */
copyTypeFrom(other) {
this.PinCategory = other.PinCategory
this.PinSubCategory = other.PinSubCategory
this.PinSubCategoryObject = other.PinSubCategoryObject
this.PinSubCategoryMemberReference = other.PinSubCategoryMemberReference
this.PinValueType = other.PinValueType
this.ContainerType = other.ContainerType
this.bIsReference = other.bIsReference
this.bIsConst = other.bIsConst
this.bIsWeakPointer = other.bIsWeakPointer
this.bIsUObjectWrapper = other.bIsUObjectWrapper
this.bSerializeAsSinglePrecisionFloat = other.bSerializeAsSinglePrecisionFloat
for (const key of this.keys) {
if (other[key] !== undefined) {
this[key] = other[key]
}
}
}
}

View File

@@ -1,21 +1,18 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import Vector2DEntity from "./Vector2DEntity.js"
export default class RBSerializationVector2DEntity extends Vector2DEntity {
static grammar = this.createGrammar()
static createGrammar() {
return Parsernostrum.alt(
Parsernostrum.regArray(new RegExp(
/X\s*=\s*/.source + "(?<x>" + Parsernostrum.number.getParser().parser.regexp.source + ")"
+ "\\s+"
+ /Y\s*=\s*/.source + "(?<y>" + Parsernostrum.number.getParser().parser.regexp.source + ")"
)).map(({ groups: { x, y } }) => new this({
X: Number(x),
Y: Number(y),
})),
Vector2DEntity.grammar
)
}
static grammar = /** @type {P<RBSerializationVector2DEntity>} */(P.alt(
P.regArray(new RegExp(
/X\s*=\s*/.source + "(?<x>" + Grammar.numberRegexSource + ")"
+ "\\s+"
+ /Y\s*=\s*/.source + "(?<y>" + Grammar.numberRegexSource + ")"
)).map(({ groups: { x, y } }) => new this({
X: Number(x),
Y: Number(y),
})),
Vector2DEntity.grammar
).label("RBSerializationVector2DEntity"))
}

View File

@@ -1,35 +1,25 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
import NumberEntity from "./NumberEntity.js"
export default class RotatorEntity extends IEntity {
static attributes = {
...super.attributes,
R: new AttributeInfo({
default: 0,
expected: true,
}),
P: new AttributeInfo({
default: 0,
expected: true,
}),
Y: new AttributeInfo({
default: 0,
expected: true,
}),
}
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.createEntityGrammar(this, false)
R: NumberEntity.withDefault(),
P: NumberEntity.withDefault(),
Y: NumberEntity.withDefault(),
}
static grammar = /** @type {P<RotatorEntity>} */(
Grammar.createEntityGrammar(this, Grammar.commaSeparation, true).label("RotatorEntity")
)
constructor(values) {
super(values)
/** @type {Number} */ this.R
/** @type {Number} */ this.P
/** @type {Number} */ this.Y
/** @type {InstanceType<typeof RotatorEntity.attributes.R>} */ this.R
/** @type {InstanceType<typeof RotatorEntity.attributes.P>} */ this.P
/** @type {InstanceType<typeof RotatorEntity.attributes.Y>} */ this.Y
}
getRoll() {

View File

@@ -1,5 +1,5 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import GuidEntity from "./GuidEntity.js"
import IEntity from "./IEntity.js"
import ObjectReferenceEntity from "./ObjectReferenceEntity.js"
@@ -8,18 +8,16 @@ export default class ScriptVariableEntity extends IEntity {
static attributes = {
...super.attributes,
ScriptVariable: AttributeInfo.createType(ObjectReferenceEntity),
OriginalChangeId: AttributeInfo.createType(GuidEntity),
ScriptVariable: ObjectReferenceEntity,
OriginalChangeId: GuidEntity,
}
static grammar = this.createGrammar()
static grammar = /** @type {P<ScriptVariableEntity>} */(
Grammar.createEntityGrammar(this).label("ScriptVariableEntity")
)
static createGrammar() {
return Grammar.createEntityGrammar(this)
}
constructor(values = {}, suppressWarns = false) {
super(values, suppressWarns)
/** @type {ObjectReferenceEntity} */ this.ScriptVariable
/** @type {GuidEntity} */ this.OriginalChangeId
constructor(values = {}) {
super(values)
/** @type {InstanceType<typeof ScriptVariableEntity.attributes.ScriptVariable>} */ this.ScriptVariable
/** @type {InstanceType<typeof ScriptVariableEntity.attributes.OriginalChangeId>} */ this.OriginalChangeId
}
}

View File

@@ -1,25 +1,41 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import RotatorEntity from "./RotatorEntity.js"
import NumberEntity from "./NumberEntity.js"
export default class SimpleSerializationRotatorEntity extends RotatorEntity {
static grammar = this.createGrammar()
static createGrammar() {
const number = Parsernostrum.number.getParser().parser.regexp.source
return Parsernostrum.alt(
Parsernostrum.regArray(new RegExp(
"(" + number + ")"
+ "\\s*,\\s*"
+ "(" + number + ")"
+ "\\s*,\\s*"
+ "(" + number + ")"
)).map(([_, p, y, r]) => new this({
R: Number(r),
P: Number(p),
Y: Number(y),
static attributeSeparator = ", "
static grammar = /** @type {P<SimpleSerializationRotatorEntity>} */(
P.alt(
P.regArray(new RegExp(
`(${NumberEntity.numberRegexSource})`
+ String.raw`\s*,\s*`
+ `(${NumberEntity.numberRegexSource})`
+ String.raw`\s*,\s*`
+ `(${NumberEntity.numberRegexSource})`
)).map(([_, p, pPrecision, y, yPrecision, r, rPrecision]) => new this({
R: new NumberEntity(r, rPrecision?.length),
P: new NumberEntity(p, pPrecision?.length),
Y: new NumberEntity(y, yPrecision?.length),
})),
RotatorEntity.grammar
)
RotatorEntity.grammar.map(v => new this({
R: v.R,
P: v.P,
Y: v.Y,
}))
).label("SimpleSerializationRotatorEntity")
)
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
return this.P.toString() + Self.attributeSeparator
+ this.Y.toString() + Self.attributeSeparator
+ this.R.toString() + (this.trailing ? Self.attributeSeparator : "")
}
}

View File

@@ -1,22 +1,36 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import Vector2DEntity from "./Vector2DEntity.js"
import NumberEntity from "./NumberEntity.js"
export default class SimpleSerializationVector2DEntity extends Vector2DEntity {
static grammar = this.createGrammar()
static createGrammar() {
const number = Parsernostrum.number.getParser().parser.regexp.source
return Parsernostrum.alt(
Parsernostrum.regArray(new RegExp(
"(" + number + ")"
+ "\\s*,\\s*"
+ "(" + number + ")"
)).map(([_, x, y]) => new this({
X: Number(x),
Y: Number(y),
static attributeSeparator = ", "
static grammar = /** @type {P<SimpleSerializationVector2DEntity>} */(
P.alt(
P.regArray(new RegExp(
`(${NumberEntity.numberRegexSource})`
+ String.raw`\s*,\s*`
+ `(${NumberEntity.numberRegexSource})`
)).map(([_, x, xPrecision, y, yPrecision]) => new this({
X: new NumberEntity(x, xPrecision?.length),
Y: new NumberEntity(y, yPrecision?.length),
})),
Vector2DEntity.grammar
)
Vector2DEntity.grammar.map(v => new this({
X: v.X,
Y: v.Y,
}))
).label("SimpleSerializationVector2DEntity")
)
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
return this.X.toString() + Self.attributeSeparator
+ this.Y.toString() + (this.trailing ? Self.attributeSeparator : "")
}
}

View File

@@ -1,21 +1,22 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import Vector4DEntity from "./Vector4DEntity.js"
export default class SimpleSerializationVector4DEntity extends Vector4DEntity {
static grammar = this.createGrammar()
static grammar = /** @type {P<SimpleSerializationVector4DEntity> } */(this.createGrammar())
static createGrammar() {
const number = Parsernostrum.number.getParser().parser.regexp.source
return Parsernostrum.alt(
Parsernostrum.regArray(new RegExp(
"(" + number + ")"
+ "\\s*,\\s*"
+ "(" + number + ")"
+ "\\s*,\\s*"
+ "(" + number + ")"
+ "\\s*,\\s*"
+ "(" + number + ")"
const number = Grammar.numberRegexSource
return P.alt(
P.regArray(new RegExp(
`(${Grammar.numberRegexSource})`
+ String.raw`\s*,\s*`
+ `(${Grammar.numberRegexSource})`
+ String.raw`\s*,\s*`
+ `(${Grammar.numberRegexSource})`
+ String.raw`\s*,\s*`
+ `(${Grammar.numberRegexSource})`
))
.map(([_0, x, y, z, w]) => new this({
X: Number(x),

View File

@@ -1,26 +1,42 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import VectorEntity from "./VectorEntity.js"
import NumberEntity from "./NumberEntity.js"
export default class SimpleSerializationVectorEntity extends VectorEntity {
static grammar = this.createGrammar()
static createGrammar() {
const number = Parsernostrum.number.getParser().parser.regexp.source
return Parsernostrum.alt(
Parsernostrum.regArray(new RegExp(
"(" + number + ")"
+ "\\s*,\\s*"
+ "(" + number + ")"
+ "\\s*,\\s*"
+ "(" + number + ")"
static attributeSeparator = ", "
static grammar = /** @type {P<SimpleSerializationVectorEntity>} */(
P.alt(
P.regArray(new RegExp(
`(${NumberEntity.numberRegexSource})`
+ String.raw`\s*,\s*`
+ `(${NumberEntity.numberRegexSource})`
+ String.raw`\s*,\s*`
+ `(${NumberEntity.numberRegexSource})`
))
.map(([_0, x, y, z]) => new this({
X: Number(x),
Y: Number(y),
Z: Number(z),
.map(([_, x, xPrecision, y, yPrecision, z, zPrecision]) => new this({
X: new NumberEntity(x, xPrecision?.length),
Y: new NumberEntity(y, yPrecision?.length),
Z: new NumberEntity(z, zPrecision?.length),
})),
VectorEntity.grammar
VectorEntity.grammar.map(v => new this({
X: v.X,
Y: v.Y,
Z: v.Z,
}))
)
)
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
return this.X.toString() + Self.attributeSeparator
+ this.Y.toString() + Self.attributeSeparator
+ this.Z.toString() + (this.trailing ? Self.attributeSeparator : "")
}
}

36
js/entity/StringEntity.js Executable file
View File

@@ -0,0 +1,36 @@
import P from "parsernostrum"
import Utility from "../Utility.js"
import IEntity from "./IEntity.js"
export default class StringEntity extends IEntity {
static grammar = /** @type {P<StringEntity>} */(
P.doubleQuotedString
.map(insideString => new this(Utility.unescapeString(insideString)))
.label("StringEntity")
)
/** @param {String} value */
constructor(value = "") {
super()
this.value = value
}
valueOf() {
return this.value
}
toString(
insideString = false,
indentation = "",
Self = this.Self(),
printKey = Self.printKey,
wrap = Self.wrap,
) {
let result = `"${Utility.escapeString(this.value)}"`
if (insideString) {
result = Utility.escapeString(result, false)
}
return result
}
}

View File

@@ -1,28 +1,20 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
export default class SymbolEntity extends IEntity {
static attributes = {
...super.attributes,
value: AttributeInfo.createValue(""),
static attributeConverter = {
fromAttribute: (value, type) => new this(value),
toAttribute: (value, type) => value.toString()
}
static grammar = this.createGrammar()
static grammar = /** @type {P<SymbolEntity>} */(
Grammar.symbol.map(v => new this(v)).label("SymbolEntity")
)
static createGrammar() {
return Grammar.symbol.map(v => new this(v))
}
/** @param {String | Object} values */
constructor(values) {
if (values.constructor !== Object) {
values = {
value: values,
}
}
super(values)
/** @type {String} */ this.value
constructor(value = "") {
super()
this.value = value
}
valueOf() {

View File

@@ -1,16 +1,22 @@
import AttributeInfo from "./AttributeInfo.js"
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import BooleanEntity from "./BooleanEntity.js"
import IEntity from "./IEntity.js"
import StringEntity from "./StringEntity.js"
export default class TerminalTypeEntity extends IEntity {
static attributes = {
...super.attributes,
TerminalCategory: AttributeInfo.createType(String),
TerminalSubCategory: AttributeInfo.createType(String),
bTerminalIsConst: AttributeInfo.createType(Boolean),
bTerminalIsWeakPointer: AttributeInfo.createType(Boolean),
bTerminalIsUObjectWrapper: AttributeInfo.createType(Boolean),
TerminalCategory: StringEntity,
TerminalSubCategory: StringEntity,
bTerminalIsConst: BooleanEntity,
bTerminalIsWeakPointer: BooleanEntity,
bTerminalIsUObjectWrapper: BooleanEntity,
}
static grammar = /** @type {P<TerminalTypeEntity>} */(
Grammar.createEntityGrammar(this)
)
constructor(values) {
super(values)

View File

@@ -1,14 +0,0 @@
/** @template {any[]} T */
export default class Union {
/** @type {T} */
#values
get values() {
return this.#values
}
/** @param {T} values */
constructor(...values) {
this.#values = values
}
}

View File

@@ -1,28 +1,21 @@
import Parsernostrum from "parsernostrum"
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
export default class UnknownKeysEntity extends IEntity {
static grammar = this.createGrammar()
static createGrammar() {
return Parsernostrum.seq(
static grammar = /** @type {P<UnknownKeysEntity>} */(
P.seq(
// Lookbehind
Parsernostrum.reg(
new RegExp(`(${Grammar.Regex.Path.source}|${Grammar.Regex.Symbol.source}\\s*)?\\(\\s*`),
1
),
Parsernostrum.seq(Grammar.attributeName, Grammar.equalSeparation).map(([attribute, equal]) => attribute)
P.reg(new RegExp(`(${Grammar.Regex.Path.source}|${Grammar.Regex.Symbol.source}\\s*)?\\(\\s*`), 1),
P.seq(Grammar.attributeName, Grammar.equalSeparation).map(([attribute, equal]) => attribute)
.chain(attributeName =>
Grammar.unknownValue.map(attributeValue =>
this.unknownEntityGrammar.map(attributeValue =>
values => values[attributeName] = attributeValue
)
)
.sepBy(Grammar.commaSeparation),
Parsernostrum.reg(/\s*(?:,\s*)?\)/),
P.reg(/\s*(?:,\s*)?\)/),
).map(([lookbehind, attributes, _2]) => {
lookbehind ??= ""
let values = {}
@@ -31,10 +24,10 @@ export default class UnknownKeysEntity extends IEntity {
}
attributes.forEach(attributeSetter => attributeSetter(values))
return new this(values)
})
}
}).label("UnknownKeysEntity")
)
constructor(values) {
super(values, true)
static {
IEntity.unknownEntity = this
}
}

View File

@@ -1,19 +1,14 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import Parsernostrum from "parsernostrum"
import PinEntity from "./PinEntity.js"
export default class UnknownPinEntity extends PinEntity {
static grammar = this.createGrammar()
static createGrammar() {
return Parsernostrum.seq(
Parsernostrum.reg(
new RegExp(`(${Grammar.Regex.Symbol.source})\\s*\\(\\s*`),
1
),
static grammar = /** @type {P<UnknownPinEntity>} */(
P.seq(
P.reg(new RegExp(`(${Grammar.Regex.Symbol.source})\\s*\\(\\s*`), 1),
Grammar.createAttributeGrammar(this).sepBy(Grammar.commaSeparation),
Parsernostrum.reg(/\s*(?:,\s*)?\)/)
P.reg(/\s*(?:,\s*)?\)/)
).map(([lookbehind, attributes, _2]) => {
lookbehind ??= ""
let values = {}
@@ -22,10 +17,6 @@ export default class UnknownPinEntity extends PinEntity {
}
attributes.forEach(attributeSetter => attributeSetter(values))
return new this(values)
})
}
constructor(values = {}) {
super(values, true)
}
}).label("UnknownPinEntity")
)
}

View File

@@ -1,27 +1,28 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import BooleanEntity from "./BooleanEntity.js"
import GuidEntity from "./GuidEntity.js"
import IEntity from "./IEntity.js"
import StringEntity from "./StringEntity.js"
export default class VariableReferenceEntity extends IEntity {
static attributes = {
...super.attributes,
MemberScope: AttributeInfo.createType(String),
MemberName: AttributeInfo.createValue(""),
MemberGuid: AttributeInfo.createType(GuidEntity),
bSelfContext: AttributeInfo.createType(Boolean),
}
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.createEntityGrammar(this)
MemberScope: StringEntity,
MemberName: StringEntity.withDefault(),
MemberGuid: GuidEntity,
bSelfContext: BooleanEntity,
}
static grammar = /** @type {P<VariableReferenceEntity>} */(
Grammar.createEntityGrammar(this).label("VariableReferenceEntity")
)
constructor(values) {
super(values)
/** @type {String} */ this.MemberName
/** @type {GuidEntity} */ this.GuidEntity
/** @type {Boolean} */ this.bSelfContext
/** @type {InstanceType<typeof VariableReferenceEntity.attributes.MemberScope>} */ this.MemberScope
/** @type {InstanceType<typeof VariableReferenceEntity.attributes.MemberName>} */ this.MemberName
/** @type {InstanceType<typeof VariableReferenceEntity.attributes.MemberGuid>} */ this.MemberGuid
/** @type {InstanceType<typeof VariableReferenceEntity.attributes.bSelfContext>} */ this.bSelfContext
}
}

View File

@@ -1,34 +1,27 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
import NumberEntity from "./NumberEntity.js"
export default class Vector2DEntity extends IEntity {
static attributes = {
...super.attributes,
X: new AttributeInfo({
default: 0,
expected: true,
}),
Y: new AttributeInfo({
default: 0,
expected: true,
}),
}
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.createEntityGrammar(this, false)
X: NumberEntity.withDefault(),
Y: NumberEntity.withDefault(),
}
static grammar = /** @type {P<Vector2DEntity>} */(
Grammar.createEntityGrammar(this, Grammar.commaSeparation, true).label("Vector2DEntity")
)
constructor(values) {
super(values)
/** @type {Number} */ this.X
/** @type {Number} */ this.Y
/** @type {InstanceType<typeof Vector2DEntity.attributes.X>} */ this.X
/** @type {InstanceType<typeof Vector2DEntity.attributes.Y>} */ this.Y
}
/** @returns {[Number, Number]} */
toArray() {
return [this.X, this.Y]
return [this.X.valueOf(), this.Y.valueOf()]
}
}

View File

@@ -1,44 +1,31 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
import NumberEntity from "./NumberEntity.js"
export default class Vector4DEntity extends IEntity {
static attributes = {
...super.attributes,
X: new AttributeInfo({
default: 0,
expected: true,
}),
Y: new AttributeInfo({
default: 0,
expected: true,
}),
Z: new AttributeInfo({
default: 0,
expected: true,
}),
W: new AttributeInfo({
default: 0,
expected: true,
}),
}
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.createEntityGrammar(Vector4DEntity, false)
X: NumberEntity.withDefault(),
Y: NumberEntity.withDefault(),
Z: NumberEntity.withDefault(),
W: NumberEntity.withDefault(),
}
static grammar = /** @type {P<Vector4DEntity>} */(
Grammar.createEntityGrammar(this, Grammar.commaSeparation, true).label("Vector4DEntity")
)
constructor(values) {
super(values)
/** @type {Number} */ this.X
/** @type {Number} */ this.Y
/** @type {Number} */ this.Z
/** @type {Number} */ this.W
/** @type {InstanceType<typeof Vector4DEntity.attributes.X>} */ this.X
/** @type {InstanceType<typeof Vector4DEntity.attributes.Y>} */ this.Y
/** @type {InstanceType<typeof Vector4DEntity.attributes.Z>} */ this.Z
/** @type {InstanceType<typeof Vector4DEntity.attributes.W>} */ this.W
}
/** @returns {[Number, Number, Number, Number]} */
toArray() {
return [this.X, this.Y, this.Z, this.W]
return [this.X.valueOf(), this.Y.valueOf(), this.Z.valueOf(), this.W.valueOf()]
}
}

View File

@@ -1,39 +1,29 @@
import P from "parsernostrum"
import Grammar from "../serialization/Grammar.js"
import AttributeInfo from "./AttributeInfo.js"
import IEntity from "./IEntity.js"
import NumberEntity from "./NumberEntity.js"
export default class VectorEntity extends IEntity {
static attributes = {
...super.attributes,
X: new AttributeInfo({
default: 0,
expected: true,
}),
Y: new AttributeInfo({
default: 0,
expected: true,
}),
Z: new AttributeInfo({
default: 0,
expected: true,
}),
}
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.createEntityGrammar(VectorEntity, false)
X: NumberEntity.withDefault(),
Y: NumberEntity.withDefault(),
Z: NumberEntity.withDefault(),
}
static grammar = /** @type {P<VectorEntity>} */(
Grammar.createEntityGrammar(this, Grammar.commaSeparation, true).label("VectorEntity")
)
constructor(values) {
super(values)
/** @type {Number} */ this.X
/** @type {Number} */ this.Y
/** @type {Number} */ this.Z
/** @type {InstanceType<typeof VectorEntity.attributes.X>} */ this.X
/** @type {InstanceType<typeof VectorEntity.attributes.Y>} */ this.Y
/** @type {InstanceType<typeof VectorEntity.attributes.X>} */ this.Z
}
/** @returns {[Number, Number, Number]} */
toArray() {
return [this.X, this.Y, this.Z]
return [this.X.valueOf(), this.Y.valueOf(), this.Z.valueOf()]
}
}

View File

@@ -14,20 +14,18 @@ export default class KnotEntity extends ObjectEntity {
values.Name = "K2Node_Knot"
const inputPinEntity = new PinEntity(
{ PinName: "InputPin" },
true
)
const outputPinEntity = new PinEntity(
{
PinName: "OutputPin",
Direction: "EGPD_Output",
},
true
)
if (pinReferenceForType) {
inputPinEntity.copyTypeFrom(pinReferenceForType)
outputPinEntity.copyTypeFrom(pinReferenceForType)
}
values["CustomProperties"] = [inputPinEntity, outputPinEntity]
super(values, true)
super(values)
}
}

View File

@@ -1,4 +1,3 @@
import ObjectSerializer from "../../serialization/ObjectSerializer.js"
import IInput from "../IInput.js"
/**
@@ -10,8 +9,6 @@ import IInput from "../IInput.js"
export default class Copy extends IInput {
static #serializer = new ObjectSerializer()
/** @type {(e: ClipboardEvent) => void} */
#copyHandler
@@ -33,11 +30,11 @@ export default class Copy extends IInput {
getSerializedText() {
const allNodes = this.blueprint.getNodes(true).map(n => n.entity)
const exported = allNodes.filter(n => n.isExported).map(n => Copy.#serializer.write(n, false))
const result = allNodes.filter(n => !n.isExported).map(n => Copy.#serializer.write(n, false))
const exported = allNodes.filter(n => n.exported).map(n => n.toString())
const result = allNodes.filter(n => !n.exported).map(n => n.toString())
if (exported.length) {
this.blueprint.entity.ExportedNodes = btoa(exported.join(""))
result.splice(0, 0, Copy.#serializer.write(this.blueprint.entity, false))
result.splice(0, 0, this.blueprint.entity.toString(false))
delete this.blueprint.entity.ExportedNodes
}
return result.join("")

View File

@@ -1,4 +1,3 @@
import ObjectSerializer from "../../serialization/ObjectSerializer.js"
import IInput from "../IInput.js"
/**
@@ -10,8 +9,6 @@ import IInput from "../IInput.js"
export default class Cut extends IInput {
static #serializer = new ObjectSerializer()
/** @type {(e: ClipboardEvent) => void} */
#cutHandler
@@ -39,7 +36,7 @@ export default class Cut extends IInput {
getSerializedText() {
return this.blueprint
.getNodes(true)
.map(node => Cut.#serializer.write(node.entity, false))
.map(node => node.entity.toString())
.join("")
}

View File

@@ -1,6 +1,6 @@
import ElementFactory from "../../element/ElementFactory.js"
import ObjectEntity from "../../entity/ObjectEntity.js"
import IInput from "../IInput.js"
import ObjectSerializer from "../../serialization/ObjectSerializer.js"
/**
* @typedef {import("../IInput.js").Options & {
@@ -11,8 +11,6 @@ import ObjectSerializer from "../../serialization/ObjectSerializer.js"
export default class Paste extends IInput {
static #serializer = new ObjectSerializer()
/** @type {(e: ClipboardEvent) => void} */
#pasteHandle
@@ -42,7 +40,7 @@ export default class Paste extends IInput {
let top = 0
let left = 0
let count = 0
let nodes = Paste.#serializer.readMultiple(value).map(entity => {
let nodes = ObjectEntity.grammarMultipleObjects.parse(value).map(entity => {
let node = /** @type {NodeElementConstructor} */(ElementFactory.getConstructor("ueb-node"))
.newObject(entity)
top += node.locationY

View File

@@ -64,9 +64,12 @@ export default class KeyboardShortcut extends IInput {
this.#activationKeys = this.options.activationKeys ?? []
const wantsShift = keyEntry => keyEntry.bShift || keyEntry.Key == "LeftShift" || keyEntry.Key == "RightShift"
const wantsCtrl = keyEntry => keyEntry.bCtrl || keyEntry.Key == "LeftControl" || keyEntry.Key == "RightControl"
const wantsAlt = keyEntry => keyEntry.bAlt || keyEntry.Key == "LeftAlt" || keyEntry.Key == "RightAlt"
/** @param {KeyBindingEntity} keyEntry */
const wantsShift = keyEntry => keyEntry.bShift?.valueOf() || keyEntry.Key.valueOf() == "LeftShift" || keyEntry.Key.valueOf() == "RightShift"
/** @param {KeyBindingEntity} keyEntry */
const wantsCtrl = keyEntry => keyEntry.bCtrl?.valueOf() || keyEntry.Key.valueOf() == "LeftControl" || keyEntry.Key.valueOf() == "RightControl"
/** @param {KeyBindingEntity} keyEntry */
const wantsAlt = keyEntry => keyEntry.bAlt?.valueOf() || keyEntry.Key.valueOf() == "LeftAlt" || keyEntry.Key.valueOf() == "RightAlt"
let self = this
/** @param {KeyboardEvent} e */
@@ -94,10 +97,10 @@ export default class KeyboardShortcut extends IInput {
this.keyUpHandler = e => {
if (
self.#activationKeys.some(keyEntry =>
keyEntry.bShift && e.key == "Shift"
|| keyEntry.bCtrl && e.key == "Control"
|| keyEntry.bAlt && e.key == "Alt"
|| keyEntry.bCmd && e.key == "Meta"
keyEntry.bShift?.valueOf() && e.key == "Shift"
|| keyEntry.bCtrl?.valueOf() && e.key == "Control"
|| keyEntry.bAlt?.valueOf() && e.key == "Alt"
|| keyEntry.bCmd?.valueOf() && e.key == "Meta"
|| Configuration.Keys[keyEntry.Key.value] == e.code
)
) {

View File

@@ -43,7 +43,7 @@ export default class MouseCreateLink extends IMouseClickDrag {
this.link.setMessageReplaceOutputLink()
this.linkValid = true
} else if (
(a.entity.PinType.PinCategory != "object" || b.entity.PinType.PinCategory != "object")
(a.entity.PinType.PinCategory.valueOf() != "object" || b.entity.PinType.PinCategory.valueOf() != "object")
&& a.pinType != b.pinType
) {
this.link.setMessageTypesIncompatible(a, b)

View File

@@ -1,29 +0,0 @@
import Serializer from "./Serializer.js"
/**
* @template {AttributeConstructor<Attribute>} T
* @extends {Serializer<T>}
*/
export default class CustomSerializer extends Serializer {
#objectWriter
/**
* @param {(v: ConstructedType<T>, insideString: Boolean) => String} objectWriter
* @param {T} entityType
*/
constructor(objectWriter, entityType) {
super(entityType)
this.#objectWriter = objectWriter
}
/**
* @param {ConstructedType<T>} entity
* @param {Boolean} insideString
* @returns {String}
*/
doWrite(entity, insideString, indentation = "") {
let result = this.#objectWriter(entity, insideString)
return result
}
}

View File

@@ -1,14 +1,15 @@
import Parsernostrum from "parsernostrum"
import Configuration from "../Configuration.js"
import Utility from "../Utility.js"
import AttributeInfo from "../entity/AttributeInfo.js"
import AlternativesEntity from "../entity/AlternativesEntity.js"
import IEntity from "../entity/IEntity.js"
import MirroredEntity from "../entity/MirroredEntity.js"
import Union from "../entity/Union.js"
import Serializable from "./Serializable.js"
export default class Grammar {
/** @type {String} */
// @ts-expect-error
static numberRegexSource = Parsernostrum.number.getParser().parser.regexp.source
static separatedBy = (source, separator, min = 1) =>
new RegExp(
source + "(?:" + separator + source + ")"
@@ -36,7 +37,6 @@ export default class Grammar {
static null = Parsernostrum.reg(/\(\s*\)/).map(() => null)
static true = Parsernostrum.reg(/true/i).map(() => true)
static false = Parsernostrum.reg(/false/i).map(() => false)
static boolean = Parsernostrum.regArray(/(true)|false/i).map(v => v[1] ? true : false)
static number = Parsernostrum.regArray(
new RegExp(`(${Parsernostrum.number.getParser().parser.regexp.source})|(\\+?inf)|(-inf)`)
).map(([_0, n, plusInf, minusInf]) => n ? Number(n) : plusInf ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY)
@@ -68,103 +68,36 @@ export default class Grammar {
/* --- Factory --- */
/**
* @template T
* @param {AttributeInfo<T>} attribute
* @param {Parsernostrum<any>} defaultGrammar
* @returns {Parsernostrum<T>}
* @param {typeof IEntity} entityType
* @param {String[]} key
* @returns {typeof IEntity}
*/
static grammarFor(attribute, type = attribute?.type, defaultGrammar = this.unknownValue) {
let result = defaultGrammar
if (type === Array || type instanceof Array) {
if (attribute?.inlined) {
return this.grammarFor(undefined, type[0])
}
result = Parsernostrum.seq(
Parsernostrum.reg(/\(\s*/),
this.grammarFor(undefined, type[0]).sepBy(this.commaSeparation).opt(),
Parsernostrum.reg(/\s*(?:,\s*)?\)/),
).map(([_0, values, _3]) => values instanceof Array ? values : [])
} else if (type instanceof Union) {
result = type.values
.map(v => this.grammarFor(undefined, v))
.reduce((acc, cur) => !cur || cur === this.unknownValue || acc === this.unknownValue
? this.unknownValue
: Parsernostrum.alt(acc, cur)
)
} else if (type instanceof MirroredEntity) {
// @ts-expect-error
return this.grammarFor(undefined, type.getTargetType())
.map(v => new MirroredEntity(type.type, () => v))
} else if (attribute?.constructor === Object) {
result = this.grammarFor(undefined, type)
} else {
switch (type) {
case Boolean:
result = this.boolean
break
case null:
result = this.null
break
case Number:
result = this.number
break
case BigInt:
result = this.bigInt
break
case String:
result = this.string
break
default:
if (/** @type {AttributeConstructor<any>} */(type)?.prototype instanceof Serializable) {
result = /** @type {typeof Serializable} */(type).grammar
}
}
static getAttribute(entityType, [key, ...keys]) {
const attribute = entityType?.attributes?.[key]
if (!attribute) {
return
}
if (attribute) {
if (attribute.serialized && type.constructor !== String) {
if (result == this.unknownValue) {
result = this.string
} else {
result = Parsernostrum.seq(Parsernostrum.str('"'), result, Parsernostrum.str('"')).map(([_0, value, _2]) => value)
if (attribute.prototype instanceof AlternativesEntity) {
for (const alternative of /** @type {typeof AlternativesEntity} */(attribute).alternatives) {
const candidate = this.getAttribute(alternative, keys)
if (candidate) {
return candidate
}
}
if (attribute.nullable) {
result = Parsernostrum.alt(result, this.null)
}
}
return result
if (keys.length > 0) {
return this.getAttribute(attribute, keys)
}
return attribute
}
/**
* @template {AttributeConstructor<Attribute>} T
* @param {T} entityType
* @param {String[]} key
* @returns {AttributeInfo}
* @param {typeof IEntity} entityType
* @param {*} attributeName
* @param {*} valueSeparator
* @param {*} handleObjectSet
* @returns
*/
static getAttribute(entityType, key) {
let result
let type
if (entityType instanceof Union) {
for (let t of entityType.values) {
if (result = this.getAttribute(t, key)) {
return result
}
}
}
if (entityType instanceof IEntity.constructor) {
// @ts-expect-error
result = entityType.attributes[key[0]]
type = result?.type
} else if (entityType instanceof Array) {
result = entityType[key[0]]
type = result
}
if (key.length > 1) {
return this.getAttribute(type, key.slice(1))
}
return result
}
static createAttributeGrammar(
entityType,
attributeName = this.attributeName,
@@ -177,64 +110,47 @@ export default class Grammar {
).chain(([attributeName, _1]) => {
const attributeKey = attributeName.split(Configuration.keysSeparator)
const attributeValue = this.getAttribute(entityType, attributeKey)
return this
.grammarFor(attributeValue)
.map(attributeValue =>
values => {
handleObjectSet(values, attributeKey, attributeValue)
Utility.objectSet(values, attributeKey, attributeValue)
}
)
return (attributeValue?.grammar ?? IEntity.unknownEntityGrammar).map(attributeValue =>
values => {
handleObjectSet(values, attributeKey, attributeValue)
Utility.objectSet(values, attributeKey, attributeValue)
}
)
})
}
/**
* @template {IEntity} T
* @param {(new (...args: any) => T) & EntityConstructor} entityType
* @param {Boolean | Number} acceptUnknownKeys Number to specify the limit or true, to let it be a reasonable value
* @template {typeof IEntity} T
* @param {T} entityType
* @return {Parsernostrum<InstanceType<T>>}
*/
static createEntityGrammar(entityType, acceptUnknownKeys = true, entriesSeparator = this.commaSeparation) {
const lookbehind = entityType.attributes.lookbehind.default
static createEntityGrammar(entityType, entriesSeparator = this.commaSeparation, complete = false, minKeys = 1) {
const lookbehind = entityType.lookbehind instanceof Array ? entityType.lookbehind.join("|") : entityType.lookbehind
return Parsernostrum.seq(
Parsernostrum.reg(
lookbehind instanceof Union
? new RegExp(`(${lookbehind.values.reduce((acc, cur) => acc + "|" + cur)})\\s*\\(\\s*`)
: lookbehind.constructor == String && lookbehind.length > 0
? new RegExp(`(${lookbehind})\\s*\\(\\s*`)
: /()\(\s*/,
1
),
this.createAttributeGrammar(entityType).sepBy(entriesSeparator),
Parsernostrum.reg(/\s*(?:,\s*)?\)/), // trailing comma
Parsernostrum.reg(new RegExp(String.raw`(${lookbehind}\s*)\(\s*`), 1),
this.createAttributeGrammar(entityType).sepBy(entriesSeparator, minKeys),
Parsernostrum.reg(/\s*(,\s*)?\)/, 1), // optional trailing comma
)
.map(([lookbehind, attributes, _2]) => {
.map(([lookbehind, attributes, trailing]) => {
let values = {}
attributes.forEach(attributeSetter => attributeSetter(values))
if (lookbehind.length) {
values.lookbehind = lookbehind
values["lookbehind"] = lookbehind
}
attributes.forEach(attributeSetter => attributeSetter(values))
values["trailing"] = trailing !== undefined
return values
})
// Decide if we accept the entity or not. It is accepted if it doesn't have too many unexpected keys
.chain(values => {
let totalKeys = Object.keys(values)
let missingKey
// Check missing values
if (
Object.keys(/** @type {AttributeDeclarations} */(entityType.attributes))
.filter(key => entityType.attributes[key].expected)
.find(key => !totalKeys.includes(key) && (missingKey = key))
) {
return Parsernostrum.failure()
if (entityType.lookbehind instanceof Array || entityType.lookbehind !== lookbehind) {
entityType = entityType.withLookbehind(lookbehind)
}
const unknownKeys = Object.keys(values).filter(key => !(key in entityType.attributes)).length
if (!acceptUnknownKeys && unknownKeys > 0) {
return Parsernostrum.failure()
}
return Parsernostrum.success().map(() => new entityType(values))
const keys = Object.keys(values)
return complete
? Parsernostrum.success()
.assert(v => Object.keys(entityType.attributes).every(k => keys.includes(k)))
.map(() => new entityType(values))
: Parsernostrum.success().map(() => new entityType(values))
})
}
/** @type {Parsernostrum<any>} */
static unknownValue // Defined in initializeSerializerFactor to avoid circular include
}

View File

@@ -1,104 +0,0 @@
import Configuration from "../Configuration.js"
import AttributeInfo from "../entity/AttributeInfo.js"
import ObjectEntity from "../entity/ObjectEntity.js"
import PinEntity from "../entity/PinEntity.js"
import Grammar from "./Grammar.js"
import Serializer from "./Serializer.js"
import SerializerFactory from "./SerializerFactory.js"
/** @extends Serializer<ObjectEntityConstructor> */
export default class ObjectSerializer extends Serializer {
constructor(entityType = ObjectEntity) {
super(entityType, undefined, "\n", true, undefined, Serializer.same)
}
showProperty(entity, key) {
switch (key) {
case "Class":
case "Name":
case "Archetype":
case "ExportPath":
case "CustomProperties":
// Serielized separately, check doWrite()
return false
}
return super.showProperty(entity, key)
}
/** @param {ObjectEntity} value */
write(value, insideString = false) {
return this.doWrite(value, insideString) + "\n"
}
/** @param {String} value */
doRead(value) {
return Grammar.grammarFor(undefined, this.entityType).parse(value)
}
/**
* @param {String} value
* @returns {ObjectEntity[]}
*/
readMultiple(value) {
return ObjectEntity.getMultipleObjectsGrammar().parse(value)
}
/**
* @param {ObjectEntity} entity
* @param {Boolean} insideString
* @returns {String}
*/
doWrite(
entity,
insideString,
indentation = "",
wrap = this.wrap,
attributeSeparator = this.attributeSeparator,
trailingSeparator = this.trailingSeparator,
attributeValueConjunctionSign = this.attributeValueConjunctionSign,
attributeKeyPrinter = this.attributeKeyPrinter,
) {
const moreIndentation = indentation + Configuration.indentation
if (!(entity instanceof ObjectEntity)) {
return super.doWrite(
entity,
insideString,
indentation,
wrap,
attributeSeparator,
trailingSeparator,
attributeValueConjunctionSign,
// @ts-expect-error
key => entity[key] instanceof ObjectEntity ? "" : attributeKeyPrinter(key)
)
}
let result = indentation + "Begin Object"
+ (entity.Class?.type || entity.Class?.path ? ` Class=${this.doWriteValue(entity.Class, insideString)}` : "")
+ (entity.Name ? ` Name=${this.doWriteValue(entity.Name, insideString)}` : "")
+ (entity.Archetype ? ` Archetype=${this.doWriteValue(entity.Archetype, insideString)}` : "")
+ (entity.ExportPath?.type || entity.ExportPath?.path ? ` ExportPath=${this.doWriteValue(entity.ExportPath, insideString)}` : "")
+ "\n"
+ super.doWrite(
entity,
insideString,
moreIndentation,
wrap,
attributeSeparator,
true,
attributeValueConjunctionSign,
key => entity[key] instanceof ObjectEntity ? "" : attributeKeyPrinter(key)
)
+ (!AttributeInfo.getAttribute(entity, "CustomProperties", "ignored")
? entity.getCustomproperties().map(pin =>
moreIndentation
+ attributeKeyPrinter("CustomProperties ")
+ SerializerFactory.getSerializer(PinEntity).doWrite(pin, insideString)
+ this.attributeSeparator
).join("")
: ""
)
+ indentation + "End Object"
return result
}
}

View File

@@ -1,11 +0,0 @@
import Parsernostrum from "parsernostrum"
export default class Serializable {
static grammar = this.createGrammar()
/** @protected */
static createGrammar() {
return /** @type {Parsernostrum<any>} */(Parsernostrum.failure())
}
}

View File

@@ -1,168 +0,0 @@
import Utility from "../Utility.js"
import AttributeInfo from "../entity/AttributeInfo.js"
import IEntity from "../entity/IEntity.js"
import Grammar from "./Grammar.js"
import SerializerFactory from "./SerializerFactory.js"
/** @template {AttributeConstructor<Attribute>} T */
export default class Serializer {
/** @type {(v: String) => String} */
static same = v => v
/** @type {(entity: Attribute, serialized: String) => String} */
static notWrapped = (entity, serialized) => serialized
/** @type {(entity: Attribute, serialized: String) => String} */
static bracketsWrapped = (entity, serialized) => `(${serialized})`
/** @param {T} entityType */
constructor(
entityType,
/** @type {(entity: ConstructedType<T>, serialized: String) => String} */
wrap = (entity, serialized) => serialized,
attributeSeparator = ",",
trailingSeparator = false,
attributeValueConjunctionSign = "=",
attributeKeyPrinter = Serializer.same
) {
this.entityType = entityType
this.wrap = wrap
this.attributeSeparator = attributeSeparator
this.trailingSeparator = trailingSeparator
this.attributeValueConjunctionSign = attributeValueConjunctionSign
this.attributeKeyPrinter = attributeKeyPrinter
}
/**
* @param {String} value
* @returns {ConstructedType<T>}
*/
read(value) {
return this.doRead(value.trim())
}
/** @param {ConstructedType<T>} value */
write(value, insideString = false) {
return this.doWrite(value, insideString)
}
/**
* @param {String} value
* @returns {ConstructedType<T>}
*/
doRead(value) {
let grammar = Grammar.grammarFor(undefined, this.entityType)
const parseResult = grammar.run(value)
if (!parseResult.status) {
throw new Error(
this.entityType
? `Error when trying to parse the entity ${this.entityType.prototype.constructor.name}`
: "Error when trying to parse null"
)
}
return parseResult.value
}
/**
* @param {ConstructedType<T>} entity
* @param {Boolean} insideString
* @returns {String}
*/
doWrite(
entity,
insideString = false,
indentation = "",
wrap = this.wrap,
attributeSeparator = this.attributeSeparator,
trailingSeparator = this.trailingSeparator,
attributeValueConjunctionSign = this.attributeValueConjunctionSign,
attributeKeyPrinter = this.attributeKeyPrinter
) {
let result = ""
const keys = entity._keys ?? Object.keys(entity)
let first = true
for (const key of keys) {
const value = entity[key]
if (value !== undefined && this.showProperty(entity, key)) {
let keyValue = entity instanceof Array ? `(${key})` : key
if (AttributeInfo.getAttribute(entity, key, "quoted")) {
keyValue = `"${keyValue}"`
}
const isSerialized = AttributeInfo.getAttribute(entity, key, "serialized")
if (first) {
first = false
} else {
result += attributeSeparator
}
if (AttributeInfo.getAttribute(entity, key, "inlined")) {
result += this.doWrite(
value,
insideString,
indentation,
Serializer.notWrapped,
attributeSeparator,
false,
attributeValueConjunctionSign,
AttributeInfo.getAttribute(entity, key, "type") instanceof Array
? k => attributeKeyPrinter(`${keyValue}${k}`)
: k => attributeKeyPrinter(`${keyValue}.${k}`)
)
continue
}
const keyPrinted = attributeKeyPrinter(keyValue)
const indentationPrinted = attributeSeparator.includes("\n") ? indentation : ""
result += (
keyPrinted.length
? (indentationPrinted + keyPrinted + this.attributeValueConjunctionSign)
: ""
)
+ (
isSerialized
? `"${this.doWriteValue(value, true, indentation)}"`
: this.doWriteValue(value, insideString, indentation)
)
}
}
if (trailingSeparator && result.length) {
// append separator at the end if asked and there was printed content
result += attributeSeparator
}
return wrap(entity, result)
}
/** @param {Boolean} insideString */
doWriteValue(value, insideString, indentation = "") {
const type = Utility.getType(value)
const serializer = SerializerFactory.getSerializer(type)
if (!serializer) {
throw new Error(
`Unknown value type "${type.name}", a serializer must be registered in the SerializerFactory class, `
+ "check initializeSerializerFactory.js"
)
}
return serializer.doWrite(value, insideString, indentation)
}
/**
* @param {IEntity} entity
* @param {String} key
*/
showProperty(entity, key) {
if (entity instanceof IEntity) {
if (AttributeInfo.getAttribute(entity, key, "ignored")) {
return false
}
if (AttributeInfo.getAttribute(entity, key, "silent")) {
let defaultValue = AttributeInfo.getAttribute(entity, key, "default")
if (defaultValue instanceof Function) {
defaultValue = defaultValue(entity)
}
if (Utility.equals(entity[key], defaultValue)) {
return false
}
}
}
return true
}
}

View File

@@ -1,22 +0,0 @@
export default class SerializerFactory {
static #serializers = new Map()
/**
* @template {AttributeConstructor<Attribute>} T
* @param {T} type
* @param {Serializer<T>} object
*/
static registerSerializer(type, object) {
SerializerFactory.#serializers.set(type, object)
}
/**
* @template {AttributeConstructor<Attribute>} T
* @param {T} type
* @returns {Serializer<T>}
*/
static getSerializer(type) {
return SerializerFactory.#serializers.get(type)
}
}

View File

@@ -1,28 +0,0 @@
import Utility from "../Utility.js"
import Serializer from "./Serializer.js"
/**
* @template {AttributeConstructor<Attribute>} T
* @extends {Serializer<T>}
*/
export default class ToStringSerializer extends Serializer {
/** @param {T} entityType */
constructor(entityType, escape = true) {
super(entityType)
if (escape) {
this.wrap = (entity, serialized) => Utility.escapeString(serialized)
}
}
/**
* @param {ConstructedType<T>} entity
* @param {Boolean} insideString
*/
doWrite(entity, insideString, indentation = "") {
return !insideString && entity.constructor === String
? `"${this.wrap(entity, entity.toString())}"` // String will have quotes if not inside a string already
: this.wrap(entity, entity.toString())
}
}

View File

@@ -1,342 +1,50 @@
import Parsernostrum from "parsernostrum"
import Utility from "../Utility.js"
import BlueprintEntity from "../entity/BlueprintEntity.js"
import ByteEntity from "../entity/ByteEntity.js"
import ColorChannelEntity from "../entity/ColorChannelEntity.js"
import EnumDisplayValueEntity from "../entity/EnumDisplayValueEntity.js"
import EnumEntity from "../entity/EnumEntity.js"
import FormatTextEntity from "../entity/FormatTextEntity.js"
import FunctionReferenceEntity from "../entity/FunctionReferenceEntity.js"
import GuidEntity from "../entity/GuidEntity.js"
import IdentifierEntity from "../entity/IdentifierEntity.js"
import Integer64Entity from "../entity/Integer64Entity.js"
import IntegerEntity from "../entity/IntegerEntity.js"
import InvariantTextEntity from "../entity/InvariantTextEntity.js"
import KeyBindingEntity from "../entity/KeyBindingEntity.js"
import LinearColorEntity from "../entity/LinearColorEntity.js"
import LocalizedTextEntity from "../entity/LocalizedTextEntity.js"
import MacroGraphReferenceEntity from "../entity/MacroGraphReferenceEntity.js"
import MirroredEntity from "../entity/MirroredEntity.js"
import ObjectEntity from "../entity/ObjectEntity.js"
import ObjectReferenceEntity from "../entity/ObjectReferenceEntity.js"
import PathSymbolEntity from "../entity/PathSymbolEntity.js"
import PinEntity from "../entity/PinEntity.js"
import PinReferenceEntity from "../entity/PinReferenceEntity.js"
import PinTypeEntity from "../entity/PinTypeEntity.js"
import RBSerializationVector2DEntity from "../entity/RBSerializationVector2DEntity.js"
import RotatorEntity from "../entity/RotatorEntity.js"
import ScriptVariableEntity from "../entity/ScriptVariableEntity.js"
import SimpleSerializationRotatorEntity from "../entity/SimpleSerializationRotatorEntity.js"
import SimpleSerializationVector2DEntity from "../entity/SimpleSerializationVector2DEntity.js"
import SimpleSerializationVector4DEntity from "../entity/SimpleSerializationVector4DEntity.js"
import SimpleSerializationVectorEntity from "../entity/SimpleSerializationVectorEntity.js"
import SymbolEntity from "../entity/SymbolEntity.js"
import TerminalTypeEntity from "../entity/TerminalTypeEntity.js"
import Union from "../entity/Union.js"
import UnknownKeysEntity from "../entity/UnknownKeysEntity.js"
import VariableReferenceEntity from "../entity/VariableReferenceEntity.js"
import Vector2DEntity from "../entity/Vector2DEntity.js"
import Vector4DEntity from "../entity/Vector4DEntity.js"
import VectorEntity from "../entity/VectorEntity.js"
import CustomSerializer from "./CustomSerializer.js"
import Grammar from "./Grammar.js"
import ObjectSerializer from "./ObjectSerializer.js"
import Serializer from "./Serializer.js"
import SerializerFactory from "./SerializerFactory.js"
import ToStringSerializer from "./ToStringSerializer.js"
Grammar.unknownValue =
Parsernostrum.alt(
// Remember to keep the order, otherwise parsing might fail
Grammar.boolean,
GuidEntity.grammar,
Parsernostrum.str("None").map(() => new ObjectReferenceEntity({ type: "None" })),
Grammar.null,
Grammar.number,
ObjectReferenceEntity.fullReferenceGrammar,
Grammar.string,
LocalizedTextEntity.grammar,
InvariantTextEntity.grammar,
FormatTextEntity.grammar,
PinReferenceEntity.grammar,
Vector4DEntity.grammar,
VectorEntity.grammar,
RotatorEntity.grammar,
LinearColorEntity.grammar,
Vector2DEntity.grammar,
UnknownKeysEntity.grammar,
SymbolEntity.grammar,
Grammar.grammarFor(undefined, [PinReferenceEntity]),
Grammar.grammarFor(undefined, [new Union(Number, String, SymbolEntity)]),
Parsernostrum.lazy(() => Grammar.grammarFor(undefined, [undefined])),
)
import BooleanEntity from "../entity/BooleanEntity.js"
import NumberEntity from "../entity/NumberEntity.js"
import StringEntity from "../entity/StringEntity.js"
import ArrayEntity from "../entity/ArrayEntity.js"
import AlternativesEntity from "../entity/AlternativesEntity.js"
import NullEntity from "../entity/NullEntity.js"
import IEntity from "../entity/IEntity.js"
export default function initializeSerializerFactory() {
SerializerFactory.registerSerializer(
null,
new CustomSerializer(
(nullValue, insideString) => "()",
null
IEntity.unknownEntityGrammar =
Parsernostrum.alt(
// Remember to keep the order, otherwise parsing might fail
BooleanEntity.grammar,
GuidEntity.grammar,
Parsernostrum.str("None").map(() => ObjectReferenceEntity.createNoneInstance()),
NullEntity.grammar,
NumberEntity.grammar,
ObjectReferenceEntity.fullReferenceGrammar,
StringEntity.grammar,
LocalizedTextEntity.grammar,
InvariantTextEntity.grammar,
FormatTextEntity.grammar,
PinReferenceEntity.grammar,
Vector4DEntity.grammar,
VectorEntity.grammar,
Vector2DEntity.grammar,
RotatorEntity.grammar,
LinearColorEntity.grammar,
UnknownKeysEntity.grammar,
SymbolEntity.grammar,
ArrayEntity.of(PinReferenceEntity).grammar,
ArrayEntity.of(AlternativesEntity.accepting(NumberEntity, StringEntity, SymbolEntity)).grammar,
Parsernostrum.lazy(() => ArrayEntity.createGrammar(IEntity.unknownEntityGrammar)),
)
)
SerializerFactory.registerSerializer(
Array,
new CustomSerializer(
(array, insideString) =>
`(${array
.map(v => SerializerFactory.getSerializer(Utility.getType(v)).write(v, insideString))
.join(",")
})`,
Array
)
)
SerializerFactory.registerSerializer(
BigInt,
new ToStringSerializer(BigInt)
)
SerializerFactory.registerSerializer(
BlueprintEntity,
new ObjectSerializer(BlueprintEntity),
)
SerializerFactory.registerSerializer(
Boolean,
new CustomSerializer(
/** @param {Boolean} boolean */
(boolean, insideString) => boolean
? insideString
? "true"
: "True"
: insideString
? "false"
: "False",
Boolean
)
)
SerializerFactory.registerSerializer(
ByteEntity,
new ToStringSerializer(ByteEntity)
)
SerializerFactory.registerSerializer(
ColorChannelEntity,
new ToStringSerializer(ColorChannelEntity)
)
SerializerFactory.registerSerializer(
EnumDisplayValueEntity,
new ToStringSerializer(EnumDisplayValueEntity)
)
SerializerFactory.registerSerializer(
EnumEntity,
new ToStringSerializer(EnumEntity)
)
SerializerFactory.registerSerializer(
FormatTextEntity,
new CustomSerializer(
(v, insideString) => {
let result = v.getLookbehind() + "("
+ v.value.map(v =>
SerializerFactory.getSerializer(Utility.getType(v)).write(v, insideString)
).join(", ")
+ ")"
return result
},
FormatTextEntity)
)
SerializerFactory.registerSerializer(
FunctionReferenceEntity,
new Serializer(FunctionReferenceEntity, Serializer.bracketsWrapped)
)
SerializerFactory.registerSerializer(
GuidEntity,
new ToStringSerializer(GuidEntity)
)
SerializerFactory.registerSerializer(
IdentifierEntity,
new ToStringSerializer(IdentifierEntity)
)
SerializerFactory.registerSerializer(
Integer64Entity,
new ToStringSerializer(Integer64Entity)
)
SerializerFactory.registerSerializer(
IntegerEntity,
new ToStringSerializer(IntegerEntity)
)
SerializerFactory.registerSerializer(
InvariantTextEntity,
new Serializer(InvariantTextEntity, (entity, v) => `${entity.getLookbehind()}(${v})`, ", ", false, "", () => "")
)
SerializerFactory.registerSerializer(
KeyBindingEntity,
new Serializer(KeyBindingEntity, Serializer.bracketsWrapped)
)
SerializerFactory.registerSerializer(
LinearColorEntity,
new Serializer(LinearColorEntity, Serializer.bracketsWrapped)
)
SerializerFactory.registerSerializer(
LocalizedTextEntity,
new Serializer(LocalizedTextEntity, (entity, v) => `${entity.getLookbehind()}(${v})`, ", ", false, "", () => "")
)
SerializerFactory.registerSerializer(
MacroGraphReferenceEntity,
new Serializer(MacroGraphReferenceEntity, Serializer.bracketsWrapped)
)
SerializerFactory.registerSerializer(
MirroredEntity,
new CustomSerializer(
(v, insideString) => SerializerFactory.getSerializer(v.getTargetType()).write(v.get(), insideString),
MirroredEntity
)
)
SerializerFactory.registerSerializer(
Number,
new ToStringSerializer(Number)
)
SerializerFactory.registerSerializer(
ObjectEntity,
new ObjectSerializer()
)
SerializerFactory.registerSerializer(
ObjectReferenceEntity,
new ToStringSerializer(ObjectReferenceEntity, false)
)
SerializerFactory.registerSerializer(
PathSymbolEntity,
new ToStringSerializer(PathSymbolEntity)
)
SerializerFactory.registerSerializer(
PinEntity,
new Serializer(PinEntity, (entity, v) => `${entity.getLookbehind()} (${v})`, ",", true)
)
SerializerFactory.registerSerializer(
PinReferenceEntity,
new Serializer(PinReferenceEntity, undefined, " ", false, "", () => "")
)
SerializerFactory.registerSerializer(
PinTypeEntity,
new Serializer(PinTypeEntity)
)
SerializerFactory.registerSerializer(
TerminalTypeEntity,
new Serializer(TerminalTypeEntity, Serializer.bracketsWrapped)
)
SerializerFactory.registerSerializer(
RBSerializationVector2DEntity,
new CustomSerializer(
(value, insideString) => `X=${value.X} Y=${value.Y}`,
RBSerializationVector2DEntity
)
)
SerializerFactory.registerSerializer(
RotatorEntity,
new Serializer(RotatorEntity, Serializer.bracketsWrapped)
)
SerializerFactory.registerSerializer(
ScriptVariableEntity,
new Serializer(ScriptVariableEntity, Serializer.bracketsWrapped)
)
SerializerFactory.registerSerializer(
String,
new CustomSerializer(
(value, insideString) => insideString
? Utility.escapeString(value)
: `"${Utility.escapeString(value)}"`,
String
)
)
SerializerFactory.registerSerializer(
SimpleSerializationRotatorEntity,
new CustomSerializer(
(value, insideString) => `${value.P}, ${value.Y}, ${value.R}`,
SimpleSerializationRotatorEntity
)
)
SerializerFactory.registerSerializer(
SimpleSerializationVector2DEntity,
new CustomSerializer(
(value, insideString) => `${value.X}, ${value.Y}`,
SimpleSerializationVector2DEntity
)
)
SerializerFactory.registerSerializer(
SimpleSerializationVectorEntity,
new CustomSerializer(
(value, insideString) => `${value.X}, ${value.Y}, ${value.Z}`,
SimpleSerializationVectorEntity
)
)
SerializerFactory.registerSerializer(
SimpleSerializationVector4DEntity,
new CustomSerializer(
(value, insideString) => `${value.X}, ${value.Y}, ${value.Z}, ${value.W}`,
SimpleSerializationVector4DEntity
)
)
SerializerFactory.registerSerializer(
SymbolEntity,
new ToStringSerializer(SymbolEntity)
)
SerializerFactory.registerSerializer(
UnknownKeysEntity,
new Serializer(UnknownKeysEntity, (entity, string) => `${entity.getLookbehind() ?? ""}(${string})`)
)
SerializerFactory.registerSerializer(
VariableReferenceEntity,
new Serializer(VariableReferenceEntity, Serializer.bracketsWrapped)
)
SerializerFactory.registerSerializer(
Vector2DEntity,
new Serializer(Vector2DEntity, Serializer.bracketsWrapped)
)
SerializerFactory.registerSerializer(
VectorEntity,
new Serializer(VectorEntity, Serializer.bracketsWrapped)
)
SerializerFactory.registerSerializer(
Vector4DEntity,
new Serializer(Vector4DEntity, Serializer.bracketsWrapped)
)
}

View File

@@ -26,7 +26,7 @@ export default class CommentNodeTemplate extends IResizeableTemplate {
<div class="ueb-node-border">
<div class="ueb-node-wrapper">
<div class="ueb-node-top"
.innerText="${Utility.encodeHTMLWhitespace(this.element.entity.NodeComment)}">
.innerText="${Utility.encodeHTMLWhitespace(this.element.entity.NodeComment?.valueOf())}">
</div>
</div>
</div>

View File

@@ -40,7 +40,7 @@ export default class EventNodeTemplate extends NodeTemplate {
createDelegatePinElement() {
const pin = /** @type {PinElementConstructor} */(ElementFactory.getConstructor("ueb-pin")).newObject(
this.element.getPinEntities().find(v => !v.isHidden() && v.PinType.PinCategory === "delegate"),
this.element.getPinEntities().find(v => !v.isHidden() && v.PinType.PinCategory?.valueOf() === "delegate"),
new MinimalPinTemplate(),
this.element
)

View File

@@ -1,5 +1,6 @@
import { html } from "lit"
import Configuration from "../../Configuration.js"
import StringEntity from "../../entity/StringEntity.js"
import Utility from "../../Utility.js"
import IInputPinTemplate from "./IInputPinTemplate.js"
@@ -15,11 +16,11 @@ export default class EnumPinTemplate extends IInputPinTemplate {
setup() {
super.setup()
const enumEntries = this.element.nodeElement.entity.EnumEntries
const enumEntries = this.element.nodeElement.entity.EnumEntries.valueOf()
this.#dropdownEntries =
enumEntries?.map(k => {
if (k === "") {
k = "None"
if (k.valueOf() === "") {
k = new StringEntity("None")
}
return [
k,

View File

@@ -11,7 +11,7 @@ export default class ExecPinTemplate extends PinTemplate {
renderName() {
let pinName = this.element.entity.PinName
if (this.element.entity.PinFriendlyName) {
pinName = this.element.entity.PinFriendlyName.toString()
pinName = this.element.entity.PinFriendlyName.valueOf()
} else if (pinName === "execute" || pinName === "then") {
return html``
}

View File

@@ -115,7 +115,7 @@ export default class PinTemplate extends ITemplate {
case "Set": return SVGIcon.setPin
case "Map": return SVGIcon.mapPin
}
if (this.element.entity.PinType?.PinCategory?.toLocaleLowerCase() === "delegate") {
if (this.element.entity.PinType?.PinCategory?.valueOf().toLocaleLowerCase() === "delegate") {
return SVGIcon.delegate
}
if (this.element.nodeElement?.template instanceof VariableOperationNodeTemplate) {
@@ -141,8 +141,8 @@ export default class PinTemplate extends ITemplate {
isInputRendered() {
return this.element.isInput()
&& !this.element.entity.bDefaultValueIsIgnored
&& !this.element.entity.PinType.bIsReference
&& !this.element.entity.bDefaultValueIsIgnored?.valueOf()
&& !this.element.entity.PinType.bIsReference?.valueOf()
}
renderInput() {
@@ -170,6 +170,7 @@ export default class PinTemplate extends ITemplate {
getLinkLocation() {
const rect = this.iconElement.getBoundingClientRect()
/** @type {[Number, Number]} */
const boundingLocation = [this.element.isInput() ? rect.left : rect.right + 1, (rect.top + rect.bottom) / 2]
const location = Utility.convertLocation(boundingLocation, this.blueprint.template.gridElement)
return this.blueprint.compensateTranslation(location[0], location[1])

View File

@@ -7,15 +7,15 @@ import INumericPinTemplate from "./INumericPinTemplate.js"
export default class RotatorPinTemplate extends INumericPinTemplate {
#getR() {
return Utility.printNumber(this.element.getDefaultValue()?.R ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.R.valueOf() ?? 0)
}
#getP() {
return Utility.printNumber(this.element.getDefaultValue()?.P ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.P.valueOf() ?? 0)
}
#getY() {
return Utility.printNumber(this.element.getDefaultValue()?.Y ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.Y.valueOf() ?? 0)
}
setDefaultValue(values = [], rawValues = values) {
@@ -23,9 +23,9 @@ export default class RotatorPinTemplate extends INumericPinTemplate {
if (!(rotator instanceof RotatorEntity)) {
throw new TypeError("Expected DefaultValue to be a RotatorEntity")
}
rotator.R = values[0] // Roll
rotator.P = values[1] // Pitch
rotator.Y = values[2] // Yaw
rotator.R.value = values[0] // Roll
rotator.P.value = values[1] // Pitch
rotator.Y.value = values[2] // Yaw
this.element.requestUpdate("DefaultValue", rotator)
}

View File

@@ -9,11 +9,11 @@ import INumericPinTemplate from "./INumericPinTemplate.js"
export default class Vector2DPinTemplate extends INumericPinTemplate {
#getX() {
return Utility.printNumber(this.element.getDefaultValue()?.X ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.X.valueOf() ?? 0)
}
#getY() {
return Utility.printNumber(this.element.getDefaultValue()?.Y ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.Y.valueOf() ?? 0)
}
/**
@@ -25,8 +25,8 @@ export default class Vector2DPinTemplate extends INumericPinTemplate {
if (!(vector instanceof Vector2DEntity)) {
throw new TypeError("Expected DefaultValue to be a Vector2DEntity")
}
vector.X = values[0]
vector.Y = values[1]
vector.X.value = values[0]
vector.Y.value = values[1]
this.element.requestUpdate("DefaultValue", vector)
}

View File

@@ -7,19 +7,19 @@ import Vector4DEntity from "../../entity/Vector4DEntity.js"
export default class Vector4DPinTemplate extends INumericPinTemplate {
#getX() {
return Utility.printNumber(this.element.getDefaultValue()?.X ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.X.valueOf() ?? 0)
}
#getY() {
return Utility.printNumber(this.element.getDefaultValue()?.Y ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.Y.valueOf() ?? 0)
}
#getZ() {
return Utility.printNumber(this.element.getDefaultValue()?.Z ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.Z.valueOf() ?? 0)
}
#getW() {
return Utility.printNumber(this.element.getDefaultValue()?.W ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.W.valueOf() ?? 0)
}
/**
@@ -31,10 +31,10 @@ export default class Vector4DPinTemplate extends INumericPinTemplate {
if (!(vector instanceof Vector4DEntity)) {
throw new TypeError("Expected DefaultValue to be a Vector4DEntity")
}
vector.X = values[0]
vector.Y = values[1]
vector.Z = values[2]
vector.W = values[3]
vector.X.value = values[0]
vector.Y.value = values[1]
vector.Z.value = values[2]
vector.W.value = values[3]
this.element.requestUpdate("DefaultValue", vector)
}

View File

@@ -7,15 +7,15 @@ import INumericPinTemplate from "./INumericPinTemplate.js"
export default class VectorPinTemplate extends INumericPinTemplate {
#getX() {
return Utility.printNumber(this.element.getDefaultValue()?.X ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.X.valueOf() ?? 0)
}
#getY() {
return Utility.printNumber(this.element.getDefaultValue()?.Y ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.Y.valueOf() ?? 0)
}
#getZ() {
return Utility.printNumber(this.element.getDefaultValue()?.Z ?? 0)
return Utility.printNumber(this.element.getDefaultValue()?.Z.valueOf() ?? 0)
}
/**
@@ -27,9 +27,9 @@ export default class VectorPinTemplate extends INumericPinTemplate {
if (!(vector instanceof VectorEntity)) {
throw new TypeError("Expected DefaultValue to be a VectorEntity")
}
vector.X = values[0]
vector.Y = values[1]
vector.Z = values[2]
vector.X.value = values[0]
vector.Y.value = values[1]
vector.Z.value = values[2]
this.element.requestUpdate("DefaultValue", vector)
}

View File

@@ -0,0 +1,69 @@
// @ts-nocheck
import StringEntity from "../js/entity/StringEntity.js"
import initializeSerializerFactory from "../js/serialization/initializeSerializerFactory.js"
import { expect, test } from "./fixtures/test.js"
import Entity1 from "./resources/Entity1.js"
import Entity2 from "./resources/Entity2.js"
import Entity3 from "./resources/Entity3.js"
import Entity4 from "./resources/Entity4.js"
import entity2Value from "./resources/serializedEntity2.js"
import entity2Value1 from "./resources/serializedEntity2-1.js"
import entity3Value from "./resources/serializedEntity3.js"
import entity4Value from "./resources/serializedEntity4.js"
test.beforeAll(() => initializeSerializerFactory())
test.describe.configure({ mode: "parallel" })
test("Entity2", () => {
const value = new Entity2()
expect(Object.keys(value)).toHaveLength(9)
expect(value.toString()).toEqual(entity2Value)
const other = new Entity2({ someString2: new StringEntity("gamma") })
expect(value.equals(other)).toBeFalsy()
other.someString2 = new StringEntity("beta")
expect(value.equals(other)).toBeTruthy()
})
test("Entity2-1", () => {
Entity2.attributes.someEntity = Entity2.attributes.someEntity.flagInlined()
const value = new Entity2()
expect(value.toString()).toEqual(entity2Value1)
})
test("Entity3", () => {
let value = new Entity3()
const keys = [
"alpha",
"bravo",
"charlie",
"delta",
"echo",
"foxtrot",
"golf",
"hotel",
"india",
"juliett",
"kilo",
// "lima", // Not defined by default
"mike",
"november",
"oscar",
"papa",
// "quebec", // Not defined by default
"romeo",
"sierra",
]
expect(Object.keys(value)).toStrictEqual(keys)
expect(value.toString()).toEqual(entity3Value)
})
test("Entity4", () => {
Entity1.attributeSeparator = " - "
Entity1.keySeparator = ":"
Entity1.printKey = k => k.toUpperCase()
Entity1.wrap = (entity, v) => `E1[${v}]`
const entity = new Entity4()
expect(entity.toString()).toEqual(entity4Value)
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,581 +0,0 @@
import { expect, test } from "@playwright/test"
import Utility from "../js/Utility.js"
import FormatTextEntity from "../js/entity/FormatTextEntity.js"
import GuidEntity from "../js/entity/GuidEntity.js"
import IntegerEntity from "../js/entity/IntegerEntity.js"
import KeyBindingEntity from "../js/entity/KeyBindingEntity.js"
import LinearColorEntity from "../js/entity/LinearColorEntity.js"
import ObjectReferenceEntity from "../js/entity/ObjectReferenceEntity.js"
import PinEntity from "../js/entity/PinEntity.js"
import RotatorEntity from "../js/entity/RotatorEntity.js"
import SimpleSerializationRotatorEntity from "../js/entity/SimpleSerializationRotatorEntity.js"
import SimpleSerializationVector2DEntity from "../js/entity/SimpleSerializationVector2DEntity.js"
import SimpleSerializationVectorEntity from "../js/entity/SimpleSerializationVectorEntity.js"
import SymbolEntity from "../js/entity/SymbolEntity.js"
import UnknownKeysEntity from "../js/entity/UnknownKeysEntity.js"
import Vector2DEntity from "../js/entity/Vector2DEntity.js"
import VectorEntity from "../js/entity/VectorEntity.js"
import Grammar from "../js/serialization/Grammar.js"
import SerializerFactory from "../js/serialization/SerializerFactory.js"
import initializeSerializerFactory from "../js/serialization/initializeSerializerFactory.js"
test.beforeAll(() => initializeSerializerFactory())
test.describe.configure({ mode: "parallel" })
test("Array", () => {
const serializer = SerializerFactory.getSerializer(Array)
expect(serializer.read("()")).toStrictEqual([])
expect(serializer.read("( )")).toStrictEqual([])
expect(serializer.read("(1, 2, 3, 4, 5, 6)")).toStrictEqual([1, 2, 3, 4, 5, 6])
expect(serializer.read(`(
"alpha",
"beta",
123,
3BEF2168446CAA32D5B54289FAB2F0BA,
Some(a=1, b="2")
)`)).toStrictEqual([
"alpha",
"beta",
123,
new GuidEntity("3BEF2168446CAA32D5B54289FAB2F0BA"),
new UnknownKeysEntity({
lookbehind: "Some",
a: 1,
b: "2",
})
])
expect(serializer.read(`(
A(first = (9,8,7,6,5), second = 00000000000000000000000000000000),
B(key="hello"),
)`)).toStrictEqual([
new UnknownKeysEntity({
lookbehind: "A",
first: [9, 8, 7, 6, 5],
second: new GuidEntity("00000000000000000000000000000000"),
}),
new UnknownKeysEntity({
lookbehind: "B",
key: "hello",
})
])
// Nested
expect(serializer.read("((1, 2), (3, 4))")).toStrictEqual([[1, 2], [3, 4]])
expect(serializer.read('(((1, 2), (3, 4)), 5)')).toStrictEqual([[[1, 2], [3, 4]], 5])
expect(serializer.read(`(
One(a = (1,(2,(3,(4)))), b = ()),
)`)).toStrictEqual([
new UnknownKeysEntity({
lookbehind: "One",
a: [1, [2, [3, [4]]]],
b: null,
}),
])
})
test("Boolean", () => {
let serializer = SerializerFactory.getSerializer(Boolean)
expect(serializer.read("true")).toStrictEqual(true)
expect(serializer.read("True")).toStrictEqual(true)
expect(serializer.read("false")).toStrictEqual(false)
expect(serializer.read("False")).toStrictEqual(false)
})
test("FormatTextEntity", () => {
let serializer = SerializerFactory.getSerializer(FormatTextEntity)
expect(
serializer.read(`LOCGEN_FORMAT_NAMED(NSLOCTEXT("KismetSchema", "SplitPinFriendlyNameFormat", "{PinDisplayName} {ProtoPinDisplayName}"), "PinDisplayName", "Out Hit", "ProtoPinDisplayName", "Blocking Hit")`)
.toString()
).toBe("Out Hit Blocking Hit")
expect(
serializer.read(`LOCGEN_FORMAT_NAMED(NSLOCTEXT("KismetSchema", "SplitPinFriendlyNameFormat", "{PinDisplayName} {ProtoPinDisplayName}"), "PinDisplayName", "Out Hit", "ProtoPinDisplayName", "Hit Bone Name")`)
.toString()
).toBe("Out Hit Hit Bone Name")
expect(
serializer.read(String.raw`LOCGEN_FORMAT_ORDERED(
NSLOCTEXT(
"PCGSettings",
"OverridableParamPinTooltip",
"{0}Attribute type is \"{1}\" and its exact name is \"{2}\""
),
"If InRangeMin = InRangeMax, then that density value is mapped to the average of OutRangeMin and OutRangeMax\n",
"float",
"InRangeMin"
)`)
.toString()
).toBe(`If InRangeMin = InRangeMax, then that density value is mapped to the average of OutRangeMin and OutRangeMax\nAttribute type is "float" and its exact name is "InRangeMin"`)
})
test("GuidEntity", () => {
let serializer = SerializerFactory.getSerializer(GuidEntity)
let guid = serializer.read("0556a3ecabf648d0a5c07b2478e9dd32")
expect(guid).toBeInstanceOf(GuidEntity)
expect(guid.value).toBe("0556a3ecabf648d0a5c07b2478e9dd32")
guid = serializer.read("64023BC344E0453DBB583FAC411489BC")
expect(guid).toBeInstanceOf(GuidEntity)
expect(guid.value).toBe("64023BC344E0453DBB583FAC411489BC")
guid = serializer.read("6edC4a425ca948da8bC78bA52DED6C6C")
expect(guid).toBeInstanceOf(GuidEntity)
expect(guid.value).toBe("6edC4a425ca948da8bC78bA52DED6C6C")
expect(() => serializer.read("172087193 9B04362973544B3564FDB2C")).toThrow()
expect(() => serializer.read("E25F14F8F3E9441AB07153E7DA2BA2B")).toThrow()
expect(() => serializer.read("A78988B0097E48418C8CB87EC5A67ABF7")).toThrow()
})
test("IntegerEntity", () => {
let serializer = SerializerFactory.getSerializer(IntegerEntity)
let integer = serializer.read("0")
expect(integer).toBeInstanceOf(IntegerEntity)
expect(integer.value).toStrictEqual(0)
integer = serializer.read("+0")
expect(integer).toBeInstanceOf(IntegerEntity)
expect(integer.value).toStrictEqual(0)
integer = serializer.read("-0")
expect(integer).toBeInstanceOf(IntegerEntity)
expect(integer.value).toStrictEqual(0)
integer = serializer.read("99")
expect(integer).toBeInstanceOf(IntegerEntity)
expect(integer.value).toStrictEqual(99)
integer = serializer.read("-8685")
expect(integer).toBeInstanceOf(IntegerEntity)
expect(integer.value).toStrictEqual(-8685)
integer = serializer.read("+555")
expect(integer).toBeInstanceOf(IntegerEntity)
expect(integer.value).toStrictEqual(555)
integer = serializer.read("1000000000")
expect(integer).toBeInstanceOf(IntegerEntity)
expect(integer.value).toStrictEqual(1000000000)
expect(() => serializer.read("1.2").value).toThrow()
})
test("KeyBindingEntity", () => {
let serializer = SerializerFactory.getSerializer(KeyBindingEntity)
let binding = serializer.read("A")
expect(binding).toBeInstanceOf(KeyBindingEntity)
expect(binding).toMatchObject({ Key: { value: "A" } })
binding = serializer.read("(bCtrl=True,Key=A)")
expect(binding).toBeInstanceOf(KeyBindingEntity)
expect(binding).toMatchObject({ Key: { value: "A" }, bCtrl: true })
binding = serializer.read("(bCtrl=false,bShift=false,bCmd=true,bAlt=false,Key=X)")
expect(binding).toBeInstanceOf(KeyBindingEntity)
expect(binding).toMatchObject({ Key: { value: "X" }, bAlt: false, bCtrl: false, bCmd: true })
binding = serializer.read("( bCtrl= false \n, Key \n\n\n =Y ,bAlt=true )")
expect(binding).toBeInstanceOf(KeyBindingEntity)
expect(binding).toMatchObject({ Key: { value: "Y" }, bAlt: true, bCtrl: false })
})
test("LinearColorEntity", () => {
const serializer = SerializerFactory.getSerializer(LinearColorEntity)
let color = LinearColorEntity.getWhite()
expect(color.toRGBA()).toStrictEqual([255, 255, 255, 255])
expect(color.toRGBAString()).toStrictEqual("FFFFFFFF")
expect(color.toNumber()).toStrictEqual(-1)
expect(color.toHSVA()).toStrictEqual([0, 0, 1, 1])
color = serializer.read("(R=1,G=0,B=0)")
expect(color.toRGBA()).toStrictEqual([255, 0, 0, 255])
expect(color.toRGBAString()).toStrictEqual("FF0000FF")
expect(color.toNumber()).toStrictEqual(-16776961)
expect(color.toHSVA()).toStrictEqual([0, 1, 1, 1])
color = serializer.read("(R=0.000000,G=0.660000,B=1.000000,A=1.000000)")
expect(color.toRGBA()).toStrictEqual([0, 168, 255, 255])
expect(color.toRGBAString()).toStrictEqual("00A8FFFF")
expect(color.toNumber()).toStrictEqual(11075583)
expect(color.toHSVA()).toStrictEqual([0.55666666666666666666, 1, 1, 1])
color = serializer.read("(B=0.04394509003266556,G=0.026789300067696642,A=0.83663232408635,R=0.6884158028074934,)")
expect(color.toRGBA()).toStrictEqual([176, 7, 11, 213])
expect(color.toRGBAString()).toStrictEqual("B0070BD5")
expect(color.toNumber()).toStrictEqual(-1341715499)
expect(color.toHSVA().map(v => Utility.roundDecimals(v, 3))).toStrictEqual([0.996, 0.961, 0.688, 0.837])
color = serializer.read(`(
A = 0.327 ,
R=0.530 , G = 0.685
,B
= 0.9 ,)`)
expect(color.toRGBA()).toStrictEqual([135, 175, 230, 83])
expect(color.toRGBAString()).toStrictEqual("87AFE653")
expect(color.toNumber()).toStrictEqual(-2018515373)
expect(color.toHSVA().map(v => Utility.roundDecimals(v, 3))).toStrictEqual([0.597, 0.411, 0.9, 0.327])
expect(() => serializer.read("(R=0.000000,G=0.660000,A=1.000000)")).toThrow()
expect(() => serializer.read("(R=0.000000,G=\"hello\",A=1.000000)")).toThrow()
})
test("Null", () => {
const serializer = SerializerFactory.getSerializer(null)
expect(serializer.read("()")).toBeNull()
expect(() => serializer.read("123")).toThrow()
expect(() => serializer.read("(a)")).toThrow()
expect(() => serializer.read("(")).toThrow()
})
test("Number", () => {
const serializer = SerializerFactory.getSerializer(Number)
expect(serializer.read("0")).toBeCloseTo(0, 0.00001)
expect(serializer.read("+0")).toBeCloseTo(0, 0.00001)
expect(serializer.read("-0")).toBeCloseTo(0, 0.00001)
expect(serializer.read("5")).toBeCloseTo(5, 0.00001)
expect(serializer.read("0.05")).toBeCloseTo(0.05, 0.00001)
expect(serializer.read("-999.666")).toBeCloseTo(-999.666, 0.001)
expect(serializer.read("+45.4545")).toBeCloseTo(45.4545, 0.001)
expect(serializer.read("+1000000000")).toBeCloseTo(1E9, 0.1)
expect(serializer.read("inf")).toBe(Number.POSITIVE_INFINITY)
expect(serializer.read("+inf")).toBe(Number.POSITIVE_INFINITY)
expect(serializer.read("-inf")).toBe(Number.NEGATIVE_INFINITY)
expect(() => serializer.read("alpha")).toThrow()
})
test("ObjectReferenceEntity", () => {
const serializer = SerializerFactory.getSerializer(ObjectReferenceEntity)
let reference = serializer.read("Class")
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({ type: "Class", path: "" })
expect(serializer.write(reference)).toBe("Class")
reference = serializer.read(`Class'/Script/ShooterGame.ShooterGameMode'`)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({ type: "Class", path: "/Script/ShooterGame.ShooterGameMode" })
expect(serializer.write(reference)).toBe(`Class'/Script/ShooterGame.ShooterGameMode'`)
reference = serializer.read(`EdGraphPin'EdGraphPin_45417'`)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({ type: "EdGraphPin", path: "EdGraphPin_45417" })
expect(serializer.write(reference)).toBe(`EdGraphPin'EdGraphPin_45417'`)
reference = serializer.read(`EdGraphPin'"K2Node_DynamicCast_2126.EdGraphPin_3990988"'`)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({ type: "EdGraphPin", path: "K2Node_DynamicCast_2126.EdGraphPin_3990988" })
expect(serializer.write(reference)).toBe(`EdGraphPin'"K2Node_DynamicCast_2126.EdGraphPin_3990988"'`)
reference = serializer.read(
`"/Script/Engine.MaterialExpressionMaterialFunctionCall'MaterialExpressionMaterialFunctionCall_0'"`
)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({
type: "/Script/Engine.MaterialExpressionMaterialFunctionCall",
path: "MaterialExpressionMaterialFunctionCall_0",
})
expect(serializer.write(reference)).toBe(
`"/Script/Engine.MaterialExpressionMaterialFunctionCall'MaterialExpressionMaterialFunctionCall_0'"`
)
reference = serializer.read(
`/Script/Engine.EdGraph'"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:Do N"'`
)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({
type: "/Script/Engine.EdGraph",
path: "/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:Do N",
})
expect(serializer.write(reference)).toBe(
`/Script/Engine.EdGraph'"/Engine/EditorBlueprintResources/StandardMacros.StandardMacros:Do N"'`
)
reference = serializer.read(
`EdGraphPin'"K2Node_CommutativeAssociativeBinaryOperator_152.EdGraphPin_4045"'`
)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({
type: "EdGraphPin",
path: "K2Node_CommutativeAssociativeBinaryOperator_152.EdGraphPin_4045",
})
expect(serializer.write(reference)).toBe(
`EdGraphPin'"K2Node_CommutativeAssociativeBinaryOperator_152.EdGraphPin_4045"'`
)
reference = serializer.read(
`Function'"/Game/Mods/CrazyDinos/ElementalDragon/CDElementalDragon_Character_BP.SKEL_CDElementalDragon_Character_BP_C:ROS Change Element"'`
)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({
type: "Function",
path: "/Game/Mods/CrazyDinos/ElementalDragon/CDElementalDragon_Character_BP.SKEL_CDElementalDragon_Character_BP_C:ROS Change Element",
})
expect(serializer.write(reference)).toBe(
`Function'"/Game/Mods/CrazyDinos/ElementalDragon/CDElementalDragon_Character_BP.SKEL_CDElementalDragon_Character_BP_C:ROS Change Element"'`
)
reference = serializer.read(`EdGraph'/Game/Systems/BP_MacroGlobal.BP_MacroGlobal:Or+Branch'`)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({
type: "EdGraph",
path: "/Game/Systems/BP_MacroGlobal.BP_MacroGlobal:Or+Branch",
})
expect(serializer.write(reference)).toBe(`EdGraph'/Game/Systems/BP_MacroGlobal.BP_MacroGlobal:Or+Branch'`)
reference = serializer.read(`/Script/Engine.EdGraph'"+-Weird/2,Macro"'`)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({ type: "/Script/Engine.EdGraph", path: "+-Weird/2,Macro" })
expect(serializer.write(reference)).toBe(`/Script/Engine.EdGraph'"+-Weird/2,Macro"'`)
reference = serializer.read(`/Script/BlueprintGraph.K2Node_VariableGet`)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({ type: "/Script/BlueprintGraph.K2Node_VariableGet", path: "" })
expect(serializer.write(reference)).toBe(`/Script/BlueprintGraph.K2Node_VariableGet`)
reference = serializer.read(
`/Script/Engine.MaterialExpressionMaterialFunctionCall'MaterialExpressionMaterialFunctionCall_0'`
)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({
type: "/Script/Engine.MaterialExpressionMaterialFunctionCall",
path: "MaterialExpressionMaterialFunctionCall_0",
})
expect(serializer.write(reference)).toBe(
`/Script/Engine.MaterialExpressionMaterialFunctionCall'MaterialExpressionMaterialFunctionCall_0'`
)
reference = serializer.read(
`/Script/Engine.MaterialExpressionMaterialFunctionCall'/Engine/Transient.Material_0:MaterialGraph_0.MaterialGraphNode_3.MaterialExpressionMaterialFunctionCall_0'`
)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({
type: "/Script/Engine.MaterialExpressionMaterialFunctionCall",
path: "/Engine/Transient.Material_0:MaterialGraph_0.MaterialGraphNode_3.MaterialExpressionMaterialFunctionCall_0",
})
expect(serializer.write(reference)).toBe(
`/Script/Engine.MaterialExpressionMaterialFunctionCall'/Engine/Transient.Material_0:MaterialGraph_0.MaterialGraphNode_3.MaterialExpressionMaterialFunctionCall_0'`
)
reference = serializer.read(`/Script/CoreUObject.Class'"/Script/Engine.GameModeBase"'`)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({
type: "/Script/CoreUObject.Class",
path: "/Script/Engine.GameModeBase",
})
expect(serializer.write(reference)).toBe(`/Script/CoreUObject.Class'"/Script/Engine.GameModeBase"'`)
reference = serializer.read(`"/Game/_YukiritoLib/Textures/T_紫色渐变01.T_紫色渐变01"`)
expect(reference).toBeInstanceOf(ObjectReferenceEntity)
expect(reference).toMatchObject({
type: "/Game/_YukiritoLib/Textures/T_紫色渐变01.T_紫色渐变01",
path: "",
})
})
test("PinEntity", () => {
const serializer = SerializerFactory.getSerializer(PinEntity)
expect(serializer.read("Pin (PinType.PinSubCategoryMemberReference=())")).toMatchObject({
"PinType": { "PinSubCategoryMemberReference": null }
})
})
test("SimpleSerializationRotatorEntity", () => {
const serializer = SerializerFactory.getSerializer(SimpleSerializationRotatorEntity)
expect(serializer.read("0, 0, 0")).toEqual(new SimpleSerializationRotatorEntity({
R: 0,
P: 0,
Y: 0,
}))
expect(serializer.read("0.65, 1.0, 0.99")).toEqual(new SimpleSerializationRotatorEntity({
P: 0.65,
Y: 1.0,
R: 0.99,
}))
expect(serializer.read("7,6,5")).toEqual(new SimpleSerializationRotatorEntity({
P: 7,
Y: 6,
R: 5,
}))
})
test("SimpleSerializationVector2DEntity", () => {
const serializer = SerializerFactory.getSerializer(SimpleSerializationVector2DEntity)
expect(serializer.read("0, 0")).toEqual(new SimpleSerializationVector2DEntity({
X: 0,
Y: 0,
}))
expect(serializer.read("127.8, 13.3")).toEqual(new SimpleSerializationVector2DEntity({
X: 127.8,
Y: 13.3,
}))
expect(serializer.read("5,0")).toEqual(new SimpleSerializationVector2DEntity({
X: 5,
Y: 0,
}))
})
test("SimpleSerializationVectorEntity", () => {
const serializer = SerializerFactory.getSerializer(SimpleSerializationVectorEntity)
expect(serializer.read("0, 0, 0")).toEqual(new SimpleSerializationVectorEntity({
X: 0,
Y: 0,
Z: 0,
}))
expect(serializer.read("1001, 56.4, 0.5")).toEqual(new SimpleSerializationVectorEntity({
X: 1001,
Y: 56.4,
Z: 0.5,
}))
expect(serializer.read("-1,-2,-3")).toEqual(new SimpleSerializationVectorEntity({
X: -1,
Y: -2,
Z: -3,
}))
})
test("String", () => {
const serializer = SerializerFactory.getSerializer(String)
expect(serializer.read('""')).toStrictEqual("")
expect(serializer.read('"hello"')).toStrictEqual("hello")
expect(serializer.read('"hello world 123 - éèàò@ç ^ ^^^"')).toStrictEqual("hello world 123 - éèàò@ç ^ ^^^")
expect(serializer.read('"\\""')).toStrictEqual('"')
expect(() => serializer.read("Hello")).toThrow()
expect(serializer.write(`"/Script/CoreUObject.Class'/Script/Interhaptics.HapticSource'"`))
.toBe(String.raw`"\"/Script/CoreUObject.Class'/Script/Interhaptics.HapticSource'\""`)
})
test("UnknownKeysValue", () => {
const parser = Grammar.unknownValue
expect(parser.parse('"Hello"').constructor).toStrictEqual(String)
expect(parser.parse("()")).toBeNull()
expect(parser.parse("8345").constructor).toStrictEqual(Number)
expect(parser.parse("True").constructor).toStrictEqual(Boolean)
expect(parser.parse("False").constructor).toStrictEqual(Boolean)
expect(parser.parse("F0223D3742E67C0D9FEFB2A64946B7F0").constructor).toStrictEqual(GuidEntity)
expect(parser.parse("SYMBOL1").constructor).toStrictEqual(SymbolEntity)
expect(parser.parse("Symbol_2_3_4").constructor).toStrictEqual(SymbolEntity)
expect(parser.parse("(X=-0.495, Y=0, )").constructor).toStrictEqual(Vector2DEntity)
expect(parser.parse("(X=-0.495,Y=+765.0,Z=7)").constructor).toStrictEqual(VectorEntity)
expect(parser.parse("(R=1.000000,P=7.6,Y=+88.99)").constructor).toStrictEqual(RotatorEntity)
expect(parser.parse("(R=0.000000,G=0.660000,B=1.000000,A=1.000000)").constructor)
.toStrictEqual(LinearColorEntity)
expect(parser.parse(`Class'"/Script/Engine.KismetSystemLibrary"'`).constructor)
.toStrictEqual(ObjectReferenceEntity)
expect(parser.parse("(1,2,3,4,5,6,7,8,9)")).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9])
expect(parser.parse(`( "Hello", "World", )`)).toStrictEqual(["Hello", "World"])
expect(parser.parse(`( "Alpha", 123, Beta, "Gamma", "Delta", 99 )`))
.toStrictEqual(["Alpha", 123, new SymbolEntity({ value: "Beta" }), "Gamma", "Delta", 99])
})
test("UnknownKeysEntity", () => {
const serializer = SerializerFactory.getSerializer(UnknownKeysEntity)
let unknown = serializer.read('LookbehindValue(FirstKey=1,SecondKey=SOME_SYMBOL2,ThirdKey="Hello")')
expect(unknown).toBeInstanceOf(UnknownKeysEntity)
expect(unknown).toMatchObject({
lookbehind: "LookbehindValue",
FirstKey: 1,
SecondKey: new SymbolEntity("SOME_SYMBOL2"),
ThirdKey: "Hello",
})
unknown = serializer.read('(A = (-1,-2,-3), B = SomeFunction(B1 = "b1", B2 = (X=101,Y=102,Z=103)))')
expect(unknown).toBeInstanceOf(UnknownKeysEntity)
expect(unknown).toMatchObject({
A: [-1, -2, -3],
B: new UnknownKeysEntity({
lookbehind: "SomeFunction",
B1: "b1",
B2: new VectorEntity({ X: 101, Y: 102, Z: 103 }),
}),
})
})
test("VectorEntity", () => {
const serializer = SerializerFactory.getSerializer(VectorEntity)
let vector = serializer.read("(X=1,Y=2,Z=3.5)")
expect(vector).toBeInstanceOf(VectorEntity)
expect(vector).toStrictEqual(new VectorEntity({
X: 1,
Y: 2,
Z: 3.5,
}))
vector = serializer.read("(X=10,Y=+20.88,Z=-30.54,)")
expect(vector).toBeInstanceOf(VectorEntity)
expect(vector).toStrictEqual(new VectorEntity({
X: 10,
Y: 20.88,
Z: -30.54,
}))
vector = serializer.read(`(
Z = -3.66 ,
X
= -1 , Y =
-2
,
)`)
expect(vector).toBeInstanceOf(VectorEntity)
expect(vector).toStrictEqual(new VectorEntity({
X: -1,
Y: -2,
Z: -3.66,
}))
expect(() => serializer.read("(X=1,Y=\"2\",Z=3)")).toThrow()
expect(() => serializer.read("(X=1,Z=3)")).toThrow()
expect(() => serializer.read("(X=1,Y=2,Unexpected=6,Z=3.5)")).toThrow()
})
test("Vector2DEntity", () => {
let serializer = SerializerFactory.getSerializer(Vector2DEntity)
let vector = serializer.read("(X=78,Y=56.3)")
expect(vector).toBeInstanceOf(Vector2DEntity)
expect(vector).toStrictEqual(new Vector2DEntity({
X: 78,
Y: 56.3,
}))
vector = serializer.read("(X=+4.5,Y=-8.88,)")
expect(vector).toBeInstanceOf(Vector2DEntity)
expect(vector).toStrictEqual(new Vector2DEntity({
X: 4.5,
Y: -8.88,
}))
vector = serializer.read(`(
Y = +93.004 ,
X
= 0 ,
)`)
expect(vector).toBeInstanceOf(Vector2DEntity)
expect(vector).toStrictEqual(new Vector2DEntity({
X: 0,
Y: 93.004,
}))
expect(() => serializer.read("(X=1,Y=\"2\")")).toThrow()
expect(() => serializer.read("(X=1)")).toThrow()
expect(() => serializer.read("(X=777, Y=555, Unexpected=6, HH=2)")).toThrow()
})

View File

@@ -1,20 +1,13 @@
import AttributeInfo from "../../js/entity/AttributeInfo.js"
import IEntity from "../../js/entity/IEntity.js"
import NumberEntity from "../../js/entity/NumberEntity.js"
export default class Entity1 extends IEntity {
static attributeSeparator = ", "
static wrap = (entity, v) => `Entity1(${v})`
static attributes = {
a: new AttributeInfo({
type: Number,
default: 8,
}),
b: new AttributeInfo({
type: Number,
default: 9,
}),
}
constructor(values = {}) {
super(values)
...super.attributes,
a: NumberEntity.withDefault(type => new type(8)),
b: NumberEntity.withDefault(type => new type(9)),
}
}

View File

@@ -1,21 +1,38 @@
import AttributeInfo from "../../js/entity/AttributeInfo.js"
import ArrayEntity from "../../js/entity/ArrayEntity.js"
import BooleanEntity from "../../js/entity/BooleanEntity.js"
import IEntity from "../../js/entity/IEntity.js"
import NumberEntity from "../../js/entity/NumberEntity.js"
import StringEntity from "../../js/entity/StringEntity.js"
import Entity1 from "./Entity1.js"
export default class Entity2 extends IEntity {
static attributeSeparator = "\n"
static keySeparator = ": "
static printKey = k => ` ${k}`
static wrap = (entity, v) => `{\n${v}\n}`
static attributes = {
someNumber: AttributeInfo.createValue(567),
someString: AttributeInfo.createValue("alpha"),
someString2: AttributeInfo.createValue("beta"),
someBoolean: AttributeInfo.createValue(true),
someBoolean2: AttributeInfo.createValue(false),
someObjectString: AttributeInfo.createValue("gamma"),
someArray: AttributeInfo.createValue([400, 500, 600, 700, 800]),
someArray2: AttributeInfo.createValue(() => [400, 500, 600, 700, 800]),
someEntity: new AttributeInfo({
type: Entity1,
default: new Entity1()
}),
...super.attributes,
someNumber: NumberEntity.withDefault(type => new type(567)),
someString: StringEntity.withDefault(type => new type("alpha")),
someString2: StringEntity.withDefault(type => new type("beta")),
someBoolean: BooleanEntity.withDefault(type => new type(true)),
someBoolean2: BooleanEntity.withDefault(),
someObjectString: StringEntity.withDefault(type => new type("gamma")),
someArray: ArrayEntity.of(NumberEntity).withDefault(type => new type([
new NumberEntity(400),
new NumberEntity(500),
new NumberEntity(600),
new NumberEntity(700),
new NumberEntity(800),
])),
someArray2: ArrayEntity.of(NumberEntity).withDefault(type => new type([
new NumberEntity(-400),
new NumberEntity(-500),
new NumberEntity(-600),
new NumberEntity(-700),
new NumberEntity(-800),
])),
someEntity: Entity1.withDefault(),
}
}

View File

@@ -1,74 +1,58 @@
import AttributeInfo from "../../js/entity/AttributeInfo.js"
import AlternativesEntity from "../../js/entity/AlternativesEntity.js"
import ArrayEntity from "../../js/entity/ArrayEntity.js"
import BooleanEntity from "../../js/entity/BooleanEntity.js"
import IEntity from "../../js/entity/IEntity.js"
import Union from "../../js/entity/Union.js"
import NullEntity from "../../js/entity/NullEntity.js"
import NumberEntity from "../../js/entity/NumberEntity.js"
import StringEntity from "../../js/entity/StringEntity.js"
import Entity1 from "./Entity1.js"
import Entity2 from "./Entity2.js"
export default class Entity3 extends IEntity {
static attributeSeparator = "\n"
static keySeparator = ": "
static printKey = k => ` ${k}`
static wrap = (entity, v) => `[[\n${v}\n]]`
static attributes = {
alpha: AttributeInfo.createValue(32),
bravo: new AttributeInfo({
type: Number,
default: 78,
}),
charlie: new AttributeInfo({
type: String,
default: "Charlie",
}),
delta: new AttributeInfo({
type: String,
default: null,
}),
echo: AttributeInfo.createValue("echo"),
foxtrot: AttributeInfo.createValue(false),
golf: AttributeInfo.createValue([]),
hotel: new AttributeInfo({
type: Array,
default: null,
}),
india: new AttributeInfo({
type: [Number],
default: () => [],
}),
juliett: new AttributeInfo({
type: [String],
default: ["a", "b", "c", "d", "e"],
}),
kilo: new AttributeInfo({
type: [Boolean],
default: () => [true, false, false, true, true],
}),
lima: AttributeInfo.createType(String),
mike: new AttributeInfo({
type: new Union(Number, String, Array),
default: "Bar",
}),
november: new AttributeInfo({
type: new Union(Number, String, Array),
default: 0,
}),
oscar: new AttributeInfo({
type: Entity1,
default: () => new Entity1()
}),
papa: new AttributeInfo({
type: Entity1,
default: () => new Entity1({ a: 12, b: 13 }),
}),
quebec: new AttributeInfo({
default: 0, // will assign undefined because it does not satisfy the predicate
predicate: v => v >= 1 && v <= 10,
}),
romeo: new AttributeInfo({
type: Entity1,
default: new Entity1(),
inlined: true,
}),
sierra: new AttributeInfo({
type: Entity2,
default: new Entity2(),
inlined: true,
}),
...super.attributes,
alpha: NumberEntity.withDefault(type => new type(32)),
bravo: NumberEntity.withDefault(type => new type(78)),
charlie: StringEntity.withDefault(type => new type("Charlie")),
delta: StringEntity.withDefault(type => new NullEntity()),
echo: StringEntity.withDefault(type => new type("echo")),
foxtrot: BooleanEntity.withDefault(),
golf: ArrayEntity.of(StringEntity).withDefault(),
hotel: ArrayEntity.of(NumberEntity).withDefault(() => new NullEntity()),
india: ArrayEntity.of(NumberEntity).withDefault(),
juliett: ArrayEntity.of(StringEntity).withDefault(type => new type([
new StringEntity("a"),
new StringEntity("b"),
new StringEntity("c"),
new StringEntity("d"),
new StringEntity("e"),
])),
kilo: ArrayEntity.of(BooleanEntity).withDefault(type => new type([
new BooleanEntity(true),
new BooleanEntity(),
new BooleanEntity(),
new BooleanEntity(true),
new BooleanEntity(true),
])),
lima: StringEntity,
mike: AlternativesEntity
.accepting(NumberEntity, StringEntity, ArrayEntity)
.withDefault(type => new StringEntity("Bar")),
november: AlternativesEntity
.accepting(NumberEntity, StringEntity, ArrayEntity)
.withDefault(type => new NumberEntity(0)),
oscar: Entity1.withDefault(),
papa: Entity1.withDefault(type => new type({
a: new NumberEntity(12),
b: new NumberEntity(13),
})),
quebec: NumberEntity,
romeo: Entity1.withDefault().flagInlined(),
sierra: Entity2.withDefault().flagInlined(),
}
}

View File

@@ -1,33 +1,23 @@
import AttributeInfo from "../../js/entity/AttributeInfo.js"
import ArrayEntity from "../../js/entity/ArrayEntity.js"
import IEntity from "../../js/entity/IEntity.js"
import NullEntity from "../../js/entity/NullEntity.js"
import NumberEntity from "../../js/entity/NumberEntity.js"
import Entity1 from "./Entity1.js"
import Entity3 from "./Entity3.js"
export default class Entity4 extends IEntity {
static attributeSeparator = "\n"
static keySeparator = " => "
static printKey = k => ` \${${k}}`
static wrap = (entity, v) => `Begin\n${v}\nEnd`
static attributes = {
first: new AttributeInfo({
type: Entity3,
default: new Entity3(),
inlined: true,
}),
second: new AttributeInfo({
default: [new Entity1({ a: 1, b: 2 }), new Entity1({ a: 11, b: 22 })],
inlined: true,
}),
third: new AttributeInfo({
type: Array,
default: null,
})
}
constructor() {
super()
/** @type {Entity1} */ this.second
IEntity.defineAttributes(this.second, {
0: {
inlined: true,
},
})
...super.attributes,
first: Entity3.withDefault().flagInlined(),
second: ArrayEntity.of(Entity1).withDefault(type => new type([
new (Entity1.flagInlined())({ a: new NumberEntity(1), b: new NumberEntity(2) }),
new Entity1({ a: new NumberEntity(11), b: new NumberEntity(22) }),
])).flagInlined(),
third: ArrayEntity.withDefault(() => new NullEntity()),
}
}

View File

@@ -1,19 +0,0 @@
import AttributeInfo from "../../js/entity/AttributeInfo.js"
import IntegerEntity from "../../js/entity/IntegerEntity.js"
import ObjectEntity from "../../js/entity/ObjectEntity.js"
import EntityF from "./EntityF.js"
// @ts-expect-error
export default class Entity5 extends ObjectEntity {
static attributes = {
key1: AttributeInfo.createType(String),
key2: AttributeInfo.createType(EntityF),
key3: new AttributeInfo({
type: IntegerEntity,
default: new IntegerEntity(5),
silent: true,
}),
}
static grammar = this.createGrammar()
}

View File

@@ -1,27 +0,0 @@
import AttributeInfo from "../../js/entity/AttributeInfo.js"
import IEntity from "../../js/entity/IEntity.js"
import Union from "../../js/entity/Union.js"
import Grammar from "../../js/serialization/Grammar.js"
export default class EntityF extends IEntity {
static attributes = {
...super.attributes,
arg1: AttributeInfo.createType(Number),
arg2: AttributeInfo.createType(String),
lookbehind: new AttributeInfo({
...super.attributes.lookbehind,
default: new Union("Foo", "Bar"),
})
}
static grammar = this.createGrammar()
static createGrammar() {
return Grammar.createEntityGrammar(this, false)
}
constructor(values = {}) {
super(values)
}
}

View File

@@ -44,7 +44,93 @@ export default class IssuesNodes1 extends NodeTests {
const relevantPins = (await Promise.all(
pins.map(async p => {
const innerText = await p.innerText()
// @ts-expect-error
return [Configuration.rgba.includes(innerText), p]
})
))
.filter(([flag, value]) => flag)
.map(([flag, value]) => /** @type {Locator<PinElement>} */(value))
expect(await Promise.all(relevantPins.map(async pin => await pin.innerText()))).toStrictEqual(Configuration.rgba)
for (const p of relevantPins) {
const pinName = await p.innerText()
expect(p.locator('input[type="checkbox"]')).toBeChecked({ checked: pinName === "R" })
}
await relevantPins[0].locator('input[type="checkbox"]').uncheck() // Uncheck "R"
await relevantPins[2].locator('input[type="checkbox"]').check() // Check "B"
await relevantPins[3].locator('input[type="checkbox"]').check() // Check "A"
await relevantPins[2].locator('input[type="checkbox"]').uncheck() // Uncheck "B"
await relevantPins[2].locator('input[type="checkbox"]').check() // Check "B"
expect(node.locator(".ueb-node-name")).toHaveText("Mask ( B A )")
const resultSerialization = await blueprintPage.blueprintLocator.evaluate(blueprint => {
blueprint.selectAll()
return blueprint.template.getCopyInputObject().getSerializedText()
})
const expectedSerialization = String.raw`
Begin Object Class=/Script/UnrealEd.MaterialGraphNode Name="MaterialGraphNode_37" ExportPath="/Script/UnrealEd.MaterialGraphNode'/Engine/Transient.NewMaterial:MaterialGraph_0.MaterialGraphNode_37'"
Begin Object Class=/Script/Engine.MaterialExpressionComponentMask Name="MaterialExpressionComponentMask_0" ExportPath="/Script/Engine.MaterialExpressionComponentMask'/Engine/Transient.NewMaterial:MaterialGraph_0.MaterialGraphNode_37.MaterialExpressionComponentMask_0'"
End Object
Begin Object Name="MaterialExpressionComponentMask_0" ExportPath="/Script/Engine.MaterialExpressionComponentMask'/Engine/Transient.NewMaterial:MaterialGraph_0.MaterialGraphNode_37.MaterialExpressionComponentMask_0'"
B=True
A=True
MaterialExpressionEditorX=-544
MaterialExpressionEditorY=32
MaterialExpressionGuid=8EFA535CAE3A4DAF9DAE27B200E06EDC
Material="/Script/UnrealEd.PreviewMaterial'/Engine/Transient.NewMaterial'"
End Object
MaterialExpression="/Script/Engine.MaterialExpressionComponentMask'MaterialExpressionComponentMask_0'"
NodePosX=-544
NodePosY=32
AdvancedPinDisplay=Shown
NodeGuid=54A40610EEC646A0954F310727D1B888
CustomProperties Pin (PinId=DC3859AB4C8C12645EEA1AA4E500A637,PinName="Input",PinFriendlyName=NSLOCTEXT("MaterialGraphNode", "Space", " "),PinType.PinCategory="required",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=D5C8F4DF4AFE5EEB605ED382CD5744DE,PinName="R",PinType.PinCategory="optional",PinType.PinSubCategory="bool",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="false",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=True,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,)
CustomProperties Pin (PinId=7E43455B4D2232C4E99BB098631CAFCE,PinName="G",PinType.PinCategory="optional",PinType.PinSubCategory="bool",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="false",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=True,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,)
CustomProperties Pin (PinId=46CEC6754365CB39F9FC39944B40D5C6,PinName="B",PinType.PinCategory="optional",PinType.PinSubCategory="bool",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="true",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=True,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,)
CustomProperties Pin (PinId=F658E76C400B0AF242DFE292C92702C8,PinName="A",PinType.PinCategory="optional",PinType.PinSubCategory="bool",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="true",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=True,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=True,bOrphanedPin=False,)
CustomProperties Pin (PinId=2EC8C8234D570AB2A03DB59A1FF65987,PinName="Output",PinFriendlyName=NSLOCTEXT("MaterialGraphNode", "Space", " "),Direction="EGPD_Output",PinType.PinCategory="",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
`
const words = expectedSerialization
.split("\n")
.map(row => row.match(/\s*("?\w+(\s+\w+)*).+/)?.[1])
.filter(v => v?.length > 0)
expect(resultSerialization).toMatch(Utility.getFirstWordOrder(words))
}
},
{
name: "Issue 21",
title: "Mask ( R )",
value: String.raw`
Begin Object Class=/Script/UnrealEd.MaterialGraphNode Name="MaterialGraphNode_202" ExportPath=/Script/UnrealEd.MaterialGraphNode'/Engine/Transient.卡通:MaterialGraph_0.MaterialGraphNode_202'
Begin Object Class=/Script/Engine.MaterialExpressionSubtract Name="MaterialExpressionSubtract_10" ExportPath=/Script/Engine.MaterialExpressionSubtract'/Engine/Transient.卡通:MaterialGraph_0.MaterialGraphNode_202.MaterialExpressionSubtract_10'
End Object
Begin Object Name="MaterialExpressionSubtract_10" ExportPath=/Script/Engine.MaterialExpressionSubtract'/Engine/Transient.卡通:MaterialGraph_0.MaterialGraphNode_202.MaterialExpressionSubtract_10'
A=(Expression="/Script/Engine.MaterialExpressionSaturate'MaterialGraphNode_237.MaterialExpressionSaturate_3'")
B=(Expression="/Script/Engine.MaterialExpressionSaturate'MaterialGraphNode_201.MaterialExpressionSaturate_7'")
MaterialExpressionEditorX=0
MaterialExpressionEditorY=0
MaterialExpressionGuid=7202C13642DA1225C118CF867599387C
Material="/Script/UnrealEd.PreviewMaterial'/Engine/Transient.卡通'"
End Object
MaterialExpression=/Script/Engine.MaterialExpressionSubtract'MaterialExpressionSubtract_10'
NodePosX=0
NodePosY=0
NodeGuid=7008F5AC49E8F5BFD4C707819A58C021
CustomProperties Pin (PinId=86D4DE5E48C71A576ED0519B982907B3,PinName="A",PinType.PinCategory="optional",PinType.PinSubCategory="red",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1",LinkedTo=(),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=5C75E1374E1E7436C72B9FA072875C04,PinName="B",PinType.PinCategory="optional",PinType.PinSubCategory="red",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultValue="1",LinkedTo=(),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
CustomProperties Pin (PinId=528D346A49976B0854764CA755AF2F93,PinName="Output",PinFriendlyName=NSLOCTEXT("MaterialGraphNode", "Space", " "),Direction="EGPD_Output",PinType.PinCategory="",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
`,
size: [7, 14.5],
color: Configuration.nodeColors.green,
icon: null,
pins: 6,
pinNames: Configuration.rgba,
delegate: false,
development: false,
additionalTest: async (node, pins, blueprintPage) => {
const relevantPins = (await Promise.all(
pins.map(async p => {
const innerText = await p.innerText()
return [Configuration.rgba.includes(innerText), p]
})
))

View File

@@ -0,0 +1,12 @@
export default `{
someNumber: 567
someString: "alpha"
someString2: "beta"
someBoolean: True
someBoolean2: False
someObjectString: "gamma"
someArray: (400,500,600,700,800)
someArray2: (-400,-500,-600,-700,-800)
someEntity.a: 8
someEntity.b: 9
}`

View File

@@ -6,6 +6,6 @@ export default `{
someBoolean2: False
someObjectString: "gamma"
someArray: (400,500,600,700,800)
someArray2: (400,500,600,700,800)
someArray2: (-400,-500,-600,-700,-800)
someEntity: Entity1(a=8, b=9)
}`

View File

@@ -14,7 +14,6 @@ export default `[[
november: 0
oscar: Entity1(a=8, b=9)
papa: Entity1(a=12, b=13)
quebec: 6
romeo.a: 8
romeo.b: 9
sierra.someNumber: 567
@@ -24,6 +23,6 @@ export default `[[
sierra.someBoolean2: False
sierra.someObjectString: "gamma"
sierra.someArray: (400,500,600,700,800)
sierra.someArray2: (400,500,600,700,800)
sierra.someArray2: (-400,-500,-600,-700,-800)
sierra.someEntity: Entity1(a=8, b=9)
]]`

View File

@@ -23,7 +23,7 @@ export default `Begin
\${first.sierra.someBoolean2} => False
\${first.sierra.someObjectString} => "gamma"
\${first.sierra.someArray} => (400,500,600,700,800)
\${first.sierra.someArray2} => (400,500,600,700,800)
\${first.sierra.someArray2} => (-400,-500,-600,-700,-800)
\${first.sierra.someEntity} => E1[A:8 - B:9]
\${second(0).a} => 1
\${second(0).b} => 2

Some files were not shown because too many files have changed in this diff Show More