mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	This will allow to easily implement camelCase wrappers aside stdlib, even without changing hashes of existing contracts. Also, stdlib renamings could be easily performed in the same manner, even with arguments reordered.
		
			
				
	
	
		
			1903 lines
		
	
	
	
		
			60 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1903 lines
		
	
	
	
		
			60 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|     This file is part of TON Blockchain Library.
 | |
| 
 | |
|     TON Blockchain Library is free software: you can redistribute it and/or modify
 | |
|     it under the terms of the GNU Lesser General Public License as published by
 | |
|     the Free Software Foundation, either version 2 of the License, or
 | |
|     (at your option) any later version.
 | |
| 
 | |
|     TON Blockchain Library is distributed in the hope that it will be useful,
 | |
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|     GNU Lesser General Public License for more details.
 | |
| 
 | |
|     You should have received a copy of the GNU Lesser General Public License
 | |
|     along with TON Blockchain Library.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
|     Copyright 2017-2020 Telegram Systems LLP
 | |
| */
 | |
| #include "func.h"
 | |
| #include "td/utils/crypto.h"
 | |
| #include "common/refint.h"
 | |
| #include "openssl/digest.hpp"
 | |
| #include "block/block.h"
 | |
| #include "block-parse.h"
 | |
| 
 | |
| namespace sym {
 | |
| 
 | |
| int compute_symbol_subclass(std::string str) {
 | |
|   using funC::IdSc;
 | |
|   if (str.size() < 2) {
 | |
|     return IdSc::undef;
 | |
|   } else if (str[0] == '.') {
 | |
|     return IdSc::dotid;
 | |
|   } else if (str[0] == '~') {
 | |
|     return IdSc::tildeid;
 | |
|   } else {
 | |
|     return IdSc::undef;
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace sym
 | |
| 
 | |
| namespace funC {
 | |
| using namespace std::literals::string_literals;
 | |
| using src::Lexer;
 | |
| using sym::symbols;
 | |
| using td::Ref;
 | |
| 
 | |
| inline bool is_dot_ident(sym_idx_t idx) {
 | |
|   return symbols.get_subclass(idx) == IdSc::dotid;
 | |
| }
 | |
| 
 | |
| inline bool is_tilde_ident(sym_idx_t idx) {
 | |
|   return symbols.get_subclass(idx) == IdSc::tildeid;
 | |
| }
 | |
| 
 | |
| inline bool is_special_ident(sym_idx_t idx) {
 | |
|   return symbols.get_subclass(idx) != IdSc::undef;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 
 | |
|  *   PARSE SOURCE
 | |
|  * 
 | |
|  */
 | |
| 
 | |
| // TE ::= TA | TA -> TE
 | |
| // TA ::= int | ... | cont | var | _ | () | ( TE { , TE } ) | [ TE { , TE } ]
 | |
| TypeExpr* parse_type(Lexer& lex);
 | |
| 
 | |
| TypeExpr* parse_type1(Lexer& lex) {
 | |
|   switch (lex.tp()) {
 | |
|     case _Int:
 | |
|       lex.next();
 | |
|       return TypeExpr::new_atomic(_Int);
 | |
|     case _Cell:
 | |
|       lex.next();
 | |
|       return TypeExpr::new_atomic(_Cell);
 | |
|     case _Slice:
 | |
|       lex.next();
 | |
|       return TypeExpr::new_atomic(_Slice);
 | |
|     case _Builder:
 | |
|       lex.next();
 | |
|       return TypeExpr::new_atomic(_Builder);
 | |
|     case _Cont:
 | |
|       lex.next();
 | |
|       return TypeExpr::new_atomic(_Cont);
 | |
|     case _Tuple:
 | |
|       lex.next();
 | |
|       return TypeExpr::new_atomic(_Tuple);
 | |
|     case _Var:
 | |
|     case '_':
 | |
|       lex.next();
 | |
|       return TypeExpr::new_hole();
 | |
|     case _Ident: {
 | |
|       auto sym = sym::lookup_symbol(lex.cur().val);
 | |
|       if (sym && dynamic_cast<SymValType*>(sym->value)) {
 | |
|         auto val = dynamic_cast<SymValType*>(sym->value);
 | |
|         lex.next();
 | |
|         return val->get_type();
 | |
|       }
 | |
|       lex.cur().error_at("`", "` is not a type identifier");
 | |
|     }
 | |
|   }
 | |
|   int c;
 | |
|   if (lex.tp() == '[') {
 | |
|     lex.next();
 | |
|     c = ']';
 | |
|   } else {
 | |
|     lex.expect('(');
 | |
|     c = ')';
 | |
|   }
 | |
|   if (lex.tp() == c) {
 | |
|     lex.next();
 | |
|     return c == ')' ? TypeExpr::new_unit() : TypeExpr::new_tuple({});
 | |
|   }
 | |
|   auto t1 = parse_type(lex);
 | |
|   if (lex.tp() == ')') {
 | |
|     lex.expect(c);
 | |
|     return t1;
 | |
|   }
 | |
|   std::vector<TypeExpr*> tlist{1, t1};
 | |
|   while (lex.tp() == ',') {
 | |
|     lex.next();
 | |
|     tlist.push_back(parse_type(lex));
 | |
|   }
 | |
|   lex.expect(c);
 | |
|   return c == ')' ? TypeExpr::new_tensor(std::move(tlist)) : TypeExpr::new_tuple(std::move(tlist));
 | |
| }
 | |
| 
 | |
| TypeExpr* parse_type(Lexer& lex) {
 | |
|   auto res = parse_type1(lex);
 | |
|   if (lex.tp() == _Mapsto) {
 | |
|     lex.next();
 | |
|     auto to = parse_type(lex);
 | |
|     return TypeExpr::new_map(res, to);
 | |
|   } else {
 | |
|     return res;
 | |
|   }
 | |
| }
 | |
| 
 | |
| FormalArg parse_formal_arg(Lexer& lex, int fa_idx) {
 | |
|   TypeExpr* arg_type = 0;
 | |
|   SrcLocation loc = lex.cur().loc;
 | |
|   if (lex.tp() == '_') {
 | |
|     lex.next();
 | |
|     if (lex.tp() == ',' || lex.tp() == ')') {
 | |
|       return std::make_tuple(TypeExpr::new_hole(), (SymDef*)nullptr, loc);
 | |
|     }
 | |
|     arg_type = TypeExpr::new_hole();
 | |
|     loc = lex.cur().loc;
 | |
|   } else if (lex.tp() != _Ident) {
 | |
|     arg_type = parse_type(lex);
 | |
|   } else {
 | |
|     auto sym = sym::lookup_symbol(lex.cur().val);
 | |
|     if (sym && dynamic_cast<SymValType*>(sym->value)) {
 | |
|       auto val = dynamic_cast<SymValType*>(sym->value);
 | |
|       lex.next();
 | |
|       arg_type = val->get_type();
 | |
|     } else {
 | |
|       arg_type = TypeExpr::new_hole();
 | |
|     }
 | |
|   }
 | |
|   if (lex.tp() == '_' || lex.tp() == ',' || lex.tp() == ')') {
 | |
|     if (lex.tp() == '_') {
 | |
|       loc = lex.cur().loc;
 | |
|       lex.next();
 | |
|     }
 | |
|     return std::make_tuple(arg_type, (SymDef*)nullptr, loc);
 | |
|   }
 | |
|   if (lex.tp() != _Ident) {
 | |
|     lex.expect(_Ident, "formal parameter name");
 | |
|   }
 | |
|   loc = lex.cur().loc;
 | |
|   if (prohibited_var_names.count(sym::symbols.get_name(lex.cur().val))) {
 | |
|     throw src::ParseError{
 | |
|         loc, PSTRING() << "symbol `" << sym::symbols.get_name(lex.cur().val) << "` cannot be redefined as a variable"};
 | |
|   }
 | |
|   SymDef* new_sym_def = sym::define_symbol(lex.cur().val, true, loc);
 | |
|   if (!new_sym_def) {
 | |
|     lex.cur().error_at("cannot define symbol `", "`");
 | |
|   }
 | |
|   if (new_sym_def->value) {
 | |
|     lex.cur().error_at("redefined formal parameter `", "`");
 | |
|   }
 | |
|   new_sym_def->value = new SymVal{SymVal::_Param, fa_idx, arg_type};
 | |
|   lex.next();
 | |
|   return std::make_tuple(arg_type, new_sym_def, loc);
 | |
| }
 | |
| 
 | |
| void parse_global_var_decl(Lexer& lex) {
 | |
|   TypeExpr* var_type = 0;
 | |
|   SrcLocation loc = lex.cur().loc;
 | |
|   if (lex.tp() == '_') {
 | |
|     lex.next();
 | |
|     var_type = TypeExpr::new_hole();
 | |
|     loc = lex.cur().loc;
 | |
|   } else if (lex.tp() != _Ident) {
 | |
|     var_type = parse_type(lex);
 | |
|   } else {
 | |
|     auto sym = sym::lookup_symbol(lex.cur().val);
 | |
|     if (sym && dynamic_cast<SymValType*>(sym->value)) {
 | |
|       auto val = dynamic_cast<SymValType*>(sym->value);
 | |
|       lex.next();
 | |
|       var_type = val->get_type();
 | |
|     } else {
 | |
|       var_type = TypeExpr::new_hole();
 | |
|     }
 | |
|   }
 | |
|   if (lex.tp() != _Ident) {
 | |
|     lex.expect(_Ident, "global variable 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) {
 | |
|     auto val = dynamic_cast<SymValGlobVar*>(sym_def->value);
 | |
|     if (!val) {
 | |
|       lex.cur().error_at("symbol `", "` cannot be redefined as a global variable");
 | |
|     }
 | |
|     try {
 | |
|       unify(var_type, val->sym_type);
 | |
|     } catch (UnifyError& ue) {
 | |
|       std::ostringstream os;
 | |
|       os << "cannot unify new type " << var_type << " of global variable `" << sym_def->name()
 | |
|          << "` with its previous type " << val->sym_type << ": " << ue;
 | |
|       lex.cur().error(os.str());
 | |
|     }
 | |
|   } else {
 | |
|     sym_def->value = new SymValGlobVar{glob_var_cnt++, var_type};
 | |
| #ifdef FUNC_DEBUG
 | |
|     dynamic_cast<SymValGlobVar*>(sym_def->value)->name = lex.cur().str;
 | |
| #endif
 | |
|     glob_vars.push_back(sym_def);
 | |
|   }
 | |
|   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 `", "`");
 | |
|   }
 | |
|   Lexem ident = lex.cur();
 | |
|   lex.next();
 | |
|   if (lex.tp() != '=') {
 | |
|     lex.cur().error_at("expected = instead of ", "");
 | |
|   }
 | |
|   lex.next();
 | |
|   CodeBlob code;
 | |
|   if (pragma_allow_post_modification.enabled()) {
 | |
|     code.flags |= CodeBlob::_AllowPostModification;
 | |
|   }
 | |
|   if (pragma_compute_asm_ltr.enabled()) {
 | |
|     code.flags |= CodeBlob::_ComputeAsmLtr;
 | |
|   }
 | |
|   // 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");
 | |
|   }
 | |
|   SymValConst* new_value = nullptr;
 | |
|   if (x->cls == Expr::_Const) { // Integer constant
 | |
|     new_value = new SymValConst{const_cnt++, x->intval};
 | |
|   } else if (x->cls == Expr::_SliceConst) { // Slice constant (string)
 | |
|     new_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");
 | |
|     }
 | |
|     new_value = new SymValConst{const_cnt++, op.origin};
 | |
|   } else {
 | |
|     lex.cur().error("integer or slice literal or constant expected");
 | |
|   }
 | |
|   if (sym_def->value) {
 | |
|     SymValConst* old_value = dynamic_cast<SymValConst*>(sym_def->value);
 | |
|     Keyword new_type = new_value->get_type();
 | |
|     if (!old_value || old_value->get_type() != new_type ||
 | |
|         (new_type == _Int && *old_value->get_int_value() != *new_value->get_int_value()) ||
 | |
|         (new_type == _Slice && old_value->get_str_value() != new_value->get_str_value())) {
 | |
|       ident.error_at("global symbol `", "` already exists");
 | |
|     }
 | |
|   }
 | |
|   sym_def->value = new_value;
 | |
| }
 | |
| 
 | |
| FormalArgList parse_formal_args(Lexer& lex) {
 | |
|   FormalArgList args;
 | |
|   lex.expect('(', "formal argument list");
 | |
|   if (lex.tp() == ')') {
 | |
|     lex.next();
 | |
|     return args;
 | |
|   }
 | |
|   int fa_idx = 0;
 | |
|   args.push_back(parse_formal_arg(lex, fa_idx++));
 | |
|   while (lex.tp() == ',') {
 | |
|     lex.next();
 | |
|     args.push_back(parse_formal_arg(lex, fa_idx++));
 | |
|   }
 | |
|   lex.expect(')');
 | |
|   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();
 | |
|   }
 | |
|   if (arg_list.size() == 1) {
 | |
|     return std::get<0>(arg_list[0]);
 | |
|   }
 | |
|   std::vector<TypeExpr*> type_list;
 | |
|   for (auto& x : arg_list) {
 | |
|     type_list.push_back(std::get<0>(x));
 | |
|   }
 | |
|   return TypeExpr::new_tensor(std::move(type_list));
 | |
| }
 | |
| 
 | |
| void parse_global_var_decls(Lexer& lex) {
 | |
|   lex.expect(_Global);
 | |
|   while (true) {
 | |
|     parse_global_var_decl(lex);
 | |
|     if (lex.tp() != ',') {
 | |
|       break;
 | |
|     }
 | |
|     lex.expect(',');
 | |
|   }
 | |
|   lex.expect(';');
 | |
| }
 | |
| 
 | |
| SymValCodeFunc* make_new_glob_func(SymDef* func_sym, TypeExpr* func_type, bool impure = false) {
 | |
|   SymValCodeFunc* res = new SymValCodeFunc{glob_func_cnt, func_type, impure};
 | |
| #ifdef FUNC_DEBUG
 | |
|   res->name = func_sym->name();
 | |
| #endif
 | |
|   func_sym->value = res;
 | |
|   glob_func.push_back(func_sym);
 | |
|   glob_func_cnt++;
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| bool check_global_func(const Lexem& cur, sym_idx_t func_name = 0) {
 | |
|   if (!func_name) {
 | |
|     func_name = cur.val;
 | |
|   }
 | |
|   SymDef* def = sym::lookup_symbol(func_name);
 | |
|   if (!def) {
 | |
|     cur.loc.show_error(std::string{"undefined function `"} + symbols.get_name(func_name) +
 | |
|                        "`, defining a global function of unknown type");
 | |
|     def = sym::define_global_symbol(func_name, 0, cur.loc);
 | |
|     func_assert(def && "cannot define global function");
 | |
|     ++undef_func_cnt;
 | |
|     make_new_glob_func(def, TypeExpr::new_func());  // was: ... ::new_func()
 | |
|     return true;
 | |
|   }
 | |
|   SymVal* val = dynamic_cast<SymVal*>(def->value);
 | |
|   if (!val) {
 | |
|     cur.error(std::string{"symbol `"} + symbols.get_name(func_name) + "` has no value and no type");
 | |
|     return false;
 | |
|   } else if (!val->get_type()) {
 | |
|     cur.error(std::string{"symbol `"} + symbols.get_name(func_name) + "` has no type, possibly not a function");
 | |
|     return false;
 | |
|   } else {
 | |
|     return true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| Expr* make_func_apply(Expr* fun, Expr* x) {
 | |
|   Expr* res{nullptr};
 | |
|   if (fun->cls == Expr::_GlobFunc) {
 | |
|     if (x->cls == Expr::_Tensor) {
 | |
|       res = new Expr{Expr::_Apply, fun->sym, x->args};
 | |
|     } else {
 | |
|       res = new Expr{Expr::_Apply, fun->sym, {x}};
 | |
|     }
 | |
|     res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure);
 | |
|   } else {
 | |
|     res = new Expr{Expr::_VarApply, {fun, x}};
 | |
|     res->flags = Expr::_IsRvalue;
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| // parse ( E { , E } ) | () | [ E { , E } ] | [] | id | num | _
 | |
| Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   if (lex.tp() == '(' || lex.tp() == '[') {
 | |
|     bool tf = (lex.tp() == '[');
 | |
|     int clbr = (tf ? ']' : ')');
 | |
|     SrcLocation loc{lex.cur().loc};
 | |
|     lex.next();
 | |
|     if (lex.tp() == clbr) {
 | |
|       lex.next();
 | |
|       Expr* res = new Expr{Expr::_Tensor, {}};
 | |
|       res->flags = Expr::_IsRvalue;
 | |
|       res->here = loc;
 | |
|       res->e_type = TypeExpr::new_unit();
 | |
|       if (tf) {
 | |
|         res = new Expr{Expr::_MkTuple, {res}};
 | |
|         res->flags = Expr::_IsRvalue;
 | |
|         res->here = loc;
 | |
|         res->e_type = TypeExpr::new_tuple(res->args.at(0)->e_type);
 | |
|       }
 | |
|       return res;
 | |
|     }
 | |
|     Expr* res = parse_expr(lex, code, nv);
 | |
|     if (lex.tp() == ')') {
 | |
|       lex.expect(clbr);
 | |
|       return res;
 | |
|     }
 | |
|     std::vector<TypeExpr*> type_list;
 | |
|     type_list.push_back(res->e_type);
 | |
|     int f = res->flags;
 | |
|     res = new Expr{Expr::_Tensor, {res}};
 | |
|     while (lex.tp() == ',') {
 | |
|       lex.next();
 | |
|       auto x = parse_expr(lex, code, nv);
 | |
|       res->pb_arg(x);
 | |
|       if ((f ^ x->flags) & Expr::_IsType) {
 | |
|         lex.cur().error("mixing type and non-type expressions inside the same tuple");
 | |
|       }
 | |
|       f &= x->flags;
 | |
|       type_list.push_back(x->e_type);
 | |
|     }
 | |
|     res->here = loc;
 | |
|     res->flags = f;
 | |
|     res->e_type = TypeExpr::new_tensor(std::move(type_list), !tf);
 | |
|     if (tf) {
 | |
|       res = new Expr{Expr::_MkTuple, {res}};
 | |
|       res->flags = f;
 | |
|       res->here = loc;
 | |
|       res->e_type = TypeExpr::new_tuple(res->args.at(0)->e_type);
 | |
|     }
 | |
|     lex.expect(clbr);
 | |
|     return res;
 | |
|   }
 | |
|   int t = lex.tp();
 | |
|   if (t == Lexem::Number) {
 | |
|     Expr* res = new Expr{Expr::_Const, lex.cur().loc};
 | |
|     res->flags = Expr::_IsRvalue;
 | |
|     res->intval = td::string_to_int256(lex.cur().str);
 | |
|     if (res->intval.is_null() || !res->intval->signed_fits_bits(257)) {
 | |
|       lex.cur().error_at("invalid integer constant `", "`");
 | |
|     }
 | |
|     res->e_type = TypeExpr::new_atomic(_Int);
 | |
|     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;
 | |
|     res->flags = Expr::_IsLvalue;
 | |
|     res->e_type = TypeExpr::new_hole();
 | |
|     lex.next();
 | |
|     return res;
 | |
|   }
 | |
|   if (t == _Var) {
 | |
|     Expr* res = new Expr{Expr::_Type, lex.cur().loc};
 | |
|     res->flags = Expr::_IsType;
 | |
|     res->e_type = TypeExpr::new_hole();
 | |
|     lex.next();
 | |
|     return res;
 | |
|   }
 | |
|   if (t == _Int || t == _Cell || t == _Slice || t == _Builder || t == _Cont || t == _Type || t == _Tuple) {
 | |
|     Expr* res = new Expr{Expr::_Type, lex.cur().loc};
 | |
|     res->flags = Expr::_IsType;
 | |
|     res->e_type = TypeExpr::new_atomic(t);
 | |
|     lex.next();
 | |
|     return res;
 | |
|   }
 | |
|   if (t == _Ident) {
 | |
|     auto sym = sym::lookup_symbol(lex.cur().val);
 | |
|     if (sym && dynamic_cast<SymValType*>(sym->value)) {
 | |
|       auto val = dynamic_cast<SymValType*>(sym->value);
 | |
|       Expr* res = new Expr{Expr::_Type, lex.cur().loc};
 | |
|       res->flags = Expr::_IsType;
 | |
|       res->e_type = val->get_type();
 | |
|       lex.next();
 | |
|       return res;
 | |
|     }
 | |
|     if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
 | |
|       auto val = dynamic_cast<SymValGlobVar*>(sym->value);
 | |
|       Expr* res = new Expr{Expr::_GlobVar, lex.cur().loc};
 | |
|       res->e_type = val->get_type();
 | |
|       res->sym = sym;
 | |
|       res->flags = Expr::_IsLvalue | Expr::_IsRvalue | Expr::_IsImpure;
 | |
|       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) {
 | |
|       res->val = ~lex.cur().val;
 | |
|       res->e_type = TypeExpr::new_hole();
 | |
|       res->flags = Expr::_IsLvalue;
 | |
|       // std::cerr << "defined new variable " << lex.cur().str << " : " << res->e_type << std::endl;
 | |
|     } else {
 | |
|       if (!sym) {
 | |
|         check_global_func(lex.cur());
 | |
|         sym = sym::lookup_symbol(lex.cur().val);
 | |
|       }
 | |
|       res->sym = sym;
 | |
|       SymVal* val = nullptr;
 | |
|       if (sym) {
 | |
|         val = dynamic_cast<SymVal*>(sym->value);
 | |
|       }
 | |
|       if (!val) {
 | |
|         lex.cur().error_at("undefined identifier `", "`");
 | |
|       } else if (val->type == SymVal::_Func) {
 | |
|         res->e_type = val->get_type();
 | |
|         res->cls = Expr::_GlobFunc;
 | |
|         auto_apply = val->auto_apply;
 | |
|       } else if (val->idx < 0) {
 | |
|         lex.cur().error_at("accessing variable `", "` being defined");
 | |
|       } else {
 | |
|         res->val = val->idx;
 | |
|         res->e_type = val->get_type();
 | |
|         // std::cerr << "accessing variable " << lex.cur().str << " : " << res->e_type << std::endl;
 | |
|       }
 | |
|       // std::cerr << "accessing symbol " << lex.cur().str << " : " << res->e_type << (val->impure ? " (impure)" : " (pure)") << std::endl;
 | |
|       res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (val->impure ? Expr::_IsImpure : 0);
 | |
|     }
 | |
|     if (auto_apply) {
 | |
|       int impure = res->flags & Expr::_IsImpure;
 | |
|       delete res;
 | |
|       res = new Expr{Expr::_Apply, sym, {}};
 | |
|       res->flags = Expr::_IsRvalue | impure;
 | |
|     }
 | |
|     res->deduce_type(lex.cur());
 | |
|     lex.next();
 | |
|     return res;
 | |
|   }
 | |
|   lex.expect(Lexem::Ident);
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| // parse E { E }
 | |
| Expr* parse_expr90(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   Expr* res = parse_expr100(lex, code, nv);
 | |
|   while (lex.tp() == '(' || lex.tp() == '[' || (lex.tp() == _Ident && !is_special_ident(lex.cur().val))) {
 | |
|     if (res->is_type()) {
 | |
|       Expr* x = parse_expr100(lex, code, true);
 | |
|       x->chk_lvalue(lex.cur());  // chk_lrvalue() ?
 | |
|       TypeExpr* tp = res->e_type;
 | |
|       delete res;
 | |
|       res = new Expr{Expr::_TypeApply, {x}};
 | |
|       res->e_type = tp;
 | |
|       res->here = lex.cur().loc;
 | |
|       try {
 | |
|         unify(res->e_type, x->e_type);
 | |
|       } catch (UnifyError& ue) {
 | |
|         std::ostringstream os;
 | |
|         os << "cannot transform expression of type " << x->e_type << " to explicitly requested type " << res->e_type
 | |
|            << ": " << ue;
 | |
|         lex.cur().error(os.str());
 | |
|       }
 | |
|       res->flags = x->flags;
 | |
|     } else {
 | |
|       Expr* x = parse_expr100(lex, code, false);
 | |
|       x->chk_rvalue(lex.cur());
 | |
|       res = make_func_apply(res, x);
 | |
|       res->here = lex.cur().loc;
 | |
|       res->deduce_type(lex.cur());
 | |
|     }
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| // parse E { .method E | ~method E }
 | |
| Expr* parse_expr80(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   Expr* res = parse_expr90(lex, code, nv);
 | |
|   while (lex.tp() == _Ident && is_special_ident(lex.cur().val)) {
 | |
|     auto modify = is_tilde_ident(lex.cur().val);
 | |
|     auto obj = res;
 | |
|     if (modify) {
 | |
|       obj->chk_lvalue(lex.cur());
 | |
|     } else {
 | |
|       obj->chk_rvalue(lex.cur());
 | |
|     }
 | |
|     auto loc = lex.cur().loc;
 | |
|     auto name = lex.cur().val;
 | |
|     auto sym = sym::lookup_symbol(name);
 | |
|     if (!sym || !dynamic_cast<SymValFunc*>(sym->value)) {
 | |
|       auto name1 = symbols.lookup(lex.cur().str.substr(1));
 | |
|       if (name1) {
 | |
|         auto sym1 = sym::lookup_symbol(name1);
 | |
|         if (sym1 && dynamic_cast<SymValFunc*>(sym1->value)) {
 | |
|           name = name1;
 | |
|           sym = sym1;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     check_global_func(lex.cur(), name);
 | |
|     if (verbosity >= 2) {
 | |
|       std::cerr << "using symbol `" << symbols.get_name(name) << "` for method call of " << lex.cur().str << std::endl;
 | |
|     }
 | |
|     sym = sym::lookup_symbol(name);
 | |
|     SymValFunc* val = sym ? dynamic_cast<SymValFunc*>(sym->value) : nullptr;
 | |
|     if (!val) {
 | |
|       lex.cur().error_at("undefined method identifier `", "`");
 | |
|     }
 | |
|     lex.next();
 | |
|     auto x = parse_expr100(lex, code, false);
 | |
|     x->chk_rvalue(lex.cur());
 | |
|     if (x->cls == Expr::_Tensor) {
 | |
|       res = new Expr{Expr::_Apply, name, {obj}};
 | |
|       res->args.insert(res->args.end(), x->args.begin(), x->args.end());
 | |
|     } else {
 | |
|       res = new Expr{Expr::_Apply, name, {obj, x}};
 | |
|     }
 | |
|     res->here = loc;
 | |
|     res->flags = Expr::_IsRvalue | (val->impure ? Expr::_IsImpure : 0);
 | |
|     res->deduce_type(lex.cur());
 | |
|     if (modify) {
 | |
|       auto tmp = res;
 | |
|       res = new Expr{Expr::_LetFirst, {obj->copy(), tmp}};
 | |
|       res->here = loc;
 | |
|       res->flags = tmp->flags;
 | |
|       res->set_val(name);
 | |
|       res->deduce_type(lex.cur());
 | |
|     }
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| // parse [ ~ ] E
 | |
| Expr* parse_expr75(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   if (lex.tp() == '~') {
 | |
|     sym_idx_t name = symbols.lookup_add("~_");
 | |
|     check_global_func(lex.cur(), name);
 | |
|     SrcLocation loc{lex.cur().loc};
 | |
|     lex.next();
 | |
|     auto x = parse_expr80(lex, code, false);
 | |
|     x->chk_rvalue(lex.cur());
 | |
|     auto res = new Expr{Expr::_Apply, name, {x}};
 | |
|     res->here = loc;
 | |
|     res->set_val('~');
 | |
|     res->flags = Expr::_IsRvalue;
 | |
|     res->deduce_type(lex.cur());
 | |
|     return res;
 | |
|   } else {
 | |
|     return parse_expr80(lex, code, nv);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // parse E { (* | / | % | /% ) E }
 | |
| Expr* parse_expr30(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   Expr* res = parse_expr75(lex, code, nv);
 | |
|   while (lex.tp() == '*' || lex.tp() == '/' || lex.tp() == '%' || lex.tp() == _DivMod || lex.tp() == _DivC ||
 | |
|          lex.tp() == _DivR || lex.tp() == _ModC || lex.tp() == _ModR || lex.tp() == '&') {
 | |
|     res->chk_rvalue(lex.cur());
 | |
|     int t = lex.tp();
 | |
|     sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_");
 | |
|     SrcLocation loc{lex.cur().loc};
 | |
|     check_global_func(lex.cur(), name);
 | |
|     lex.next();
 | |
|     auto x = parse_expr75(lex, code, false);
 | |
|     x->chk_rvalue(lex.cur());
 | |
|     res = new Expr{Expr::_Apply, name, {res, x}};
 | |
|     res->here = loc;
 | |
|     res->set_val(t);
 | |
|     res->flags = Expr::_IsRvalue;
 | |
|     res->deduce_type(lex.cur());
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| // parse [-] E { (+ | - | `|` | ^) E }
 | |
| Expr* parse_expr20(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   Expr* res;
 | |
|   int t = lex.tp();
 | |
|   if (t == '-') {
 | |
|     sym_idx_t name = symbols.lookup_add("-_");
 | |
|     check_global_func(lex.cur(), name);
 | |
|     SrcLocation loc{lex.cur().loc};
 | |
|     lex.next();
 | |
|     auto x = parse_expr30(lex, code, false);
 | |
|     x->chk_rvalue(lex.cur());
 | |
|     res = new Expr{Expr::_Apply, name, {x}};
 | |
|     res->here = loc;
 | |
|     res->set_val(t);
 | |
|     res->flags = Expr::_IsRvalue;
 | |
|     res->deduce_type(lex.cur());
 | |
|   } else {
 | |
|     res = parse_expr30(lex, code, nv);
 | |
|   }
 | |
|   while (lex.tp() == '-' || lex.tp() == '+' || lex.tp() == '|' || lex.tp() == '^') {
 | |
|     res->chk_rvalue(lex.cur());
 | |
|     t = lex.tp();
 | |
|     sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_");
 | |
|     check_global_func(lex.cur(), name);
 | |
|     SrcLocation loc{lex.cur().loc};
 | |
|     lex.next();
 | |
|     auto x = parse_expr30(lex, code, false);
 | |
|     x->chk_rvalue(lex.cur());
 | |
|     res = new Expr{Expr::_Apply, name, {res, x}};
 | |
|     res->here = loc;
 | |
|     res->set_val(t);
 | |
|     res->flags = Expr::_IsRvalue;
 | |
|     res->deduce_type(lex.cur());
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| // parse E { ( << | >> | >>~ | >>^ ) E }
 | |
| Expr* parse_expr17(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   Expr* res = parse_expr20(lex, code, nv);
 | |
|   while (lex.tp() == _Lshift || lex.tp() == _Rshift || lex.tp() == _RshiftC || lex.tp() == _RshiftR) {
 | |
|     res->chk_rvalue(lex.cur());
 | |
|     int t = lex.tp();
 | |
|     sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_");
 | |
|     check_global_func(lex.cur(), name);
 | |
|     SrcLocation loc{lex.cur().loc};
 | |
|     lex.next();
 | |
|     auto x = parse_expr20(lex, code, false);
 | |
|     x->chk_rvalue(lex.cur());
 | |
|     res = new Expr{Expr::_Apply, name, {res, x}};
 | |
|     res->here = loc;
 | |
|     res->set_val(t);
 | |
|     res->flags = Expr::_IsRvalue;
 | |
|     res->deduce_type(lex.cur());
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| // parse E [ (== | < | > | <= | >= | != | <=> ) E ]
 | |
| Expr* parse_expr15(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   Expr* res = parse_expr17(lex, code, nv);
 | |
|   if (lex.tp() == _Eq || lex.tp() == '<' || lex.tp() == '>' || lex.tp() == _Leq || lex.tp() == _Geq ||
 | |
|       lex.tp() == _Neq || lex.tp() == _Spaceship) {
 | |
|     res->chk_rvalue(lex.cur());
 | |
|     int t = lex.tp();
 | |
|     sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_");
 | |
|     check_global_func(lex.cur(), name);
 | |
|     SrcLocation loc{lex.cur().loc};
 | |
|     lex.next();
 | |
|     auto x = parse_expr17(lex, code, false);
 | |
|     x->chk_rvalue(lex.cur());
 | |
|     res = new Expr{Expr::_Apply, name, {res, x}};
 | |
|     res->here = loc;
 | |
|     res->set_val(t);
 | |
|     res->flags = Expr::_IsRvalue;
 | |
|     res->deduce_type(lex.cur());
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| // parse E [ ? E : E ]
 | |
| Expr* parse_expr13(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   Expr* res = parse_expr15(lex, code, nv);
 | |
|   if (lex.tp() == '?') {
 | |
|     res->chk_rvalue(lex.cur());
 | |
|     SrcLocation loc{lex.cur().loc};
 | |
|     lex.next();
 | |
|     auto x = parse_expr(lex, code, false);
 | |
|     x->chk_rvalue(lex.cur());
 | |
|     lex.expect(':');
 | |
|     auto y = parse_expr13(lex, code, false);
 | |
|     y->chk_rvalue(lex.cur());
 | |
|     res = new Expr{Expr::_CondExpr, {res, x, y}};
 | |
|     res->here = loc;
 | |
|     res->flags = Expr::_IsRvalue;
 | |
|     res->deduce_type(lex.cur());
 | |
|   }
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| // parse LE1 (= | += | -= | ... ) E2
 | |
| Expr* parse_expr10(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   auto x = parse_expr13(lex, code, nv);
 | |
|   int t = lex.tp();
 | |
|   if (t == _PlusLet || t == _MinusLet || t == _TimesLet || t == _DivLet || t == _DivRLet || t == _DivCLet ||
 | |
|       t == _ModLet || t == _ModCLet || t == _ModRLet || t == _LshiftLet || t == _RshiftLet || t == _RshiftCLet ||
 | |
|       t == _RshiftRLet || t == _AndLet || t == _OrLet || t == _XorLet) {
 | |
|     x->chk_lvalue(lex.cur());
 | |
|     x->chk_rvalue(lex.cur());
 | |
|     sym_idx_t name = symbols.lookup_add(std::string{"^_"} + lex.cur().str + "_");
 | |
|     check_global_func(lex.cur(), name);
 | |
|     SrcLocation loc{lex.cur().loc};
 | |
|     lex.next();
 | |
|     auto y = parse_expr10(lex, code, false);
 | |
|     y->chk_rvalue(lex.cur());
 | |
|     Expr* z = new Expr{Expr::_Apply, name, {x, y}};
 | |
|     z->here = loc;
 | |
|     z->set_val(t);
 | |
|     z->flags = Expr::_IsRvalue;
 | |
|     z->deduce_type(lex.cur());
 | |
|     Expr* res = new Expr{Expr::_Letop, {x->copy(), z}};
 | |
|     res->here = loc;
 | |
|     res->flags = (x->flags & ~Expr::_IsType) | Expr::_IsRvalue;
 | |
|     res->set_val(t);
 | |
|     res->deduce_type(lex.cur());
 | |
|     return res;
 | |
|   } else if (t == '=') {
 | |
|     x->chk_lvalue(lex.cur());
 | |
|     SrcLocation loc{lex.cur().loc};
 | |
|     lex.next();
 | |
|     auto y = parse_expr10(lex, code, false);
 | |
|     y->chk_rvalue(lex.cur());
 | |
|     x->predefine_vars();
 | |
|     x->define_new_vars(code);
 | |
|     Expr* res = new Expr{Expr::_Letop, {x, y}};
 | |
|     res->here = loc;
 | |
|     res->flags = (x->flags & ~Expr::_IsType) | Expr::_IsRvalue;
 | |
|     res->set_val(t);
 | |
|     res->deduce_type(lex.cur());
 | |
|     return res;
 | |
|   } else {
 | |
|     return x;
 | |
|   }
 | |
| }
 | |
| 
 | |
| Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv) {
 | |
|   return parse_expr10(lex, code, nv);
 | |
| }
 | |
| 
 | |
| namespace blk_fl {
 | |
| enum { end = 1, ret = 2, empty = 4 };
 | |
| typedef int val;
 | |
| constexpr val init = end | empty;
 | |
| void combine(val& x, const val y) {
 | |
|   x |= y & ret;
 | |
|   x &= y | ~(end | empty);
 | |
| }
 | |
| void combine_parallel(val& x, const val y) {
 | |
|   x &= y | ~(ret | empty);
 | |
|   x |= y & end;
 | |
| }
 | |
| }  // namespace blk_fl
 | |
| 
 | |
| blk_fl::val parse_return_stmt(Lexer& lex, CodeBlob& code) {
 | |
|   auto expr = parse_expr(lex, code);
 | |
|   expr->chk_rvalue(lex.cur());
 | |
|   try {
 | |
|     // std::cerr << "in return: ";
 | |
|     unify(expr->e_type, code.ret_type);
 | |
|   } catch (UnifyError& ue) {
 | |
|     std::ostringstream os;
 | |
|     os << "previous function return type " << code.ret_type
 | |
|        << " cannot be unified with return statement expression type " << expr->e_type << ": " << ue;
 | |
|     lex.cur().error(os.str());
 | |
|   }
 | |
|   std::vector<var_idx_t> tmp_vars = expr->pre_compile(code);
 | |
|   code.emplace_back(lex.cur().loc, Op::_Return, std::move(tmp_vars));
 | |
|   lex.expect(';');
 | |
|   return blk_fl::ret;
 | |
| }
 | |
| 
 | |
| blk_fl::val parse_implicit_ret_stmt(Lexer& lex, CodeBlob& code) {
 | |
|   auto ret_type = TypeExpr::new_unit();
 | |
|   try {
 | |
|     // std::cerr << "in implicit return: ";
 | |
|     unify(ret_type, code.ret_type);
 | |
|   } catch (UnifyError& ue) {
 | |
|     std::ostringstream os;
 | |
|     os << "previous function return type " << code.ret_type
 | |
|        << " cannot be unified with implicit end-of-block return type " << ret_type << ": " << ue;
 | |
|     lex.cur().error(os.str());
 | |
|   }
 | |
|   code.emplace_back(lex.cur().loc, Op::_Return);
 | |
|   return blk_fl::ret;
 | |
| }
 | |
| 
 | |
| blk_fl::val parse_stmt(Lexer& lex, CodeBlob& code);
 | |
| 
 | |
| blk_fl::val parse_block_stmt(Lexer& lex, CodeBlob& code, bool no_new_scope = false) {
 | |
|   lex.expect('{');
 | |
|   if (!no_new_scope) {
 | |
|     sym::open_scope(lex);
 | |
|   }
 | |
|   blk_fl::val res = blk_fl::init;
 | |
|   bool warned = false;
 | |
|   while (lex.tp() != '}') {
 | |
|     if (!(res & blk_fl::end) && !warned) {
 | |
|       lex.cur().loc.show_warning("unreachable code");
 | |
|       warned = true;
 | |
|     }
 | |
|     blk_fl::combine(res, parse_stmt(lex, code));
 | |
|   }
 | |
|   if (!no_new_scope) {
 | |
|     sym::close_scope(lex);
 | |
|   }
 | |
|   lex.expect('}');
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| blk_fl::val parse_repeat_stmt(Lexer& lex, CodeBlob& code) {
 | |
|   SrcLocation loc{lex.cur().loc};
 | |
|   lex.expect(_Repeat);
 | |
|   auto expr = parse_expr(lex, code);
 | |
|   expr->chk_rvalue(lex.cur());
 | |
|   auto cnt_type = TypeExpr::new_atomic(_Int);
 | |
|   try {
 | |
|     unify(expr->e_type, cnt_type);
 | |
|   } catch (UnifyError& ue) {
 | |
|     std::ostringstream os;
 | |
|     os << "repeat count value of type " << expr->e_type << " is not an integer: " << ue;
 | |
|     lex.cur().error(os.str());
 | |
|   }
 | |
|   std::vector<var_idx_t> tmp_vars = expr->pre_compile(code);
 | |
|   if (tmp_vars.size() != 1) {
 | |
|     lex.cur().error("repeat count value is not a singleton");
 | |
|   }
 | |
|   Op& repeat_op = code.emplace_back(loc, Op::_Repeat, tmp_vars);
 | |
|   code.push_set_cur(repeat_op.block0);
 | |
|   blk_fl::val res = parse_block_stmt(lex, code);
 | |
|   code.close_pop_cur(lex.cur().loc);
 | |
|   return res | blk_fl::end;
 | |
| }
 | |
| 
 | |
| blk_fl::val parse_while_stmt(Lexer& lex, CodeBlob& code) {
 | |
|   SrcLocation loc{lex.cur().loc};
 | |
|   lex.expect(_While);
 | |
|   auto expr = parse_expr(lex, code);
 | |
|   expr->chk_rvalue(lex.cur());
 | |
|   auto cnt_type = TypeExpr::new_atomic(_Int);
 | |
|   try {
 | |
|     unify(expr->e_type, cnt_type);
 | |
|   } catch (UnifyError& ue) {
 | |
|     std::ostringstream os;
 | |
|     os << "while condition value of type " << expr->e_type << " is not an integer: " << ue;
 | |
|     lex.cur().error(os.str());
 | |
|   }
 | |
|   Op& while_op = code.emplace_back(loc, Op::_While);
 | |
|   code.push_set_cur(while_op.block0);
 | |
|   while_op.left = expr->pre_compile(code);
 | |
|   code.close_pop_cur(lex.cur().loc);
 | |
|   if (while_op.left.size() != 1) {
 | |
|     lex.cur().error("while condition value is not a singleton");
 | |
|   }
 | |
|   code.push_set_cur(while_op.block1);
 | |
|   blk_fl::val res1 = parse_block_stmt(lex, code);
 | |
|   code.close_pop_cur(lex.cur().loc);
 | |
|   return res1 | blk_fl::end;
 | |
| }
 | |
| 
 | |
| blk_fl::val parse_do_stmt(Lexer& lex, CodeBlob& code) {
 | |
|   Op& while_op = code.emplace_back(lex.cur().loc, Op::_Until);
 | |
|   lex.expect(_Do);
 | |
|   code.push_set_cur(while_op.block0);
 | |
|   sym::open_scope(lex);
 | |
|   blk_fl::val res = parse_block_stmt(lex, code, true);
 | |
|   lex.expect(_Until);
 | |
|   auto expr = parse_expr(lex, code);
 | |
|   expr->chk_rvalue(lex.cur());
 | |
|   sym::close_scope(lex);
 | |
|   auto cnt_type = TypeExpr::new_atomic(_Int);
 | |
|   try {
 | |
|     unify(expr->e_type, cnt_type);
 | |
|   } catch (UnifyError& ue) {
 | |
|     std::ostringstream os;
 | |
|     os << "`until` condition value of type " << expr->e_type << " is not an integer: " << ue;
 | |
|     lex.cur().error(os.str());
 | |
|   }
 | |
|   while_op.left = expr->pre_compile(code);
 | |
|   code.close_pop_cur(lex.cur().loc);
 | |
|   if (while_op.left.size() != 1) {
 | |
|     lex.cur().error("`until` condition value is not a singleton");
 | |
|   }
 | |
|   return res & ~blk_fl::empty;
 | |
| }
 | |
| 
 | |
| blk_fl::val parse_try_catch_stmt(Lexer& lex, CodeBlob& code) {
 | |
|   code.require_callxargs = true;
 | |
|   lex.expect(_Try);
 | |
|   Op& try_catch_op = code.emplace_back(lex.cur().loc, Op::_TryCatch);
 | |
|   code.push_set_cur(try_catch_op.block0);
 | |
|   blk_fl::val res0 = parse_block_stmt(lex, code);
 | |
|   code.close_pop_cur(lex.cur().loc);
 | |
|   lex.expect(_Catch);
 | |
|   code.push_set_cur(try_catch_op.block1);
 | |
|   sym::open_scope(lex);
 | |
|   Expr* expr = parse_expr(lex, code, true);
 | |
|   expr->chk_lvalue(lex.cur());
 | |
|   TypeExpr* tvm_error_type = TypeExpr::new_tensor(TypeExpr::new_var(), TypeExpr::new_atomic(_Int));
 | |
|   try {
 | |
|     unify(expr->e_type, tvm_error_type);
 | |
|   } catch (UnifyError& ue) {
 | |
|     std::ostringstream os;
 | |
|     os << "`catch` arguments have incorrect type " << expr->e_type << ": " << ue;
 | |
|     lex.cur().error(os.str());
 | |
|   }
 | |
|   expr->predefine_vars();
 | |
|   expr->define_new_vars(code);
 | |
|   try_catch_op.left = expr->pre_compile(code);
 | |
|   func_assert(try_catch_op.left.size() == 2 || try_catch_op.left.size() == 1);
 | |
|   blk_fl::val res1 = parse_block_stmt(lex, code);
 | |
|   sym::close_scope(lex);
 | |
|   code.close_pop_cur(lex.cur().loc);
 | |
|   blk_fl::combine_parallel(res0, res1);
 | |
|   return res0;
 | |
| }
 | |
| 
 | |
| blk_fl::val parse_if_stmt(Lexer& lex, CodeBlob& code, int first_lex = _If) {
 | |
|   SrcLocation loc{lex.cur().loc};
 | |
|   lex.expect(first_lex);
 | |
|   auto expr = parse_expr(lex, code);
 | |
|   expr->chk_rvalue(lex.cur());
 | |
|   auto flag_type = TypeExpr::new_atomic(_Int);
 | |
|   try {
 | |
|     unify(expr->e_type, flag_type);
 | |
|   } catch (UnifyError& ue) {
 | |
|     std::ostringstream os;
 | |
|     os << "`if` condition value of type " << expr->e_type << " is not an integer: " << ue;
 | |
|     lex.cur().error(os.str());
 | |
|   }
 | |
|   std::vector<var_idx_t> tmp_vars = expr->pre_compile(code);
 | |
|   if (tmp_vars.size() != 1) {
 | |
|     lex.cur().error("condition value is not a singleton");
 | |
|   }
 | |
|   Op& if_op = code.emplace_back(loc, Op::_If, tmp_vars);
 | |
|   code.push_set_cur(if_op.block0);
 | |
|   blk_fl::val res1 = parse_block_stmt(lex, code);
 | |
|   blk_fl::val res2 = blk_fl::init;
 | |
|   code.close_pop_cur(lex.cur().loc);
 | |
|   if (lex.tp() == _Else) {
 | |
|     lex.expect(_Else);
 | |
|     code.push_set_cur(if_op.block1);
 | |
|     res2 = parse_block_stmt(lex, code);
 | |
|     code.close_pop_cur(lex.cur().loc);
 | |
|   } else if (lex.tp() == _Elseif || lex.tp() == _Elseifnot) {
 | |
|     code.push_set_cur(if_op.block1);
 | |
|     res2 = parse_if_stmt(lex, code, lex.tp());
 | |
|     code.close_pop_cur(lex.cur().loc);
 | |
|   } else {
 | |
|     if_op.block1 = std::make_unique<Op>(lex.cur().loc, Op::_Nop);
 | |
|   }
 | |
|   if (first_lex == _Ifnot || first_lex == _Elseifnot) {
 | |
|     std::swap(if_op.block0, if_op.block1);
 | |
|   }
 | |
|   blk_fl::combine_parallel(res1, res2);
 | |
|   return res1;
 | |
| }
 | |
| 
 | |
| blk_fl::val parse_stmt(Lexer& lex, CodeBlob& code) {
 | |
|   switch (lex.tp()) {
 | |
|     case _Return: {
 | |
|       lex.next();
 | |
|       return parse_return_stmt(lex, code);
 | |
|     }
 | |
|     case '{': {
 | |
|       return parse_block_stmt(lex, code);
 | |
|     }
 | |
|     case ';': {
 | |
|       lex.next();
 | |
|       return blk_fl::init;
 | |
|     }
 | |
|     case _Repeat:
 | |
|       return parse_repeat_stmt(lex, code);
 | |
|     case _If:
 | |
|     case _Ifnot:
 | |
|       return parse_if_stmt(lex, code, lex.tp());
 | |
|     case _Do:
 | |
|       return parse_do_stmt(lex, code);
 | |
|     case _While:
 | |
|       return parse_while_stmt(lex, code);
 | |
|     case _Try:
 | |
|       return parse_try_catch_stmt(lex, code);
 | |
|     default: {
 | |
|       auto expr = parse_expr(lex, code);
 | |
|       expr->chk_rvalue(lex.cur());
 | |
|       expr->pre_compile(code);
 | |
|       lex.expect(';');
 | |
|       return blk_fl::end;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| CodeBlob* parse_func_body(Lexer& lex, FormalArgList arg_list, TypeExpr* ret_type) {
 | |
|   lex.expect('{');
 | |
|   CodeBlob* blob = new CodeBlob{ret_type};
 | |
|   if (pragma_allow_post_modification.enabled()) {
 | |
|     blob->flags |= CodeBlob::_AllowPostModification;
 | |
|   }
 | |
|   if (pragma_compute_asm_ltr.enabled()) {
 | |
|     blob->flags |= CodeBlob::_ComputeAsmLtr;
 | |
|   }
 | |
|   blob->import_params(std::move(arg_list));
 | |
|   blk_fl::val res = blk_fl::init;
 | |
|   bool warned = false;
 | |
|   while (lex.tp() != '}') {
 | |
|     if (!(res & blk_fl::end) && !warned) {
 | |
|       lex.cur().loc.show_warning("unreachable code");
 | |
|       warned = true;
 | |
|     }
 | |
|     blk_fl::combine(res, parse_stmt(lex, *blob));
 | |
|   }
 | |
|   if (res & blk_fl::end) {
 | |
|     parse_implicit_ret_stmt(lex, *blob);
 | |
|   }
 | |
|   blob->close_blk(lex.cur().loc);
 | |
|   lex.expect('}');
 | |
|   return blob;
 | |
| }
 | |
| 
 | |
| SymValAsmFunc* parse_asm_func_body(Lexer& lex, TypeExpr* func_type, const FormalArgList& arg_list, TypeExpr* ret_type,
 | |
|                                    bool impure = false) {
 | |
|   auto loc = lex.cur().loc;
 | |
|   lex.expect(_Asm);
 | |
|   int cnt = (int)arg_list.size();
 | |
|   int width = ret_type->get_width();
 | |
|   if (width < 0 || width > 16) {
 | |
|     throw src::ParseError{loc, "return type of an assembler built-in function must have a well-defined fixed width"};
 | |
|   }
 | |
|   if (arg_list.size() > 16) {
 | |
|     throw src::ParseError{loc, "assembler built-in function must have at most 16 arguments"};
 | |
|   }
 | |
|   std::vector<int> cum_arg_width;
 | |
|   cum_arg_width.push_back(0);
 | |
|   int tot_width = 0;
 | |
|   for (auto& arg : arg_list) {
 | |
|     int arg_width = std::get<TypeExpr*>(arg)->get_width();
 | |
|     if (arg_width < 0 || arg_width > 16) {
 | |
|       throw src::ParseError{std::get<SrcLocation>(arg),
 | |
|                             "parameters of an assembler built-in function must have a well-defined fixed width"};
 | |
|     }
 | |
|     cum_arg_width.push_back(tot_width += arg_width);
 | |
|   }
 | |
|   std::vector<AsmOp> asm_ops;
 | |
|   std::vector<int> arg_order, ret_order;
 | |
|   if (lex.tp() == '(') {
 | |
|     lex.expect('(');
 | |
|     if (lex.tp() != _Mapsto) {
 | |
|       std::vector<bool> visited(cnt, false);
 | |
|       for (int i = 0; i < cnt; i++) {
 | |
|         if (lex.tp() != _Ident) {
 | |
|           lex.expect(_Ident);
 | |
|         }
 | |
|         auto sym = sym::lookup_symbol(lex.cur().val);
 | |
|         int j;
 | |
|         for (j = 0; j < cnt; j++) {
 | |
|           if (std::get<SymDef*>(arg_list[j]) == sym) {
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|         if (j == cnt) {
 | |
|           lex.cur().error("formal argument name expected");
 | |
|         }
 | |
|         if (visited[j]) {
 | |
|           lex.cur().error("formal argument listed twice");
 | |
|         }
 | |
|         visited[j] = true;
 | |
|         int c1 = cum_arg_width[j], c2 = cum_arg_width[j + 1];
 | |
|         while (c1 < c2) {
 | |
|           arg_order.push_back(c1++);
 | |
|         }
 | |
|         lex.next();
 | |
|       }
 | |
|       func_assert(arg_order.size() == (unsigned)tot_width);
 | |
|     }
 | |
|     if (lex.tp() == _Mapsto) {
 | |
|       lex.expect(_Mapsto);
 | |
|       std::vector<bool> visited(width, false);
 | |
|       for (int i = 0; i < width; i++) {
 | |
|         if (lex.tp() != Lexem::Number || lex.cur().str.size() > 3) {
 | |
|           lex.expect(Lexem::Number);
 | |
|         }
 | |
|         int j = atoi(lex.cur().str.c_str());
 | |
|         if (j < 0 || j >= width || visited[j]) {
 | |
|           lex.cur().error("expected integer return value index 0 .. width-1");
 | |
|         }
 | |
|         visited[j] = true;
 | |
|         ret_order.push_back(j);
 | |
|         lex.next();
 | |
|       }
 | |
|     }
 | |
|     lex.expect(')');
 | |
|   }
 | |
|   while (lex.tp() == _String) {
 | |
|     std::string ops = lex.cur().str; // <op>\n<op>\n...
 | |
|     std::string op;
 | |
|     for (const char& c : ops) {
 | |
|       if (c == '\n') {
 | |
|         if (!op.empty()) {
 | |
|           asm_ops.push_back(AsmOp::Parse(op, cnt, width));
 | |
|           if (asm_ops.back().is_custom()) {
 | |
|             cnt = width;
 | |
|           }
 | |
|           op.clear();
 | |
|         }
 | |
|       } else {
 | |
|         op.push_back(c);
 | |
|       }
 | |
|     }
 | |
|     if (!op.empty()) {
 | |
|       asm_ops.push_back(AsmOp::Parse(op, cnt, width));
 | |
|       if (asm_ops.back().is_custom()) {
 | |
|         cnt = width;
 | |
|       }
 | |
|     }
 | |
|     lex.next();
 | |
|   }
 | |
|   if (asm_ops.empty()) {
 | |
|     throw src::ParseError{lex.cur().loc, "string with assembler instruction expected"};
 | |
|   }
 | |
|   lex.expect(';');
 | |
|   std::string crc_s;
 | |
|   for (const AsmOp& asm_op : asm_ops) {
 | |
|     crc_s += asm_op.op;
 | |
|   }
 | |
|   crc_s.push_back(impure);
 | |
|   for (const int& x : arg_order) {
 | |
|     crc_s += std::string((const char*) (&x), (const char*) (&x + 1));
 | |
|   }
 | |
|   for (const int& x : ret_order) {
 | |
|     crc_s += std::string((const char*) (&x), (const char*) (&x + 1));
 | |
|   }
 | |
|   auto res = new SymValAsmFunc{func_type, asm_ops, impure};
 | |
|   res->arg_order = std::move(arg_order);
 | |
|   res->ret_order = std::move(ret_order);
 | |
|   res->crc = td::crc64(crc_s);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| std::vector<TypeExpr*> parse_type_var_list(Lexer& lex) {
 | |
|   std::vector<TypeExpr*> res;
 | |
|   lex.expect(_Forall);
 | |
|   int idx = 0;
 | |
|   while (true) {
 | |
|     if (lex.tp() == _Type) {
 | |
|       lex.next();
 | |
|     }
 | |
|     if (lex.tp() != _Ident) {
 | |
|       throw src::ParseError{lex.cur().loc, "free type identifier expected"};
 | |
|     }
 | |
|     auto loc = lex.cur().loc;
 | |
|     if (prohibited_var_names.count(sym::symbols.get_name(lex.cur().val))) {
 | |
|       throw src::ParseError{loc, PSTRING() << "symbol `" << sym::symbols.get_name(lex.cur().val)
 | |
|                                            << "` cannot be redefined as a variable"};
 | |
|     }
 | |
|     SymDef* new_sym_def = sym::define_symbol(lex.cur().val, true, loc);
 | |
|     if (!new_sym_def || new_sym_def->value) {
 | |
|       lex.cur().error_at("redefined type variable `", "`");
 | |
|     }
 | |
|     auto var = TypeExpr::new_var(idx);
 | |
|     new_sym_def->value = new SymValType{SymVal::_Typename, idx++, var};
 | |
|     res.push_back(var);
 | |
|     lex.next();
 | |
|     if (lex.tp() != ',') {
 | |
|       break;
 | |
|     }
 | |
|     lex.next();
 | |
|   }
 | |
|   lex.expect(_Mapsto);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| void type_var_usage(TypeExpr* expr, const std::vector<TypeExpr*>& typevars, std::vector<bool>& used) {
 | |
|   if (expr->constr != TypeExpr::te_Var) {
 | |
|     for (auto arg : expr->args) {
 | |
|       type_var_usage(arg, typevars, used);
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
|   for (std::size_t i = 0; i < typevars.size(); i++) {
 | |
|     if (typevars[i] == expr) {
 | |
|       used.at(i) = true;
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
|   return;
 | |
| }
 | |
| 
 | |
| TypeExpr* compute_type_closure(TypeExpr* expr, const std::vector<TypeExpr*>& typevars) {
 | |
|   if (typevars.empty()) {
 | |
|     return expr;
 | |
|   }
 | |
|   std::vector<bool> used(typevars.size(), false);
 | |
|   type_var_usage(expr, typevars, used);
 | |
|   std::vector<TypeExpr*> used_vars;
 | |
|   for (std::size_t i = 0; i < typevars.size(); i++) {
 | |
|     if (used.at(i)) {
 | |
|       used_vars.push_back(typevars[i]);
 | |
|     }
 | |
|   }
 | |
|   if (!used_vars.empty()) {
 | |
|     expr = TypeExpr::new_forall(std::move(used_vars), expr);
 | |
|   }
 | |
|   return expr;
 | |
| }
 | |
| 
 | |
| // if a function looks like `T f(...args) { return anotherF(...args); }`,
 | |
| // set a bit to flags
 | |
| // then, all calls to `f(...)` will be effectively replaced with `anotherF(...)`
 | |
| void detect_if_function_just_wraps_another(SymValCodeFunc* v_current, const td::RefInt256 &method_id) {
 | |
|   const std::string& function_name = v_current->code->name;
 | |
| 
 | |
|   // in "AST" representation, the first is Op::_Import (input arguments, even if none)
 | |
|   const auto& op_import = v_current->code->ops;
 | |
|   func_assert(op_import && op_import->cl == Op::_Import);
 | |
| 
 | |
|   // then Op::_Call (anotherF)
 | |
|   // when pragma allow-post-modification, it might be prepended with empty Op::_Let todo I don't like this
 | |
|   const Op* op_call = op_import->next.get();
 | |
|   while (op_call && op_call->cl == Op::_Let && op_call->disabled())
 | |
|     op_call = op_call->next.get();
 | |
|   if (!op_call || op_call->cl != Op::_Call)
 | |
|     return;
 | |
|   func_assert(op_call->left.size() == 1);
 | |
| 
 | |
|   const auto& op_return = op_call->next;
 | |
|   if (!op_return || op_return->cl != Op::_Return || op_return->left.size() != 1)
 | |
|     return;
 | |
| 
 | |
|   bool indices_expected = op_import->left.size() == op_call->left[0] && op_call->left[0] == op_return->left[0];
 | |
|   if (!indices_expected)
 | |
|     return;
 | |
| 
 | |
|   const SymDef* f_called = op_call->fun_ref;
 | |
|   const SymValFunc* v_called = dynamic_cast<SymValFunc*>(f_called->value);
 | |
|   if (!v_called)
 | |
|     return;
 | |
| 
 | |
|   // `return` must use all arguments, e.g. `return (_0,_2,_1)`, not `return (_0,_1,_1)`
 | |
|   int args_used_mask = 0;
 | |
|   for (var_idx_t arg_idx : op_call->right) {
 | |
|     args_used_mask |= 1 << arg_idx;
 | |
|   }
 | |
|   if (args_used_mask != (1 << op_call->right.size()) - 1)
 | |
|     return;
 | |
| 
 | |
|   // detect getters (having method_id), they should not be treated as wrappers
 | |
|   // v_current->method_id will be assigned later; todo refactor function parsing completely, it's weird
 | |
|   // moreover, `recv_external()` and others are also exported, but FunC is unaware of method_id
 | |
|   // (it's assigned by Fift later)
 | |
|   // so, for now, just handle "special" function names, the same as in Asm.fif
 | |
|   if (!method_id.is_null())
 | |
|     return;
 | |
|   if (function_name == "main" || function_name == "recv_internal" || function_name == "recv_external" ||
 | |
|       function_name == "run_ticktock" || function_name == "split_prepare" || function_name == "split_install")
 | |
|     return;
 | |
| 
 | |
|   // all types must be strictly defined (on mismatch, a compilation error will be triggered anyway)
 | |
|   if (v_called->sym_type->has_unknown_inside() || v_current->sym_type->has_unknown_inside())
 | |
|     return;
 | |
|   // avoid situations like `f(int a, (int,int) b)`, inlining will be cumbersome
 | |
|   if (v_current->get_arg_type()->get_width() != op_call->right.size())
 | |
|     return;
 | |
|   // 'return true;' (false, nil) are (surprisingly) also function calls, with auto_apply=true
 | |
|   if (v_called->auto_apply)
 | |
|     return;
 | |
| 
 | |
|   // they must have the same pureness
 | |
|   if (v_called->impure != v_current->impure || v_current->is_inline_ref())
 | |
|     return;
 | |
| 
 | |
|   // ok, f_current is a wrapper
 | |
|   v_current->flags |= SymValFunc::flagWrapsAnotherF;
 | |
|   if (verbosity >= 2) {
 | |
|     std::cerr << function_name << " -> " << f_called->name() << std::endl;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void parse_func_def(Lexer& lex) {
 | |
|   SrcLocation loc{lex.cur().loc};
 | |
|   sym::open_scope(lex);
 | |
|   std::vector<TypeExpr*> type_vars;
 | |
|   if (lex.tp() == _Forall) {
 | |
|     type_vars = parse_type_var_list(lex);
 | |
|   }
 | |
|   auto ret_type = parse_type(lex);
 | |
|   if (lex.tp() != _Ident) {
 | |
|     throw src::ParseError{lex.cur().loc, "function name identifier expected"};
 | |
|   }
 | |
|   Lexem func_name = lex.cur();
 | |
|   lex.next();
 | |
|   FormalArgList arg_list = parse_formal_args(lex);
 | |
|   bool impure = (lex.tp() == _Impure);
 | |
|   if (impure) {
 | |
|     lex.next();
 | |
|   }
 | |
|   int flags_inline = 0;
 | |
|   if (lex.tp() == _Inline) {
 | |
|     flags_inline = SymValFunc::flagInline;
 | |
|     lex.next();
 | |
|   } else if (lex.tp() == _InlineRef) {
 | |
|     flags_inline = SymValFunc::flagInlineRef;
 | |
|     lex.next();
 | |
|   }
 | |
|   td::RefInt256 method_id;
 | |
|   std::string method_name;
 | |
|   if (lex.tp() == _MethodId) {
 | |
|     lex.next();
 | |
|     if (lex.tp() == '(') {
 | |
|       lex.expect('(');
 | |
|       if (lex.tp() == Lexem::String) {
 | |
|         method_name = lex.cur().str;
 | |
|       } else if (lex.tp() == Lexem::Number) {
 | |
|         method_name = lex.cur().str;
 | |
|         method_id = td::string_to_int256(method_name);
 | |
|         if (method_id.is_null()) {
 | |
|           lex.cur().error_at("invalid integer constant `", "`");
 | |
|         }
 | |
|       } else {
 | |
|         throw src::ParseError{lex.cur().loc, "integer or string method identifier expected"};
 | |
|       }
 | |
|       lex.next();
 | |
|       lex.expect(')');
 | |
|     } else {
 | |
|       method_name = func_name.str;
 | |
|     }
 | |
|     if (method_id.is_null()) {
 | |
|       unsigned crc = td::crc16(method_name);
 | |
|       method_id = td::make_refint((crc & 0xffff) | 0x10000);
 | |
|     }
 | |
|   }
 | |
|   if (lex.tp() != ';' && lex.tp() != '{' && lex.tp() != _Asm) {
 | |
|     lex.expect('{', "function body block expected");
 | |
|   }
 | |
|   TypeExpr* func_type = TypeExpr::new_map(extract_total_arg_type(arg_list), ret_type);
 | |
|   func_type = compute_type_closure(func_type, type_vars);
 | |
|   if (verbosity >= 1) {
 | |
|     std::cerr << "function " << func_name.str << " : " << func_type << std::endl;
 | |
|   }
 | |
|   SymDef* func_sym = sym::define_global_symbol(func_name.val, 0, loc);
 | |
|   func_assert(func_sym);
 | |
|   SymValFunc* func_sym_val = dynamic_cast<SymValFunc*>(func_sym->value);
 | |
|   if (func_sym->value) {
 | |
|     if (func_sym->value->type != SymVal::_Func || !func_sym_val) {
 | |
|       lex.cur().error("was not defined as a function before");
 | |
|     }
 | |
|     try {
 | |
|       unify(func_sym_val->sym_type, func_type);
 | |
|     } catch (UnifyError& ue) {
 | |
|       std::ostringstream os;
 | |
|       os << "previous type of function " << func_name.str << " : " << func_sym_val->sym_type
 | |
|          << " cannot be unified with new type " << func_type << ": " << ue;
 | |
|       lex.cur().error(os.str());
 | |
|     }
 | |
|   }
 | |
|   if (lex.tp() == ';') {
 | |
|     make_new_glob_func(func_sym, func_type, impure);
 | |
|     lex.next();
 | |
|   } else if (lex.tp() == '{') {
 | |
|     if (dynamic_cast<SymValAsmFunc*>(func_sym_val)) {
 | |
|       lex.cur().error("function `"s + func_name.str + "` has been already defined as an assembler built-in");
 | |
|     }
 | |
|     SymValCodeFunc* func_sym_code;
 | |
|     if (func_sym_val) {
 | |
|       func_sym_code = dynamic_cast<SymValCodeFunc*>(func_sym_val);
 | |
|       if (!func_sym_code) {
 | |
|         lex.cur().error("function `"s + func_name.str + "` has been already defined in an yet-unknown way");
 | |
|       }
 | |
|     } else {
 | |
|       func_sym_code = make_new_glob_func(func_sym, func_type, impure);
 | |
|     }
 | |
|     if (func_sym_code->code) {
 | |
|       lex.cur().error("redefinition of function `"s + func_name.str + "`");
 | |
|     }
 | |
|     CodeBlob* code = parse_func_body(lex, arg_list, ret_type);
 | |
|     code->name = func_name.str;
 | |
|     code->loc = loc;
 | |
|     // code->print(std::cerr);  // !!!DEBUG!!!
 | |
|     func_sym_code->code = code;
 | |
|     detect_if_function_just_wraps_another(func_sym_code, method_id);
 | |
|   } else {
 | |
|     Lexem asm_lexem = lex.cur();
 | |
|     SymValAsmFunc* asm_func = parse_asm_func_body(lex, func_type, arg_list, ret_type, impure);
 | |
| #ifdef FUNC_DEBUG
 | |
|     asm_func->name = func_name.str;
 | |
| #endif
 | |
|     if (func_sym_val) {
 | |
|       if (dynamic_cast<SymValCodeFunc*>(func_sym_val)) {
 | |
|         asm_lexem.error("function `"s + func_name.str + "` was already declared as an ordinary function");
 | |
|       }
 | |
|       SymValAsmFunc* asm_func_old = dynamic_cast<SymValAsmFunc*>(func_sym_val);
 | |
|       if (asm_func_old) {
 | |
|         if (asm_func->crc != asm_func_old->crc) {
 | |
|           asm_lexem.error("redefinition of built-in assembler function `"s + func_name.str + "`");
 | |
|         }
 | |
|       } else {
 | |
|         asm_lexem.error("redefinition of previously (somehow) defined function `"s + func_name.str + "`");
 | |
|       }
 | |
|     }
 | |
|     func_sym->value = asm_func;
 | |
|   }
 | |
|   if (method_id.not_null()) {
 | |
|     auto val = dynamic_cast<SymValFunc*>(func_sym->value);
 | |
|     if (!val) {
 | |
|       lex.cur().error("cannot set method id for unknown function `"s + func_name.str + "`");
 | |
|     }
 | |
|     if (val->method_id.is_null()) {
 | |
|       val->method_id = std::move(method_id);
 | |
|     } else if (td::cmp(val->method_id, method_id) != 0) {
 | |
|       lex.cur().error("integer method identifier for `"s + func_name.str + "` changed from " +
 | |
|                       val->method_id->to_dec_string() + " to a different value " + method_id->to_dec_string());
 | |
|     }
 | |
|   }
 | |
|   if (flags_inline) {
 | |
|     auto val = dynamic_cast<SymValFunc*>(func_sym->value);
 | |
|     if (!val) {
 | |
|       lex.cur().error("cannot set unknown function `"s + func_name.str + "` as an inline");
 | |
|     }
 | |
|     if (!val->is_inline() && !val->is_inline_ref()) {
 | |
|       val->flags |= flags_inline;
 | |
|     } else if ((val->flags & (SymValFunc::flagInline | SymValFunc::flagInlineRef)) != flags_inline) {
 | |
|       lex.cur().error("inline mode for `"s + func_name.str + "` changed with respect to a previous declaration");
 | |
|     }
 | |
|   }
 | |
|   if (verbosity >= 1) {
 | |
|     std::cerr << "new type of function " << func_name.str << " : " << func_type << std::endl;
 | |
|   }
 | |
|   sym::close_scope(lex);
 | |
| }
 | |
| 
 | |
| std::string func_ver_test = func_version;
 | |
| 
 | |
| void parse_pragma(Lexer& lex) {
 | |
|   auto pragma = lex.cur();
 | |
|   lex.next();
 | |
|   if (lex.tp() != _Ident) {
 | |
|     lex.expect(_Ident, "pragma name expected");
 | |
|   }
 | |
|   auto pragma_name = lex.cur().str;
 | |
|   lex.next();
 | |
|   if (!pragma_name.compare("version") || !pragma_name.compare("not-version")) {
 | |
|     bool negate = !pragma_name.compare("not-version");
 | |
|     char op = '='; bool eq = false;
 | |
|     int sem_ver[3] = {0, 0, 0};
 | |
|     char segs = 1;
 | |
|     auto stoi = [&](const std::string& s) {
 | |
|       auto R = td::to_integer_safe<int>(s);
 | |
|       if (R.is_error()) {
 | |
|         lex.cur().error("invalid semver format");
 | |
|       }
 | |
|       return R.move_as_ok();
 | |
|     };
 | |
|     if (lex.tp() == _Number) {
 | |
|       sem_ver[0] = stoi(lex.cur().str);
 | |
|     } else if (lex.tp() == _Ident) {
 | |
|       auto id1 = lex.cur().str;
 | |
|       char ch1 = id1[0];
 | |
|       if ((ch1 == '>') || (ch1 == '<') || (ch1 == '=') || (ch1 == '^')) {
 | |
|         op = ch1;
 | |
|       } else {
 | |
|         lex.cur().error("unexpected comparator operation");
 | |
|       }
 | |
|       if (id1.length() < 2) {
 | |
|         lex.cur().error("expected number after comparator");
 | |
|       }
 | |
|       if (id1[1] == '=') {
 | |
|         eq = true;
 | |
|         if (id1.length() < 3) {
 | |
|           lex.cur().error("expected number after comparator");
 | |
|         }
 | |
|         sem_ver[0] = stoi(id1.substr(2));
 | |
|       } else {
 | |
|         sem_ver[0] = stoi(id1.substr(1));
 | |
|       }
 | |
|     } else {
 | |
|       lex.cur().error("expected semver with optional comparator");
 | |
|     }
 | |
|     lex.next();
 | |
|     if (lex.tp() != ';') {
 | |
|       if (lex.tp() != _Ident || lex.cur().str[0] != '.') {
 | |
|         lex.cur().error("invalid semver format");
 | |
|       }
 | |
|       sem_ver[1] = stoi(lex.cur().str.substr(1));
 | |
|       segs = 2;
 | |
|       lex.next();
 | |
|     }
 | |
|     if (lex.tp() != ';') {
 | |
|       if (lex.tp() != _Ident || lex.cur().str[0] != '.') {
 | |
|         lex.cur().error("invalid semver format");
 | |
|       }
 | |
|       sem_ver[2] = stoi(lex.cur().str.substr(1));
 | |
|       segs = 3;
 | |
|       lex.next();
 | |
|     }
 | |
|     // End reading semver from source code
 | |
|     int func_ver[3] = {0, 0, 0};
 | |
|     std::istringstream iss(func_ver_test);
 | |
|     std::string s;
 | |
|     for (int idx = 0; idx < 3; idx++) {
 | |
|       std::getline(iss, s, '.');
 | |
|       func_ver[idx] = stoi(s);
 | |
|     }
 | |
|     // End parsing embedded semver
 | |
|     std::string semver_expr;
 | |
|     if (negate) {
 | |
|       semver_expr += '!';
 | |
|     }
 | |
|     semver_expr += op;
 | |
|     if (eq) {
 | |
|       semver_expr += '=';
 | |
|     }
 | |
|     for (int idx = 0; idx < 3; idx++) {
 | |
|       semver_expr += std::to_string(sem_ver[idx]);
 | |
|       if (idx < 2)
 | |
|         semver_expr += '.';
 | |
|     }
 | |
|     bool match = true;
 | |
|     switch (op) {
 | |
|       case '=':
 | |
|         if ((func_ver[0] != sem_ver[0]) ||
 | |
|             (func_ver[1] != sem_ver[1]) ||
 | |
|             (func_ver[2] != sem_ver[2])) {
 | |
|           match = false;
 | |
|         }
 | |
|         break;
 | |
|       case '>':
 | |
|         if ( ((func_ver[0] == sem_ver[0]) && (func_ver[1] == sem_ver[1]) && (func_ver[2] == sem_ver[2]) && !eq) ||
 | |
|              ((func_ver[0] == sem_ver[0]) && (func_ver[1] == sem_ver[1]) && (func_ver[2] < sem_ver[2])) ||
 | |
|              ((func_ver[0] == sem_ver[0]) && (func_ver[1] < sem_ver[1])) ||
 | |
|              ((func_ver[0] < sem_ver[0])) ) {
 | |
|           match = false;
 | |
|         }
 | |
|         break;
 | |
|       case '<':
 | |
|         if ( ((func_ver[0] == sem_ver[0]) && (func_ver[1] == sem_ver[1]) && (func_ver[2] == sem_ver[2]) && !eq) ||
 | |
|              ((func_ver[0] == sem_ver[0]) && (func_ver[1] == sem_ver[1]) && (func_ver[2] > sem_ver[2])) ||
 | |
|              ((func_ver[0] == sem_ver[0]) && (func_ver[1] > sem_ver[1])) ||
 | |
|              ((func_ver[0] > sem_ver[0])) ) {
 | |
|           match = false;
 | |
|         }
 | |
|         break;
 | |
|       case '^':
 | |
|         if ( ((segs == 3) && ((func_ver[0] != sem_ver[0]) || (func_ver[1] != sem_ver[1]) || (func_ver[2] < sem_ver[2])))
 | |
|           || ((segs == 2) && ((func_ver[0] != sem_ver[0]) || (func_ver[1] < sem_ver[1])))
 | |
|           || ((segs == 1) && ((func_ver[0] < sem_ver[0]))) ) {
 | |
|           match = false;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|     if ((match && negate) || (!match && !negate)) {
 | |
|       pragma.error(std::string("FunC version ") + func_ver_test + " does not satisfy condition " + semver_expr);
 | |
|     }
 | |
|   } else if (!pragma_name.compare("test-version-set")) {
 | |
|     if (lex.tp() != _String) {
 | |
|       lex.cur().error("version string expected");
 | |
|     }
 | |
|     func_ver_test = lex.cur().str;
 | |
|     lex.next();
 | |
|   } else if (pragma_name == pragma_allow_post_modification.name()) {
 | |
|     pragma_allow_post_modification.enable(lex.cur().loc);
 | |
|   } else if (pragma_name == pragma_compute_asm_ltr.name()) {
 | |
|     pragma_compute_asm_ltr.enable(lex.cur().loc);
 | |
|   } else {
 | |
|     lex.cur().error(std::string{"unknown pragma `"} + pragma_name + "`");
 | |
|   }
 | |
|   lex.expect(';');
 | |
| }
 | |
| 
 | |
| std::vector<const src::FileDescr*> source_fdescr;
 | |
| 
 | |
| std::map<std::string, src::FileDescr*> source_files;
 | |
| std::stack<src::SrcLocation> inclusion_locations;
 | |
| 
 | |
| void parse_include(Lexer& lex, const src::FileDescr* fdescr) {
 | |
|   auto include = lex.cur();
 | |
|   lex.expect(_IncludeHashtag);
 | |
|   if (lex.tp() != _String) {
 | |
|     lex.expect(_String, "source file name");
 | |
|   }
 | |
|   std::string val = lex.cur().str;
 | |
|   std::string parent_dir = fdescr->filename;
 | |
|   if (parent_dir.rfind('/') != std::string::npos) {
 | |
|     val = parent_dir.substr(0, parent_dir.rfind('/') + 1) + val;
 | |
|   }
 | |
|   lex.next();
 | |
|   lex.expect(';');
 | |
|   if (!parse_source_file(val.c_str(), include, false)) {
 | |
|     include.error(std::string{"failed parsing included file `"} + val + "`");
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool parse_source(std::istream* is, src::FileDescr* fdescr) {
 | |
|   src::SourceReader reader{is, fdescr};
 | |
|   Lexer lex{reader, true, ";,()[] ~."};
 | |
|   while (lex.tp() != _Eof) {
 | |
|     if (lex.tp() == _PragmaHashtag) {
 | |
|       parse_pragma(lex);
 | |
|     } else if (lex.tp() == _IncludeHashtag) {
 | |
|       parse_include(lex, fdescr);
 | |
|     } else if (lex.tp() == _Global) {
 | |
|       parse_global_var_decls(lex);
 | |
|     } else if (lex.tp() == _Const) {
 | |
|       parse_const_decls(lex);
 | |
|     } else {
 | |
|       parse_func_def(lex);
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool parse_source_file(const char* filename, src::Lexem lex, bool is_main) {
 | |
|   if (!filename || !*filename) {
 | |
|     auto msg = "source file name is an empty string";
 | |
|     if (lex.tp) {
 | |
|       lex.error(msg);
 | |
|     } else {
 | |
|       throw src::Fatal{msg};
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   auto path_res = read_callback(ReadCallback::Kind::Realpath, filename);
 | |
|   if (path_res.is_error()) {
 | |
|     auto error = path_res.move_as_error();
 | |
|     lex.error(error.message().c_str());
 | |
|     return false;
 | |
|   }
 | |
|   std::string real_filename = path_res.move_as_ok();
 | |
|   auto it = source_files.find(real_filename);
 | |
|   if (it != source_files.end()) {
 | |
|     it->second->is_main |= is_main;
 | |
|     if (verbosity >= 2) {
 | |
|       if (lex.tp) {
 | |
|         lex.loc.show_warning(std::string{"skipping file "} + real_filename + " because it was already included");
 | |
|       } else {
 | |
|         std::cerr << "warning: skipping file " << real_filename << " because it was already included" << std::endl;
 | |
|       }
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
|   if (lex.tp) { // included
 | |
|     funC::generated_from += std::string{"incl:"};
 | |
|   }
 | |
|   funC::generated_from += std::string{"`"} + filename + "` ";
 | |
|   src::FileDescr* cur_source = new src::FileDescr{filename};
 | |
|   source_files[real_filename] = cur_source;
 | |
|   cur_source->is_main = is_main;
 | |
|   source_fdescr.push_back(cur_source);
 | |
|   auto file_res = read_callback(ReadCallback::Kind::ReadFile, filename);
 | |
|   if (file_res.is_error()) {
 | |
|     auto msg = file_res.move_as_error().message().str();
 | |
|     if (lex.tp) {
 | |
|       lex.error(msg);
 | |
|     } else {
 | |
|       throw src::Fatal{msg};
 | |
|     }
 | |
|   }
 | |
|   auto file_str = file_res.move_as_ok();
 | |
|   std::stringstream ss{file_str};
 | |
|   inclusion_locations.push(lex.loc);
 | |
|   bool res = parse_source(&ss, cur_source);
 | |
|   inclusion_locations.pop();
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| bool parse_source_stdin() {
 | |
|   src::FileDescr* cur_source = new src::FileDescr{"stdin", true};
 | |
|   cur_source->is_main = true;
 | |
|   source_fdescr.push_back(cur_source);
 | |
|   return parse_source(&std::cin, cur_source);
 | |
| }
 | |
| 
 | |
| }  // namespace funC
 |