Skip to content

Commit

Permalink
feat: fromCell and fromSlice parsing methods for structs (#418)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gusarich authored Jun 19, 2024
1 parent dcf6c5e commit 880d82b
Show file tree
Hide file tree
Showing 9 changed files with 499 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The `loadBool` method for `Slice` type: PR [#412](https://github.com/tact-lang/tact/pull/412)
- CLI flag `--with-decompilation` to turn on decompilation of BoC files at the end of the compilation pipeline: PR [#417](https://github.com/tact-lang/tact/pull/417)
- Support more Tact expressions in the constant evaluator: condition expressions, struct instances, struct field access, `emptyMap()`: PR [#432](https://github.com/tact-lang/tact/pull/432)
- The `fromCell` and `fromSlice` methods for struct parsing: PR [#418](https://github.com/tact-lang/tact/pull/418)

### Changed

Expand Down
98 changes: 98 additions & 0 deletions src/abi/struct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,102 @@ export const StructFunctions: Map<string, AbiFunction> = new Map([
},
},
],
[
"fromCell",
{
name: "fromCell",
resolve: (ctx, args, ref) => {
if (args.length !== 2) {
throwSyntaxError("fromCell() expects one argument", ref);
}
if (args[0].kind !== "ref") {
throwSyntaxError(
"fromCell() is implemented only for struct types",
ref,
);
}
const tp = getType(ctx, args[0].name);
if (tp.kind !== "struct") {
throwSyntaxError(
"fromCell() is implemented only for struct types",
ref,
);
}
if (args[1].kind !== "ref" || args[1].name !== "Cell") {
throwSyntaxError(
"fromCell() expects a Cell as an argument",
ref,
);
}
return { kind: "ref", name: args[0].name, optional: false };
},
generate: (ctx, args, resolved, ref) => {
if (resolved.length !== 2) {
throwSyntaxError("fromCell() expects one argument", ref);
}
if (args[0].kind !== "ref") {
throwSyntaxError(
"fromCell() is implemented only for struct types",
ref,
);
}
if (args[1].kind !== "ref" || args[1].name !== "Cell") {
throwSyntaxError(
"fromCell() expects a Cell as an argument",
ref,
);
}
return `${ops.readerNonModifying(args[0].name, ctx)}(${writeExpression(resolved[1], ctx)}.begin_parse())`;
},
},
],
[
"fromSlice",
{
name: "fromSlice",
resolve: (ctx, args, ref) => {
if (args.length !== 2) {
throwSyntaxError("fromSlice() expects one argument", ref);
}
if (args[0].kind !== "ref") {
throwSyntaxError(
"fromSlice() is implemented only for struct types",
ref,
);
}
const tp = getType(ctx, args[0].name);
if (tp.kind !== "struct") {
throwSyntaxError(
"fromSlice() is implemented only for struct types",
ref,
);
}
if (args[1].kind !== "ref" || args[1].name !== "Slice") {
throwSyntaxError(
"fromSlice() expects a Slice as an argument",
ref,
);
}
return { kind: "ref", name: args[0].name, optional: false };
},
generate: (ctx, args, resolved, ref) => {
if (resolved.length !== 2) {
throwSyntaxError("fromSlice() expects one argument", ref);
}
if (args[0].kind !== "ref") {
throwSyntaxError(
"fromSlice() is implemented only for struct types",
ref,
);
}
if (args[1].kind !== "ref" || args[1].name !== "Slice") {
throwSyntaxError(
"fromSlice() expects a Slice as an argument",
ref,
);
}
return `${ops.readerNonModifying(args[0].name, ctx)}(${writeExpression(resolved[1], ctx)})`;
},
},
],
]);
Original file line number Diff line number Diff line change
Expand Up @@ -4598,6 +4598,22 @@ return (sc_0, (v'a, v'b, v'c, v'd, v'e, v'f, v'g));",
"name": "$A$_load",
"signature": "(slice, ((int, int, int, int, int, int, int))) $A$_load(slice sc_0)",
},
{
"code": {
"code": "var r = sc_0~$A$_load();
sc_0.end_parse();
return r;",
"kind": "generic",
},
"comment": null,
"context": "type:A",
"depends": Set {
"$A$_load",
},
"flags": Set {},
"name": "$A$_load_not_mut",
"signature": "((int, int, int, int, int, int, int)) $A$_load_not_mut(slice sc_0)",
},
]
`;

Expand Down Expand Up @@ -9199,6 +9215,22 @@ return (sc_0, (v'a, v'b, v'c, v'd, v'e, v'f, v'g));",
"name": "$B$_load",
"signature": "(slice, ((int, int, int, int, int, int, int))) $B$_load(slice sc_0)",
},
{
"code": {
"code": "var r = sc_0~$B$_load();
sc_0.end_parse();
return r;",
"kind": "generic",
},
"comment": null,
"context": "type:B",
"depends": Set {
"$B$_load",
},
"flags": Set {},
"name": "$B$_load_not_mut",
"signature": "((int, int, int, int, int, int, int)) $B$_load_not_mut(slice sc_0)",
},
]
`;

Expand Down Expand Up @@ -13806,5 +13838,21 @@ return (sc_0, (v'a, v'b, v'c, v'd, v'e, v'f, v'g, v'h));",
"name": "$C$_load",
"signature": "(slice, ((cell, cell, slice, slice, int, int, int, slice))) $C$_load(slice sc_0)",
},
{
"code": {
"code": "var r = sc_0~$C$_load();
sc_0.end_parse();
return r;",
"kind": "generic",
},
"comment": null,
"context": "type:C",
"depends": Set {
"$C$_load",
},
"flags": Set {},
"name": "$C$_load_not_mut",
"signature": "((cell, cell, slice, slice, int, int, int, slice)) $C$_load_not_mut(slice sc_0)",
},
]
`;
2 changes: 2 additions & 0 deletions src/generator/writers/ops.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export const ops = {
writerCellOpt: (type: string, ctx: WriterContext) =>
used(`$${type}$_store_opt`, ctx),
reader: (type: string, ctx: WriterContext) => used(`$${type}$_load`, ctx),
readerNonModifying: (type: string, ctx: WriterContext) =>
used(`$${type}$_load_not_mut`, ctx),
readerBounced: (type: string, ctx: WriterContext) =>
used(`$${type}$_load_bounced`, ctx),
readerOpt: (type: string, ctx: WriterContext) =>
Expand Down
20 changes: 20 additions & 0 deletions src/generator/writers/writeSerialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,26 @@ export function writeParser(
}
});
});

// Write non-modifying variant

ctx.fun(ops.readerNonModifying(name, ctx), () => {
ctx.signature(
`(${resolveFuncTypeFromAbi(
allocation.ops.map((v) => v.type),
ctx,
)}) ${ops.readerNonModifying(name, ctx)}(slice sc_0)`,
);
if (forceInline || isSmall) {
ctx.flag("inline");
}
ctx.context("type:" + name);
ctx.body(() => {
ctx.append(`var r = sc_0~${ops.reader(name, ctx)}();`);
ctx.append(`sc_0.end_parse();`);
ctx.append(`return r;`);
});
});
}

export function writeBouncedParser(
Expand Down
130 changes: 130 additions & 0 deletions src/test/e2e-emulated/__snapshots__/structs.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`structs should implement structs correctly 1`] = `
{
"$$type": "MyStruct2",
"m": Dictionary {
"_key": {
"bits": 257,
"parse": [Function],
"serialize": [Function],
},
"_map": Map {},
"_value": {
"parse": [Function],
"serialize": [Function],
},
},
"s": {
"$$type": "MyStruct1",
"a": 1n,
"b": 2n,
"c": 3n,
},
}
`;

exports[`structs should implement structs correctly 2`] = `
{
"$$type": "MyStruct2",
"m": Dictionary {
"_key": {
"bits": 257,
"parse": [Function],
"serialize": [Function],
},
"_map": Map {},
"_value": {
"parse": [Function],
"serialize": [Function],
},
},
"s": null,
}
`;

exports[`structs should implement structs correctly 3`] = `
{
"$$type": "MyStruct2",
"m": Dictionary {
"_key": {
"bits": 257,
"parse": [Function],
"serialize": [Function],
},
"_map": Map {},
"_value": {
"parse": [Function],
"serialize": [Function],
},
},
"s": {
"$$type": "MyStruct1",
"a": 1n,
"b": 2n,
"c": 3n,
},
}
`;

exports[`structs should implement structs correctly 4`] = `
{
"$$type": "MyStruct2",
"m": Dictionary {
"_key": {
"bits": 257,
"parse": [Function],
"serialize": [Function],
},
"_map": Map {},
"_value": {
"parse": [Function],
"serialize": [Function],
},
},
"s": null,
}
`;

exports[`structs should implement structs correctly 5`] = `
{
"$$type": "MyStruct2",
"m": Dictionary {
"_key": {
"bits": 257,
"parse": [Function],
"serialize": [Function],
},
"_map": Map {},
"_value": {
"parse": [Function],
"serialize": [Function],
},
},
"s": {
"$$type": "MyStruct1",
"a": 1n,
"b": 2n,
"c": 3n,
},
}
`;

exports[`structs should implement structs correctly 6`] = `
{
"$$type": "MyStruct2",
"m": Dictionary {
"_key": {
"bits": 257,
"parse": [Function],
"serialize": [Function],
},
"_map": Map {},
"_value": {
"parse": [Function],
"serialize": [Function],
},
},
"s": null,
}
`;
Loading

0 comments on commit 880d82b

Please sign in to comment.