1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

[Tolk] Embedded stdlib.tolk, CompilerState, strict includes

Several related changes:
- stdlib.tolk is embedded into a distribution (deb package or tolk-js),
  the user won't have to download it and store as a project file;
  it's an important step to maintain correct language versioning
- stdlib.tolk is auto-included, that's why all its functions are
  available out of the box
- strict includes: you can't use symbol `f` from another file
  unless you've #include'd this file
- drop all C++ global variables holding compilation state,
  merge them into a single struct CompilerState located at
  compiler-state.h; for instance, stdlib filename is also there
This commit is contained in:
tolk-vm 2024-10-31 11:02:01 +04:00
parent f0e6470d0b
commit 6c30e5a7eb
No known key found for this signature in database
GPG key ID: 7905DD7FE0324B12
21 changed files with 604 additions and 506 deletions

View file

@ -4,6 +4,7 @@ set(TOLK_SOURCE
src-file.cpp
lexer.cpp
symtable.cpp
compiler-state.cpp
unify-types.cpp
parse-tolk.cpp
abscode.cpp
@ -28,6 +29,12 @@ if (${TOLK_DEBUG}) # -DTOLK_DEBUG=1 in CMake options => #define TOLK_DEBUG (for
target_compile_definitions(tolk PRIVATE TOLK_DEBUG=1)
endif()
if (NOT USE_EMSCRIPTEN)
get_filename_component(STDLIB_TOLK_IF_BUILD_FROM_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/../crypto/smartcont/stdlib.tolk" REALPATH)
target_compile_definitions(tolk PRIVATE STDLIB_TOLK_IF_BUILD_FROM_SOURCES="${STDLIB_TOLK_IF_BUILD_FROM_SOURCES}")
endif()
if (USE_EMSCRIPTEN)
add_executable(tolkfiftlib tolk-wasm.cpp ${TOLK_SOURCE})
target_include_directories(tolkfiftlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)

View file

@ -15,6 +15,7 @@
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tolk.h"
#include "compiler-state.h"
namespace tolk {
@ -59,7 +60,7 @@ void TmpVar::dump(std::ostream& os) const {
void TmpVar::show(std::ostream& os, int omit_idx) const {
if (cls & _Named) {
os << symbols.get_name(name);
os << G.symbols.get_name(name);
if (omit_idx && (omit_idx >= 2 || (cls & _UniqueName))) {
return;
}

View file

@ -15,6 +15,7 @@
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tolk.h"
#include "compiler-state.h"
namespace tolk {
@ -768,7 +769,7 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
tolk_assert(left.size() == right.size());
for (std::size_t i = 0; i < right.size(); i++) {
const VarDescr* ov = values[right[i]];
if (!ov && verbosity >= 5) {
if (!ov && G.is_verbosity(5)) {
std::cerr << "FATAL: error in assignment at right component #" << i << " (no value for _" << right[i] << ")"
<< std::endl;
for (auto x : left) {

View file

@ -15,6 +15,7 @@
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tolk.h"
#include "compiler-state.h"
namespace tolk {
using namespace std::literals::string_literals;
@ -25,18 +26,11 @@ using namespace std::literals::string_literals;
*
*/
int glob_func_cnt, undef_func_cnt, glob_var_cnt, const_cnt;
std::vector<SymDef*> glob_func, glob_vars, glob_get_methods;
std::set<std::string> prohibited_var_names;
SymDef* define_builtin_func_impl(const std::string& name, SymValAsmFunc* func_val) {
if (name.back() == '_') {
prohibited_var_names.insert(name);
}
sym_idx_t name_idx = symbols.lookup(name, 1);
if (symbols.is_keyword(name_idx)) {
std::cerr << "fatal: global function `" << name << "` already defined as a keyword" << std::endl;
G.prohibited_var_names.insert(name);
}
sym_idx_t name_idx = G.symbols.lookup(name, 1);
SymDef* def = define_global_symbol(name_idx, true);
if (!def) {
std::cerr << "fatal: global function `" << name << "` already defined" << std::endl;

View file

@ -15,6 +15,7 @@
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "tolk.h"
#include "compiler-state.h"
namespace tolk {
@ -324,7 +325,7 @@ bool Op::generate_code_step(Stack& stack) {
if (!used || disabled()) {
return true;
}
std::string name = symbols.get_name(fun_ref->sym_idx);
std::string name = G.symbols.get_name(fun_ref->sym_idx);
stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1);
if (left.size() != 1) {
tolk_assert(left.size() <= 15);
@ -359,7 +360,7 @@ bool Op::generate_code_step(Stack& stack) {
}
func->compile(stack.o, res, args0, where); // compile res := f (args0)
} else {
std::string name = symbols.get_name(fun_ref->sym_idx);
std::string name = G.symbols.get_name(fun_ref->sym_idx);
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
}
stack.o.undent();
@ -497,7 +498,7 @@ bool Op::generate_code_step(Stack& stack) {
} else {
auto fv = dynamic_cast<const SymValCodeFunc*>(fun_ref->value);
// todo can be fv == nullptr?
std::string name = symbols.get_name(fun_ref->sym_idx);
std::string name = G.symbols.get_name(fun_ref->sym_idx);
if (fv && (fv->is_inline() || fv->is_inline_ref())) {
stack.o << AsmOp::Custom(name + " INLINECALLDICT", (int)right.size(), (int)left.size());
} else if (fv && fv->code && fv->code->require_callxargs) {
@ -534,7 +535,7 @@ bool Op::generate_code_step(Stack& stack) {
stack.o << AsmOp::Tuple((int)right.size());
}
if (!right.empty()) {
std::string name = symbols.get_name(fun_ref->sym_idx);
std::string name = G.symbols.get_name(fun_ref->sym_idx);
stack.o << AsmOp::Custom(name + " SETGLOB", 1, 0);
}
stack.s.resize(k);
@ -894,14 +895,14 @@ void CodeBlob::generate_code(AsmOpList& out, int mode) {
}
ops->generate_code_all(stack);
stack.apply_wrappers(require_callxargs && (mode & Stack::_InlineAny) ? args : -1);
if (!(mode & Stack::_DisableOpt)) {
optimize_code(out);
}
}
void CodeBlob::generate_code(std::ostream& os, int mode, int indent) {
AsmOpList out_list(indent, &vars);
generate_code(out_list, mode);
if (G.settings.optimization_level >= 2) {
optimize_code(out_list);
}
out_list.out(os, mode);
}

47
tolk/compiler-state.cpp Normal file
View file

@ -0,0 +1,47 @@
/*
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/>.
*/
#include "compiler-state.h"
namespace tolk {
std::string tolk_version{"0.5.0"};
CompilerState G; // the only mutable global variable in tolk internals
void GlobalPragma::enable(SrcLocation loc) {
if (deprecated_from_v_) {
loc.show_warning(PSTRING() << "#pragma " << name_ <<
" is deprecated since Tolk v" << deprecated_from_v_ <<
". Please, remove this line from your code.");
return;
}
if (!loc.get_src_file()->is_entrypoint_file()) {
// todo generally it's not true; rework pragmas completely
loc.show_warning(PSTRING() << "#pragma " << name_ <<
" should be used in the main file only.");
}
enabled_ = true;
}
void GlobalPragma::always_on_and_deprecated(const char *deprecated_from_v) {
deprecated_from_v_ = deprecated_from_v;
enabled_ = true;
}
} // namespace tolk

93
tolk/compiler-state.h Normal file
View file

@ -0,0 +1,93 @@
/*
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/>.
*/
#pragma once
#include "src-file.h"
#include "symtable.h"
#include "td/utils/Status.h"
#include <set>
#include <string>
namespace tolk {
extern std::string tolk_version;
class GlobalPragma {
std::string name_;
bool enabled_ = false;
const char* deprecated_from_v_ = nullptr;
public:
explicit GlobalPragma(std::string name) : name_(std::move(name)) { }
const std::string& name() const { return name_; }
bool enabled() const { return enabled_; }
void enable(SrcLocation loc);
void always_on_and_deprecated(const char* deprecated_from_v);
};
// CompilerSettings contains settings that can be passed via cmd line or (partially) wasm envelope.
// They are filled once at start and are immutable since the compilation started.
struct CompilerSettings {
enum class FsReadCallbackKind { Realpath, ReadFile };
using FsReadCallback = std::function<td::Result<std::string>(FsReadCallbackKind, const char*)>;
int verbosity = 0;
int optimization_level = 2;
bool stack_layout_comments = true;
std::string entrypoint_filename;
std::string output_filename;
std::string boc_output_filename;
std::string stdlib_filename;
FsReadCallback read_callback;
};
// CompilerState contains a mutable state that is changed while the compilation is going on.
// It's a "global state" of all compilation.
// Historically, in FunC, this global state was spread along many global C++ variables.
// Now, no global C++ variables except `CompilerState G` are present.
struct CompilerState {
CompilerSettings settings;
SymTable symbols;
int scope_level = 0;
SymDef* sym_def[SymTable::SIZE_PRIME + 1]{};
SymDef* global_sym_def[SymTable::SIZE_PRIME + 1]{};
std::vector<std::pair<int, SymDef>> symbol_stack;
std::vector<SrcLocation> scope_opened_at;
AllRegisteredSrcFiles all_src_files;
int glob_func_cnt = 0, glob_var_cnt = 0, const_cnt = 0;
std::vector<SymDef*> glob_func, glob_vars, glob_get_methods;
std::set<std::string> prohibited_var_names;
std::string generated_from;
GlobalPragma pragma_allow_post_modification{"allow-post-modification"};
GlobalPragma pragma_compute_asm_ltr{"compute-asm-ltr"};
GlobalPragma pragma_remove_unused_functions{"remove-unused-functions"};
bool is_verbosity(int gt_eq) const { return settings.verbosity >= gt_eq; }
};
extern CompilerState G;
} // namespace tolk

View file

@ -14,8 +14,8 @@
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/>.
*/
#include <numeric>
#include "tolk.h"
#include "compiler-state.h"
using namespace std::literals::string_literals;
@ -122,7 +122,7 @@ bool Expr::deduce_type(const Lexer& lex) {
} catch (UnifyError& ue) {
std::ostringstream os;
os << "cannot implicitly assign an expression of type " << args[1]->e_type
<< " to a variable or pattern of type " << rhs_type << " in modifying method `" << symbols.get_name(val)
<< " to a variable or pattern of type " << rhs_type << " in modifying method `" << G.symbols.get_name(val)
<< "` : " << ue;
lex.error(os.str());
}
@ -197,14 +197,14 @@ int Expr::predefine_vars() {
case _Var:
if (!sym) {
tolk_assert(val < 0 && here.is_defined());
if (prohibited_var_names.count(symbols.get_name(~val))) {
if (G.prohibited_var_names.count(G.symbols.get_name(~val))) {
throw ParseError{
here, PSTRING() << "symbol `" << symbols.get_name(~val) << "` cannot be redefined as a variable"};
here, PSTRING() << "symbol `" << G.symbols.get_name(~val) << "` cannot be redefined as a variable"};
}
sym = define_symbol(~val, false, here);
// std::cerr << "predefining variable " << symbols.get_name(~val) << std::endl;
if (!sym) {
throw ParseError{here, std::string{"redefined variable `"} + symbols.get_name(~val) + "`"};
throw ParseError{here, std::string{"redefined variable `"} + G.symbols.get_name(~val) + "`"};
}
sym->value = new SymVal{SymValKind::_Var, -1, e_type};
return 1;

View file

@ -15,6 +15,7 @@
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lexer.h"
#include "compiler-state.h"
#include "symtable.h"
#include <cassert>
@ -426,7 +427,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
if (TokenType kw_tok = maybe_keyword(str_val)) {
lex->add_token(kw_tok, str_val);
} else {
symbols.lookup_add(static_cast<std::string>(str_val));
G.symbols.lookup_add(static_cast<std::string>(str_val));
lex->add_token(tok_identifier, str_val);
}
return true;
@ -452,7 +453,7 @@ struct ChunkIdentifierInBackticks final : ChunkLexerBase {
std::string_view str_val(str_begin + 1, lex->c_str() - str_begin - 1);
lex->skip_chars(1);
symbols.lookup_add(static_cast<std::string>(str_val));
G.symbols.lookup_add(static_cast<std::string>(str_val));
lex->add_token(tok_identifier, str_val);
return true;
}
@ -611,7 +612,7 @@ void Lexer::next_special(TokenType parse_next_as, const char* str_expected) {
int Lexer::cur_sym_idx() const {
assert(tok() == tok_identifier);
return symbols.lookup_add(cur_str_std_string());
return G.symbols.lookup_add(cur_str_std_string());
}
void Lexer::error(const std::string& err_msg) const {

View file

@ -208,6 +208,7 @@ public:
std::string_view cur_str() const { return cur_token.str_val; }
std::string cur_str_std_string() const { return static_cast<std::string>(cur_token.str_val); }
SrcLocation cur_location() const { return location; }
const SrcFile* cur_file() const { return file; }
int cur_sym_idx() const;
void next();

View file

@ -31,7 +31,7 @@ void Optimizer::set_code(AsmOpConsList code) {
void Optimizer::unpack() {
int i = 0, j = 0;
for (AsmOpCons *p = code_.get(); p && i < n; p = p->cdr.get(), ++j) {
for (AsmOpCons *p = code_.get(); p && i < optimize_depth; p = p->cdr.get(), ++j) {
if (p->car->is_very_custom()) {
break;
}
@ -59,7 +59,7 @@ void Optimizer::apply() {
if (!p_ && !q_) {
return;
}
tolk_assert(p_ > 0 && p_ <= l_ && q_ >= 0 && q_ <= n && l_ <= n);
tolk_assert(p_ > 0 && p_ <= l_ && q_ >= 0 && q_ <= optimize_depth && l_ <= optimize_depth);
for (int i = p_; i < l_; i++) {
tolk_assert(op_[i]);
op_cons_[i]->car = std::move(op_[i]);

View file

@ -16,6 +16,7 @@
*/
#include "tolk.h"
#include "platform-utils.h"
#include "compiler-state.h"
#include "td/utils/crypto.h"
#include "common/refint.h"
#include "openssl/digest.hpp"
@ -26,15 +27,15 @@ namespace tolk {
using namespace std::literals::string_literals;
inline bool is_dot_ident(sym_idx_t idx) {
return symbols.get_subclass(idx) == SymbolSubclass::dot_identifier;
return G.symbols.get_subclass(idx) == SymbolSubclass::dot_identifier;
}
inline bool is_tilde_ident(sym_idx_t idx) {
return symbols.get_subclass(idx) == SymbolSubclass::tilde_identifier;
return G.symbols.get_subclass(idx) == SymbolSubclass::tilde_identifier;
}
inline bool is_special_ident(sym_idx_t idx) {
return symbols.get_subclass(idx) != SymbolSubclass::undef;
return G.symbols.get_subclass(idx) != SymbolSubclass::undef;
}
// given Expr::_Apply (a function call / a variable call), determine whether it's <, or >, or similar
@ -80,7 +81,7 @@ static bool is_add_or_sub_binary_op(const Expr* e_apply) {
}
static inline std::string get_builtin_operator_name(sym_idx_t sym_builtin) {
std::string underscored = symbols.get_name(sym_builtin);
std::string underscored = G.symbols.get_name(sym_builtin);
return underscored.substr(1, underscored.size() - 2);
}
@ -256,9 +257,9 @@ FormalArg parse_formal_arg(Lexer& lex, int fa_idx) {
}
lex.check(tok_identifier, "formal parameter name");
loc = lex.cur_location();
if (prohibited_var_names.count(symbols.get_name(lex.cur_sym_idx()))) {
if (G.prohibited_var_names.count(G.symbols.get_name(lex.cur_sym_idx()))) {
throw ParseError{
loc, PSTRING() << "symbol `" << symbols.get_name(lex.cur_sym_idx()) << "` cannot be redefined as a variable"};
loc, PSTRING() << "symbol `" << G.symbols.get_name(lex.cur_sym_idx()) << "` cannot be redefined as a variable"};
}
SymDef* new_sym_def = define_symbol(lex.cur_sym_idx(), true, loc);
if (!new_sym_def) {
@ -311,16 +312,15 @@ void parse_global_var_decl(Lexer& lex) {
lex.error(os.str());
}
} else {
sym_def->value = new SymValGlobVar{glob_var_cnt++, var_type};
sym_def->value = new SymValGlobVar{G.glob_var_cnt++, var_type};
#ifdef TOLK_DEBUG
dynamic_cast<SymValGlobVar*>(sym_def->value)->name = lex.cur_str();
#endif
glob_vars.push_back(sym_def);
G.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) {
@ -360,9 +360,9 @@ void parse_const_decl(Lexer& lex) {
}
SymValConst* new_value = nullptr;
if (x->cls == Expr::_Const) { // Integer constant
new_value = new SymValConst{const_cnt++, x->intval};
new_value = new SymValConst{G.const_cnt++, x->intval};
} else if (x->cls == Expr::_SliceConst) { // Slice constant (string)
new_value = new SymValConst{const_cnt++, x->strval};
new_value = new SymValConst{G.const_cnt++, x->strval};
} else if (x->cls == Expr::_Apply) { // even "1 + 2" is Expr::_Apply (it applies `_+_`)
code.emplace_back(loc, Op::_Import, std::vector<var_idx_t>());
auto tmp_vars = x->pre_compile(code);
@ -390,7 +390,7 @@ void parse_const_decl(Lexer& lex) {
if (op.origin.is_null() || !op.origin->is_valid()) {
lex.error("precompiled expression did not result in a valid integer constant");
}
new_value = new SymValConst{const_cnt++, op.origin};
new_value = new SymValConst{G.const_cnt++, op.origin};
} else {
lex.error("integer or slice literal or constant expected");
}
@ -453,28 +453,28 @@ void parse_global_var_decls(Lexer& lex) {
}
SymValCodeFunc* make_new_glob_func(SymDef* func_sym, TypeExpr* func_type, bool marked_as_pure) {
SymValCodeFunc* res = new SymValCodeFunc{glob_func_cnt, func_type, marked_as_pure};
SymValCodeFunc* res = new SymValCodeFunc{G.glob_func_cnt, func_type, marked_as_pure};
#ifdef TOLK_DEBUG
res->name = func_sym->name();
#endif
func_sym->value = res;
glob_func.push_back(func_sym);
glob_func_cnt++;
G.glob_func.push_back(func_sym);
G.glob_func_cnt++;
return res;
}
bool check_global_func(const Lexer& lex, sym_idx_t func_name) {
SymDef* def = lookup_symbol(func_name);
if (!def) {
lex.error("undefined symbol `" + symbols.get_name(func_name) + "`");
lex.error("undefined symbol `" + G.symbols.get_name(func_name) + "`");
return false;
}
SymVal* val = dynamic_cast<SymVal*>(def->value);
if (!val) {
lex.error(std::string{"symbol `"} + symbols.get_name(func_name) + "` has no value and no type");
lex.error(std::string{"symbol `"} + G.symbols.get_name(func_name) + "` has no value and no type");
return false;
} else if (!val->get_type()) {
lex.error(std::string{"symbol `"} + symbols.get_name(func_name) + "` has no type, possibly not a function");
lex.error(std::string{"symbol `"} + G.symbols.get_name(func_name) + "` has no type, possibly not a function");
return false;
} else {
return true;
@ -497,6 +497,21 @@ Expr* make_func_apply(Expr* fun, Expr* x) {
return res;
}
void check_import_exists_when_using_sym(const Lexer& lex, const SymDef* used_sym) {
if (!lex.cur_location().is_symbol_from_same_or_builtin_file(used_sym->loc)) {
const SrcFile* declared_in = used_sym->loc.get_src_file();
bool has_import = false;
for (const SrcFile::ImportStatement& import_stmt : lex.cur_file()->imports) {
if (import_stmt.imported_file == declared_in) {
has_import = true;
}
}
if (!has_import) {
lex.error("Using a non-imported symbol `" + used_sym->name() + "`. Forgot to import \"" + declared_in->rel_filename + "\"?");
}
}
}
// parse ( E { , E } ) | () | [ E { , E } ] | [] | id | num | _
Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
if (lex.tok() == tok_oppar || lex.tok() == tok_opbracket) {
@ -672,6 +687,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
return res;
}
if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
check_import_exists_when_using_sym(lex, sym);
auto val = dynamic_cast<SymValGlobVar*>(sym->value);
Expr* res = new Expr{Expr::_GlobVar, lex.cur_location()};
res->e_type = val->get_type();
@ -681,6 +697,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
return res;
}
if (sym && dynamic_cast<SymValConst*>(sym->value)) {
check_import_exists_when_using_sym(lex, sym);
auto val = dynamic_cast<SymValConst*>(sym->value);
Expr* res = new Expr{Expr::_None, lex.cur_location()};
res->flags = Expr::_IsRvalue;
@ -700,6 +717,9 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
lex.next();
return res;
}
if (sym && dynamic_cast<SymValFunc*>(sym->value)) {
check_import_exists_when_using_sym(lex, sym);
}
bool auto_apply = false;
Expr* res = new Expr{Expr::_Var, lex.cur_location()};
if (nv) {
@ -796,7 +816,7 @@ Expr* parse_expr80(Lexer& lex, CodeBlob& code, bool nv) {
sym_idx_t name = lex.cur_sym_idx();
auto sym = lookup_symbol(name);
if (!sym || !dynamic_cast<SymValFunc*>(sym->value)) {
auto name1 = symbols.lookup(lex.cur_str().substr(1));
auto name1 = G.symbols.lookup(lex.cur_str().substr(1));
if (name1) {
auto sym1 = lookup_symbol(name1);
if (sym1 && dynamic_cast<SymValFunc*>(sym1->value)) {
@ -806,8 +826,8 @@ Expr* parse_expr80(Lexer& lex, CodeBlob& code, bool nv) {
}
}
check_global_func(lex, name);
if (verbosity >= 2) {
std::cerr << "using symbol `" << symbols.get_name(name) << "` for method call of " << lex.cur_str() << std::endl;
if (G.is_verbosity(2)) {
std::cerr << "using symbol `" << G.symbols.get_name(name) << "` for method call of " << lex.cur_str() << std::endl;
}
sym = lookup_symbol(name);
SymValFunc* val = sym ? dynamic_cast<SymValFunc*>(sym->value) : nullptr;
@ -842,7 +862,7 @@ Expr* parse_expr80(Lexer& lex, CodeBlob& code, bool nv) {
Expr* parse_expr75(Lexer& lex, CodeBlob& code, bool nv) {
if (lex.tok() == tok_bitwise_not || lex.tok() == tok_minus || lex.tok() == tok_plus) {
TokenType t = lex.tok();
sym_idx_t name = symbols.lookup_add(lex.cur_str_std_string() + "_");
sym_idx_t name = G.symbols.lookup_add(lex.cur_str_std_string() + "_");
check_global_func(lex, name);
SrcLocation loc{lex.cur_location()};
lex.next();
@ -886,7 +906,7 @@ Expr* parse_expr30(Lexer& lex, CodeBlob& code, bool nv) {
lex.tok() == tok_divR || lex.tok() == tok_modC || lex.tok() == tok_modR) {
res->chk_rvalue(lex);
TokenType t = lex.tok();
sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur_str_std_string() + "_");
sym_idx_t name = G.symbols.lookup_add(std::string{"_"} + lex.cur_str_std_string() + "_");
SrcLocation loc{lex.cur_location()};
check_global_func(lex, name);
lex.next();
@ -907,7 +927,7 @@ Expr* parse_expr20(Lexer& lex, CodeBlob& code, bool nv) {
while (lex.tok() == tok_minus || lex.tok() == tok_plus) {
res->chk_rvalue(lex);
TokenType t = lex.tok();
sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur_str_std_string() + "_");
sym_idx_t name = G.symbols.lookup_add(std::string{"_"} + lex.cur_str_std_string() + "_");
check_global_func(lex, name);
SrcLocation loc{lex.cur_location()};
lex.next();
@ -928,7 +948,7 @@ Expr* parse_expr17(Lexer& lex, CodeBlob& code, bool nv) {
while (lex.tok() == tok_lshift || lex.tok() == tok_rshift || lex.tok() == tok_rshiftC || lex.tok() == tok_rshiftR) {
res->chk_rvalue(lex);
TokenType t = lex.tok();
sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur_str_std_string() + "_");
sym_idx_t name = G.symbols.lookup_add(std::string{"_"} + lex.cur_str_std_string() + "_");
check_global_func(lex, name);
SrcLocation loc{lex.cur_location()};
lex.next();
@ -951,7 +971,7 @@ Expr* parse_expr15(Lexer& lex, CodeBlob& code, bool nv) {
lex.tok() == tok_neq || lex.tok() == tok_spaceship) {
res->chk_rvalue(lex);
TokenType t = lex.tok();
sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur_str_std_string() + "_");
sym_idx_t name = G.symbols.lookup_add(std::string{"_"} + lex.cur_str_std_string() + "_");
check_global_func(lex, name);
SrcLocation loc{lex.cur_location()};
lex.next();
@ -972,7 +992,7 @@ Expr* parse_expr14(Lexer& lex, CodeBlob& code, bool nv) {
while (lex.tok() == tok_bitwise_and || lex.tok() == tok_bitwise_or || lex.tok() == tok_bitwise_xor) {
res->chk_rvalue(lex);
TokenType t = lex.tok();
sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur_str_std_string() + "_");
sym_idx_t name = G.symbols.lookup_add(std::string{"_"} + lex.cur_str_std_string() + "_");
check_global_func(lex, name);
SrcLocation loc{lex.cur_location()};
lex.next();
@ -1019,7 +1039,7 @@ Expr* parse_expr10(Lexer& lex, CodeBlob& code, bool nv) {
t == tok_set_rshiftR || t == tok_set_bitwise_and || t == tok_set_bitwise_or || t == tok_set_bitwise_xor) {
x->chk_lvalue(lex);
x->chk_rvalue(lex);
sym_idx_t name = symbols.lookup_add(std::string{"^_"} + lex.cur_str_std_string() + "_");
sym_idx_t name = G.symbols.lookup_add(std::string{"^_"} + lex.cur_str_std_string() + "_");
check_global_func(lex, name);
SrcLocation loc{lex.cur_location()};
lex.next();
@ -1464,8 +1484,8 @@ std::vector<TypeExpr*> parse_type_var_list(Lexer& lex) {
lex.error("free type identifier expected");
}
SrcLocation loc = lex.cur_location();
if (prohibited_var_names.count(symbols.get_name(lex.cur_sym_idx()))) {
throw ParseError{loc, PSTRING() << "symbol `" << symbols.get_name(lex.cur_sym_idx())
if (G.prohibited_var_names.count(G.symbols.get_name(lex.cur_sym_idx()))) {
throw ParseError{loc, PSTRING() << "symbol `" << G.symbols.get_name(lex.cur_sym_idx())
<< "` cannot be redefined as a variable"};
}
SymDef* new_sym_def = define_symbol(lex.cur_sym_idx(), true, loc);
@ -1582,7 +1602,7 @@ void detect_if_function_just_wraps_another(SymValCodeFunc* v_current, const td::
// ok, f_current is a wrapper
v_current->flags |= SymValFunc::flagWrapsAnotherF;
if (verbosity >= 2) {
if (G.is_verbosity(2)) {
std::cerr << function_name << " -> " << f_called->name() << std::endl;
}
}
@ -1658,7 +1678,7 @@ void parse_func_def(Lexer& lex) {
if (is_get_method) {
tolk_assert(method_id.is_null());
method_id = calculate_method_id_by_func_name(func_name);
for (const SymDef* other : glob_get_methods) {
for (const SymDef* other : G.glob_get_methods) {
if (!td::cmp(dynamic_cast<const SymValFunc*>(other->value)->method_id, method_id)) {
lex.error(PSTRING() << "GET methods hash collision: `" << other->name() << "` and `" + func_name + "` produce the same hash. Consider renaming one of these functions.");
}
@ -1667,7 +1687,7 @@ void parse_func_def(Lexer& lex) {
TypeExpr* func_type = TypeExpr::new_map(extract_total_arg_type(arg_list), ret_type);
func_type = compute_type_closure(func_type, type_vars);
if (lex.tok() == tok_builtin) {
const SymDef* builtin_func = lookup_symbol(symbols.lookup(func_name));
const SymDef* builtin_func = lookup_symbol(G.symbols.lookup(func_name));
const SymValFunc* func_val = builtin_func ? dynamic_cast<SymValFunc*>(builtin_func->value) : nullptr;
if (!func_val || !func_val->is_builtin()) {
lex.error("`builtin` used for non-builtin function");
@ -1686,7 +1706,7 @@ void parse_func_def(Lexer& lex) {
if (lex.tok() != tok_semicolon && lex.tok() != tok_opbrace && lex.tok() != tok_asm) {
lex.expect(tok_opbrace, "function body block");
}
if (verbosity >= 1) {
if (G.is_verbosity(1)) {
std::cerr << "function " << func_name << " : " << func_type << std::endl;
}
SymDef* func_sym = define_global_symbol(func_sym_idx, 0, loc);
@ -1783,9 +1803,9 @@ void parse_func_def(Lexer& lex) {
lex.error("cannot set unknown function `" + func_name + "` as a get method");
}
val->flags |= SymValFunc::flagGetMethod;
glob_get_methods.push_back(func_sym);
G.glob_get_methods.push_back(func_sym);
}
if (verbosity >= 1) {
if (G.is_verbosity(1)) {
std::cerr << "new type of function " << func_name << " : " << func_type << std::endl;
}
close_scope(lex.cur_location());
@ -1876,12 +1896,12 @@ void parse_pragma(Lexer& lex) {
if (!match) {
throw ParseError(loc, std::string("Tolk version ") + tolk_version + " does not satisfy this condition");
}
} else if (pragma_name == pragma_allow_post_modification.name()) {
pragma_allow_post_modification.enable(loc);
} else if (pragma_name == pragma_compute_asm_ltr.name()) {
pragma_compute_asm_ltr.enable(loc);
} else if (pragma_name == pragma_remove_unused_functions.name()) {
pragma_remove_unused_functions.enable(loc);
} else if (pragma_name == G.pragma_allow_post_modification.name()) {
G.pragma_allow_post_modification.enable(loc);
} else if (pragma_name == G.pragma_compute_asm_ltr.name()) {
G.pragma_compute_asm_ltr.enable(loc);
} else if (pragma_name == G.pragma_remove_unused_functions.name()) {
G.pragma_remove_unused_functions.enable(loc);
} else {
lex.error("unknown pragma name");
}
@ -1889,28 +1909,42 @@ void parse_pragma(Lexer& lex) {
lex.expect(tok_semicolon, "';'");
}
AllRegisteredSrcFiles all_src_files;
std::string stdlib_filename;
void parse_include(Lexer& lex, const SrcFile* parent_file) {
void parse_include(Lexer& lex, SrcFile* parent_file) {
SrcLocation loc = lex.cur_location();
lex.expect(tok_include, "#include");
if (lex.tok() != tok_string_const) {
lex.expect(tok_string_const, "source file name");
}
std::string val = static_cast<std::string>(lex.cur_str());
std::string parent_dir = parent_file->rel_filename;
if (size_t rc = parent_dir.rfind('/'); rc != std::string::npos) {
val = parent_dir.substr(0, rc + 1) + val;
std::string rel_filename = lex.cur_str_std_string();
if (rel_filename.empty()) {
lex.error("imported file name is an empty string");
}
if (size_t rc = parent_file->rel_filename.rfind('/'); rc != std::string::npos) {
rel_filename = parent_file->rel_filename.substr(0, rc + 1) + rel_filename;
}
lex.next();
lex.expect(tok_semicolon, "';'");
if (!parse_source_file(val.c_str(), loc)) {
lex.error(std::string{"failed parsing included file `"} + val + "`");
td::Result<SrcFile*> locate_res = locate_source_file(rel_filename);
if (locate_res.is_error()) {
throw ParseError(loc, "Failed to import: " + locate_res.move_as_error().message().str());
}
SrcFile* imported_file = locate_res.move_as_ok();
parent_file->imports.emplace_back(SrcFile::ImportStatement{imported_file});
if (!imported_file->was_parsed) {
parse_source_file(imported_file);
}
}
void parse_source(const SrcFile* file) {
// this function either throws (on any error) or returns nothing meaning success (filling global variables)
void parse_source_file(SrcFile* file) {
if (!file->is_stdlib_file()) {
G.generated_from += file->rel_filename;
G.generated_from += ", ";
}
file->was_parsed = true;
Lexer lex(file);
while (!lex.is_eof()) {
if (lex.tok() == tok_pragma) {
@ -1927,37 +1961,23 @@ void parse_source(const SrcFile* file) {
}
}
bool parse_source_file(const char* filename, SrcLocation loc_included_from) {
const SrcFile* included_from = loc_included_from.get_src_file();
if (!filename || !*filename) {
throw ParseError(loc_included_from, "source file name is an empty string");
td::Result<SrcFile*> locate_source_file(const std::string& rel_filename) {
td::Result<std::string> path = G.settings.read_callback(CompilerSettings::FsReadCallbackKind::Realpath, rel_filename.c_str());
if (path.is_error()) {
return path.move_as_error();
}
auto path_res = read_callback(ReadCallback::Kind::Realpath, filename);
if (path_res.is_error()) {
auto error = path_res.move_as_error();
throw ParseError(loc_included_from, error.message().c_str());
return false;
std::string abs_filename = path.move_as_ok();
if (SrcFile* file = G.all_src_files.find_file(abs_filename)) {
return file; // file was already parsed (imported from somewhere else)
}
std::string abs_filename = path_res.move_as_ok();
const SrcFile* file = all_src_files.find_file(abs_filename);
if (file != nullptr) {
if (verbosity >= 2) {
std::cerr << "skipping file " << abs_filename << " because it was already parsed" << '\n';
}
return true;
}
if (included_from) {
generated_from += std::string{"incl:"};
}
generated_from += std::string{"`"} + filename + "` ";
td::Result<std::string> text = read_callback(ReadCallback::Kind::ReadFile, abs_filename.c_str());
td::Result<std::string> text = G.settings.read_callback(CompilerSettings::FsReadCallbackKind::ReadFile, abs_filename.c_str());
if (text.is_error()) {
throw ParseError(loc_included_from, text.move_as_error().message().str());
return text.move_as_error();
}
file = all_src_files.register_file(filename, abs_filename, text.move_as_ok(), included_from);
parse_source(file);
return true;
return G.all_src_files.register_file(rel_filename, abs_filename, text.move_as_ok());
}
} // namespace tolk

View file

@ -15,17 +15,15 @@
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "src-file.h"
#include "compiler-state.h"
#include <iostream>
namespace tolk {
extern AllRegisteredSrcFiles all_src_files;
extern std::string stdlib_filename;
static_assert(sizeof(SrcLocation) == 8);
const SrcFile* AllRegisteredSrcFiles::find_file(int file_id) const {
for (const SrcFile* file : all_src_files) {
SrcFile* AllRegisteredSrcFiles::find_file(int file_id) const {
for (SrcFile* file : all_src_files) {
if (file->file_id == file_id) {
return file;
}
@ -33,8 +31,8 @@ const SrcFile* AllRegisteredSrcFiles::find_file(int file_id) const {
return nullptr;
}
const SrcFile* AllRegisteredSrcFiles::find_file(const std::string& abs_filename) const {
for (const SrcFile* file : all_src_files) {
SrcFile* AllRegisteredSrcFiles::find_file(const std::string& abs_filename) const {
for (SrcFile* file : all_src_files) {
if (file->abs_filename == abs_filename) {
return file;
}
@ -42,17 +40,13 @@ const SrcFile* AllRegisteredSrcFiles::find_file(const std::string& abs_filename)
return nullptr;
}
const SrcFile* AllRegisteredSrcFiles::register_file(const std::string& rel_filename, const std::string& abs_filename, std::string&& text, const SrcFile* included_from) {
SrcFile* created = new SrcFile(++last_file_id, rel_filename, abs_filename, std::move(text), included_from);
SrcFile* AllRegisteredSrcFiles::register_file(const std::string& rel_filename, const std::string& abs_filename, std::string&& text) {
SrcFile* created = new SrcFile(++last_file_id, rel_filename, abs_filename, std::move(text));
all_src_files.push_back(created);
return created;
}
bool SrcFile::is_entrypoint_file() const {
return file_id == (stdlib_filename.empty() ? 0 : 1);
}
bool SrcFile::is_offset_valid(int offset) const {
return offset >= 0 && offset < static_cast<int>(text.size());
}
@ -98,7 +92,7 @@ std::ostream& operator<<(std::ostream& os, const Fatal& fatal) {
}
const SrcFile* SrcLocation::get_src_file() const {
return all_src_files.find_file(file_id);
return G.all_src_files.find_file(file_id);
}
void SrcLocation::show(std::ostream& os) const {

View file

@ -29,23 +29,29 @@ struct SrcFile {
std::string_view line_str;
};
int file_id;
std::string rel_filename;
std::string abs_filename;
std::string text;
const SrcFile* included_from{nullptr};
struct ImportStatement {
const SrcFile* imported_file;
};
SrcFile(int file_id, std::string rel_filename, std::string abs_filename, std::string&& text, const SrcFile* included_from)
int file_id; // an incremental counter through all parsed files
std::string rel_filename; // relative to cwd
std::string abs_filename; // absolute from root
std::string text; // file contents loaded into memory, Token::str_val points into it
bool was_parsed = false; // to prevent double parsing when a file is imported multiple times
std::vector<ImportStatement> imports; // to check strictness (can't use a symbol without importing its file)
SrcFile(int file_id, std::string rel_filename, std::string abs_filename, std::string&& text)
: file_id(file_id)
, rel_filename(std::move(rel_filename))
, abs_filename(std::move(abs_filename))
, text(std::move(text))
, included_from(included_from) { }
, text(std::move(text)) { }
SrcFile(const SrcFile& other) = delete;
SrcFile &operator=(const SrcFile&) = delete;
bool is_entrypoint_file() const;
bool is_stdlib_file() const { return file_id == 0; /* stdlib always exists, has no imports and parsed the first */ }
bool is_entrypoint_file() const { return file_id == 1; /* after stdlib, the entrypoint file is parsed */ }
bool is_offset_valid(int offset) const;
SrcPosition convert_offset(int offset) const;
};
@ -55,12 +61,48 @@ class AllRegisteredSrcFiles {
int last_file_id = -1;
public:
const SrcFile *find_file(int file_id) const;
const SrcFile* find_file(const std::string& abs_filename) const;
const SrcFile* register_file(const std::string& rel_filename, const std::string& abs_filename, std::string&& text, const SrcFile* included_from);
SrcFile *find_file(int file_id) const;
SrcFile* find_file(const std::string& abs_filename) const;
SrcFile* register_file(const std::string& rel_filename, const std::string& abs_filename, std::string&& text);
const std::vector<SrcFile*>& get_all_files() const { return all_src_files; }
};
// SrcLocation points to a location (line, column) in some loaded .tolk source SrcFile.
// Note, that instead of storing src_file, line_no, etc., only 2 ints are stored.
// The purpose is: sizeof(SrcLocation) == 8, so it's just passed/stored without pointers/refs, just like int64_t.
// When decoding SrcLocation into human-readable format, it's converted to SrcFile::SrcPosition via offset.
class SrcLocation {
friend class Lexer;
int file_id = -1; // = SrcFile::file_id (note, that get_src_file() does linear search)
int char_offset = -1; // offset from SrcFile::text
public:
SrcLocation() = default;
explicit SrcLocation(const SrcFile* src_file) : file_id(src_file->file_id) {
}
bool is_defined() const { return file_id != -1; }
const SrcFile* get_src_file() const;
// similar to `this->get_src_file() == symbol->get_src_file() || symbol->get_src_file()->is_stdlib()`
// (but effectively, avoiding linear search)
bool is_symbol_from_same_or_builtin_file(SrcLocation symbol_loc) const {
return file_id == symbol_loc.file_id || symbol_loc.file_id < 1;
}
void show(std::ostream& os) const;
void show_context(std::ostream& os) const;
void show_general_error(std::ostream& os, const std::string& message, const std::string& err_type) const;
void show_note(const std::string& err_msg) const;
void show_warning(const std::string& err_msg) const;
void show_error(const std::string& err_msg) const;
};
std::ostream& operator<<(std::ostream& os, SrcLocation loc);
struct Fatal final : std::exception {
std::string message;
@ -73,36 +115,6 @@ struct Fatal final : std::exception {
std::ostream& operator<<(std::ostream& os, const Fatal& fatal);
// SrcLocation points to a location (line, column) in some loaded .tolk source SrcFile.
// Note, that instead of storing src_file, line_no, etc., only 2 ints are stored.
// The purpose is: sizeof(SrcLocation) == 8, so it's just passed/stored without pointers/refs, just like int64_t.
// When decoding SrcLocation into human-readable format, it's converted to SrcFile::SrcPosition via offset.
class SrcLocation {
friend class Lexer;
int file_id = -1; // file_id from AllRegisteredSrcFiles
int char_offset = -1; // offset from SrcFile::text
public:
SrcLocation() = default;
explicit SrcLocation(const SrcFile* src_file) : file_id(src_file->file_id) {
}
bool is_defined() const { return file_id != -1; }
const SrcFile* get_src_file() const;
void show(std::ostream& os) const;
void show_context(std::ostream& os) const;
void show_general_error(std::ostream& os, const std::string& message, const std::string& err_type) const;
void show_note(const std::string& err_msg) const;
void show_warning(const std::string& err_msg) const;
void show_error(const std::string& err_msg) const;
};
std::ostream& operator<<(std::ostream& os, SrcLocation loc);
struct ParseError : std::exception {
SrcLocation where;
std::string message;

View file

@ -15,25 +15,12 @@
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "symtable.h"
#include "compiler-state.h"
#include <sstream>
#include <cassert>
namespace tolk {
/*
*
* SYMBOL VALUES (DECLARED)
*
*/
int scope_level;
SymTable symbols;
SymDef* sym_def[symbols.SIZE_PRIME + 1];
SymDef* global_sym_def[symbols.SIZE_PRIME + 1];
std::vector<std::pair<int, SymDef>> symbol_stack;
std::vector<SrcLocation> scope_opened_at;
Symbol::Symbol(std::string str, sym_idx_t idx) : str(std::move(str)), idx(idx) {
subclass = this->str[0] == '.' ? SymbolSubclass::dot_identifier
@ -82,22 +69,26 @@ sym_idx_t SymTable::gen_lookup(std::string_view str, int mode, sym_idx_t idx) {
}
}
std::string SymDef::name() const {
return G.symbols.get_name(sym_idx);
}
void open_scope(SrcLocation loc) {
++scope_level;
scope_opened_at.push_back(loc);
++G.scope_level;
G.scope_opened_at.push_back(loc);
}
void close_scope(SrcLocation loc) {
if (!scope_level) {
if (!G.scope_level) {
throw Fatal{"cannot close the outer scope"};
}
while (!symbol_stack.empty() && symbol_stack.back().first == scope_level) {
SymDef old_def = symbol_stack.back().second;
while (!G.symbol_stack.empty() && G.symbol_stack.back().first == G.scope_level) {
SymDef old_def = G.symbol_stack.back().second;
auto idx = old_def.sym_idx;
symbol_stack.pop_back();
SymDef* cur_def = sym_def[idx];
G.symbol_stack.pop_back();
SymDef* cur_def = G.sym_def[idx];
assert(cur_def);
assert(cur_def->level == scope_level && cur_def->sym_idx == idx);
assert(cur_def->level == G.scope_level && cur_def->sym_idx == idx);
//std::cerr << "restoring local symbol `" << old_def.name << "` of level " << scope_level << " to its previous level " << old_def.level << std::endl;
if (cur_def->value) {
//std::cerr << "deleting value of symbol " << old_def.name << ":" << old_def.level << " at " << (const void*) it->second.value << std::endl;
@ -105,26 +96,26 @@ void close_scope(SrcLocation loc) {
}
if (!old_def.level && !old_def.value) {
delete cur_def; // ??? keep the definition always?
sym_def[idx] = nullptr;
G.sym_def[idx] = nullptr;
} else {
cur_def->value = std::move(old_def.value);
cur_def->value = old_def.value;
cur_def->level = old_def.level;
}
old_def.value = nullptr;
}
--scope_level;
scope_opened_at.pop_back();
--G.scope_level;
G.scope_opened_at.pop_back();
}
SymDef* lookup_symbol(sym_idx_t idx) {
if (!idx) {
return nullptr;
}
if (sym_def[idx]) {
return sym_def[idx];
if (G.sym_def[idx]) {
return G.sym_def[idx];
}
if (global_sym_def[idx]) {
return global_sym_def[idx];
if (G.global_sym_def[idx]) {
return G.global_sym_def[idx];
}
return nullptr;
}
@ -133,11 +124,11 @@ SymDef* define_global_symbol(sym_idx_t name_idx, bool force_new, SrcLocation loc
if (!name_idx) {
return nullptr;
}
auto found = global_sym_def[name_idx];
auto found = G.global_sym_def[name_idx];
if (found) {
return force_new && found->value ? nullptr : found;
}
found = global_sym_def[name_idx] = new SymDef(0, name_idx, loc);
found = G.global_sym_def[name_idx] = new SymDef(0, name_idx, loc);
#ifdef TOLK_DEBUG
found->sym_name = found->name();
#endif
@ -148,26 +139,26 @@ SymDef* define_symbol(sym_idx_t name_idx, bool force_new, SrcLocation loc) {
if (!name_idx) {
return nullptr;
}
if (!scope_level) {
if (!G.scope_level) {
return define_global_symbol(name_idx, force_new, loc);
}
auto found = sym_def[name_idx];
auto found = G.sym_def[name_idx];
if (found) {
if (found->level < scope_level) {
symbol_stack.push_back(std::make_pair(scope_level, *found));
found->level = scope_level;
if (found->level < G.scope_level) {
G.symbol_stack.emplace_back(G.scope_level, *found);
found->level = G.scope_level;
} else if (found->value && force_new) {
return nullptr;
}
found->value = 0;
found->value = nullptr;
found->loc = loc;
return found;
}
found = sym_def[name_idx] = new SymDef(scope_level, name_idx, loc);
symbol_stack.push_back(std::make_pair(scope_level, SymDef{0, name_idx, loc}));
found = G.sym_def[name_idx] = new SymDef(G.scope_level, name_idx, loc);
G.symbol_stack.emplace_back(G.scope_level, SymDef{0, name_idx, loc});
#ifdef TOLK_DEBUG
found->sym_name = found->name();
symbol_stack.back().second.sym_name = found->name();
G.symbol_stack.back().second.sym_name = found->name();
#endif
return found;
}

View file

@ -15,20 +15,15 @@
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "src-file.h"
#include <functional>
#include <memory>
#include <vector>
namespace tolk {
/*
*
* SYMBOL VALUES (DECLARED)
*
*/
typedef int var_idx_t;
typedef int sym_idx_t;
enum class SymValKind { _Param, _Var, _Func, _Typename, _GlobVar, _Const };
@ -40,11 +35,6 @@ struct SymValBase {
virtual ~SymValBase() = default;
};
/*
*
* SYMBOL TABLE
*
*/
enum class SymbolSubclass {
undef = 0,
@ -52,8 +42,6 @@ enum class SymbolSubclass {
tilde_identifier = 2 // begins with ~ (a non-const method)
};
typedef int sym_idx_t;
struct Symbol {
std::string str;
sym_idx_t idx;
@ -73,9 +61,6 @@ private:
std::unique_ptr<Symbol> sym[SIZE_PRIME + 1];
sym_idx_t gen_lookup(std::string_view str, int mode = 0, sym_idx_t idx = 0);
static constexpr int max_kw_idx = 10000;
sym_idx_t keywords[max_kw_idx];
public:
static constexpr sym_idx_t not_found = 0;
@ -88,22 +73,12 @@ public:
Symbol* operator[](sym_idx_t i) const {
return sym[i].get();
}
bool is_keyword(sym_idx_t i) const {
return sym[i] && sym[i]->idx < 0;
}
std::string get_name(sym_idx_t i) const {
return sym[i] ? sym[i]->str : Symbol::unknown_symbol_name(i);
}
SymbolSubclass get_subclass(sym_idx_t i) const {
return sym[i] ? sym[i]->subclass : SymbolSubclass::undef;
}
Symbol* get_keyword(int i) const {
return ((unsigned)i < (unsigned)max_kw_idx) ? sym[keywords[i]].get() : nullptr;
}
SymTable() {
std::memset(keywords, 0, sizeof(keywords));
}
};
struct SymTableOverflow {
@ -112,15 +87,6 @@ struct SymTableOverflow {
}
};
struct SymTableKwRedef {
std::string kw;
SymTableKwRedef(std::string _kw) : kw(_kw) {
}
};
extern SymTable symbols;
extern int scope_level;
struct SymDef {
int level;
@ -133,18 +99,9 @@ struct SymDef {
SymDef(int lvl, sym_idx_t idx, SrcLocation _loc, SymValBase* val = nullptr)
: level(lvl), sym_idx(idx), value(val), loc(_loc) {
}
bool has_name() const {
return sym_idx;
}
std::string name() const {
return symbols.get_name(sym_idx);
}
std::string name() const;
};
extern SymDef* sym_def[symbols.SIZE_PRIME + 1];
extern SymDef* global_sym_def[symbols.SIZE_PRIME + 1];
extern std::vector<std::pair<int, SymDef>> symbol_stack;
extern std::vector<SrcLocation> scope_opened_at;
void open_scope(SrcLocation loc);
void close_scope(SrcLocation loc);

View file

@ -24,16 +24,23 @@
from all source files in the program, then also delete it here.
*/
#include "tolk.h"
#include "compiler-state.h"
#include "td/utils/port/path.h"
#include <getopt.h>
#include <fstream>
#include <utility>
#include <sys/stat.h>
#include "git.h"
using namespace tolk;
void usage(const char* progname) {
std::cerr
<< "usage: " << progname << " [options] <filename.tolk>\n"
"\tGenerates Fift TVM assembler code from a .tolk file\n"
"-o<fif-filename>\tWrites generated code into specified .fif file instead of stdout\n"
"-b<boc-filename>\tGenerate Fift instructions to save TVM bytecode into .boc file\n"
"-s<stdlib.tolk>\tSpecify stdlib location (same as env TOLK_STDLIB; if unset, auto-discover)\n"
"-O<level>\tSets optimization level (2 by default)\n"
"-S\tDon't include stack layout comments into Fift output\n"
"-e\tIncreases verbosity level (extra output into stderr)\n"
@ -41,28 +48,100 @@ void usage(const char* progname) {
std::exit(2);
}
static std::string auto_discover_stdlib_location() {
if (const char* env_var = getenv("TOLK_STDLIB")) {
return env_var;
}
// this define is automatically set if just building this repo locally with cmake
#ifdef STDLIB_TOLK_IF_BUILD_FROM_SOURCES
return STDLIB_TOLK_IF_BUILD_FROM_SOURCES;
#endif
// this define is automatically set when compiling a linux package for distribution
// (since binaries and smartcont/ folder are installed to a predefined path)
// todo provide in cmake
#ifdef STDLIB_TOLK_IF_BUILD_TO_PACKAGE
return STDLIB_TOLK_IF_BUILD_TO_PACKAGE;
#endif
return {};
}
td::Result<std::string> fs_read_callback(CompilerSettings::FsReadCallbackKind kind, const char* query) {
switch (kind) {
case CompilerSettings::FsReadCallbackKind::ReadFile: {
struct stat f_stat;
int res = stat(query, &f_stat);
if (res != 0 || !S_ISREG(f_stat.st_mode)) {
return td::Status::Error(std::string{"cannot open file "} + query);
}
size_t file_size = static_cast<size_t>(f_stat.st_size);
std::string str;
str.resize(file_size);
FILE* f = fopen(query, "rb");
fread(str.data(), file_size, 1, f);
fclose(f);
return std::move(str);
}
case CompilerSettings::FsReadCallbackKind::Realpath: {
td::Result<std::string> res_realpath = td::realpath(td::CSlice(query));
if (res_realpath.is_error()) {
return td::Status::Error(std::string{"cannot find file "} + query);
}
return res_realpath;
}
default: {
return td::Status::Error("Unknown query kind");
}
}
}
class StdCoutRedirectToFile {
std::unique_ptr<std::fstream> output_file;
std::streambuf* backup_sbuf = nullptr;
public:
explicit StdCoutRedirectToFile(const std::string& output_filename) {
if (!output_filename.empty()) {
output_file = std::make_unique<std::fstream>(output_filename, std::fstream::trunc | std::fstream::out);
if (output_file->is_open()) {
backup_sbuf = std::cout.rdbuf(output_file->rdbuf());
}
}
}
~StdCoutRedirectToFile() {
if (backup_sbuf) {
std::cout.rdbuf(backup_sbuf);
}
}
bool is_failed() const { return output_file && !output_file->is_open(); }
};
int main(int argc, char* const argv[]) {
int i;
std::string output_filename;
while ((i = getopt(argc, argv, "o:b:O:Sevh")) != -1) {
while ((i = getopt(argc, argv, "o:b:s:O:Sevh")) != -1) {
switch (i) {
case 'o':
output_filename = optarg;
G.settings.output_filename = optarg;
break;
case 'b':
tolk::boc_output_filename = optarg;
G.settings.boc_output_filename = optarg;
break;
case 's':
G.settings.stdlib_filename = optarg;
break;
case 'O':
tolk::opt_level = std::max(0, atoi(optarg));
G.settings.optimization_level = std::max(0, atoi(optarg));
break;
case 'S':
tolk::stack_layout_comments = false;
G.settings.stack_layout_comments = false;
break;
case 'e':
++tolk::verbosity;
G.settings.verbosity++;
break;
case 'v':
std::cout << "Tolk compiler v" << tolk::tolk_version << "\n";
std::cout << "Tolk compiler v" << tolk_version << "\n";
std::cout << "Build commit: " << GitMetadata::CommitSHA1() << "\n";
std::cout << "Build date: " << GitMetadata::CommitDate() << "\n";
std::exit(0);
@ -72,16 +151,24 @@ int main(int argc, char* const argv[]) {
}
}
std::ostream *outs = &std::cout;
StdCoutRedirectToFile redirect_cout(G.settings.output_filename);
if (redirect_cout.is_failed()) {
std::cerr << "Failed to create output file " << G.settings.output_filename << '\n';
return 2;
}
std::unique_ptr<std::fstream> fs;
if (!output_filename.empty()) {
fs = std::make_unique<std::fstream>(output_filename, std::fstream::trunc | std::fstream::out);
if (!fs->is_open()) {
std::cerr << "failed to create output file " << output_filename << '\n';
return 2;
}
outs = fs.get();
// if stdlib wasn't specify as an option — locate it based on env
if (G.settings.stdlib_filename.empty()) {
G.settings.stdlib_filename = auto_discover_stdlib_location();
}
if (G.settings.stdlib_filename.empty()) {
std::cerr << "Failed to discover stdlib.tolk.\n"
"Probably, you have a non-standard Tolk installation.\n"
"Please, provide env variable TOLK_STDLIB referencing to it.\n";
return 2;
}
if (G.is_verbosity(2)) {
std::cerr << "stdlib located at " << G.settings.stdlib_filename << '\n';
}
if (optind != argc - 1) {
@ -89,9 +176,8 @@ int main(int argc, char* const argv[]) {
return 2;
}
std::string entrypoint_file_name = argv[optind];
G.settings.entrypoint_filename = argv[optind];
G.settings.read_callback = fs_read_callback;
tolk::read_callback = tolk::fs_read_callback;
return tolk::tolk_proceed(entrypoint_file_name, *outs, std::cerr);
return tolk_proceed(G.settings.entrypoint_filename);
}

View file

@ -24,28 +24,34 @@
from all source files in the program, then also delete it here.
*/
#include "tolk.h"
#include "compiler-state.h"
#include "git.h"
#include "td/utils/JsonBuilder.h"
#include "fift/utils.h"
#include "td/utils/base64.h"
#include "td/utils/Status.h"
#include <sstream>
#include <iomanip>
using namespace tolk;
td::Result<std::string> compile_internal(char *config_json) {
TRY_RESULT(input_json, td::json_decode(td::MutableSlice(config_json)))
td::JsonObject& config = input_json.get_object();
TRY_RESULT(opt_level, td::get_json_object_int_field(config, "optimizationLevel", true, 2));
TRY_RESULT(stdlib_tolk, td::get_json_object_string_field(config, "stdlibLocation", false));
TRY_RESULT(stack_comments, td::get_json_object_bool_field(config, "withStackComments", true, false));
TRY_RESULT(entrypoint_file_name, td::get_json_object_string_field(config, "entrypointFileName", false));
tolk::opt_level = std::max(0, opt_level);
tolk::verbosity = 0;
tolk::stack_layout_comments = stack_comments;
G.settings.verbosity = 0;
G.settings.optimization_level = std::max(0, opt_level);
G.settings.stdlib_filename = stdlib_tolk;
G.settings.stack_layout_comments = stack_comments;
G.settings.entrypoint_filename = entrypoint_file_name;
std::ostringstream outs, errs;
int tolk_res = tolk::tolk_proceed(entrypoint_file_name, outs, errs);
std::cout.rdbuf(outs.rdbuf());
std::cerr.rdbuf(errs.rdbuf());
int tolk_res = tolk::tolk_proceed(entrypoint_file_name);
if (tolk_res != 0) {
return td::Status::Error("Tolk compilation error: " + errs.str());
}
@ -58,6 +64,7 @@ td::Result<std::string> compile_internal(char *config_json) {
obj("fiftCode", fift_res.fiftCode);
obj("codeBoc64", fift_res.codeBoc64);
obj("codeHashHex", fift_res.codeHashHex);
obj("stderr", errs.str().c_str());
obj.leave();
return result_json.string_builder().as_cslice().str();
@ -68,11 +75,11 @@ td::Result<std::string> compile_internal(char *config_json) {
/// The implementor must use malloc() for them and use free() after tolk_compile returns.
typedef void (*CStyleReadFileCallback)(int kind, char const* data, char** destContents, char** destError);
tolk::ReadCallback::Callback wrapReadCallback(CStyleReadFileCallback _readCallback)
CompilerSettings::FsReadCallback wrapReadCallback(CStyleReadFileCallback _readCallback)
{
tolk::ReadCallback::Callback readCallback;
CompilerSettings::FsReadCallback readCallback;
if (_readCallback) {
readCallback = [=](tolk::ReadCallback::Kind kind, char const* data) -> td::Result<std::string> {
readCallback = [=](CompilerSettings::FsReadCallbackKind kind, char const* data) -> td::Result<std::string> {
char* destContents = nullptr;
char* destError = nullptr;
_readCallback(static_cast<int>(kind), data, &destContents, &destError);
@ -93,7 +100,7 @@ extern "C" {
const char* version() {
auto version_json = td::JsonBuilder();
auto obj = version_json.enter_object();
obj("tolkVersion", tolk::tolk_version);
obj("tolkVersion", tolk_version);
obj("tolkFiftLibCommitHash", GitMetadata::CommitSHA1());
obj("tolkFiftLibCommitDate", GitMetadata::CommitDate());
obj.leave();
@ -101,13 +108,9 @@ const char* version() {
}
const char *tolk_compile(char *config_json, CStyleReadFileCallback callback) {
if (callback) {
tolk::read_callback = wrapReadCallback(callback);
} else {
tolk::read_callback = tolk::fs_read_callback;
}
G.settings.read_callback = wrapReadCallback(callback);
auto res = compile_internal(config_json);
td::Result<std::string> res = compile_internal(config_json);
if (res.is_error()) {
auto result = res.move_as_error();

View file

@ -24,6 +24,7 @@
from all source files in the program, then also delete it here.
*/
#include "tolk.h"
#include "compiler-state.h"
#include "lexer.h"
#include <getopt.h>
#include "git.h"
@ -33,14 +34,6 @@
namespace tolk {
int verbosity = 0, opt_level = 2;
bool stack_layout_comments = true;
GlobalPragma pragma_allow_post_modification{"allow-post-modification"};
GlobalPragma pragma_compute_asm_ltr{"compute-asm-ltr"};
GlobalPragma pragma_remove_unused_functions{"remove-unused-functions"};
std::string generated_from, boc_output_filename;
ReadCallback::Callback read_callback;
// returns argument type of a function
// note, that when a function has multiple arguments, its arg type is a tensor (no arguments — an empty tensor)
// in other words, `f(int a, int b)` and `f((int,int) ab)` is the same when we speak about types
@ -61,7 +54,7 @@ const TypeExpr *SymValFunc::get_arg_type() const {
bool SymValCodeFunc::does_need_codegen() const {
// when a function is declared, but not referenced from code in any way, don't generate its body
if (!is_really_used && pragma_remove_unused_functions.enabled()) {
if (!is_really_used && G.pragma_remove_unused_functions.enabled()) {
return false;
}
// when a function is referenced like `var a = some_fn;` (or in some other non-call way), its continuation should exist
@ -74,53 +67,6 @@ bool SymValCodeFunc::does_need_codegen() const {
// in the future, we may want to implement a true AST inlining for `inline` functions also
}
void GlobalPragma::enable(SrcLocation loc) {
if (deprecated_from_v_) {
loc.show_warning(PSTRING() << "#pragma " << name_ <<
" is deprecated since Tolk v" << deprecated_from_v_ <<
". Please, remove this line from your code.");
return;
}
if (!loc.get_src_file()->is_entrypoint_file()) {
// todo generally it's not true; rework pragmas completely
loc.show_warning(PSTRING() << "#pragma " << name_ <<
" should be used in the main file only.");
}
enabled_ = true;
}
void GlobalPragma::always_on_and_deprecated(const char *deprecated_from_v) {
deprecated_from_v_ = deprecated_from_v;
enabled_ = true;
}
td::Result<std::string> fs_read_callback(ReadCallback::Kind kind, const char* query) {
switch (kind) {
case ReadCallback::Kind::ReadFile: {
struct stat f_stat;
int res = stat(query, &f_stat);
if (res != 0) {
return td::Status::Error(std::string{"cannot open source file: "} + query);
}
size_t file_size = static_cast<size_t>(f_stat.st_size);
std::string str;
str.resize(file_size);
FILE* f = fopen(query, "r");
fread(str.data(), file_size, 1, f);
fclose(f);
return std::move(str);
}
case ReadCallback::Kind::Realpath: {
return td::realpath(td::CSlice(query));
}
default: {
return td::Status::Error("Unknown query kind");
}
}
}
void mark_function_used_dfs(const std::unique_ptr<Op>& op);
void mark_function_used(SymValCodeFunc* func_val) {
@ -159,9 +105,9 @@ void mark_function_used_dfs(const std::unique_ptr<Op>& op) {
}
void mark_used_symbols() {
for (SymDef* func_sym : glob_func) {
for (SymDef* func_sym : G.glob_func) {
auto* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
std::string name = symbols.get_name(func_sym->sym_idx);
std::string name = G.symbols.get_name(func_sym->sym_idx);
if (func_val->method_id.not_null() ||
name == "main" || name == "recv_internal" || name == "recv_external" ||
name == "run_ticktock" || name == "split_prepare" || name == "split_install") {
@ -176,58 +122,58 @@ void mark_used_symbols() {
*
*/
void generate_output_func(SymDef* func_sym, std::ostream &outs, std::ostream &errs) {
void generate_output_func(SymDef* func_sym) {
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
tolk_assert(func_val);
std::string name = symbols.get_name(func_sym->sym_idx);
if (verbosity >= 2) {
errs << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
std::string name = G.symbols.get_name(func_sym->sym_idx);
if (G.is_verbosity(2)) {
std::cerr << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
}
if (!func_val->code) {
throw ParseError(func_sym->loc, "function `" + name + "` is just declared, not implemented");
} else {
CodeBlob& code = *(func_val->code);
if (verbosity >= 3) {
code.print(errs, 9);
if (G.is_verbosity(3)) {
code.print(std::cerr, 9);
}
code.simplify_var_types();
if (verbosity >= 5) {
errs << "after simplify_var_types: \n";
code.print(errs, 0);
if (G.is_verbosity(5)) {
std::cerr << "after simplify_var_types: \n";
code.print(std::cerr, 0);
}
code.prune_unreachable_code();
if (verbosity >= 5) {
errs << "after prune_unreachable: \n";
code.print(errs, 0);
if (G.is_verbosity(5)) {
std::cerr << "after prune_unreachable: \n";
code.print(std::cerr, 0);
}
code.split_vars(true);
if (verbosity >= 5) {
errs << "after split_vars: \n";
code.print(errs, 0);
if (G.is_verbosity(5)) {
std::cerr << "after split_vars: \n";
code.print(std::cerr, 0);
}
for (int i = 0; i < 8; i++) {
code.compute_used_code_vars();
if (verbosity >= 4) {
errs << "after compute_used_vars: \n";
code.print(errs, 6);
if (G.is_verbosity(4)) {
std::cerr << "after compute_used_vars: \n";
code.print(std::cerr, 6);
}
code.fwd_analyze();
if (verbosity >= 5) {
errs << "after fwd_analyze: \n";
code.print(errs, 6);
if (G.is_verbosity(5)) {
std::cerr << "after fwd_analyze: \n";
code.print(std::cerr, 6);
}
code.prune_unreachable_code();
if (verbosity >= 5) {
errs << "after prune_unreachable: \n";
code.print(errs, 6);
if (G.is_verbosity(5)) {
std::cerr << "after prune_unreachable: \n";
code.print(std::cerr, 6);
}
}
code.mark_noreturn();
if (verbosity >= 3) {
code.print(errs, 15);
if (G.is_verbosity(3)) {
code.print(std::cerr, 15);
}
if (verbosity >= 2) {
errs << "\n---------- resulting code for " << name << " -------------\n";
if (G.is_verbosity(2)) {
std::cerr << "\n---------- resulting code for " << name << " -------------\n";
}
const char* modifier = "";
if (func_val->is_inline()) {
@ -235,115 +181,119 @@ void generate_output_func(SymDef* func_sym, std::ostream &outs, std::ostream &er
} else if (func_val->is_inline_ref()) {
modifier = "REF";
}
outs << std::string(2, ' ') << name << " PROC" << modifier << ":<{\n";
std::cout << std::string(2, ' ') << name << " PROC" << modifier << ":<{\n";
int mode = 0;
if (stack_layout_comments) {
if (G.settings.stack_layout_comments) {
mode |= Stack::_StkCmt | Stack::_CptStkCmt;
}
if (opt_level < 2) {
mode |= Stack::_DisableOpt;
}
if (func_val->is_inline() && code.ops->noreturn()) {
mode |= Stack::_InlineFunc;
}
if (func_val->is_inline() || func_val->is_inline_ref()) {
mode |= Stack::_InlineAny;
}
code.generate_code(outs, mode, 2);
outs << std::string(2, ' ') << "}>\n";
if (verbosity >= 2) {
errs << "--------------\n";
code.generate_code(std::cout, mode, 2);
std::cout << std::string(2, ' ') << "}>\n";
if (G.is_verbosity(2)) {
std::cerr << "--------------\n";
}
}
}
int generate_output(std::ostream &outs, std::ostream &errs) {
outs << "\"Asm.fif\" include\n";
outs << "// automatically generated from " << generated_from << std::endl;
outs << "PROGRAM{\n";
// this function either throws or successfully prints whole program output to std::cout
void generate_output() {
std::cout << "\"Asm.fif\" include\n";
std::cout << "// automatically generated from " << G.generated_from << std::endl;
std::cout << "PROGRAM{\n";
mark_used_symbols();
for (SymDef* func_sym : glob_func) {
for (SymDef* func_sym : G.glob_func) {
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
tolk_assert(func_val);
if (!func_val->does_need_codegen()) {
if (verbosity >= 2) {
errs << func_sym->name() << ": code not generated, function does not need codegen\n";
if (G.is_verbosity(2)) {
std::cerr << func_sym->name() << ": code not generated, function does not need codegen\n";
}
continue;
}
std::string name = symbols.get_name(func_sym->sym_idx);
outs << std::string(2, ' ');
std::string name = G.symbols.get_name(func_sym->sym_idx);
std::cout << std::string(2, ' ');
if (func_val->method_id.is_null()) {
outs << "DECLPROC " << name << "\n";
std::cout << "DECLPROC " << name << "\n";
} else {
outs << func_val->method_id << " DECLMETHOD " << name << "\n";
std::cout << func_val->method_id << " DECLMETHOD " << name << "\n";
}
}
for (SymDef* gvar_sym : glob_vars) {
for (SymDef* gvar_sym : G.glob_vars) {
auto* glob_val = dynamic_cast<SymValGlobVar*>(gvar_sym->value);
tolk_assert(glob_val);
if (!glob_val->is_really_used && pragma_remove_unused_functions.enabled()) {
if (verbosity >= 2) {
errs << gvar_sym->name() << ": variable not generated, it's unused\n";
if (!glob_val->is_really_used && G.pragma_remove_unused_functions.enabled()) {
if (G.is_verbosity(2)) {
std::cerr << gvar_sym->name() << ": variable not generated, it's unused\n";
}
continue;
}
std::string name = symbols.get_name(gvar_sym->sym_idx);
outs << std::string(2, ' ') << "DECLGLOBVAR " << name << "\n";
std::string name = G.symbols.get_name(gvar_sym->sym_idx);
std::cout << std::string(2, ' ') << "DECLGLOBVAR " << name << "\n";
}
int errors = 0;
for (SymDef* func_sym : glob_func) {
for (SymDef* func_sym : G.glob_func) {
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
if (!func_val->does_need_codegen()) {
continue;
}
try {
generate_output_func(func_sym, outs, errs);
} catch (ParseError& err) {
errs << "cannot generate code for function `" << symbols.get_name(func_sym->sym_idx) << "`:\n"
<< err << std::endl;
++errors;
}
generate_output_func(func_sym);
}
outs << "}END>c\n";
if (!boc_output_filename.empty()) {
outs << "boc>B \"" << boc_output_filename << "\" B>file\n";
std::cout << "}END>c\n";
if (!G.settings.boc_output_filename.empty()) {
std::cout << "boc>B \"" << G.settings.boc_output_filename << "\" B>file\n";
}
return errors;
}
int tolk_proceed(const std::string &entrypoint_file_name, std::ostream &outs, std::ostream &errs) {
int tolk_proceed(const std::string &entrypoint_file_name) {
define_builtins();
lexer_init();
pragma_allow_post_modification.always_on_and_deprecated("0.5.0");
pragma_compute_asm_ltr.always_on_and_deprecated("0.5.0");
G.pragma_allow_post_modification.always_on_and_deprecated("0.5.0");
G.pragma_compute_asm_ltr.always_on_and_deprecated("0.5.0");
try {
bool ok = parse_source_file(entrypoint_file_name.c_str(), {});
if (!ok) {
throw Fatal{"output code generation omitted because of errors"};
{
if (G.settings.stdlib_filename.empty()) {
throw Fatal("stdlib filename not specified");
}
td::Result<SrcFile*> locate_res = locate_source_file(G.settings.stdlib_filename);
if (locate_res.is_error()) {
throw Fatal("Failed to locate stdlib: " + locate_res.error().message().str());
}
parse_source_file(locate_res.move_as_ok());
}
td::Result<SrcFile*> locate_res = locate_source_file(entrypoint_file_name);
if (locate_res.is_error()) {
throw Fatal("Failed to locate " + entrypoint_file_name + ": " + locate_res.error().message().str());
}
parse_source_file(locate_res.move_as_ok());
// todo #ifdef TOLK_PROFILING + comment
// lexer_measure_performance(all_src_files.get_all_files());
return generate_output(outs, errs);
generate_output();
return 0;
} catch (Fatal& fatal) {
errs << "fatal: " << fatal << std::endl;
std::cerr << "fatal: " << fatal << std::endl;
return 2;
} catch (ParseError& error) {
errs << error << std::endl;
std::cerr << error << std::endl;
return 2;
} catch (UnifyError& unif_err) {
errs << "fatal: ";
unif_err.print_message(errs);
errs << std::endl;
std::cerr << "fatal: ";
unif_err.print_message(std::cerr);
std::cerr << std::endl;
return 2;
}
return 0;
}
} // namespace tolk

View file

@ -15,22 +15,16 @@
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <utility>
#include <vector>
#include <string>
#include <set>
#include <stack>
#include <utility>
#include <algorithm>
#include <iostream>
#include <functional>
#include "common/refcnt.hpp"
#include "common/bigint.hpp"
#include "common/refint.h"
#include "src-file.h"
#include "lexer.h"
#include "symtable.h"
#include "crypto/common/refint.h"
#include "td/utils/Status.h"
#include <vector>
#include <string>
#include <stack>
#include <iostream>
#define tolk_assert(expr) \
(bool(expr) ? void(0) \
@ -38,14 +32,6 @@
namespace tolk {
extern int verbosity;
extern bool op_rewrite_comments;
extern std::string generated_from;
constexpr int optimize_depth = 20;
const std::string tolk_version{"0.4.5"};
/*
*
* TYPE EXPRESSIONS
@ -200,8 +186,6 @@ std::ostream& operator<<(std::ostream& os, const UnifyError& ue);
void unify(TypeExpr*& te1, TypeExpr*& te2);
// extern int TypeExpr::holes;
/*
*
* ABSTRACT CODE
@ -596,7 +580,7 @@ struct CodeBlob {
CodeBlob(TypeExpr* ret = nullptr) : var_cnt(0), in_var_cnt(0), op_cnt(0), ret_type(ret), cur_ops(&ops) {
}
template <typename... Args>
Op& emplace_back(const Args&... args) {
Op& emplace_back(Args&&... args) {
Op& res = *(*cur_ops = std::make_unique<Op>(args...));
cur_ops = &(res.next);
return res;
@ -768,9 +752,6 @@ struct SymValConst : SymValBase {
}
};
extern int glob_func_cnt, undef_func_cnt, glob_var_cnt;
extern std::vector<SymDef*> glob_func, glob_vars, glob_get_methods;
extern std::set<std::string> prohibited_var_names;
/*
*
@ -778,27 +759,11 @@ extern std::set<std::string> prohibited_var_names;
*
*/
class ReadCallback {
public:
/// Noncopyable.
ReadCallback(ReadCallback const&) = delete;
ReadCallback& operator=(ReadCallback const&) = delete;
enum class Kind {
Realpath,
ReadFile,
};
/// File reading or generic query callback.
using Callback = std::function<td::Result<std::string>(Kind, const char*)>;
};
// defined in parse-tolk.cpp
void parse_source(const SrcFile* file);
bool parse_source_file(const char* filename, SrcLocation loc_included_from);
td::Result<SrcFile*> locate_source_file(const std::string& rel_filename);
void parse_source_file(SrcFile* file);
extern std::stack<SrcLocation> inclusion_locations;
extern AllRegisteredSrcFiles all_src_files;
/*
*
@ -1359,8 +1324,6 @@ struct StackTransform {
bool try_store(int x, int y); // appends (x,y) to A
};
//extern const StackTransform StackTransform::rot, StackTransform::rot_rev;
inline std::ostream& operator<<(std::ostream& os, const StackTransform& trans) {
trans.show(os);
return os;
@ -1375,14 +1338,14 @@ bool apply_op(StackTransform& trans, const AsmOp& op);
*/
struct Optimizer {
enum { n = optimize_depth };
static constexpr int optimize_depth = 20;
AsmOpConsList code_;
int l_{0}, l2_{0}, p_, pb_, q_, indent_;
bool debug_{false};
std::unique_ptr<AsmOp> op_[n], oq_[n];
AsmOpCons* op_cons_[n];
int offs_[n];
StackTransform tr_[n];
std::unique_ptr<AsmOp> op_[optimize_depth], oq_[optimize_depth];
AsmOpCons* op_cons_[optimize_depth];
int offs_[optimize_depth];
StackTransform tr_[optimize_depth];
int mode_{0};
Optimizer() {
}
@ -1475,7 +1438,7 @@ struct Stack {
StackLayoutExt s;
AsmOpList& o;
enum {
_StkCmt = 1, _CptStkCmt = 2, _DisableOpt = 4, _DisableOut = 128, _Shown = 256,
_StkCmt = 1, _CptStkCmt = 2, _DisableOut = 128, _Shown = 256,
_InlineFunc = 512, _NeedRetAlt = 1024, _InlineAny = 2048,
_ModeSave = _InlineFunc | _NeedRetAlt | _InlineAny,
_Garbage = -0x10000
@ -1640,33 +1603,6 @@ AsmOp push_const(td::RefInt256 x);
void define_builtins();
extern int verbosity, opt_level;
extern bool stack_layout_comments;
extern std::string generated_from, boc_output_filename;
extern ReadCallback::Callback read_callback;
td::Result<std::string> fs_read_callback(ReadCallback::Kind kind, const char* query);
class GlobalPragma {
public:
explicit GlobalPragma(std::string name) : name_(std::move(name)) {
}
const std::string& name() const {
return name_;
}
bool enabled() const {
return enabled_;
}
void enable(SrcLocation loc);
void always_on_and_deprecated(const char *deprecated_from_v);
private:
std::string name_;
bool enabled_ = false;
const char *deprecated_from_v_ = nullptr;
};
extern GlobalPragma pragma_allow_post_modification, pragma_compute_asm_ltr, pragma_remove_unused_functions;
/*
*
@ -1674,7 +1610,7 @@ extern GlobalPragma pragma_allow_post_modification, pragma_compute_asm_ltr, prag
*
*/
int tolk_proceed(const std::string &entrypoint_file_name, std::ostream &outs, std::ostream &errs);
int tolk_proceed(const std::string &entrypoint_file_name);
} // namespace tolk