mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Add constants and string literals to funC
* Add special string literals "..."? (s,a,u,h,) * Add string literal H (256-bit hash) * Add string literal c (crc32) * Use td::hex_encode instead of homebrew function and add test * Fix error codes and use more generic address * Add support for int and slice constants * Add support for strongly typed constants * Add support for precompiled constant expressions (hard!) Co-authored-by: starlightduck <starlightduck@gmail.com>
This commit is contained in:
parent
0e955793ed
commit
0c772185ef
14 changed files with 397 additions and 26 deletions
|
@ -19,6 +19,9 @@
|
|||
#include "func.h"
|
||||
#include "td/utils/crypto.h"
|
||||
#include "common/refint.h"
|
||||
#include "openssl/digest.hpp"
|
||||
#include "block/block.h"
|
||||
#include "block-parse.h"
|
||||
#include <fstream>
|
||||
|
||||
namespace sym {
|
||||
|
@ -229,6 +232,83 @@ void parse_global_var_decl(Lexer& lex) {
|
|||
lex.next();
|
||||
}
|
||||
|
||||
extern int const_cnt;
|
||||
Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false);
|
||||
|
||||
void parse_const_decl(Lexer& lex) {
|
||||
SrcLocation loc = lex.cur().loc;
|
||||
int wanted_type = Expr::_None;
|
||||
if (lex.tp() == _Int) {
|
||||
wanted_type = Expr::_Const;
|
||||
lex.next();
|
||||
} else if (lex.tp() == _Slice) {
|
||||
wanted_type = Expr::_SliceConst;
|
||||
lex.next();
|
||||
}
|
||||
if (lex.tp() != _Ident) {
|
||||
lex.expect(_Ident, "constant name");
|
||||
}
|
||||
loc = lex.cur().loc;
|
||||
SymDef* sym_def = sym::define_global_symbol(lex.cur().val, false, loc);
|
||||
if (!sym_def) {
|
||||
lex.cur().error_at("cannot define global symbol `", "`");
|
||||
}
|
||||
if (sym_def->value) {
|
||||
lex.cur().error_at("global symbol `", "` already exists");
|
||||
}
|
||||
lex.next();
|
||||
if (lex.tp() != '=') {
|
||||
lex.cur().error_at("expected = instead of ", "");
|
||||
}
|
||||
lex.next();
|
||||
CodeBlob code;
|
||||
// Handles processing and resolution of literals and consts
|
||||
auto x = parse_expr(lex, code, false); // also does lex.next() !
|
||||
if (x->flags != Expr::_IsRvalue) {
|
||||
lex.cur().error("expression is not strictly Rvalue");
|
||||
}
|
||||
if ((wanted_type == Expr::_Const) && (x->cls == Expr::_Apply))
|
||||
wanted_type = Expr::_None; // Apply is additionally checked to result in an integer
|
||||
if ((wanted_type != Expr::_None) && (x->cls != wanted_type)) {
|
||||
lex.cur().error("expression type does not match wanted type");
|
||||
}
|
||||
if (x->cls == Expr::_Const) { // Integer constant
|
||||
sym_def->value = new SymValConst{const_cnt++, x->intval};
|
||||
} else if (x->cls == Expr::_SliceConst) { // Slice constant (string)
|
||||
sym_def->value = new SymValConst{const_cnt++, x->strval};
|
||||
} else if (x->cls == Expr::_Apply) {
|
||||
code.emplace_back(loc, Op::_Import, std::vector<var_idx_t>());
|
||||
auto tmp_vars = x->pre_compile(code);
|
||||
code.emplace_back(loc, Op::_Return, std::move(tmp_vars));
|
||||
code.emplace_back(loc, Op::_Nop); // This is neccessary to prevent SIGSEGV!
|
||||
// It is REQUIRED to execute "optimizations" as in func.cpp
|
||||
code.simplify_var_types();
|
||||
code.prune_unreachable_code();
|
||||
code.split_vars(true);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
code.compute_used_code_vars();
|
||||
code.fwd_analyze();
|
||||
code.prune_unreachable_code();
|
||||
}
|
||||
code.mark_noreturn();
|
||||
AsmOpList out_list(0, &code.vars);
|
||||
code.generate_code(out_list);
|
||||
if (out_list.list_.size() != 1) {
|
||||
lex.cur().error("precompiled expression must result in single operation");
|
||||
}
|
||||
auto op = out_list.list_[0];
|
||||
if (!op.is_const()) {
|
||||
lex.cur().error("precompiled expression must result in compilation time constant");
|
||||
}
|
||||
if (op.origin.is_null() || !op.origin->is_valid()) {
|
||||
lex.cur().error("precompiled expression did not result in a valid integer constant");
|
||||
}
|
||||
sym_def->value = new SymValConst{const_cnt++, op.origin};
|
||||
} else {
|
||||
lex.cur().error("integer or slice literal or constant expected");
|
||||
}
|
||||
}
|
||||
|
||||
FormalArgList parse_formal_args(Lexer& lex) {
|
||||
FormalArgList args;
|
||||
lex.expect('(', "formal argument list");
|
||||
|
@ -246,6 +326,18 @@ FormalArgList parse_formal_args(Lexer& lex) {
|
|||
return args;
|
||||
}
|
||||
|
||||
void parse_const_decls(Lexer& lex) {
|
||||
lex.expect(_Const);
|
||||
while (true) {
|
||||
parse_const_decl(lex);
|
||||
if (lex.tp() != ',') {
|
||||
break;
|
||||
}
|
||||
lex.expect(',');
|
||||
}
|
||||
lex.expect(';');
|
||||
}
|
||||
|
||||
TypeExpr* extract_total_arg_type(const FormalArgList& arg_list) {
|
||||
if (arg_list.empty()) {
|
||||
return TypeExpr::new_unit();
|
||||
|
@ -322,8 +414,6 @@ Expr* make_func_apply(Expr* fun, Expr* x) {
|
|||
return res;
|
||||
}
|
||||
|
||||
Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false);
|
||||
|
||||
// parse ( E { , E } ) | () | [ E { , E } ] | [] | id | num | _
|
||||
Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
||||
if (lex.tp() == '(' || lex.tp() == '[') {
|
||||
|
@ -388,6 +478,87 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
lex.next();
|
||||
return res;
|
||||
}
|
||||
if (t == Lexem::String) {
|
||||
std::string str = lex.cur().str;
|
||||
int str_type = lex.cur().val;
|
||||
Expr* res;
|
||||
switch (str_type) {
|
||||
case 0:
|
||||
case 's':
|
||||
case 'a':
|
||||
{
|
||||
res = new Expr{Expr::_SliceConst, lex.cur().loc};
|
||||
res->e_type = TypeExpr::new_atomic(_Slice);
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
case 'h':
|
||||
case 'H':
|
||||
case 'c':
|
||||
{
|
||||
res = new Expr{Expr::_Const, lex.cur().loc};
|
||||
res->e_type = TypeExpr::new_atomic(_Int);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
res = new Expr{Expr::_Const, lex.cur().loc};
|
||||
res->e_type = TypeExpr::new_atomic(_Int);
|
||||
lex.cur().error("invalid string type `" + std::string(1, static_cast<char>(str_type)) + "`");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
res->flags = Expr::_IsRvalue;
|
||||
switch (str_type) {
|
||||
case 0: {
|
||||
res->strval = td::hex_encode(str);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
res->strval = str;
|
||||
unsigned char buff[128];
|
||||
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), str.data(), str.data() + str.size());
|
||||
if (bits < 0) {
|
||||
lex.cur().error_at("Invalid hex bitstring constant `", "`");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'a': { // MsgAddressInt
|
||||
block::StdAddress a;
|
||||
if (a.parse_addr(str)) {
|
||||
res->strval = block::tlb::MsgAddressInt().pack_std_address(a)->as_bitslice().to_hex();
|
||||
} else {
|
||||
lex.cur().error_at("invalid standard address `", "`");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'u': {
|
||||
res->intval = td::hex_string_to_int256(td::hex_encode(str));
|
||||
if (!str.size()) {
|
||||
lex.cur().error("empty integer ascii-constant");
|
||||
}
|
||||
if (res->intval.is_null()) {
|
||||
lex.cur().error_at("too long integer ascii-constant `", "`");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'h':
|
||||
case 'H':
|
||||
{
|
||||
unsigned char hash[32];
|
||||
digest::hash_str<digest::SHA256>(hash, str.data(), str.size());
|
||||
res->intval = td::bits_to_refint(hash, (str_type == 'h') ? 32 : 256, false);
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
{
|
||||
res->intval = td::make_refint(td::crc32(td::Slice{str}));
|
||||
break;
|
||||
}
|
||||
}
|
||||
lex.next();
|
||||
return res;
|
||||
}
|
||||
if (t == '_') {
|
||||
Expr* res = new Expr{Expr::_Hole, lex.cur().loc};
|
||||
res->val = -1;
|
||||
|
@ -429,6 +600,25 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
lex.next();
|
||||
return res;
|
||||
}
|
||||
if (sym && dynamic_cast<SymValConst*>(sym->value)) {
|
||||
auto val = dynamic_cast<SymValConst*>(sym->value);
|
||||
Expr* res = new Expr{Expr::_None, lex.cur().loc};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
if (val->type == _Int) {
|
||||
res->cls = Expr::_Const;
|
||||
res->intval = val->get_int_value();
|
||||
}
|
||||
else if (val->type == _Slice) {
|
||||
res->cls = Expr::_SliceConst;
|
||||
res->strval = val->get_str_value();
|
||||
}
|
||||
else {
|
||||
lex.cur().error("Invalid symbolic constant type");
|
||||
}
|
||||
res->e_type = TypeExpr::new_atomic(val->type);
|
||||
lex.next();
|
||||
return res;
|
||||
}
|
||||
bool auto_apply = false;
|
||||
Expr* res = new Expr{Expr::_Var, lex.cur().loc};
|
||||
if (nv) {
|
||||
|
@ -1425,6 +1615,8 @@ bool parse_source(std::istream* is, src::FileDescr* fdescr) {
|
|||
parse_pragma(lex);
|
||||
} else if (lex.tp() == _Global) {
|
||||
parse_global_var_decls(lex);
|
||||
} else if (lex.tp() == _Const) {
|
||||
parse_const_decls(lex);
|
||||
} else {
|
||||
parse_func_def(lex);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue