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:
parent
f0e6470d0b
commit
6c30e5a7eb
21 changed files with 604 additions and 506 deletions
|
@ -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}>)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
47
tolk/compiler-state.cpp
Normal 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
93
tolk/compiler-state.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
228
tolk/tolk.cpp
228
tolk/tolk.cpp
|
@ -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
|
||||
|
|
96
tolk/tolk.h
96
tolk/tolk.h
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue