Skip to content

Commit

Permalink
JSON.Raw
Browse files Browse the repository at this point in the history
  • Loading branch information
JairusSW committed Jul 20, 2024
1 parent 2f71d6f commit c4dd3da
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 594 deletions.
2 changes: 1 addition & 1 deletion assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ declare function omitnull(): Function;
* Property decorator that allows a field to be flattened.
* @param fieldName - Points to the field to flatten. Can use dot-notation here like @omit("foo.identifier.text")
*/
declare function flatten(fieldName: string = "value"): Function;
declare function flatten(fieldName: string = "value"): Function;
138 changes: 134 additions & 4 deletions assembly/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,151 @@ import { deserializeFloat } from "./deserialize/float";
import { deserializeObject } from "./deserialize/object";
import { deserializeMap } from "./deserialize/map";
import { deserializeDate } from "./deserialize/date";
import { NULL_WORD } from "./custom/chars";
import { BRACKET_LEFT, NULL_WORD } from "./custom/chars";
import { deserializeInteger } from "./deserialize/integer";
import { deserializeString } from "./deserialize/string";
import { Sink } from "./custom/sink";
import { bs } from "./custom/bs";

/**
* Offset of the 'storage' property in the JSON.Value class.
*/
// @ts-ignore: Decorator valid here
@inline const STORAGE = offsetof<JSON.Value>("storage");

