Skip to content

Commit 3740efd

Browse files
committed
[Tolk] Replace "..."c postfixes with stringCrc32("...") functions
With the introduction function `ton("0.05")`, I decided to remove old FunC-style string postfixes "..."c, "..."H, etc. in favor of a clearer and more flexible approach. "..."c -> stringCrc32("...") no way -> stringCrc16("...") "..."H -> stringSha256("...") "..."h -> stringSha256_32("...") "..."a -> stringAddressToSlice("...") "..."s -> stringHexToSlice("...") "..."u -> stringToBase256("...") These functions are compile-time only. They can be used in constant initialization.
1 parent 35f45b0 commit 3740efd

19 files changed

+382
-271
lines changed

crypto/smartcont/tolk-stdlib/common.tolk

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,42 @@ fun commitContractDataAndActions(): void
194194
Signature checks, hashing, cryptography.
195195
*/
196196

197+
/// Compile-time function that calculates crc32 of a constant string.
198+
/// Example: `const op = stringCrc32("some_str")` = 4013618352 = 0xEF3AF4B0
199+
/// Note: stringCrc32(slice_var) does not work! It accepts a constant string and works at compile-time.
200+
@pure
201+
fun stringCrc32(constString: slice): int
202+
builtin;
203+
204+
/// Compile-time function that calculates crc16 (XMODEM) of a constant string.
205+
/// Example: `const op = stringCrc16("some_str")` = 53407 = 0xD09F
206+
/// Note: stringCrc16(slice_var) does not work! It accepts a constant string and works at compile-time.
207+
@pure
208+
fun stringCrc16(constString: slice): int
209+
builtin;
210+
211+
/// Compile-time function that calculates sha256 of a constant string and returns 256-bit integer.
212+
/// Example: `const hash = stringSha256("some_crypto_key")`
213+
/// Note: it's a compile-time function, `stringSha256(slice_var)` does not work.
214+
/// Use `sliceBitsHash` or `sliceHash` (declared below) to hash a slice without/with its refs at runtime.
215+
@pure
216+
fun stringSha256(constString: slice): int
217+
builtin;
218+
219+
/// Compile-time function that calculates sha256 of a constant string and takes the first 32 bits.
220+
/// Example: `const minihash = stringSha256_32("some_crypto_key")`
221+
/// Note: stringSha256_32(slice_var) does not work! It accepts a constant string and works at compile-time.
222+
@pure
223+
fun stringSha256_32(constString: slice): int
224+
builtin;
225+
226+
/// Compile-time function that takes N-chars ascii string and interprets it as a number in base 256.
227+
/// Example: `const value = stringToBase256("AB")` = 16706 (65*256 + 66)
228+
/// Note: stringToBase256(slice_var) does not work! It accepts a constant string and works at compile-time.
229+
@pure
230+
fun stringToBase256(constString: slice): int
231+
builtin;
232+
197233
/// Computes the representation hash of a `cell` [c] and returns it as a 256-bit unsigned integer `x`.
198234
/// Useful for signing and checking signatures of arbitrary entities represented by a tree of cells.
199235
@pure
@@ -210,7 +246,7 @@ fun sliceHash(s: slice): int
210246
/// Computes sha256 of the data bits of `slice` [s]. If the bit length of `s` is not divisible by eight,
211247
/// throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`.
212248
@pure
213-
fun stringHash(s: slice): int
249+
fun sliceBitsHash(s: slice): int
214250
asm "SHA256U";
215251

216252
/// Checks the Ed25519-`signature` of a `hash` (a 256-bit unsigned integer, usually computed as the hash of some data)
@@ -340,6 +376,22 @@ fun debugDumpStack(): void
340376
When you _preload_ some data, you just get the result without mutating the slice.
341377
*/
342378

379+
/// Compile-time function that converts a constant string to TL-encoded MsgAddressInt (TVM slice).
380+
/// Example: stringAddressToSlice("EQCRDM9h4k3UJdOePPuyX40mCgA4vxge5Dc5vjBR8djbEKC5")
381+
/// Example: stringAddressToSlice("0:527964d55cfa6eb731f4bfc07e9d025098097ef8505519e853986279bd8400d8")
382+
/// Note: it's a compile-time function, `stringAddressToSlice(slice_var)` does not work.
383+
/// Use `parseStandardAddress` to decode a slice at runtime into workchain and hash.
384+
@pure
385+
fun stringAddressToSlice(constStringAddress: slice): slice
386+
builtin;
387+
388+
/// Compile-time function that converts a constant hex-encoded string to N/2 bytes.
389+
/// Example: `const v = stringHexToSlice("abcdef")` = slice with 3 bytes `[ 0xAB, 0xCD, 0xEF ]`
390+
/// Note: stringHexToSlice(slice_var) does not work! It accepts a constant string and works at compile-time.
391+
@pure
392+
fun stringHexToSlice(constStringBytesHex: slice): slice
393+
builtin;
394+
343395
/// Converts a `cell` [c] into a `slice`. Notice that [c] must be either an ordinary cell,
344396
/// or an exotic cell (see [TVM.pdf](https://ton-blockchain.github.io/docs/tvm.pdf), 3.1.2)
345397
/// which is automatically loaded to yield an ordinary cell `c'`, converted into a `slice` afterwards.

