Arrays elements can now be inlined

This commit is contained in:
barsdeveloper
2023-04-11 21:21:03 +02:00
parent 1015f4787c
commit d10589f0bd
9 changed files with 189 additions and 29 deletions

View File

@@ -0,0 +1,66 @@
/// <reference types="cypress" />
import { generateNodeTest } from "../fixtures/testUtilities.js"
const tests = [
{
name: "ROS Change Element",
value: String.raw`
Begin Object Class=/Script/BlueprintGraph.K2Node_CustomEvent Name="K2Node_CustomEvent_13465"
Begin Object Class=/Script/Engine.EdGraphPin_Deprecated Name="EdGraphPin_2859957"
End Object
Begin Object Class=/Script/Engine.EdGraphPin_Deprecated Name="EdGraphPin_2859956"
End Object
Begin Object Class=/Script/Engine.EdGraphPin_Deprecated Name="EdGraphPin_2859955"
End Object
Begin Object Name="EdGraphPin_2859957"
PinName="Element"
Direction=EGPD_Output
PinType=(PinCategory="int")
LinkedTo(0)=None
LinkedTo(1)=None
End Object
Begin Object Name="EdGraphPin_2859956"
PinName="then"
Direction=EGPD_Output
PinType=(PinCategory="exec")
LinkedTo(0)=None
End Object
Begin Object Name="EdGraphPin_2859955"
PinName="OutputDelegate"
Direction=EGPD_Output
PinType=(PinCategory="delegate")
End Object
CustomFunctionName="ROS Change Element"
FunctionFlags=2097344
NodePosX=-3696
NodePosY=-128
ErrorType=1
NodeGuid=A7AFBC3557734BFDA0D1E917569CA6A1
CustomProperties Pin (PinId=989B6502AF0240A28DE51122C9F3F5D7,PinName="OutputDelegate",Direction="EGPD_Output",PinType.PinCategory="delegate",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent=/Script/Engine.BlueprintGeneratedClass'"/Temp/Untitled_1.Untitled_1_C"',MemberName="ROS Change Element",MemberGuid=A7AFBC3557734BFDA0D1E917569CA6A1),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=385BD405C63F4EC5B7D55D902D37A6CE,PinName="then",Direction="EGPD_Output",PinType.PinCategory="exec",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 UserDefinedPin ()
End Object
`,
pins: 2,
delegate: false,
development: false,
},
]
/** @type {Blueprint} */
let blueprint
before(() => {
cy.visit(`http://127.0.0.1:${Cypress.env("UEBLUEPRINT_TEST_SERVER_PORT")}/empty.html`, {
onLoad: () => {
cy.get("ueb-blueprint")
.then(b => blueprint = b[0])
.click(100, 300)
}
})
})
tests.forEach(
testObject => generateNodeTest(testObject, () => blueprint)
)

View File

@@ -308,6 +308,11 @@ describe("Serializer", () => {
expect(parser.parse(`Class'"/Script/Engine.KismetSystemLibrary"'`).value.constructor)
.equals(ObjectReferenceEntity)
)
it("Parses ObjectReferenceEntity 2", () =>
expect(parser.parse(`Function'"/Game/Mods/CrazyDinos/ElementalDragon/CDElementalDragon_Character_BP.SKEL_CDElementalDragon_Character_BP_C:ROS Change Element"'`)
.value.constructor)
.equals(ObjectReferenceEntity)
)
it("Parses Numbers array", () =>
expect(parser.parse("(1,2,3,4,5,6,7,8,9)").value).to.be.deep.equal([1, 2, 3, 4, 5, 6, 7, 8, 9])
)

View File

@@ -15,6 +15,15 @@ export default class Entity4 extends IEntity {
},
}
constructor() {
super()
IEntity.defineAttributes(this.second, {
0: {
inlined: true,
},
})
}
static {
this.cleanupAttributes(this.attributes)
}

