Grammar refactoring

* Grammar refactoring WIP

* ISerializer.grammar

* Fixing various bugs in the grammar

* Small touch that improoves performance

* Fix unknown values grammar

* Various fixes

* Serialization refactoring to drop suboject logic

* Details fixed

* Entity attributes initialization refactoring

* JSDoc error fixed

* Rename value key to default

* Remove useless default

* Revert string keys
This commit is contained in:
barsdeveloper
2023-04-01 19:55:16 +02:00
committed by GitHub
parent b235d63348
commit bb24486303
44 changed files with 6395 additions and 6248 deletions

View File

@@ -1,7 +1,6 @@
import GeneralSerializer from "./GeneralSerializer.js"
/**
* @typedef {import("../entity/IEntity").default} IEntity
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
* @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
*/
@@ -24,12 +23,12 @@ export default class CustomSerializer extends GeneralSerializer {
}
/**
* @param {T} object
* @param {T} entity
* @param {Boolean} insideString
* @returns {String}
*/
write(entity, object, insideString = false) {
let result = this.#objectWriter(object, insideString)
write(entity, insideString = false) {
let result = this.#objectWriter(entity, insideString)
return result
}
}

View File

@@ -28,8 +28,7 @@ export default class GeneralSerializer extends ISerializer {
* @returns {T}
*/
read(value) {
// @ts-expect-error
let grammar = Grammar.getGrammarForType(ISerializer.grammar, this.entityType)
let grammar = Grammar.grammarFor(undefined, this.entityType)
const parseResult = grammar.parse(value)
if (!parseResult.status) {
throw new Error(`Error when trying to parse the entity ${this.entityType.prototype.constructor.name}.`)
@@ -38,12 +37,12 @@ export default class GeneralSerializer extends ISerializer {
}
/**
* @param {T} object
* @param {T} entity
* @param {Boolean} insideString
* @returns {String}
*/
write(entity, object, insideString = false) {
let result = this.wrap(this.subWrite(entity, [], object, insideString), object)
write(entity, insideString = false) {
let result = this.wrap(super.write(entity, insideString), entity)
return result
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,5 @@
import Grammar from "./Grammar.js"
import Parsimmon from "parsimmon"
import SerializerFactory from "./SerializerFactory.js"
import Utility from "../Utility.js"
import IEntity from "../entity/IEntity.js"
/**
* @typedef {import("../entity/IEntity").EntityConstructor} EntityConstructor
@@ -13,8 +10,6 @@ import IEntity from "../entity/IEntity.js"
/** @template {AnyValue} T */
export default class ISerializer {
static grammar = Parsimmon.createLanguage(new Grammar())
/** @param {AnyValueConstructor} entityType */
constructor(
entityType,
@@ -22,7 +17,7 @@ export default class ISerializer {
attributeSeparator = ",",
trailingSeparator = false,
attributeValueConjunctionSign = "=",
attributeKeyPrinter = k => k.join(".")
attributeKeyPrinter = k => k
) {
this.entityType = entityType
this.attributePrefix = attributePrefix
@@ -40,9 +35,9 @@ export default class ISerializer {
return this.read(value)
}
/** @param {T} object */
serialize(object, insideString = false, entity = object) {
return this.write(entity, object, insideString)
/** @param {T} value */
serialize(value, insideString = false) {
return this.write(value, insideString)
}
/**
@@ -56,21 +51,46 @@ export default class ISerializer {
/**
* @protected
* @param {T} object
* @param {T} entity
* @param {Boolean} insideString
* @returns {String}
*/
write(entity, object, insideString) {
throw new Error("Not implemented")
write(entity, insideString) {
let result = ""
const attributes = /** @type {EntityConstructor} */(entity.constructor).attributes ?? {}
const keys = Utility.mergeArrays(
Object.keys(attributes),
Object.keys(entity)
)
for (const key of keys) {
const value = entity[key]
if (value !== undefined && this.showProperty(entity, key)) {
const isSerialized = Utility.isSerialized(entity, key)
result += (result.length ? this.attributeSeparator : "")
+ this.attributePrefix
+ Utility.decodeKeyName(this.attributeKeyPrinter(key))
+ this.attributeValueConjunctionSign
+ (
isSerialized
? `"${this.writeValue(entity, key, true)}"`
: this.writeValue(entity, key, insideString)
)
}
}
if (this.trailingSeparator && result.length) {
// append separator at the end if asked and there was printed content
result += this.attributeSeparator
}
return result
}
/**
* @protected
* @param {AnyValue} value
* @param {String[]} fullKey
* @param {String} key
* @param {Boolean} insideString
*/
writeValue(entity, value, fullKey, insideString) {
writeValue(entity, key, insideString) {
const value = entity[key]
const type = Utility.getType(value)
// @ts-expect-error
const serializer = SerializerFactory.getSerializer(type)
@@ -78,65 +98,20 @@ export default class ISerializer {
throw new Error(`Unknown value type "${type.name}", a serializer must be registered in the SerializerFactory class, check initializeSerializerFactory.js`)
}
return serializer.write(
value instanceof IEntity ? value : entity,
value,
entity[key],
insideString
)
}
/**
* @protected
* @param {String[]} key
* @param {Object} object
* @param {Boolean} insideString
* @returns {String}
*/
subWrite(entity, key, object, insideString) {
let result = ""
let fullKey = key.concat("")
const last = fullKey.length - 1
const attributes = /** @type {EntityConstructor} */(object.constructor).attributes
const keys = attributes
? Utility.mergeArrays(
Object.keys(attributes),
Object.keys(object)
)
: Object.keys(object)
for (const property of keys) {
fullKey[last] = property
const value = object[property]
if (value?.constructor === Object) {
// Recursive call when finding an object
result += (result.length ? this.attributeSeparator : "")
+ this.subWrite(entity, fullKey, value, insideString)
} else if (value !== undefined && this.showProperty(entity, object, fullKey, value)) {
const isSerialized = Utility.isSerialized(entity, fullKey)
result += (result.length ? this.attributeSeparator : "")
+ this.attributePrefix
+ this.attributeKeyPrinter(fullKey)
+ this.attributeValueConjunctionSign
+ (
isSerialized
? `"${this.writeValue(entity, value, fullKey, true)}"`
: this.writeValue(entity, value, fullKey, insideString)
)
}
}
if (this.trailingSeparator && result.length && fullKey.length === 1) {
// append separator at the end if asked and there was printed content
result += this.attributeSeparator
}
return result
}
showProperty(entity, object, attributeKey, attributeValue) {
showProperty(entity, key) {
const attributes = /** @type {EntityConstructor} */(this.entityType).attributes
const attribute = Utility.objectGet(attributes, attributeKey)
const attribute = attributes[key]
const value = entity[key]
if (attribute?.constructor === Object) {
if (attribute.ignored) {
return false
}
return !Utility.equals(attribute.value, attributeValue) || attribute.showDefault
return !Utility.equals(attribute.value, value) || attribute.showDefault
}
return true
}

View File

@@ -1,3 +1,4 @@
import Grammar from "./Grammar.js"
import ISerializer from "./ISerializer.js"
import ObjectEntity from "../entity/ObjectEntity.js"
import PinEntity from "../entity/PinEntity.js"
@@ -9,20 +10,20 @@ export default class ObjectSerializer extends ISerializer {
super(ObjectEntity, " ", "\n", false)
}
showProperty(entity, object, attributeKey, attributeValue) {
switch (attributeKey.toString()) {
showProperty(entity, key) {
switch (key) {
case "Class":
case "Name":
case "CustomProperties":
// Serielized separately
// Serielized separately, check write()
return false
}
return super.showProperty(entity, object, attributeKey, attributeValue)
return super.showProperty(entity, key)
}
/** @param {String} value */
read(value) {
const parseResult = ISerializer.grammar.Object.parse(value)
const parseResult = Grammar.objectEntity.parse(value)
if (!parseResult.status) {
throw new Error("Error when trying to parse the object.")
}
@@ -34,7 +35,7 @@ export default class ObjectSerializer extends ISerializer {
* @returns {ObjectEntity[]}
*/
readMultiple(value) {
const parseResult = ISerializer.grammar.MultipleObject.parse(value)
const parseResult = Grammar.multipleObject.parse(value)
if (!parseResult.status) {
throw new Error("Error when trying to parse the object.")
}
@@ -42,21 +43,20 @@ export default class ObjectSerializer extends ISerializer {
}
/**
* @param {ObjectEntity} object
* @param {ObjectEntity} entity
* @param {Boolean} insideString
*/
write(entity, object, insideString) {
let result = `Begin Object Class=${object.Class.path} Name=${this.writeValue(entity, object.Name, ["Name"], insideString)}
${this.subWrite(entity, [], object, insideString)
+ object
.CustomProperties.map(pin =>
this.attributeSeparator
+ this.attributePrefix
+ "CustomProperties "
+ SerializerFactory.getSerializer(PinEntity).serialize(pin)
)
.join("")}
End Object\n`
write(entity, insideString) {
let result = `Begin Object Class=${entity.Class.path} Name=${this.writeValue(entity, "Name", insideString)}\n`
+ super.write(entity, insideString)
+ entity.CustomProperties.map(pin =>
this.attributeSeparator
+ this.attributePrefix
+ "CustomProperties "
+ SerializerFactory.getSerializer(PinEntity).serialize(pin)
)
.join("")
+ "\nEnd Object\n"
return result
}
}

View File

@@ -1,8 +1,8 @@
import Utility from "../Utility.js"
import GeneralSerializer from "./GeneralSerializer.js"
/**
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
/**
* @typedef {import("../entity/IEntity").AnyValue} AnyValue
* @typedef {import("../entity/IEntity").AnyValueConstructor<*>} AnyValueConstructor
*/
@@ -18,12 +18,12 @@ export default class ToStringSerializer extends GeneralSerializer {
}
/**
* @param {T} object
* @param {T} entity
* @param {Boolean} insideString
*/
write(entity, object, insideString) {
return !insideString && object.constructor === String
? `"${Utility.escapeString(object.toString())}"` // String will have quotes if not inside a string already
: Utility.escapeString(object.toString())
write(entity, insideString) {
return !insideString && entity.constructor === String
? `"${Utility.escapeString(entity.toString())}"` // String will have quotes if not inside a string already
: Utility.escapeString(entity.toString())
}
}