Skip to content

Commit d110022

Browse files
committed
[Tolk] Implement logical operators && ||
Unary logical NOT was already implemented earlier. Logical AND OR are expressed via conditional expression: * a && b -> a ? (b != 0) : 0 * a || b -> a ? 1 : (b != 0) They work as expected in any expressions. For instance, having `cond && f()`, f is called only if cond is true. For primitive cases, like `a > 0 && b > 0`, Fift code is not optimal, it could potentially be without IFs. These are moments of future optimizations. For now, it's more than enough.
1 parent 16824fc commit d110022

File tree

4 files changed

+204
-46
lines changed

4 files changed

+204
-46
lines changed

tolk-tester/tests/invalid-logical-1.tolk

Lines changed: 0 additions & 8 deletions
This file was deleted.

tolk-tester/tests/logical-operators.tolk

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,72 @@ fun testNotNull(x: int) {
5757
return [x == null, null == x, !(x == null), null == null, +(null != null)];
5858
}
5959

60+
@method_id(106)
61+
fun testAndConstCodegen() {
62+
return (
63+
[1 && 0, 0 && 1, 0 && 0, 1 && 1],
64+
[4 && 3 && 0, 5 && 0 && 7 && 8, (7 && 0) && -19],
65+
[4 && 3 && -1, 5 && -100 && 7 && 8, (7 && (1 + 2)) && -19],
66+
[true && false, true && true]
67+
);
68+
}
69+
70+
@method_id(107)
71+
fun testOrConstCodegen() {
72+
return (
73+
[1 || 0, 0 || 1, 0 || 0, 1 || 1],
74+
[0 || 0 || 0, 0 || (0 || 0), ((0 || 0) || 0) || 0],
75+
[4 || 3 || -1, 0 || -100 || 0 || 0, (0 || (1 + -1)) || -19],
76+
[true || false, false || false]
77+
);
78+
}
79+
80+
global eqCallsCnt: int;
81+
82+
fun eq(x: int) { return x; }
83+
fun eqCnt(x: int) { eqCallsCnt += 1; return x; }
84+
fun isGt0(x: int) { return x > 0; }
85+
86+
fun alwaysThrows(): int { throw 444 ; return 444; }
87+
88+
@method_id(108)
89+
fun testAndSimpleCodegen(a: int, b: int) {
90+
return a && b;
91+
}
92+
93+
@method_id(109)
94+
fun testOrSimpleCodegen(a: int, b: int) {
95+
return a > 0 || b > 0;
96+
}
97+
98+
@method_id(110)
99+
fun testLogicalOps1(x: int) {
100+
eqCallsCnt = 0;
101+
return (
102+
isGt0(x) || !isGt0(x) || alwaysThrows(),
103+
x && eqCnt(x) && eqCnt(x - 1) && eqCnt(x - 2),
104+
(400 == eq(x)) && alwaysThrows(),
105+
(500 == eq(x)) || eqCnt(x) || false,
106+
(500 == eq(x)) || eqCnt(x) || true,
107+
eqCallsCnt
108+
);
109+
}
110+
111+
@method_id(111)
112+
fun testLogicalOps2(first: int) {
113+
var s = beginCell().storeInt(1, 32).storeInt(2, 32).storeInt(3, 32).storeInt(4, 32).storeInt(5, 32).endCell().beginParse();
114+
var sum = 0;
115+
if (first && s.loadUint(32)) {
116+
(2 == s.loadUint(32)) && (sum += s.loadUint(32));
117+
(3 == s.loadUint(32)) && (sum += s.loadUint(32));
118+
(5 == s.preloadUint(32)) && (sum += s.loadUint(32));
119+
} else {
120+
(10 == s.loadUint(32)) || (20 == s.loadUint(32)) || (3 == s.loadUint(32)) || (4 == s.loadUint(32));
121+
sum += s.loadUint(32);
122+
}
123+
return (s.getRemainingBitsCount(), sum);
124+
}
125+
60126
fun main() {
61127

62128
}
@@ -80,6 +146,19 @@ fun main() {
80146
@testcase | 104 | 0 | 3 -1 5
81147
@testcase | 105 | 0 | [ 0 0 -1 -1 0 ]
82148
@testcase | 105 | null | [ -1 -1 0 -1 0 ]
149+
@testcase | 106 | | [ 0 0 0 -1 ] [ 0 0 0 ] [ -1 -1 -1 ] [ 0 -1 ]
150+
@testcase | 107 | | [ -1 -1 0 -1 ] [ 0 0 0 ] [ -1 -1 -1 ] [ -1 0 ]
151+
@testcase | 108 | 1 2 | -1
152+
@testcase | 108 | 1 0 | 0
153+
@testcase | 109 | -5 -4 | 0
154+
@testcase | 109 | -5 4 | -1
155+
@testcase | 109 | 1 99 | -1
156+
@testcase | 110 | 0 | -1 0 0 0 -1 2
157+
@testcase | 110 | 1 | -1 0 0 -1 -1 4
158+
@testcase | 110 | 2 | -1 0 0 -1 -1 5
159+
@testcase | 110 | 500 | -1 -1 0 -1 -1 3
160+
@testcase | 111 | 0 | 32 4
161+
@testcase | 111 | -1 | 0 8
83162

84163
@fif_codegen
85164
"""
@@ -134,4 +213,83 @@ fun main() {
134213
}>
135214
"""
136215

216+
@fif_codegen
217+
"""
218+
testAndConstCodegen PROC:<{
219+
//
220+
FALSE
221+
0 PUSHINT
222+
DUP
223+
TRUE
224+
4 TUPLE
225+
FALSE
226+
0 PUSHINT
227+
DUP
228+
TRIPLE
229+
TRUE
230+
TRUE
231+
TRUE
232+
TRIPLE
233+
FALSE
234+
TRUE
235+
PAIR
236+
}>
237+
"""
238+
239+
@fif_codegen
240+
"""
241+
testOrConstCodegen PROC:<{
242+
//
243+
-1 PUSHINT
244+
TRUE
245+
FALSE
246+
s2 PUSH
247+
4 TUPLE
248+
FALSE
249+
FALSE
250+
FALSE
251+
TRIPLE
252+
-1 PUSHINT
253+
DUP
254+
TRUE
255+
TRIPLE
256+
-1 PUSHINT
257+
FALSE
258+
PAIR
259+
}>
260+
"""
261+
262+
Currently, && operator is implemented via ?: and is not optimal in primitive cases.
263+
For example, `a && b` can be expressed without IFs.
264+
These are moments of future optimizations. For now, it's more than enough.
265+
@fif_codegen
266+
"""
267+
testAndSimpleCodegen PROC:<{
268+
// a b
269+
SWAP // b a
270+
IF:<{ // b
271+
0 NEQINT // _2
272+
}>ELSE<{ // b
273+
DROP //
274+
0 PUSHINT // _2=0
275+
}>
276+
}>
277+
"""
278+
279+
@fif_codegen
280+
"""
281+
testOrSimpleCodegen PROC:<{
282+
// a b
283+
SWAP // b a
284+
0 GTINT // b _3
285+
IF:<{ // b
286+
DROP //
287+
-1 PUSHINT // _4=-1
288+
}>ELSE<{ // b
289+
0 GTINT // _7
290+
0 NEQINT // _4
291+
}>
292+
}>
293+
"""
294+
137295
*/

tolk/pipe-ast-to-legacy.cpp

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,22 @@ static bool parse_raw_address(const std::string& acc_string, int& workchain, ton
160160
return true;
161161
}
162162

163+
static Expr* create_expr_apply(SrcLocation loc, SymDef* sym, std::vector<Expr*>&& args) {
164+
Expr* apply = new Expr(Expr::_Apply, sym, std::move(args));
165+
apply->here = loc;
166+
apply->flags = Expr::_IsRvalue;
167+
apply->deduce_type();
168+
return apply;
169+
}
170+
171+
static Expr* create_expr_int_const(SrcLocation loc, int int_val) {
172+
Expr* int_const = new Expr(Expr::_Const, loc);
173+
int_const->intval = td::make_refint(int_val);
174+
int_const->flags = Expr::_IsRvalue;
175+
int_const->e_type = TypeExpr::new_atomic(TypeExpr::_Int);
176+
return int_const;
177+
}
178+
163179
namespace blk_fl {
164180
enum { end = 1, ret = 2, empty = 4 };
165181
typedef int val;
@@ -238,13 +254,10 @@ static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code) {
238254
if (x->is_immutable()) {
239255
x->fire_error_modifying_immutable("left side of assignment");
240256
}
241-
sym_idx_t name = G.symbols.lookup_add("^_" + operator_name + "_");
257+
SymDef* sym = lookup_symbol(calc_sym_idx("^_" + operator_name + "_"));
242258
Expr* y = process_expr(v->get_rhs(), code);
243259
y->chk_rvalue();
244-
Expr* z = new Expr{Expr::_Apply, name, {x, y}};
245-
z->here = v->loc;
246-
z->flags = Expr::_IsRvalue;
247-
z->deduce_type();
260+
Expr* z = create_expr_apply(v->loc, sym, {x, y});
248261
Expr* res = new Expr{Expr::_Letop, {x->copy(), z}};
249262
res->here = v->loc;
250263
res->flags = x->flags | Expr::_IsRvalue;
@@ -276,25 +289,35 @@ static Expr* process_expr(V<ast_binary_operator> v, CodeBlob& code) {
276289
t == tok_mul || t == tok_div || t == tok_mod || t == tok_divC || t == tok_divR) {
277290
Expr* res = process_expr(v->get_lhs(), code);
278291
res->chk_rvalue();
279-
sym_idx_t name = G.symbols.lookup_add("_" + operator_name + "_");
292+
SymDef* sym = lookup_symbol(calc_sym_idx("_" + operator_name + "_"));
280293
Expr* x = process_expr(v->get_rhs(), code);
281294
x->chk_rvalue();
282-
res = new Expr{Expr::_Apply, name, {res, x}};
283-
res->here = v->loc;
284-
res->flags = Expr::_IsRvalue;
285-
res->deduce_type();
295+
res = create_expr_apply(v->loc, sym, {res, x});
286296
return res;
287297
}
288298
if (t == tok_logical_and || t == tok_logical_or) {
289-
v->error("logical operators are not supported yet");
299+
// do the following transformations:
300+
// a && b -> a ? (b != 0) : 0
301+
// a || b -> a ? 1 : (b != 0)
302+
SymDef* sym_neq = lookup_symbol(calc_sym_idx("_!=_"));
303+
Expr* lhs = process_expr(v->get_lhs(), code);
304+
Expr* rhs = process_expr(v->get_rhs(), code);
305+
Expr* e_neq0 = create_expr_apply(v->loc, sym_neq, {rhs, create_expr_int_const(v->loc, 0)});
306+
Expr* e_when_true = t == tok_logical_and ? e_neq0 : create_expr_int_const(v->loc, -1);
307+
Expr* e_when_false = t == tok_logical_and ? create_expr_int_const(v->loc, 0) : e_neq0;
308+
Expr* e_ternary = new Expr(Expr::_CondExpr, {lhs, e_when_true, e_when_false});
309+
e_ternary->here = v->loc;
310+
e_ternary->flags = Expr::_IsRvalue;
311+
e_ternary->deduce_type();
312+
return e_ternary;
290313
}
291314

292315
v->error("unsupported binary operator");
293316
}
294317

295318
static Expr* process_expr(V<ast_unary_operator> v, CodeBlob& code) {
296319
TokenType t = v->tok;
297-
sym_idx_t name = G.symbols.lookup_add(static_cast<std::string>(v->operator_name) + "_");
320+
SymDef* sym = lookup_symbol(calc_sym_idx(static_cast<std::string>(v->operator_name) + "_"));
298321
Expr* x = process_expr(v->get_rhs(), code);
299322
x->chk_rvalue();
300323

@@ -316,11 +339,7 @@ static Expr* process_expr(V<ast_unary_operator> v, CodeBlob& code) {
316339
return x;
317340
}
318341

319-
auto res = new Expr{Expr::_Apply, name, {x}};
320-
res->here = v->loc;
321-
res->flags = Expr::_IsRvalue;
322-
res->deduce_type();
323-
return res;
342+
return create_expr_apply(v->loc, sym, {x});
324343
}
325344

326345
static Expr* process_expr(V<ast_ternary_operator> v, CodeBlob& code) {
@@ -683,19 +702,12 @@ static Expr* process_expr(V<ast_string_const> v) {
683702

684703
static Expr* process_expr(V<ast_bool_const> v) {
685704
SymDef* builtin_sym = lookup_symbol(calc_sym_idx(v->bool_val ? "__true" : "__false"));
686-
Expr* res = new Expr{Expr::_Apply, builtin_sym, {}};
687-
res->flags = Expr::_IsRvalue;
688-
res->deduce_type();
689-
return res;
705+
return create_expr_apply(v->loc, builtin_sym, {});
690706
}
691707

692708
static Expr* process_expr(V<ast_null_keyword> v) {
693709
SymDef* builtin_sym = lookup_symbol(calc_sym_idx("__null"));
694-
Expr* res = new Expr{Expr::_Apply, builtin_sym, {}};
695-
res->here = v->loc;
696-
res->flags = Expr::_IsRvalue;
697-
res->deduce_type();
698-
return res;
710+
return create_expr_apply(v->loc, builtin_sym, {});
699711
}
700712

701713
static Expr* process_expr(V<ast_self_keyword> v, CodeBlob& code) {
@@ -1116,11 +1128,9 @@ static blk_fl::val process_vertex(V<ast_throw_statement> v, CodeBlob& code) {
11161128
args.push_back(process_expr(v->get_thrown_code(), code));
11171129
}
11181130

1119-
Expr* expr = new Expr{Expr::_Apply, builtin_sym, std::move(args)};
1120-
expr->here = v->loc;
1121-
expr->flags = Expr::_IsRvalue | Expr::_IsImpure;
1122-
expr->deduce_type();
1123-
expr->pre_compile(code);
1131+
Expr* apply = create_expr_apply(v->loc, builtin_sym, std::move(args));
1132+
apply->flags |= Expr::_IsImpure;
1133+
apply->pre_compile(code);
11241134
return blk_fl::end;
11251135
}
11261136

@@ -1137,11 +1147,9 @@ static blk_fl::val process_vertex(V<ast_assert_statement> v, CodeBlob& code) {
11371147
}
11381148

11391149
SymDef* builtin_sym = lookup_symbol(calc_sym_idx("__throw_if_unless"));
1140-
Expr* expr = new Expr{Expr::_Apply, builtin_sym, std::move(args)};
1141-
expr->here = v->loc;
1142-
expr->flags = Expr::_IsRvalue | Expr::_IsImpure;
1143-
expr->deduce_type();
1144-
expr->pre_compile(code);
1150+
Expr* apply = create_expr_apply(v->loc, builtin_sym, std::move(args));
1151+
apply->flags |= Expr::_IsImpure;
1152+
apply->pre_compile(code);
11451153
return blk_fl::end;
11461154
}
11471155

tolk/symtable.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ class SymTable {
6767
public:
6868

6969
static constexpr sym_idx_t not_found = 0;
70-
sym_idx_t lookup(std::string_view str, int mode = 0) {
71-
return gen_lookup(str, mode);
70+
sym_idx_t lookup(std::string_view str) {
71+
return gen_lookup(str, 0);
7272
}
7373
sym_idx_t lookup_add(std::string_view str) {
7474
return gen_lookup(str, 1);

0 commit comments

Comments
 (0)