Skip to content

Commit

Permalink
feat: Slices and Strings equality comparisons (#105)
Browse files Browse the repository at this point in the history
We actually compare the hashes for both slices and strings,
essentially using the `HASHSU` TVM instruction under-the-hood.

---------
Co-authored-by: Anton Trunov <[email protected]>
  • Loading branch information
Gusarich authored Feb 16, 2024
1 parent 116a135 commit ef7165e
Show file tree
Hide file tree
Showing 7 changed files with 542 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed
- Decimal and hexadecimal literals now allow underscores as numerical separators: PR [#99](https://github.com/tact-lang/tact/pull/99)
- The equality and non-equality operators (`==` and `!=`) now support slices and strings by comparing the hashes of the left-hand and right-hand sides : PR [#105](https://github.com/tact-lang/tact/pull/105)

### Fixed
- Relative imports from parent directories: PR [#125](https://github.com/tact-lang/tact/pull/125)
Expand Down
264 changes: 264 additions & 0 deletions src/generator/writers/__snapshots__/writeSerialization.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,94 @@ return ( a_is_null & b_is_null ) ? ( false ) : ( ( ( ~ a_is_null ) & ( ~ b_is_nu
"name": "__tact_cell_neq_nullable",
"signature": "int __tact_cell_neq_nullable(cell a, cell b)",
},
{
"code": {
"code": "return (a.slice_hash() == b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_eq",
"signature": "int __tact_slice_eq(slice a, slice b)",
},
{
"code": {
"code": "return (a.slice_hash() != b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_neq",
"signature": "int __tact_slice_neq(slice a, slice b)",
},
{
"code": {
"code": "return (null?(a)) ? (false) : (a.slice_hash() == b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_eq_nullable_one",
"signature": "int __tact_slice_eq_nullable_one(slice a, slice b)",
},
{
"code": {
"code": "return (null?(a)) ? (true) : (a.slice_hash() != b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_neq_nullable_one",
"signature": "int __tact_slice_neq_nullable_one(slice a, slice b)",
},
{
"code": {
"code": "var a_is_null = null?(a);
var b_is_null = null?(b);
return ( a_is_null & b_is_null ) ? ( true ) : ( ( ( ~ a_is_null ) & ( ~ b_is_null ) ) ? ( a.slice_hash() == b.slice_hash() ) : ( false ) );",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_eq_nullable",
"signature": "int __tact_slice_eq_nullable(slice a, slice b)",
},
{
"code": {
"code": "var a_is_null = null?(a);
var b_is_null = null?(b);
return ( a_is_null & b_is_null ) ? ( false ) : ( ( ( ~ a_is_null ) & ( ~ b_is_null ) ) ? ( a.slice_hash() != b.slice_hash() ) : ( true ) );",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_neq_nullable",
"signature": "int __tact_slice_neq_nullable(slice a, slice b)",
},
{
"code": {
"code": "return udict_set_ref(dict, 16, id, code);",
Expand Down Expand Up @@ -4803,6 +4891,94 @@ return ( a_is_null & b_is_null ) ? ( false ) : ( ( ( ~ a_is_null ) & ( ~ b_is_nu
"name": "__tact_cell_neq_nullable",
"signature": "int __tact_cell_neq_nullable(cell a, cell b)",
},
{
"code": {
"code": "return (a.slice_hash() == b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_eq",
"signature": "int __tact_slice_eq(slice a, slice b)",
},
{
"code": {
"code": "return (a.slice_hash() != b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_neq",
"signature": "int __tact_slice_neq(slice a, slice b)",
},
{
"code": {
"code": "return (null?(a)) ? (false) : (a.slice_hash() == b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_eq_nullable_one",
"signature": "int __tact_slice_eq_nullable_one(slice a, slice b)",
},
{
"code": {
"code": "return (null?(a)) ? (true) : (a.slice_hash() != b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_neq_nullable_one",
"signature": "int __tact_slice_neq_nullable_one(slice a, slice b)",
},
{
"code": {
"code": "var a_is_null = null?(a);
var b_is_null = null?(b);
return ( a_is_null & b_is_null ) ? ( true ) : ( ( ( ~ a_is_null ) & ( ~ b_is_null ) ) ? ( a.slice_hash() == b.slice_hash() ) : ( false ) );",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_eq_nullable",
"signature": "int __tact_slice_eq_nullable(slice a, slice b)",
},
{
"code": {
"code": "var a_is_null = null?(a);
var b_is_null = null?(b);
return ( a_is_null & b_is_null ) ? ( false ) : ( ( ( ~ a_is_null ) & ( ~ b_is_null ) ) ? ( a.slice_hash() != b.slice_hash() ) : ( true ) );",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_neq_nullable",
"signature": "int __tact_slice_neq_nullable(slice a, slice b)",
},
{
"code": {
"code": "return udict_set_ref(dict, 16, id, code);",
Expand Down Expand Up @@ -8538,6 +8714,94 @@ return ( a_is_null & b_is_null ) ? ( false ) : ( ( ( ~ a_is_null ) & ( ~ b_is_nu
"name": "__tact_cell_neq_nullable",
"signature": "int __tact_cell_neq_nullable(cell a, cell b)",
},
{
"code": {
"code": "return (a.slice_hash() == b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_eq",
"signature": "int __tact_slice_eq(slice a, slice b)",
},
{
"code": {
"code": "return (a.slice_hash() != b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_neq",
"signature": "int __tact_slice_neq(slice a, slice b)",
},
{
"code": {
"code": "return (null?(a)) ? (false) : (a.slice_hash() == b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_eq_nullable_one",
"signature": "int __tact_slice_eq_nullable_one(slice a, slice b)",
},
{
"code": {
"code": "return (null?(a)) ? (true) : (a.slice_hash() != b.slice_hash());",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_neq_nullable_one",
"signature": "int __tact_slice_neq_nullable_one(slice a, slice b)",
},
{
"code": {
"code": "var a_is_null = null?(a);
var b_is_null = null?(b);
return ( a_is_null & b_is_null ) ? ( true ) : ( ( ( ~ a_is_null ) & ( ~ b_is_null ) ) ? ( a.slice_hash() == b.slice_hash() ) : ( false ) );",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_eq_nullable",
"signature": "int __tact_slice_eq_nullable(slice a, slice b)",
},
{
"code": {
"code": "var a_is_null = null?(a);
var b_is_null = null?(b);
return ( a_is_null & b_is_null ) ? ( false ) : ( ( ( ~ a_is_null ) & ( ~ b_is_null ) ) ? ( a.slice_hash() != b.slice_hash() ) : ( true ) );",
"kind": "generic",
},
"comment": null,
"context": "stdlib",
"depends": Set {},
"flags": Set {
"inline",
},
"name": "__tact_slice_neq_nullable",
"signature": "int __tact_slice_neq_nullable(slice a, slice b)",
},
{
"code": {
"code": "return udict_set_ref(dict, 16, id, code);",
Expand Down
24 changes: 24 additions & 0 deletions src/generator/writers/writeExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,30 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string {
return `__tact_cell_${op}(${writeExpression(f.right, ctx)}, ${writeExpression(f.left, ctx)})`;
}

// Case for slices and strings equality
if (
lt.kind === 'ref' &&
rt.kind === 'ref' &&
lt.name === rt.name &&
(lt.name === 'Slice' || lt.name === 'String')
) {
let op = f.op === '==' ? 'eq' : 'neq';
if (lt.optional && rt.optional) {
ctx.used(`__tact_slice_${op}_nullable`);
return `__tact_slice_${op}_nullable(${writeExpression(f.left, ctx)}, ${writeExpression(f.right, ctx)})`;
}
if (lt.optional && !rt.optional) {
ctx.used(`__tact_slice_${op}_nullable_one`);
return `__tact_slice_${op}_nullable_one(${writeExpression(f.left, ctx)}, ${writeExpression(f.right, ctx)})`;
}
if (!lt.optional && rt.optional) {
ctx.used(`__tact_slice_${op}_nullable_one`);
return `__tact_slice_${op}_nullable_one(${writeExpression(f.right, ctx)}, ${writeExpression(f.left, ctx)})`;
}
ctx.used(`__tact_slice_${op}`);
return `__tact_slice_${op}(${writeExpression(f.right, ctx)}, ${writeExpression(f.left, ctx)})`;
}

// Case for maps eqality
if (lt.kind === 'map' && rt.kind === 'map') {
let op = f.op === '==' ? 'eq' : 'neq';
Expand Down
Loading

0 comments on commit ef7165e

Please sign in to comment.