tolk-tester/tests/constants-tests.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const int111: int = 111;
77
const int1r = int1;
88

99
const str1 = "const1";
10-
const str2 = "aabbcc"s;
10+
const str2 = stringHexToSlice("aabbcc");
1111

1212
const str2r: slice = str2;
1313

tolk-tester/tests/inference-tests.tolk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ fun eq<X>(value: X): X { return value; }
55

66
fun test1(x: int, y: int) {
77
__expect_type(0, "int");
8-
__expect_type("0"c, "int");
8+
__expect_type(stringCrc32("0"), "int");
99
__expect_type(x, "int");
1010
__expect_type(x + y, "int");
1111
__expect_type(x * y, "int");

tolk-tester/tests/parse-address.tolk

Lines changed: 97 additions & 97 deletions
Large diffs are not rendered by default.

tolk-tester/tests/strings-tests.tolk

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,31 @@ get ascii_slice(): slice {
33
}
44

55
get raw_slice(): slice {
6-
return "abcdef"s;
6+
return stringHexToSlice("abcdef");
77
}
88

99
get addr_slice(): slice {
10-
return "Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF"a;
10+
return stringAddressToSlice("Ef8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzM0vF");
1111
}
1212

1313
get string_hex(): int {
14-
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"u;
14+
return stringToBase256("ABCDEFGHIJKLMNOPQRSTUVWXYZ012345");
1515
}
1616

1717
get fun string_minihash(): int { // 'get' and 'get fun' both possible
18-
return "transfer(slice, int)"h;
18+
return stringSha256_32("transfer(slice, int)");
1919
}
2020

2121
get fun string_maxihash(): int {
22-
return "transfer(slice, int)"H;
22+
return stringSha256("transfer(slice, int)");
2323
}
2424

2525
get fun string_crc32(): int {
26-
return "transfer(slice, int)"c;
26+
return stringCrc32("transfer(slice, int)");
27+
}
28+
29+
get fun string_crc16(): int {
30+
return stringCrc16("transfer(slice, int)");
2731
}
2832

2933
@pure
@@ -35,27 +39,44 @@ asm "ENDC" "CTOS";
3539
fun sdeq(s1: slice, s2: slice): int
3640
asm "SDEQ";
3741

42+
fun calcSliceBytes(slice: slice): tuple {
43+
var t = createEmptyTuple();
44+
var n = 0;
45+
while (n = slice.getRemainingBitsCount()) {
46+
t.tuplePush(slice.loadUint(min(8, n)));
47+
}
48+
return t;
49+
}
50+
3851
fun main() {
3952
var s_ascii: slice = ascii_slice();
4053
var s_raw: slice = raw_slice();
4154
var s_addr: slice = addr_slice();
4255
var i_hex: int = string_hex();
4356
var i_mini: int = string_minihash();
4457
var i_maxi: int = string_maxihash();
45-
var i_crc: int = string_crc32();
58+
var i_crc32: int = string_crc32();
59+
var i_crc16: int = string_crc16();
4660
assert(sdeq(s_ascii, newc().storeUint(0x737472696E67, 12 * 4).endcs())) throw 101;
4761
assert(sdeq(s_raw, newc().storeUint(0xABCDEF, 6 * 4).endcs())) throw 102;
4862
assert(sdeq(s_addr, newc().storeUint(4, 3).storeInt(-1, 8)
4963
.storeUint(0x3333333333333333333333333333333333333333333333333333333333333333, 256).endcs()), 103);
5064
assert(i_hex == 0x4142434445464748494A4B4C4D4E4F505152535455565758595A303132333435) throw 104;
5165
assert(i_mini == 0x7a62e8a8) throw 105;
5266
assert(i_maxi == 0x7a62e8a8ebac41bd6de16c65e7be363bc2d2cbc6a0873778dead4795c13db979) throw 106;
53-
assert(i_crc == 2235694568) throw 107;
67+
assert(i_crc32 == 2235694568 && i_crc32 == 0x8541fde8) throw 107;
68+
assert(i_crc16 == 52550 && i_crc16 == 0xCD46) throw 108;
5469
return 0;
5570
}
5671

72+
@method_id(101)
73+
fun test1() {
74+
return calcSliceBytes("ABCD");
75+
}
76+
5777
/**
58-
@testcase | 0 | | 0
78+
@testcase | 0 | | 0
79+
@testcase | 101 | | [ 65 66 67 68 ]
5980

60-
@code_hash 13830542019509784148027107880226447201604257839069192762244575629978154217223
81+
@code_hash 38184847030631877916087987911699475358017315230885358090110033079289166112584
6182
*/

tolk/ast-from-tokens.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -357,12 +357,7 @@ static AnyExprV parse_expr100(Lexer& lex) {
357357
case tok_string_const: {
358358
std::string_view str_val = lex.cur_str();
359359
lex.next();
360-
char modifier = 0;
361-
if (lex.tok() == tok_string_modifier) {
362-
modifier = lex.cur_str()[0];
363-
lex.next();
364-
}
365-
return createV<ast_string_const>(loc, str_val, modifier);
360+
return createV<ast_string_const>(loc, str_val);
366361
}
367362
case tok_underscore: {
368363
lex.next();
@@ -927,7 +922,7 @@ static AnyV parse_asm_func_body(Lexer& lex, V<ast_parameter_list> param_list) {
927922
lex.check(tok_string_const, "\"ASM COMMAND\"");
928923
while (lex.tok() == tok_string_const) {
929924
std::string_view asm_command = lex.cur_str();
930-
asm_commands.push_back(createV<ast_string_const>(lex.cur_location(), asm_command, 0));
925+
asm_commands.push_back(createV<ast_string_const>(lex.cur_location(), asm_command));
931926
lex.next();
932927
}
933928
lex.expect(tok_semicolon, "`;`");
@@ -1142,7 +1137,7 @@ static AnyV parse_import_directive(Lexer& lex) {
11421137
if (rel_filename.empty()) {
11431138
lex.error("imported file name is an empty string");
11441139
}
1145-
auto v_str = createV<ast_string_const>(lex.cur_location(), rel_filename, 0);
1140+
auto v_str = createV<ast_string_const>(lex.cur_location(), rel_filename);
11461141
lex.next();
11471142
return createV<ast_import_directive>(loc, v_str); // semicolon is not necessary
11481143
}

tolk/ast-replicator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class ASTReplicatorFunction : public ASTReplicator {
8080
return createV<ast_int_const>(v->loc, v->intval, v->orig_str);
8181
}
8282
virtual V<ast_string_const> clone(V<ast_string_const> v) {
83-
return createV<ast_string_const>(v->loc, v->str_val, v->modifier);
83+
return createV<ast_string_const>(v->loc, v->str_val);
8484
}
8585
virtual V<ast_bool_const> clone(V<ast_bool_const> v) {
8686
return createV<ast_bool_const>(v->loc, v->bool_val);

tolk/ast-stringifier.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,7 @@ class ASTStringifier final : public ASTVisitor {
136136
case ast_int_const:
137137
return static_cast<std::string>(v->as<ast_int_const>()->orig_str);
138138
case ast_string_const:
139-
if (char modifier = v->as<ast_string_const>()->modifier) {
140-
return "\"" + static_cast<std::string>(v->as<ast_string_const>()->str_val) + "\"" + std::string(1, modifier);
141-
} else {
142-
return "\"" + static_cast<std::string>(v->as<ast_string_const>()->str_val) + "\"";
143-
}
139+
return "\"" + static_cast<std::string>(v->as<ast_string_const>()->str_val) + "\"";
144140
case ast_bool_const:
145141
return v->as<ast_bool_const>()->bool_val ? "true" : "false";
146142
case ast_dot_access: {

tolk/ast.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ void Vertex<ast_reference>::assign_sym(const Symbol* sym) {
126126
this->sym = sym;
127127
}
128128

129+
void Vertex<ast_string_const>::assign_literal_value(ConstantValue&& literal_value) {
130+
this->literal_value = std::move(literal_value);
131+
}
132+
129133
void Vertex<ast_function_call>::assign_fun_ref(FunctionPtr fun_ref) {
130134
this->fun_maybe = fun_ref;
131135
}

tolk/ast.h

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -458,25 +458,18 @@ struct Vertex<ast_int_const> final : ASTExprLeaf {
458458

459459
template<>
460460
// ast_string_const is a string literal in double quotes or """ when multiline
461-
// examples: "asdf" / "Ef8zMz..."a / "to_calc_crc32_from"c
462-
// an optional modifier specifies how a string is parsed (probably, like an integer)
461+
// examples: "asdf" / "LTIME" (in asm body) / stringCrc32("asdf") (as an argument)
463462
// note, that TVM doesn't have strings, it has only slices, so "hello" has type slice
464463
struct Vertex<ast_string_const> final : ASTExprLeaf {
465464
std::string_view str_val;
466-
char modifier;
465+
ConstantValue literal_value; // value of type `slice`, calculated after type inferring, at constants evaluation
467466

468-
bool is_bitslice() const {
469-
char m = modifier;
470-
return m == 0 || m == 's' || m == 'a';
471-
}
472-
bool is_intval() const {
473-
char m = modifier;
474-
return m == 'u' || m == 'h' || m == 'H' || m == 'c';
475-
}
467+
Vertex* mutate() const { return const_cast<Vertex*>(this); }
468+
void assign_literal_value(ConstantValue&& literal_value);
476469

477-
Vertex(SrcLocation loc, std::string_view str_val, char modifier)
470+
Vertex(SrcLocation loc, std::string_view str_val)
478471
: ASTExprLeaf(ast_string_const, loc)
479-
, str_val(str_val), modifier(modifier) {}
472+
, str_val(str_val) {}
480473
};
481474

482475
template<>

0 commit comments

Comments
 (0)