Skip to content

Commit 8b63885

Browse files
authored
feat(tolk): add constant evaluator, inlay hints for evaluated constants and show this value on hover (#150)
Fixes #148
1 parent 0c00cfa commit 8b63885

File tree

12 files changed

+880
-3
lines changed

12 files changed

+880
-3
lines changed

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,11 @@
388388
"default": true,
389389
"description": "Show method ID hints for get methods"
390390
},
391+
"ton.tolk.hints.constantValues": {
392+
"type": "boolean",
393+
"default": true,
394+
"description": "Show computed values for constants"
395+
},
391396
"ton.func.hints.disable": {
392397
"type": "boolean",
393398
"default": false,
@@ -558,6 +563,7 @@
558563
"@ton/core": "0.60.1",
559564
"@ton/crypto": "^3.3.0",
560565
"glob": "^11.0.1",
566+
"jssha": "^3.3.1",
561567
"tolkfmt-test-dev": "0.0.20",
562568
"ton-assembly": "0.2.0",
563569
"vscode-languageclient": "^8.0.2",
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
========================================================================
2+
Basic constant evaluation, literals
3+
========================================================================
4+
const INT_CONST = 42;
5+
const HEX_CONST = 0xFF;
6+
const BIN_CONST = 0b1010;
7+
const BOOL_CONST = true;
8+
const NULL_CONST = null;
9+
10+
fun main() {
11+
return 0;
12+
}
13+
------------------------------------------------------------------------
14+
const INT_CONST/* : int */ = 42;
15+
const HEX_CONST/* : int */ = 0xFF;
16+
const BIN_CONST/* : int */ = 0b1010;
17+
const BOOL_CONST/* : bool */ = true;
18+
const NULL_CONST/* : null */ = null;
19+
20+
fun main()/* : int */ {
21+
return 0;
22+
}
23+
24+
========================================================================
25+
Binary operations evaluation
26+
========================================================================
27+
const ADD_CONST = 10 + 5;
28+
const SUB_CONST = 20 - 8;
29+
const MUL_CONST = 6 * 7;
30+
const DIV_CONST = 100 / 4;
31+
const MOD_CONST = 17 % 5;
32+
const SHIFT_LEFT = 1 << 3;
33+
const SHIFT_RIGHT = 16 >> 2;
34+
const BIT_AND = 0xFF & 0x0F;
35+
const BIT_OR = 0xF0 | 0x0F;
36+
const BIT_XOR = 0xFF ^ 0xAA;
37+
38+
fun main() {
39+
return 0;
40+
}
41+
------------------------------------------------------------------------
42+
const ADD_CONST/* : int */ = 10 + 5/* /* = 15 (0xF) */ */;
43+
const SUB_CONST/* : int */ = 20 - 8/* /* = 12 (0xC) */ */;
44+
const MUL_CONST/* : int */ = 6 * 7/* /* = 42 (0x2A) */ */;
45+
const DIV_CONST/* : int */ = 100 / 4/* /* = 25 (0x19) */ */;
46+
const MOD_CONST/* : int */ = 17 % 5/* /* = 2 (0x2) */ */;
47+
const SHIFT_LEFT/* : int */ = 1 << 3/* /* = 8 (0x8) */ */;
48+
const SHIFT_RIGHT/* : int */ = 16 >> 2/* /* = 4 (0x4) */ */;
49+
const BIT_AND/* : int */ = 0xFF & 0x0F/* /* = 15 (0xF) */ */;
50+
const BIT_OR/* : int */ = 0xF0 | 0x0F/* /* = 255 (0xFF) */ */;
51+
const BIT_XOR/* : int */ = 0xFF ^ 0xAA/* /* = 85 (0x55) */ */;
52+
53+
fun main()/* : int */ {
54+
return 0;
55+
}
56+
57+
========================================================================
58+
Unary operations evaluation
59+
========================================================================
60+
const NEG_CONST = -42;
61+
const POS_CONST = +100;
62+
const NOT_CONST = !true;
63+
const NOT_FALSE = !false;
64+
const BIT_NOT = ~0xFF;
65+
66+
fun main() {
67+
return 0;
68+
}
69+
------------------------------------------------------------------------
70+
const NEG_CONST/* : int */ = -42/* /* = 0x-2A */ */;
71+
const POS_CONST/* : int */ = +100/* /* = 100 (0x64) */ */;
72+
const NOT_CONST/* : bool */ = !true/* /* = false */ */;
73+
const NOT_FALSE/* : bool */ = !false/* /* = true */ */;
74+
const BIT_NOT/* : int */ = ~0xFF/* /* = 0x-100 */ */;
75+
76+
fun main()/* : int */ {
77+
return 0;
78+
}
79+
80+
========================================================================
81+
Reference evaluation
82+
========================================================================
83+
const BASE_CONST = 10;
84+
const REF_CONST = BASE_CONST;
85+
const EXPR_CONST = BASE_CONST * 2;
86+
const CHAIN_CONST = REF_CONST + 5;
87+
88+
fun main() {
89+
return 0;
90+
}
91+
------------------------------------------------------------------------
92+
const BASE_CONST/* : int */ = 10;
93+
const REF_CONST/* : int */ = BASE_CONST/* /* = 10 (0xA) */ */;
94+
const EXPR_CONST/* : int */ = BASE_CONST * 2/* /* = 20 (0x14) */ */;
95+
const CHAIN_CONST/* : int */ = REF_CONST + 5/* /* = 15 (0xF) */ */;
96+
97+
fun main()/* : int */ {
98+
return 0;
99+
}
100+
101+
========================================================================
102+
Complex expressions evaluation
103+
========================================================================
104+
const COMPLEX1 = (10 + 5) * 2;
105+
const COMPLEX2 = 100 / (4 + 1);
106+
const COMPLEX3 = (1 << 4) | (1 << 2);
107+
108+
fun main() {
109+
return 0;
110+
}
111+
------------------------------------------------------------------------
112+
const COMPLEX1/* : int */ = (10 + 5) * 2/* /* = 30 (0x1E) */ */;
113+
const COMPLEX2/* : int */ = 100 / (4 + 1)/* /* = 20 (0x14) */ */;
114+
const COMPLEX3/* : int */ = (1 << 4) | (1 << 2)/* /* = 20 (0x14) */ */;
115+
116+
fun main()/* : int */ {
117+
return 0;
118+
}
119+
120+
========================================================================
121+
Circular dependency handling
122+
========================================================================
123+
const CIRC_A = CIRC_B + 1;
124+
const CIRC_B = CIRC_A - 1;
125+
126+
fun main() {
127+
return 0;
128+
}
129+
------------------------------------------------------------------------
130+
const CIRC_A/* : int */ = CIRC_B + 1;
131+
const CIRC_B/* : int */ = CIRC_A - 1;
132+
133+
fun main()/* : int */ {
134+
return 0;
135+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
========================================================================
2+
Compile-time functions evaluation, CRC functions
3+
========================================================================
4+
const CRC32_TEST = stringCrc32("some_str");
5+
const CRC16_TEST = stringCrc16("some_str");
6+
const CRC32_HELLO = stringCrc32("hello");
7+
const CRC16_HELLO = stringCrc16("hello");
8+
9+
fun main() {
10+
return 0;
11+
}
12+
------------------------------------------------------------------------
13+
const CRC32_TEST/* : int */ = stringCrc32("some_str")/* /* = 4013618352 (0xEF3AF4B0) */ */;
14+
const CRC16_TEST/* : int */ = stringCrc16("some_str")/* /* = 53407 (0xD09F) */ */;
15+
const CRC32_HELLO/* : int */ = stringCrc32("hello")/* /* = 907060870 (0x3610A686) */ */;
16+
const CRC16_HELLO/* : int */ = stringCrc16("hello")/* /* = 50018 (0xC362) */ */;
17+
18+
fun main()/* : int */ {
19+
return 0;
20+
}
21+
22+
========================================================================
23+
Compile-time functions evaluation, SHA256 functions
24+
========================================================================
25+
const SHA256_TEST = stringSha256("some_crypto_key");
26+
const SHA256_32_TEST = stringSha256_32("some_crypto_key");
27+
const SHA256_HELLO = stringSha256("hello");
28+
const SHA256_32_HELLO = stringSha256_32("hello");
29+
30+
fun main() {
31+
return 0;
32+
}
33+
------------------------------------------------------------------------
34+
const SHA256_TEST/* : int */ = stringSha256("some_crypto_key")/* /* = 0x1C30C3FA846E4D85FB39C4A1C791F66A66DA7DE5D1ED24FCA94208F7F6D3CB21 */ */;
35+
const SHA256_32_TEST/* : int */ = stringSha256_32("some_crypto_key")/* /* = 472957946 (0x1C30C3FA) */ */;
36+
const SHA256_HELLO/* : int */ = stringSha256("hello")/* /* = 0x2CF24DBA5FB0A30E26E83B2AC5B9E29E1B161E5C1FA7425E73043362938B9824 */ */;
37+
const SHA256_32_HELLO/* : int */ = stringSha256_32("hello")/* /* = 754077114 (0x2CF24DBA) */ */;
38+
39+
fun main()/* : int */ {
40+
return 0;
41+
}
42+
43+
========================================================================
44+
Compile-time functions evaluation, stringToBase256
45+
========================================================================
46+
const BASE256_AB = stringToBase256("AB");
47+
const BASE256_HELLO = stringToBase256("hello");
48+
const BASE256_A = stringToBase256("A");
49+
50+
fun main() {
51+
return 0;
52+
}
53+
------------------------------------------------------------------------
54+
const BASE256_AB/* : int */ = stringToBase256("AB")/* /* = 16706 (0x4142) */ */;
55+
const BASE256_HELLO/* : int */ = stringToBase256("hello")/* /* = 0x68656C6C6F */ */;
56+
const BASE256_A/* : int */ = stringToBase256("A")/* /* = 65 (0x41) */ */;
57+
58+
fun main()/* : int */ {
59+
return 0;
60+
}
61+
62+
========================================================================
63+
Invalid compile-time function calls
64+
========================================================================
65+
const INVALID_ARG = stringCrc32(42); // Should not evaluate
66+
const NON_CONST_STR = someFunction();
67+
const INVALID_REF = stringCrc32(NON_CONST_STR); // Should not evaluate
68+
69+
fun main() {
70+
return 0;
71+
}
72+
------------------------------------------------------------------------
73+
const INVALID_ARG/* : int */ = stringCrc32(42); // Should not evaluate
74+
const NON_CONST_STR = someFunction();
75+
const INVALID_REF/* : int */ = stringCrc32(NON_CONST_STR); // Should not evaluate
76+
77+
fun main()/* : int */ {
78+
return 0;
79+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
========================================================================
2+
Constant value inlay hints, basic
3+
========================================================================
4+
const SIMPLE_CONST = 42 + 8;
5+
const HEX_CONST = 0xFF & 0x0F;
6+
const BOOL_CONST = !false;
7+
8+
fun main() {
9+
return 0;
10+
}
11+
------------------------------------------------------------------------
12+
const SIMPLE_CONST/* : int */ = 42 + 8/* /* = 50 (0x32) */ */;
13+
const HEX_CONST/* : int */ = 0xFF & 0x0F/* /* = 15 (0xF) */ */;
14+
const BOOL_CONST/* : bool */ = !false/* /* = true */ */;
15+
16+
fun main()/* : int */ {
17+
return 0;
18+
}
19+
20+
========================================================================
21+
Constant value inlay hints, references
22+
========================================================================
23+
const BASE = 10;
24+
const DERIVED = BASE * 3;
25+
const COMPLEX = (BASE + DERIVED) / 2;
26+
27+
fun main() {
28+
return 0;
29+
}
30+
------------------------------------------------------------------------
31+
const BASE/* : int */ = 10;
32+
const DERIVED/* : int */ = BASE * 3/* /* = 30 (0x1E) */ */;
33+
const COMPLEX/* : int */ = (BASE + DERIVED) / 2/* /* = 20 (0x14) */ */;
34+
35+
fun main()/* : int */ {
36+
return 0;
37+
}
38+
39+
========================================================================
40+
Constant value inlay hints, circular dependency
41+
========================================================================
42+
const CIRC_A = CIRC_B + 1;
43+
const CIRC_B = CIRC_A, 1;
44+
45+
fun main() {
46+
return 0;
47+
}
48+
------------------------------------------------------------------------
49+
const CIRC_A/* : int */ = CIRC_B + 1;
50+
const CIRC_B/* : int */ = CIRC_A, 1;
51+
52+
fun main()/* : int */ {
53+
return 0;
54+
}

server/src/languages/tolk/documentation/documentation.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {TypeInferer} from "@server/languages/tolk/TypeInferer"
2222
import {functionTypeOf, typeOf} from "@server/languages/tolk/type-inference"
2323
import {StructTy, UnionTy, UnknownTy} from "@server/languages/tolk/types/ty"
2424
import {EstimateContext, sizeOfPresentation} from "@server/languages/tolk/types/size-of"
25+
import {ConstantEvaluator} from "@server/languages/tolk/evaluation/ConstantEvaluator"
2526

2627
/**
2728
* Returns the documentation for the given symbol in Markdown format, or null
@@ -180,8 +181,22 @@ export function generateTolkDocFor(node: NamedNode, place: SyntaxNode): string |
180181
const value = constant.value()
181182
if (!value) return null
182183

184+
let evaluatedValueText = ""
185+
186+
if (!ConstantEvaluator.isSimpleLiteral(constant.value()?.node)) {
187+
const evaluationResult = ConstantEvaluator.evaluateConstant(constant)
188+
189+
if (evaluationResult.value !== null && evaluationResult.type !== "unknown") {
190+
const formattedValue = ConstantEvaluator.formatValue(evaluationResult)
191+
evaluatedValueText = ` // ${formattedValue}`
192+
}
193+
}
194+
183195
const doc = node.documentation()
184-
return defaultResult(`const ${node.name()}: ${type} = ${value.node.text}`, doc)
196+
return defaultResult(
197+
`const ${node.name()}: ${type} = ${value.node.text}${evaluatedValueText}`,
198+
doc,
199+
)
185200
}
186201
case "global_var_declaration": {
187202
const variable = new GlobalVariable(astNode, node.file)

0 commit comments

Comments
 (0)