View File

@@ -24,6 +24,7 @@ export default `Begin
\${first.sierra.someObjectString} => "gamma"
\${first.sierra.someArray} => (400,500,600,700,800,)
\${first.sierra.someEntity} => Entity1(a=8, b=9)
\${second(0)} => Entity1(a=1, b=2)
\${second(0).a} => 1
\${second(0).b} => 2
\${second(1)} => Entity1(a=11, b=22)
End`

63
dist/ueblueprint.js vendored
View File

@@ -880,8 +880,9 @@ class Utility {
* @typedef {IEntity | String | Number | BigInt | Boolean} AnySimpleValue
* @typedef {AnySimpleValue | AnySimpleValue[]} AnyValue
* @typedef {(entity: IEntity) => AnyValue} ValueSupplier
* @typedef {AnyValueConstructor<AnyValue> | AnyValueConstructor<AnyValue>[] | UnionType | UnionType[] | ComputedType} AttributeType
* @typedef {{
* type?: AnyValueConstructor<AnyValue> | AnyValueConstructor<AnyValue>[] | UnionType | ComputedType,
* type?: AttributeType,
* default?: AnyValue | ValueSupplier,
* showDefault?: Boolean,
* nullable?: Boolean,
@@ -919,7 +920,16 @@ class IEntity {
constructor(values = {}, suppressWarns = false) {
const Self = /** @type {EntityConstructor} */(this.constructor);
const attributes = Self.attributes;
let attributes = Self.attributes;
if (values.attributes) {
Utility.mergeArrays(Object.keys(attributes), Object.keys(values.attributes))
.forEach(k => attributes[k] = {
...attributes[k],
...values.attributes[k]
});
IEntity.defineAttributes(this, attributes);
}
/** @type {AttributeDeclarations?} */ this.attributes;
if (values.constructor !== Object && Object.keys(attributes).length === 1) {
// Where there is just one attribute, option can be the value of that attribute
values = {
@@ -1078,6 +1088,22 @@ class IEntity {
.some(/** @param {AttributeInformation} attribute */attribute => !attribute.expected)
}
static getAttribute(object, attribute) {
return this.getAttributes(object)[attribute]
}
static getAttributes(object) {
return object.attributes ?? object.constructor?.attributes ?? {}
}
static defineAttributes(object, attributes) {
Object.defineProperty(object, "attributes", {
writable: true,
configurable: false,
});
object.attributes = attributes;
}
unexpectedKeys() {
return Object.keys(this).length
- Object.keys(/** @type {typeof IEntity} */(this.constructor).attributes).length
@@ -3294,11 +3320,13 @@ class UnknownKeysEntity extends IEntity {
}
/**
* @typedef {import ("../entity/IEntity").AnyValue} AnyValue
* @typedef {import ("../entity/IEntity").AttributeType} AttributeType
* @typedef {import ("../entity/IEntity").AttributeInformation} AttributeInformation
* @typedef {import ("../entity/IEntity").EntityConstructor} EntityConstructor
*/
/**
* @template T
* @template {AnyValue} T
* @typedef {import ("../entity/IEntity").AnyValueConstructor<T>} AnyValueConstructor
*/
@@ -3391,7 +3419,7 @@ class Grammar {
}
/**
* @param {AnyValueConstructor<any>} type
* @param {AttributeType} type
* @returns {Parsimmon.Parser<any>}
*/
static grammarFor(
@@ -3855,8 +3883,8 @@ class Grammar {
})
)
static inlinedArrayEntry = P.lazy(() => {
return P.seq(
static inlinedArrayEntry = P.lazy(() =>
P.seq(
this.symbol,
this.regexMap(
new RegExp(`\\s*\\(\\s*(\\d+)\\s*\\)\\s*\\=\\s*`),
@@ -3869,8 +3897,16 @@ class Grammar {
values => (values[symbol] ??= []).push(currentValue)
)
)
})
)
static subObjectEntity = P.lazy(() =>
this.objectEntity
.map(object =>
values => values["SubObject_" + object.Name] = object
)
)
/** @type {Parsimmon.Parser<ObjectEntity>} */
static objectEntity = P.lazy(() =>
P.seq(
P.regex(/Begin\s+Object/),
@@ -3879,7 +3915,9 @@ class Grammar {
P.alt(
this.customProperty,
this.createAttributeGrammar(ObjectEntity),
this.inlinedArrayEntry
this.inlinedArrayEntry,
// Legacy subobject
this.subObjectEntity
)
)
.map(([_0, entry]) => entry)
@@ -4043,7 +4081,7 @@ class Serializer {
}
/**
* @param {T} entity
* @param {T & IEntity} entity
* @param {Boolean} insideString
* @returns {String}
*/
@@ -4057,7 +4095,7 @@ class Serializer {
attributeKeyPrinter = this.attributeKeyPrinter
) {
let result = "";
const attributes = /** @type {EntityConstructor} */(entity.constructor).attributes ?? {};
const attributes = IEntity.getAttributes(entity);
const keys = Utility.mergeArrays(
Object.keys(attributes),
Object.keys(entity)
@@ -4073,6 +4111,7 @@ class Serializer {
result += attributeSeparator;
}
if (attributes[key]?.inlined) {
const keyValue = entity instanceof Array ? `(${key})` : key;
result += this.doWrite(
value,
insideString,
@@ -4081,8 +4120,8 @@ class Serializer {
false,
attributeValueConjunctionSign,
attributes[key].type instanceof Array
? k => attributeKeyPrinter(`${key}(${k})`)
: k => attributeKeyPrinter(`${key}.${k}`),
? k => attributeKeyPrinter(`${keyValue}${k}`)
: k => attributeKeyPrinter(`${keyValue}.${k}`)
);
continue
}

File diff suppressed because one or more lines are too long

View File

@@ -7,8 +7,9 @@ import Utility from "../Utility.js"
* @typedef {IEntity | String | Number | BigInt | Boolean} AnySimpleValue
* @typedef {AnySimpleValue | AnySimpleValue[]} AnyValue
* @typedef {(entity: IEntity) => AnyValue} ValueSupplier
* @typedef {AnyValueConstructor<AnyValue> | AnyValueConstructor<AnyValue>[] | UnionType | UnionType[] | ComputedType} AttributeType
* @typedef {{
* type?: AnyValueConstructor<AnyValue> | AnyValueConstructor<AnyValue>[] | UnionType | ComputedType,
* type?: AttributeType,
* default?: AnyValue | ValueSupplier,
* showDefault?: Boolean,
* nullable?: Boolean,
@@ -46,7 +47,16 @@ export default class IEntity {
constructor(values = {}, suppressWarns = false) {
const Self = /** @type {EntityConstructor} */(this.constructor)
const attributes = Self.attributes
let attributes = Self.attributes
if (values.attributes) {
Utility.mergeArrays(Object.keys(attributes), Object.keys(values.attributes))
.forEach(k => attributes[k] = {
...attributes[k],
...values.attributes[k]
})
IEntity.defineAttributes(this, attributes)
}
/** @type {AttributeDeclarations?} */ this.attributes
if (values.constructor !== Object && Object.keys(attributes).length === 1) {
// Where there is just one attribute, option can be the value of that attribute
values = {
@@ -205,6 +215,22 @@ export default class IEntity {
.some(/** @param {AttributeInformation} attribute */attribute => !attribute.expected)
}
static getAttribute(object, attribute) {
return this.getAttributes(object)[attribute]
}
static getAttributes(object) {
return object.attributes ?? object.constructor?.attributes ?? {}
}
static defineAttributes(object, attributes) {
Object.defineProperty(object, "attributes", {
writable: true,
configurable: false,
})
object.attributes = attributes
}
unexpectedKeys() {
return Object.keys(this).length
- Object.keys(/** @type {typeof IEntity} */(this.constructor).attributes).length

View File

@@ -36,11 +36,13 @@ import Vector2DEntity from "../entity/Vector2DEntity.js"
import VectorEntity from "../entity/VectorEntity.js"
/**
* @typedef {import ("../entity/IEntity").AnyValue} AnyValue
* @typedef {import ("../entity/IEntity").AttributeType} AttributeType
* @typedef {import ("../entity/IEntity").AttributeInformation} AttributeInformation
* @typedef {import ("../entity/IEntity").EntityConstructor} EntityConstructor
*/
/**
* @template T
* @template {AnyValue} T
* @typedef {import ("../entity/IEntity").AnyValueConstructor<T>} AnyValueConstructor
*/
@@ -133,7 +135,7 @@ export default class Grammar {
}
/**
* @param {AnyValueConstructor<any>} type
* @param {AttributeType} type
* @returns {Parsimmon.Parser<any>}
*/
static grammarFor(
@@ -597,8 +599,8 @@ export default class Grammar {
})
)
static inlinedArrayEntry = P.lazy(() => {
return P.seq(
static inlinedArrayEntry = P.lazy(() =>
P.seq(
this.symbol,
this.regexMap(
new RegExp(`\\s*\\(\\s*(\\d+)\\s*\\)\\s*\\=\\s*`),
@@ -611,8 +613,16 @@ export default class Grammar {
values => (values[symbol] ??= []).push(currentValue)
)
)
})
)
static subObjectEntity = P.lazy(() =>
this.objectEntity
.map(object =>
values => values["SubObject_" + object.Name] = object
)
)
/** @type {Parsimmon.Parser<ObjectEntity>} */
static objectEntity = P.lazy(() =>
P.seq(
P.regex(/Begin\s+Object/),
@@ -621,7 +631,9 @@ export default class Grammar {
P.alt(
this.customProperty,
this.createAttributeGrammar(ObjectEntity),
this.inlinedArrayEntry
this.inlinedArrayEntry,
// Legacy subobject
this.subObjectEntity
)
)
.map(([_0, entry]) => entry)

View File

@@ -1,4 +1,5 @@
import Grammar from "./Grammar.js"
import IEntity from "../entity/IEntity.js"
import SerializerFactory from "./SerializerFactory.js"
import Utility from "../Utility.js"
@@ -60,7 +61,7 @@ export default class Serializer {
}
/**
* @param {T} entity
* @param {T & IEntity} entity
* @param {Boolean} insideString
* @returns {String}
*/
@@ -74,7 +75,7 @@ export default class Serializer {
attributeKeyPrinter = this.attributeKeyPrinter
) {
let result = ""
const attributes = /** @type {EntityConstructor} */(entity.constructor).attributes ?? {}
const attributes = IEntity.getAttributes(entity)
const keys = Utility.mergeArrays(
Object.keys(attributes),
Object.keys(entity)
@@ -83,6 +84,7 @@ export default class Serializer {
for (const key of keys) {
const value = entity[key]
if (value !== undefined && this.showProperty(entity, key)) {
const keyValue = entity instanceof Array ? `(${key})` : key
const isSerialized = Utility.isSerialized(entity, key)
if (first) {
first = false
@@ -98,13 +100,13 @@ export default class Serializer {
false,
attributeValueConjunctionSign,
attributes[key].type instanceof Array
? k => attributeKeyPrinter(`${key}(${k})`)
: k => attributeKeyPrinter(`${key}.${k}`),
? k => attributeKeyPrinter(`${keyValue}${k}`)
: k => attributeKeyPrinter(`${keyValue}.${k}`)
)
continue
}
result +=
attributeKeyPrinter(key)
attributeKeyPrinter(keyValue)
+ this.attributeValueConjunctionSign
+ (
isSerialized