/**
* JSON Encoder/Decoder for AssemblyScript
*/
export namespace JSON {
/**
* Enum representing the different types supported by JSON.
*/
export enum Types {
Raw = 0,
U8 = 1,
U16 = 2,
U32 = 3,
U64 = 4,
F32 = 5,
F64 = 6,
Bool = 7,
String = 8,
Obj = 8,
Array = 9
}
export type Raw = string;
export class Value {
public type: i32;

// @ts-ignore
private storage: u64;

private constructor() { unreachable(); }

/**
* Creates an JSON.Value instance from a given value.
* @param value - The value to be encapsulated.
* @returns An instance of JSON.Value.
*/
@inline static from<T>(value: T): JSON.Value {
if (value instanceof JSON.Value) {
return value;
}
const out = changetype<JSON.Value>(__new(offsetof<JSON.Value>(), idof<JSON.Value>()));
out.set<T>(value);
return out;
}

/**
* Sets the value of the JSON.Value instance.
* @param value - The value to be set.
*/
@inline set<T>(value: T): void {
if (isBoolean<T>()) {
this.type = JSON.Types.Boolean;
store<T>(changetype<usize>(this), value, STORAGE);
} else if (value instanceof u8 || value instanceof i8) {
this.type = JSON.Types.U8;
store<T>(changetype<usize>(this), value, STORAGE);
} else if (value instanceof u16 || value instanceof i16) {
this.type = JSON.Types.U16;
store<T>(changetype<usize>(this), value, STORAGE);
} else if (value instanceof u32 || value instanceof i32) {
this.type = JSON.Types.U32;
store<T>(changetype<usize>(this), value, STORAGE);
} else if (value instanceof u64 || value instanceof i64) {
this.type = JSON.Types.U64;
store<T>(changetype<usize>(this), value, STORAGE);
} else if (value instanceof f32) {
this.type = JSON.Types.F64;
store<T>(changetype<usize>(this), value, STORAGE);
} else if (value instanceof f64) {
this.type = JSON.Types.F64;
store<T>(changetype<usize>(this), value, STORAGE);
} else if (isString<T>()) {
this.type = JSON.Types.String;
store<T>(changetype<usize>(this), value, STORAGE);
} else if (value instanceof Map) {
if (idof<T>() !== idof<Map<string, JSON.Value>>()) {
abort("Maps must be of type Map<string, JSON.Value>!");
}
this.type = JSON.Types.Obj;
store<T>(changetype<usize>(this), value, STORAGE);
} else if (isArray<T>()) {
// @ts-ignore: T satisfies constraints of any[]
this.type = JSON.Types.Array + getArrayDepth<T>(0);
store<T>(changetype<usize>(this), value, STORAGE);
}
}

/**
* Gets the value of the JSON.Value instance.
* @returns The encapsulated value.
*/
@inline get<T>(): T {
return load<T>(changetype<usize>(this), STORAGE);
}

/**
* Converts the JSON.Value to a string representation.
* @param useString - If true, treats Buffer as a string.
* @returns The string representation of the JSON.Value.
*/
toString(): string {
switch (this.type) {
case JSON.Types.U8: return this.get<u8>().toString();
case JSON.Types.U16: return this.get<u16>().toString();
case JSON.Types.U32: return this.get<u32>().toString();
case JSON.Types.U64: return this.get<u64>().toString();
case JSON.Types.String: return "\"" + this.get<string>() + "\"";
case JSON.Types.Bool: return this.get<boolean>() ? "true" : "false";
default: {
const arr = this.get<JSON.Value[]>();
if (!arr.length) return "[]";
const out = Sink.fromStringLiteral("[");
const end = arr.length - 1;
for (let i = 0; i < end; i++) {
const element = unchecked(arr[i]);
out.write(element.toString());
out.write(",");
}

const element = unchecked(arr[end]);
out.write(element.toString());

out.write("]");
return out.toString();
}
}
}
}
export class Box<T> {
constructor(public value: T) {}
constructor(public value: T) { }
@inline static from<T>(value: T): Box<T> {
return new Box(value);
}
}

/**
* Stringifies valid JSON data.
* ```js
Expand All @@ -52,7 +182,7 @@ export namespace JSON {
// @ts-ignore
} else if (isString<nonnull<T>>()) {
return serializeString(changetype<string>(data));
// @ts-ignore: Supplied by trasnform
// @ts-ignore: Supplied by transform
} else if (isDefined(data.__SERIALIZE)) {
// @ts-ignore
return serializeObject(changetype<nonnull<T>>(data));
Expand Down
70 changes: 32 additions & 38 deletions assembly/test.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,36 @@
// import { JSON } from ".";
import { bs } from "./custom/bs";
// @json
// class Vec3 {
// x: f32 = 0.0;
// y: f32 = 0.0;
// z: f32 = 0.0;
// }
import { JSON } from ".";
@json
class Vec3 {
x: f32 = 0.0;
y: f32 = 0.0;
z: f32 = 0.0;
}

// @json
// class Player {
// @alias("first name")
// firstName!: string;
// lastName!: string;
// lastActive!: i32[];
// // Drop in a code block, function, or expression that evaluates to a boolean
// @omitif("this.age < 18")
// age!: i32;
// @omitnull()
// pos!: Vec3 | null;
// isVerified!: boolean;
// }
@json
class Player {
@alias("first name")
firstName!: string;
lastName!: string;
lastActive!: i32[];
// Drop in a code block, function, or expression that evaluates to a boolean
@omitif("this.age < 18")
age!: i32;
pos!: JSON.Raw;
isVerified!: boolean;
}

// const player: Player = {
// firstName: "Emmet",
// lastName: "West",
// lastActive: [8, 27, 2022],
// age: 23,
// pos: {
// x: 3.4,
// y: 1.2,
// z: 8.3
// },
// isVerified: true
// };
const player: Player = {
firstName: "Emmet",
lastName: "West",
lastActive: [8, 27, 2022],
age: 23,
pos: "{\"x\":3.4,\"y\":1.2,\"z\":8.3}",
isVerified: true
};

// const stringified = JSON.stringify<Player>(player);

// const parsed = JSON.parse<Player>(stringified);

bs.write_32(6422620);
console.log(bs.out<string>())
const stringified = JSON.stringify<Player>(player);
console.log(stringified);
console.log(idof<JSON.Raw>().toString());
console.log(idof<string>().toString())
// const parsed = JSON.parse<Player>(stringified);
18 changes: 14 additions & 4 deletions transform/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,13 @@ class JSONTransform extends BaseVisitor {
}
if (!mem.flags.length) {
mem.flags = [PropertyFlags.None];
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${__SERIALIZE<" + type + ">(this." + name.text + ")}";
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));";
if (type == "JSON.Raw") {
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":" + "\"${this." + name.text + "}\"";
}
else {
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${__SERIALIZE<" + type + ">(this." + name.text + ")}";
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));";
}
}
if (mem.flags.includes(PropertyFlags.OmitNull)) {
mem.serialize = "${changetype<usize>(this." + mem.name + ") == <usize>0" + " ? \"\" : '" + escapeString(JSON.stringify(mem.alias || mem.name)) + ":' + __SERIALIZE<" + type + ">(this." + name.text + ") + \",\"}";
Expand All @@ -136,11 +141,11 @@ class JSONTransform extends BaseVisitor {
else if (mem.flags.includes(PropertyFlags.Flatten)) {
const nullable = mem.node.type.isNullable;
if (nullable) {
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(changetype<nonnull<" + type + ">>(this." + name.text + ")" + (mem.args?.length ? '.' + mem.args[0] : '') + ") : \"null\"}";
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(changetype<nonnull<" + type + ">>(this." + name.text + ")" + (mem.args?.length ? '.' + mem.args.join(".") : '') + ") : \"null\"}";
mem.deserialize = "if (value_end - value_start == 4 && load<u64>(changetype<usize>(data) + <usize>(value_start << 1)) == " + charCodeAt64("null", 0) + ") {\n this." + name.text + " = null;\n } else {\n this." + name.text + " = " + "__DESERIALIZE<" + type + ">('{\"" + mem.args[0] + "\":' + data.substring(value_start, value_end) + \"}\");\n }";
}
else {
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(this." + name.text + (mem.args?.length ? '.' + mem.args[0] : '') + ") : \"null\"}";
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(this." + name.text + (mem.args?.length ? '.' + mem.args.join(".") : '') + ") : \"null\"}";
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">('{\"" + mem.args[0] + "\":' + data.substring(value_start, value_end) + \"}\");";
}
mem.name = name.text;
Expand All @@ -164,6 +169,9 @@ class JSONTransform extends BaseVisitor {
else if (t === "bool" || t === "boolean") {
mem.initialize = "this." + name.text + " = false";
}
else if (t === "JSON.Raw") {
mem.initialize = "this." + name.text + " = \"\"";
}
else if (t === "u8" ||
t === "u16" ||
t === "u32" ||
Expand Down Expand Up @@ -284,6 +292,8 @@ class JSONTransform extends BaseVisitor {
let f = true;
for (let i = 0; i < memberSet.length; i++) {
const member = memberSet[i];
if (!member.deserialize)
continue;
const name = encodeKey(member.alias || member.name);
if (name.length === 1) {
DESERIALIZE += ` case ${name.charCodeAt(0)}: {\n ${member.deserialize}\n return true;\n }\n`;
Expand Down
15 changes: 11 additions & 4 deletions transform/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,12 @@ class JSONTransform extends BaseVisitor {

if (!mem.flags.length) {
mem.flags = [PropertyFlags.None];
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${__SERIALIZE<" + type + ">(this." + name.text + ")}";
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));"
if (type == "JSON.Raw") {
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":" + "\"${this." + name.text + "}\"";
} else {
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${__SERIALIZE<" + type + ">(this." + name.text + ")}";
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">(data.substring(value_start, value_end));"
}
}

if (mem.flags.includes(PropertyFlags.OmitNull)) {
Expand All @@ -152,10 +156,10 @@ class JSONTransform extends BaseVisitor {
} else if (mem.flags.includes(PropertyFlags.Flatten)) {
const nullable = (mem.node.type as NamedTypeNode).isNullable;
if (nullable) {
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(changetype<nonnull<" + type + ">>(this." + name.text + ")" + (mem.args?.length ? '.' + mem.args[0]! : '') + ") : \"null\"}";
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(changetype<nonnull<" + type + ">>(this." + name.text + ")" + (mem.args?.length ? '.' + mem.args.join(".") : '') + ") : \"null\"}";
mem.deserialize = "if (value_end - value_start == 4 && load<u64>(changetype<usize>(data) + <usize>(value_start << 1)) == " + charCodeAt64("null", 0) + ") {\n this." + name.text + " = null;\n } else {\n this." + name.text + " = " + "__DESERIALIZE<" + type + ">('{\"" + mem.args![0]! + "\":' + data.substring(value_start, value_end) + \"}\");\n }";
} else {
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(this." + name.text + (mem.args?.length ? '.' + mem.args[0]! : '') + ") : \"null\"}";
mem.serialize = escapeString(JSON.stringify(mem.alias || mem.name)) + ":${this." + name.text + " ? __SERIALIZE(this." + name.text + (mem.args?.length ? '.' + mem.args.join(".") : '') + ") : \"null\"}";
mem.deserialize = "this." + name.text + " = " + "__DESERIALIZE<" + type + ">('{\"" + mem.args![0]! + "\":' + data.substring(value_start, value_end) + \"}\");";
}
mem.name = name.text;
Expand All @@ -174,6 +178,8 @@ class JSONTransform extends BaseVisitor {
mem.initialize = "this." + name.text + " = instantiate<" + mem.type + ">()";
} else if (t === "bool" || t === "boolean") {
mem.initialize = "this." + name.text + " = false";
} else if (t === "JSON.Raw") {
mem.initialize = "this." + name.text + " = \"\"";
} else if (
t === "u8" ||
t === "u16" ||
Expand Down Expand Up @@ -300,6 +306,7 @@ class JSONTransform extends BaseVisitor {
let f = true;
for (let i = 0; i < memberSet.length; i++) {
const member = memberSet[i]!;
if (!member.deserialize) continue;
const name = encodeKey(member.alias || member.name);
if (name.length === 1) {
DESERIALIZE += ` case ${name.charCodeAt(0)}: {\n ${member.deserialize}\n return true;\n }\n`;
Expand Down
Loading

0 comments on commit c4dd3da

Please sign in to comment.