mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-12 19:22:37 +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
|
@ -1,6 +1,7 @@
|
||||||
// Standard library for Tolk
|
// Standard library for Tolk
|
||||||
// (initially copied from stdlib.fc)
|
// (initially copied from stdlib.fc)
|
||||||
//
|
//
|
||||||
|
#pragma version >=0.5;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This file is part of TON Tolk Standard Library.
|
This file is part of TON Tolk Standard Library.
|
||||||
|
@ -405,6 +406,7 @@ cell preload_dict(slice s) pure asm "PLDDICT";
|
||||||
|
|
||||||
/// Loads a dictionary as [load_dict], but returns only the remainder of the slice.
|
/// Loads a dictionary as [load_dict], but returns only the remainder of the slice.
|
||||||
slice skip_dict(slice s) pure asm "SKIPDICT";
|
slice skip_dict(slice s) pure asm "SKIPDICT";
|
||||||
|
(slice, ()) ~skip_dict(slice s) pure asm "SKIPDICT";
|
||||||
|
|
||||||
/// Loads (Maybe ^Cell) from `slice` [s].
|
/// Loads (Maybe ^Cell) from `slice` [s].
|
||||||
/// In other words loads 1 bit and if it is true
|
/// In other words loads 1 bit and if it is true
|
||||||
|
@ -661,6 +663,7 @@ int dict_empty?(cell c) pure asm "DICTEMPTY";
|
||||||
cell config_param(int x) pure asm "CONFIGOPTPARAM";
|
cell config_param(int x) pure asm "CONFIGOPTPARAM";
|
||||||
/// Checks whether c is a null. Note, that Tolk also has polymorphic null? built-in.
|
/// Checks whether c is a null. Note, that Tolk also has polymorphic null? built-in.
|
||||||
int cell_null?(cell c) pure asm "ISNULL";
|
int cell_null?(cell c) pure asm "ISNULL";
|
||||||
|
int builder_null?(builder b) asm "ISNULL";
|
||||||
|
|
||||||
/// Creates an output action which would reserve exactly amount nanotoncoins (if mode = 0), at most amount nanotoncoins (if mode = 2), or all but amount nanotoncoins (if mode = 1 or mode = 3), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying amount nanotoncoins (or b − amount nanotoncoins, where b is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit +2 in mode means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit +8 in mode means `amount <- -amount` before performing any further actions. Bit +4 in mode means that amount is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently, amount must be a non-negative integer, and mode must be in the range 0..15.
|
/// Creates an output action which would reserve exactly amount nanotoncoins (if mode = 0), at most amount nanotoncoins (if mode = 2), or all but amount nanotoncoins (if mode = 1 or mode = 3), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying amount nanotoncoins (or b − amount nanotoncoins, where b is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit +2 in mode means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit +8 in mode means `amount <- -amount` before performing any further actions. Bit +4 in mode means that amount is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently, amount must be a non-negative integer, and mode must be in the range 0..15.
|
||||||
() raw_reserve(int amount, int mode) asm "RAWRESERVE";
|
() raw_reserve(int amount, int mode) asm "RAWRESERVE";
|
||||||
|
|
|
@ -4,6 +4,7 @@ set(TOLK_SOURCE
|
||||||
src-file.cpp
|
src-file.cpp
|
||||||
lexer.cpp
|
lexer.cpp
|
||||||
symtable.cpp
|
symtable.cpp
|
||||||
|
compiler-state.cpp
|
||||||
unify-types.cpp
|
unify-types.cpp
|
||||||
parse-tolk.cpp
|
parse-tolk.cpp
|
||||||
abscode.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)
|
target_compile_definitions(tolk PRIVATE TOLK_DEBUG=1)
|
||||||
endif()
|
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)
|
if (USE_EMSCRIPTEN)
|
||||||
add_executable(tolkfiftlib tolk-wasm.cpp ${TOLK_SOURCE})
|
add_executable(tolkfiftlib tolk-wasm.cpp ${TOLK_SOURCE})
|
||||||
target_include_directories(tolkfiftlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
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/>.
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "tolk.h"
|
#include "tolk.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
|
|
||||||
namespace tolk {
|
namespace tolk {
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ void TmpVar::dump(std::ostream& os) const {
|
||||||
|
|
||||||
void TmpVar::show(std::ostream& os, int omit_idx) const {
|
void TmpVar::show(std::ostream& os, int omit_idx) const {
|
||||||
if (cls & _Named) {
|
if (cls & _Named) {
|
||||||
os << symbols.get_name(name);
|
os << G.symbols.get_name(name);
|
||||||
if (omit_idx && (omit_idx >= 2 || (cls & _UniqueName))) {
|
if (omit_idx && (omit_idx >= 2 || (cls & _UniqueName))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "tolk.h"
|
#include "tolk.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
|
|
||||||
namespace tolk {
|
namespace tolk {
|
||||||
|
|
||||||
|
@ -768,7 +769,7 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
|
||||||
tolk_assert(left.size() == right.size());
|
tolk_assert(left.size() == right.size());
|
||||||
for (std::size_t i = 0; i < right.size(); i++) {
|
for (std::size_t i = 0; i < right.size(); i++) {
|
||||||
const VarDescr* ov = values[right[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::cerr << "FATAL: error in assignment at right component #" << i << " (no value for _" << right[i] << ")"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
for (auto x : left) {
|
for (auto x : left) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "tolk.h"
|
#include "tolk.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
|
|
||||||
namespace tolk {
|
namespace tolk {
|
||||||
using namespace std::literals::string_literals;
|
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) {
|
SymDef* define_builtin_func_impl(const std::string& name, SymValAsmFunc* func_val) {
|
||||||
if (name.back() == '_') {
|
if (name.back() == '_') {
|
||||||
prohibited_var_names.insert(name);
|
G.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;
|
|
||||||
}
|
}
|
||||||
|
sym_idx_t name_idx = G.symbols.lookup(name, 1);
|
||||||
SymDef* def = define_global_symbol(name_idx, true);
|
SymDef* def = define_global_symbol(name_idx, true);
|
||||||
if (!def) {
|
if (!def) {
|
||||||
std::cerr << "fatal: global function `" << name << "` already defined" << std::endl;
|
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/>.
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "tolk.h"
|
#include "tolk.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
|
|
||||||
namespace tolk {
|
namespace tolk {
|
||||||
|
|
||||||
|
@ -324,7 +325,7 @@ bool Op::generate_code_step(Stack& stack) {
|
||||||
if (!used || disabled()) {
|
if (!used || disabled()) {
|
||||||
return true;
|
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);
|
stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1);
|
||||||
if (left.size() != 1) {
|
if (left.size() != 1) {
|
||||||
tolk_assert(left.size() <= 15);
|
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)
|
func->compile(stack.o, res, args0, where); // compile res := f (args0)
|
||||||
} else {
|
} 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 << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
|
||||||
}
|
}
|
||||||
stack.o.undent();
|
stack.o.undent();
|
||||||
|
@ -497,7 +498,7 @@ bool Op::generate_code_step(Stack& stack) {
|
||||||
} else {
|
} else {
|
||||||
auto fv = dynamic_cast<const SymValCodeFunc*>(fun_ref->value);
|
auto fv = dynamic_cast<const SymValCodeFunc*>(fun_ref->value);
|
||||||
// todo can be fv == nullptr?
|
// 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())) {
|
if (fv && (fv->is_inline() || fv->is_inline_ref())) {
|
||||||
stack.o << AsmOp::Custom(name + " INLINECALLDICT", (int)right.size(), (int)left.size());
|
stack.o << AsmOp::Custom(name + " INLINECALLDICT", (int)right.size(), (int)left.size());
|
||||||
} else if (fv && fv->code && fv->code->require_callxargs) {
|
} 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());
|
stack.o << AsmOp::Tuple((int)right.size());
|
||||||
}
|
}
|
||||||
if (!right.empty()) {
|
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.o << AsmOp::Custom(name + " SETGLOB", 1, 0);
|
||||||
}
|
}
|
||||||
stack.s.resize(k);
|
stack.s.resize(k);
|
||||||
|
@ -894,14 +895,14 @@ void CodeBlob::generate_code(AsmOpList& out, int mode) {
|
||||||
}
|
}
|
||||||
ops->generate_code_all(stack);
|
ops->generate_code_all(stack);
|
||||||
stack.apply_wrappers(require_callxargs && (mode & Stack::_InlineAny) ? args : -1);
|
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) {
|
void CodeBlob::generate_code(std::ostream& os, int mode, int indent) {
|
||||||
AsmOpList out_list(indent, &vars);
|
AsmOpList out_list(indent, &vars);
|
||||||
generate_code(out_list, mode);
|
generate_code(out_list, mode);
|
||||||
|
if (G.settings.optimization_level >= 2) {
|
||||||
|
optimize_code(out_list);
|
||||||
|
}
|
||||||
out_list.out(os, mode);
|
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
|
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/>.
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <numeric>
|
|
||||||
#include "tolk.h"
|
#include "tolk.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
|
|
||||||
using namespace std::literals::string_literals;
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
|
@ -122,7 +122,7 @@ bool Expr::deduce_type(const Lexer& lex) {
|
||||||
} catch (UnifyError& ue) {
|
} catch (UnifyError& ue) {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << "cannot implicitly assign an expression of type " << args[1]->e_type
|
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;
|
<< "` : " << ue;
|
||||||
lex.error(os.str());
|
lex.error(os.str());
|
||||||
}
|
}
|
||||||
|
@ -197,14 +197,14 @@ int Expr::predefine_vars() {
|
||||||
case _Var:
|
case _Var:
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
tolk_assert(val < 0 && here.is_defined());
|
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{
|
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);
|
sym = define_symbol(~val, false, here);
|
||||||
// std::cerr << "predefining variable " << symbols.get_name(~val) << std::endl;
|
// std::cerr << "predefining variable " << symbols.get_name(~val) << std::endl;
|
||||||
if (!sym) {
|
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};
|
sym->value = new SymVal{SymValKind::_Var, -1, e_type};
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
#include "symtable.h"
|
#include "symtable.h"
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
@ -426,7 +427,7 @@ struct ChunkIdentifierOrKeyword final : ChunkLexerBase {
|
||||||
if (TokenType kw_tok = maybe_keyword(str_val)) {
|
if (TokenType kw_tok = maybe_keyword(str_val)) {
|
||||||
lex->add_token(kw_tok, str_val);
|
lex->add_token(kw_tok, str_val);
|
||||||
} else {
|
} 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);
|
lex->add_token(tok_identifier, str_val);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -452,7 +453,7 @@ struct ChunkIdentifierInBackticks final : ChunkLexerBase {
|
||||||
|
|
||||||
std::string_view str_val(str_begin + 1, lex->c_str() - str_begin - 1);
|
std::string_view str_val(str_begin + 1, lex->c_str() - str_begin - 1);
|
||||||
lex->skip_chars(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);
|
lex->add_token(tok_identifier, str_val);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -611,7 +612,7 @@ void Lexer::next_special(TokenType parse_next_as, const char* str_expected) {
|
||||||
|
|
||||||
int Lexer::cur_sym_idx() const {
|
int Lexer::cur_sym_idx() const {
|
||||||
assert(tok() == tok_identifier);
|
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 {
|
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_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); }
|
std::string cur_str_std_string() const { return static_cast<std::string>(cur_token.str_val); }
|
||||||
SrcLocation cur_location() const { return location; }
|
SrcLocation cur_location() const { return location; }
|
||||||
|
const SrcFile* cur_file() const { return file; }
|
||||||
int cur_sym_idx() const;
|
int cur_sym_idx() const;
|
||||||
|
|
||||||
void next();
|
void next();
|
||||||
|
|
|
@ -31,7 +31,7 @@ void Optimizer::set_code(AsmOpConsList code) {
|
||||||
|
|
||||||
void Optimizer::unpack() {
|
void Optimizer::unpack() {
|
||||||
int i = 0, j = 0;
|
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()) {
|
if (p->car->is_very_custom()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ void Optimizer::apply() {
|
||||||
if (!p_ && !q_) {
|
if (!p_ && !q_) {
|
||||||
return;
|
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++) {
|
for (int i = p_; i < l_; i++) {
|
||||||
tolk_assert(op_[i]);
|
tolk_assert(op_[i]);
|
||||||
op_cons_[i]->car = std::move(op_[i]);
|
op_cons_[i]->car = std::move(op_[i]);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
#include "tolk.h"
|
#include "tolk.h"
|
||||||
#include "platform-utils.h"
|
#include "platform-utils.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
#include "td/utils/crypto.h"
|
#include "td/utils/crypto.h"
|
||||||
#include "common/refint.h"
|
#include "common/refint.h"
|
||||||
#include "openssl/digest.hpp"
|
#include "openssl/digest.hpp"
|
||||||
|
@ -26,15 +27,15 @@ namespace tolk {
|
||||||
using namespace std::literals::string_literals;
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
inline bool is_dot_ident(sym_idx_t idx) {
|
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) {
|
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) {
|
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
|
// 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) {
|
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);
|
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");
|
lex.check(tok_identifier, "formal parameter name");
|
||||||
loc = lex.cur_location();
|
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{
|
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);
|
SymDef* new_sym_def = define_symbol(lex.cur_sym_idx(), true, loc);
|
||||||
if (!new_sym_def) {
|
if (!new_sym_def) {
|
||||||
|
@ -311,16 +312,15 @@ void parse_global_var_decl(Lexer& lex) {
|
||||||
lex.error(os.str());
|
lex.error(os.str());
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
#ifdef TOLK_DEBUG
|
||||||
dynamic_cast<SymValGlobVar*>(sym_def->value)->name = lex.cur_str();
|
dynamic_cast<SymValGlobVar*>(sym_def->value)->name = lex.cur_str();
|
||||||
#endif
|
#endif
|
||||||
glob_vars.push_back(sym_def);
|
G.glob_vars.push_back(sym_def);
|
||||||
}
|
}
|
||||||
lex.next();
|
lex.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int const_cnt;
|
|
||||||
Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false);
|
Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false);
|
||||||
|
|
||||||
void parse_const_decl(Lexer& lex) {
|
void parse_const_decl(Lexer& lex) {
|
||||||
|
@ -360,9 +360,9 @@ void parse_const_decl(Lexer& lex) {
|
||||||
}
|
}
|
||||||
SymValConst* new_value = nullptr;
|
SymValConst* new_value = nullptr;
|
||||||
if (x->cls == Expr::_Const) { // Integer constant
|
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)
|
} 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 `_+_`)
|
} 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>());
|
code.emplace_back(loc, Op::_Import, std::vector<var_idx_t>());
|
||||||
auto tmp_vars = x->pre_compile(code);
|
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()) {
|
if (op.origin.is_null() || !op.origin->is_valid()) {
|
||||||
lex.error("precompiled expression did not result in a valid integer constant");
|
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 {
|
} else {
|
||||||
lex.error("integer or slice literal or constant expected");
|
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* 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
|
#ifdef TOLK_DEBUG
|
||||||
res->name = func_sym->name();
|
res->name = func_sym->name();
|
||||||
#endif
|
#endif
|
||||||
func_sym->value = res;
|
func_sym->value = res;
|
||||||
glob_func.push_back(func_sym);
|
G.glob_func.push_back(func_sym);
|
||||||
glob_func_cnt++;
|
G.glob_func_cnt++;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool check_global_func(const Lexer& lex, sym_idx_t func_name) {
|
bool check_global_func(const Lexer& lex, sym_idx_t func_name) {
|
||||||
SymDef* def = lookup_symbol(func_name);
|
SymDef* def = lookup_symbol(func_name);
|
||||||
if (!def) {
|
if (!def) {
|
||||||
lex.error("undefined symbol `" + symbols.get_name(func_name) + "`");
|
lex.error("undefined symbol `" + G.symbols.get_name(func_name) + "`");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SymVal* val = dynamic_cast<SymVal*>(def->value);
|
SymVal* val = dynamic_cast<SymVal*>(def->value);
|
||||||
if (!val) {
|
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;
|
return false;
|
||||||
} else if (!val->get_type()) {
|
} 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;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
|
@ -497,6 +497,21 @@ Expr* make_func_apply(Expr* fun, Expr* x) {
|
||||||
return res;
|
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 | _
|
// parse ( E { , E } ) | () | [ E { , E } ] | [] | id | num | _
|
||||||
Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
||||||
if (lex.tok() == tok_oppar || lex.tok() == tok_opbracket) {
|
if (lex.tok() == tok_oppar || lex.tok() == tok_opbracket) {
|
||||||
|
@ -672,6 +687,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
|
if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
|
||||||
|
check_import_exists_when_using_sym(lex, sym);
|
||||||
auto val = dynamic_cast<SymValGlobVar*>(sym->value);
|
auto val = dynamic_cast<SymValGlobVar*>(sym->value);
|
||||||
Expr* res = new Expr{Expr::_GlobVar, lex.cur_location()};
|
Expr* res = new Expr{Expr::_GlobVar, lex.cur_location()};
|
||||||
res->e_type = val->get_type();
|
res->e_type = val->get_type();
|
||||||
|
@ -681,6 +697,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
if (sym && dynamic_cast<SymValConst*>(sym->value)) {
|
if (sym && dynamic_cast<SymValConst*>(sym->value)) {
|
||||||
|
check_import_exists_when_using_sym(lex, sym);
|
||||||
auto val = dynamic_cast<SymValConst*>(sym->value);
|
auto val = dynamic_cast<SymValConst*>(sym->value);
|
||||||
Expr* res = new Expr{Expr::_None, lex.cur_location()};
|
Expr* res = new Expr{Expr::_None, lex.cur_location()};
|
||||||
res->flags = Expr::_IsRvalue;
|
res->flags = Expr::_IsRvalue;
|
||||||
|
@ -700,6 +717,9 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
||||||
lex.next();
|
lex.next();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
if (sym && dynamic_cast<SymValFunc*>(sym->value)) {
|
||||||
|
check_import_exists_when_using_sym(lex, sym);
|
||||||
|
}
|
||||||
bool auto_apply = false;
|
bool auto_apply = false;
|
||||||
Expr* res = new Expr{Expr::_Var, lex.cur_location()};
|
Expr* res = new Expr{Expr::_Var, lex.cur_location()};
|
||||||
if (nv) {
|
if (nv) {
|
||||||
|
@ -796,7 +816,7 @@ Expr* parse_expr80(Lexer& lex, CodeBlob& code, bool nv) {
|
||||||
sym_idx_t name = lex.cur_sym_idx();
|
sym_idx_t name = lex.cur_sym_idx();
|
||||||
auto sym = lookup_symbol(name);
|
auto sym = lookup_symbol(name);
|
||||||
if (!sym || !dynamic_cast<SymValFunc*>(sym->value)) {
|
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) {
|
if (name1) {
|
||||||
auto sym1 = lookup_symbol(name1);
|
auto sym1 = lookup_symbol(name1);
|
||||||
if (sym1 && dynamic_cast<SymValFunc*>(sym1->value)) {
|
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);
|
check_global_func(lex, name);
|
||||||
if (verbosity >= 2) {
|
if (G.is_verbosity(2)) {
|
||||||
std::cerr << "using symbol `" << symbols.get_name(name) << "` for method call of " << lex.cur_str() << std::endl;
|
std::cerr << "using symbol `" << G.symbols.get_name(name) << "` for method call of " << lex.cur_str() << std::endl;
|
||||||
}
|
}
|
||||||
sym = lookup_symbol(name);
|
sym = lookup_symbol(name);
|
||||||
SymValFunc* val = sym ? dynamic_cast<SymValFunc*>(sym->value) : nullptr;
|
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) {
|
Expr* parse_expr75(Lexer& lex, CodeBlob& code, bool nv) {
|
||||||
if (lex.tok() == tok_bitwise_not || lex.tok() == tok_minus || lex.tok() == tok_plus) {
|
if (lex.tok() == tok_bitwise_not || lex.tok() == tok_minus || lex.tok() == tok_plus) {
|
||||||
TokenType t = lex.tok();
|
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);
|
check_global_func(lex, name);
|
||||||
SrcLocation loc{lex.cur_location()};
|
SrcLocation loc{lex.cur_location()};
|
||||||
lex.next();
|
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) {
|
lex.tok() == tok_divR || lex.tok() == tok_modC || lex.tok() == tok_modR) {
|
||||||
res->chk_rvalue(lex);
|
res->chk_rvalue(lex);
|
||||||
TokenType t = lex.tok();
|
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()};
|
SrcLocation loc{lex.cur_location()};
|
||||||
check_global_func(lex, name);
|
check_global_func(lex, name);
|
||||||
lex.next();
|
lex.next();
|
||||||
|
@ -907,7 +927,7 @@ Expr* parse_expr20(Lexer& lex, CodeBlob& code, bool nv) {
|
||||||
while (lex.tok() == tok_minus || lex.tok() == tok_plus) {
|
while (lex.tok() == tok_minus || lex.tok() == tok_plus) {
|
||||||
res->chk_rvalue(lex);
|
res->chk_rvalue(lex);
|
||||||
TokenType t = lex.tok();
|
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);
|
check_global_func(lex, name);
|
||||||
SrcLocation loc{lex.cur_location()};
|
SrcLocation loc{lex.cur_location()};
|
||||||
lex.next();
|
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) {
|
while (lex.tok() == tok_lshift || lex.tok() == tok_rshift || lex.tok() == tok_rshiftC || lex.tok() == tok_rshiftR) {
|
||||||
res->chk_rvalue(lex);
|
res->chk_rvalue(lex);
|
||||||
TokenType t = lex.tok();
|
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);
|
check_global_func(lex, name);
|
||||||
SrcLocation loc{lex.cur_location()};
|
SrcLocation loc{lex.cur_location()};
|
||||||
lex.next();
|
lex.next();
|
||||||
|
@ -951,7 +971,7 @@ Expr* parse_expr15(Lexer& lex, CodeBlob& code, bool nv) {
|
||||||
lex.tok() == tok_neq || lex.tok() == tok_spaceship) {
|
lex.tok() == tok_neq || lex.tok() == tok_spaceship) {
|
||||||
res->chk_rvalue(lex);
|
res->chk_rvalue(lex);
|
||||||
TokenType t = lex.tok();
|
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);
|
check_global_func(lex, name);
|
||||||
SrcLocation loc{lex.cur_location()};
|
SrcLocation loc{lex.cur_location()};
|
||||||
lex.next();
|
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) {
|
while (lex.tok() == tok_bitwise_and || lex.tok() == tok_bitwise_or || lex.tok() == tok_bitwise_xor) {
|
||||||
res->chk_rvalue(lex);
|
res->chk_rvalue(lex);
|
||||||
TokenType t = lex.tok();
|
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);
|
check_global_func(lex, name);
|
||||||
SrcLocation loc{lex.cur_location()};
|
SrcLocation loc{lex.cur_location()};
|
||||||
lex.next();
|
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) {
|
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_lvalue(lex);
|
||||||
x->chk_rvalue(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);
|
check_global_func(lex, name);
|
||||||
SrcLocation loc{lex.cur_location()};
|
SrcLocation loc{lex.cur_location()};
|
||||||
lex.next();
|
lex.next();
|
||||||
|
@ -1464,8 +1484,8 @@ std::vector<TypeExpr*> parse_type_var_list(Lexer& lex) {
|
||||||
lex.error("free type identifier expected");
|
lex.error("free type identifier expected");
|
||||||
}
|
}
|
||||||
SrcLocation loc = lex.cur_location();
|
SrcLocation 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())
|
throw ParseError{loc, PSTRING() << "symbol `" << G.symbols.get_name(lex.cur_sym_idx())
|
||||||
<< "` cannot be redefined as a variable"};
|
<< "` cannot be redefined as a variable"};
|
||||||
}
|
}
|
||||||
SymDef* new_sym_def = define_symbol(lex.cur_sym_idx(), true, loc);
|
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
|
// ok, f_current is a wrapper
|
||||||
v_current->flags |= SymValFunc::flagWrapsAnotherF;
|
v_current->flags |= SymValFunc::flagWrapsAnotherF;
|
||||||
if (verbosity >= 2) {
|
if (G.is_verbosity(2)) {
|
||||||
std::cerr << function_name << " -> " << f_called->name() << std::endl;
|
std::cerr << function_name << " -> " << f_called->name() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1658,7 +1678,7 @@ void parse_func_def(Lexer& lex) {
|
||||||
if (is_get_method) {
|
if (is_get_method) {
|
||||||
tolk_assert(method_id.is_null());
|
tolk_assert(method_id.is_null());
|
||||||
method_id = calculate_method_id_by_func_name(func_name);
|
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)) {
|
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.");
|
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);
|
TypeExpr* func_type = TypeExpr::new_map(extract_total_arg_type(arg_list), ret_type);
|
||||||
func_type = compute_type_closure(func_type, type_vars);
|
func_type = compute_type_closure(func_type, type_vars);
|
||||||
if (lex.tok() == tok_builtin) {
|
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;
|
const SymValFunc* func_val = builtin_func ? dynamic_cast<SymValFunc*>(builtin_func->value) : nullptr;
|
||||||
if (!func_val || !func_val->is_builtin()) {
|
if (!func_val || !func_val->is_builtin()) {
|
||||||
lex.error("`builtin` used for non-builtin function");
|
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) {
|
if (lex.tok() != tok_semicolon && lex.tok() != tok_opbrace && lex.tok() != tok_asm) {
|
||||||
lex.expect(tok_opbrace, "function body block");
|
lex.expect(tok_opbrace, "function body block");
|
||||||
}
|
}
|
||||||
if (verbosity >= 1) {
|
if (G.is_verbosity(1)) {
|
||||||
std::cerr << "function " << func_name << " : " << func_type << std::endl;
|
std::cerr << "function " << func_name << " : " << func_type << std::endl;
|
||||||
}
|
}
|
||||||
SymDef* func_sym = define_global_symbol(func_sym_idx, 0, loc);
|
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");
|
lex.error("cannot set unknown function `" + func_name + "` as a get method");
|
||||||
}
|
}
|
||||||
val->flags |= SymValFunc::flagGetMethod;
|
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;
|
std::cerr << "new type of function " << func_name << " : " << func_type << std::endl;
|
||||||
}
|
}
|
||||||
close_scope(lex.cur_location());
|
close_scope(lex.cur_location());
|
||||||
|
@ -1876,12 +1896,12 @@ void parse_pragma(Lexer& lex) {
|
||||||
if (!match) {
|
if (!match) {
|
||||||
throw ParseError(loc, std::string("Tolk version ") + tolk_version + " does not satisfy this condition");
|
throw ParseError(loc, std::string("Tolk version ") + tolk_version + " does not satisfy this condition");
|
||||||
}
|
}
|
||||||
} else if (pragma_name == pragma_allow_post_modification.name()) {
|
} else if (pragma_name == G.pragma_allow_post_modification.name()) {
|
||||||
pragma_allow_post_modification.enable(loc);
|
G.pragma_allow_post_modification.enable(loc);
|
||||||
} else if (pragma_name == pragma_compute_asm_ltr.name()) {
|
} else if (pragma_name == G.pragma_compute_asm_ltr.name()) {
|
||||||
pragma_compute_asm_ltr.enable(loc);
|
G.pragma_compute_asm_ltr.enable(loc);
|
||||||
} else if (pragma_name == pragma_remove_unused_functions.name()) {
|
} else if (pragma_name == G.pragma_remove_unused_functions.name()) {
|
||||||
pragma_remove_unused_functions.enable(loc);
|
G.pragma_remove_unused_functions.enable(loc);
|
||||||
} else {
|
} else {
|
||||||
lex.error("unknown pragma name");
|
lex.error("unknown pragma name");
|
||||||
}
|
}
|
||||||
|
@ -1889,28 +1909,42 @@ void parse_pragma(Lexer& lex) {
|
||||||
lex.expect(tok_semicolon, "';'");
|
lex.expect(tok_semicolon, "';'");
|
||||||
}
|
}
|
||||||
|
|
||||||
AllRegisteredSrcFiles all_src_files;
|
void parse_include(Lexer& lex, SrcFile* parent_file) {
|
||||||
std::string stdlib_filename;
|
|
||||||
|
|
||||||
void parse_include(Lexer& lex, const SrcFile* parent_file) {
|
|
||||||
SrcLocation loc = lex.cur_location();
|
SrcLocation loc = lex.cur_location();
|
||||||
lex.expect(tok_include, "#include");
|
lex.expect(tok_include, "#include");
|
||||||
if (lex.tok() != tok_string_const) {
|
if (lex.tok() != tok_string_const) {
|
||||||
lex.expect(tok_string_const, "source file name");
|
lex.expect(tok_string_const, "source file name");
|
||||||
}
|
}
|
||||||
std::string val = static_cast<std::string>(lex.cur_str());
|
std::string rel_filename = lex.cur_str_std_string();
|
||||||
std::string parent_dir = parent_file->rel_filename;
|
if (rel_filename.empty()) {
|
||||||
if (size_t rc = parent_dir.rfind('/'); rc != std::string::npos) {
|
lex.error("imported file name is an empty string");
|
||||||
val = parent_dir.substr(0, rc + 1) + val;
|
}
|
||||||
|
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.next();
|
||||||
lex.expect(tok_semicolon, "';'");
|
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);
|
Lexer lex(file);
|
||||||
while (!lex.is_eof()) {
|
while (!lex.is_eof()) {
|
||||||
if (lex.tok() == tok_pragma) {
|
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) {
|
td::Result<SrcFile*> locate_source_file(const std::string& rel_filename) {
|
||||||
const SrcFile* included_from = loc_included_from.get_src_file();
|
td::Result<std::string> path = G.settings.read_callback(CompilerSettings::FsReadCallbackKind::Realpath, rel_filename.c_str());
|
||||||
if (!filename || !*filename) {
|
if (path.is_error()) {
|
||||||
throw ParseError(loc_included_from, "source file name is an empty string");
|
return path.move_as_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto path_res = read_callback(ReadCallback::Kind::Realpath, filename);
|
std::string abs_filename = path.move_as_ok();
|
||||||
if (path_res.is_error()) {
|
if (SrcFile* file = G.all_src_files.find_file(abs_filename)) {
|
||||||
auto error = path_res.move_as_error();
|
return file; // file was already parsed (imported from somewhere else)
|
||||||
throw ParseError(loc_included_from, error.message().c_str());
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
std::string abs_filename = path_res.move_as_ok();
|
|
||||||
const SrcFile* file = all_src_files.find_file(abs_filename);
|
td::Result<std::string> text = G.settings.read_callback(CompilerSettings::FsReadCallbackKind::ReadFile, abs_filename.c_str());
|
||||||
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());
|
|
||||||
if (text.is_error()) {
|
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 G.all_src_files.register_file(rel_filename, abs_filename, text.move_as_ok());
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tolk
|
} // namespace tolk
|
||||||
|
|
|
@ -15,17 +15,15 @@
|
||||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "src-file.h"
|
#include "src-file.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace tolk {
|
namespace tolk {
|
||||||
|
|
||||||
extern AllRegisteredSrcFiles all_src_files;
|
|
||||||
extern std::string stdlib_filename;
|
|
||||||
|
|
||||||
static_assert(sizeof(SrcLocation) == 8);
|
static_assert(sizeof(SrcLocation) == 8);
|
||||||
|
|
||||||
const SrcFile* AllRegisteredSrcFiles::find_file(int file_id) const {
|
SrcFile* AllRegisteredSrcFiles::find_file(int file_id) const {
|
||||||
for (const SrcFile* file : all_src_files) {
|
for (SrcFile* file : all_src_files) {
|
||||||
if (file->file_id == file_id) {
|
if (file->file_id == file_id) {
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
@ -33,8 +31,8 @@ const SrcFile* AllRegisteredSrcFiles::find_file(int file_id) const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SrcFile* AllRegisteredSrcFiles::find_file(const std::string& abs_filename) const {
|
SrcFile* AllRegisteredSrcFiles::find_file(const std::string& abs_filename) const {
|
||||||
for (const SrcFile* file : all_src_files) {
|
for (SrcFile* file : all_src_files) {
|
||||||
if (file->abs_filename == abs_filename) {
|
if (file->abs_filename == abs_filename) {
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
@ -42,17 +40,13 @@ const SrcFile* AllRegisteredSrcFiles::find_file(const std::string& abs_filename)
|
||||||
return nullptr;
|
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* 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), included_from);
|
SrcFile* created = new SrcFile(++last_file_id, rel_filename, abs_filename, std::move(text));
|
||||||
all_src_files.push_back(created);
|
all_src_files.push_back(created);
|
||||||
return 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 {
|
bool SrcFile::is_offset_valid(int offset) const {
|
||||||
return offset >= 0 && offset < static_cast<int>(text.size());
|
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 {
|
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 {
|
void SrcLocation::show(std::ostream& os) const {
|
||||||
|
|
|
@ -29,23 +29,29 @@ struct SrcFile {
|
||||||
std::string_view line_str;
|
std::string_view line_str;
|
||||||
};
|
};
|
||||||
|
|
||||||
int file_id;
|
struct ImportStatement {
|
||||||
std::string rel_filename;
|
const SrcFile* imported_file;
|
||||||
std::string abs_filename;
|
};
|
||||||
std::string text;
|
|
||||||
const SrcFile* included_from{nullptr};
|
|
||||||
|
|
||||||
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)
|
: file_id(file_id)
|
||||||
, rel_filename(std::move(rel_filename))
|
, rel_filename(std::move(rel_filename))
|
||||||
, abs_filename(std::move(abs_filename))
|
, abs_filename(std::move(abs_filename))
|
||||||
, text(std::move(text))
|
, text(std::move(text)) { }
|
||||||
, included_from(included_from) { }
|
|
||||||
|
|
||||||
SrcFile(const SrcFile& other) = delete;
|
SrcFile(const SrcFile& other) = delete;
|
||||||
SrcFile &operator=(const SrcFile&) = 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;
|
bool is_offset_valid(int offset) const;
|
||||||
SrcPosition convert_offset(int offset) const;
|
SrcPosition convert_offset(int offset) const;
|
||||||
};
|
};
|
||||||
|
@ -55,12 +61,48 @@ class AllRegisteredSrcFiles {
|
||||||
int last_file_id = -1;
|
int last_file_id = -1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const SrcFile *find_file(int file_id) const;
|
SrcFile *find_file(int file_id) const;
|
||||||
const SrcFile* find_file(const std::string& abs_filename) 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* 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; }
|
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 {
|
struct Fatal final : std::exception {
|
||||||
std::string message;
|
std::string message;
|
||||||
|
|
||||||
|
@ -73,36 +115,6 @@ struct Fatal final : std::exception {
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const Fatal& fatal);
|
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 {
|
struct ParseError : std::exception {
|
||||||
SrcLocation where;
|
SrcLocation where;
|
||||||
std::string message;
|
std::string message;
|
||||||
|
|
|
@ -15,25 +15,12 @@
|
||||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include "symtable.h"
|
#include "symtable.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
namespace tolk {
|
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) {
|
Symbol::Symbol(std::string str, sym_idx_t idx) : str(std::move(str)), idx(idx) {
|
||||||
subclass = this->str[0] == '.' ? SymbolSubclass::dot_identifier
|
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) {
|
void open_scope(SrcLocation loc) {
|
||||||
++scope_level;
|
++G.scope_level;
|
||||||
scope_opened_at.push_back(loc);
|
G.scope_opened_at.push_back(loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_scope(SrcLocation loc) {
|
void close_scope(SrcLocation loc) {
|
||||||
if (!scope_level) {
|
if (!G.scope_level) {
|
||||||
throw Fatal{"cannot close the outer scope"};
|
throw Fatal{"cannot close the outer scope"};
|
||||||
}
|
}
|
||||||
while (!symbol_stack.empty() && symbol_stack.back().first == scope_level) {
|
while (!G.symbol_stack.empty() && G.symbol_stack.back().first == G.scope_level) {
|
||||||
SymDef old_def = symbol_stack.back().second;
|
SymDef old_def = G.symbol_stack.back().second;
|
||||||
auto idx = old_def.sym_idx;
|
auto idx = old_def.sym_idx;
|
||||||
symbol_stack.pop_back();
|
G.symbol_stack.pop_back();
|
||||||
SymDef* cur_def = sym_def[idx];
|
SymDef* cur_def = G.sym_def[idx];
|
||||||
assert(cur_def);
|
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;
|
//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) {
|
if (cur_def->value) {
|
||||||
//std::cerr << "deleting value of symbol " << old_def.name << ":" << old_def.level << " at " << (const void*) it->second.value << std::endl;
|
//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) {
|
if (!old_def.level && !old_def.value) {
|
||||||
delete cur_def; // ??? keep the definition always?
|
delete cur_def; // ??? keep the definition always?
|
||||||
sym_def[idx] = nullptr;
|
G.sym_def[idx] = nullptr;
|
||||||
} else {
|
} else {
|
||||||
cur_def->value = std::move(old_def.value);
|
cur_def->value = old_def.value;
|
||||||
cur_def->level = old_def.level;
|
cur_def->level = old_def.level;
|
||||||
}
|
}
|
||||||
old_def.value = nullptr;
|
old_def.value = nullptr;
|
||||||
}
|
}
|
||||||
--scope_level;
|
--G.scope_level;
|
||||||
scope_opened_at.pop_back();
|
G.scope_opened_at.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
SymDef* lookup_symbol(sym_idx_t idx) {
|
SymDef* lookup_symbol(sym_idx_t idx) {
|
||||||
if (!idx) {
|
if (!idx) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (sym_def[idx]) {
|
if (G.sym_def[idx]) {
|
||||||
return sym_def[idx];
|
return G.sym_def[idx];
|
||||||
}
|
}
|
||||||
if (global_sym_def[idx]) {
|
if (G.global_sym_def[idx]) {
|
||||||
return global_sym_def[idx];
|
return G.global_sym_def[idx];
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -133,11 +124,11 @@ SymDef* define_global_symbol(sym_idx_t name_idx, bool force_new, SrcLocation loc
|
||||||
if (!name_idx) {
|
if (!name_idx) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto found = global_sym_def[name_idx];
|
auto found = G.global_sym_def[name_idx];
|
||||||
if (found) {
|
if (found) {
|
||||||
return force_new && found->value ? nullptr : 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
|
#ifdef TOLK_DEBUG
|
||||||
found->sym_name = found->name();
|
found->sym_name = found->name();
|
||||||
#endif
|
#endif
|
||||||
|
@ -148,26 +139,26 @@ SymDef* define_symbol(sym_idx_t name_idx, bool force_new, SrcLocation loc) {
|
||||||
if (!name_idx) {
|
if (!name_idx) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (!scope_level) {
|
if (!G.scope_level) {
|
||||||
return define_global_symbol(name_idx, force_new, loc);
|
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) {
|
||||||
if (found->level < scope_level) {
|
if (found->level < G.scope_level) {
|
||||||
symbol_stack.push_back(std::make_pair(scope_level, *found));
|
G.symbol_stack.emplace_back(G.scope_level, *found);
|
||||||
found->level = scope_level;
|
found->level = G.scope_level;
|
||||||
} else if (found->value && force_new) {
|
} else if (found->value && force_new) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
found->value = 0;
|
found->value = nullptr;
|
||||||
found->loc = loc;
|
found->loc = loc;
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
found = sym_def[name_idx] = new SymDef(scope_level, name_idx, loc);
|
found = G.sym_def[name_idx] = new SymDef(G.scope_level, name_idx, loc);
|
||||||
symbol_stack.push_back(std::make_pair(scope_level, SymDef{0, name_idx, loc}));
|
G.symbol_stack.emplace_back(G.scope_level, SymDef{0, name_idx, loc});
|
||||||
#ifdef TOLK_DEBUG
|
#ifdef TOLK_DEBUG
|
||||||
found->sym_name = found->name();
|
found->sym_name = found->name();
|
||||||
symbol_stack.back().second.sym_name = found->name();
|
G.symbol_stack.back().second.sym_name = found->name();
|
||||||
#endif
|
#endif
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,20 +15,15 @@
|
||||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "src-file.h"
|
#include "src-file.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace tolk {
|
namespace tolk {
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* SYMBOL VALUES (DECLARED)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef int var_idx_t;
|
typedef int var_idx_t;
|
||||||
|
typedef int sym_idx_t;
|
||||||
|
|
||||||
enum class SymValKind { _Param, _Var, _Func, _Typename, _GlobVar, _Const };
|
enum class SymValKind { _Param, _Var, _Func, _Typename, _GlobVar, _Const };
|
||||||
|
|
||||||
|
@ -40,11 +35,6 @@ struct SymValBase {
|
||||||
virtual ~SymValBase() = default;
|
virtual ~SymValBase() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* SYMBOL TABLE
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum class SymbolSubclass {
|
enum class SymbolSubclass {
|
||||||
undef = 0,
|
undef = 0,
|
||||||
|
@ -52,8 +42,6 @@ enum class SymbolSubclass {
|
||||||
tilde_identifier = 2 // begins with ~ (a non-const method)
|
tilde_identifier = 2 // begins with ~ (a non-const method)
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int sym_idx_t;
|
|
||||||
|
|
||||||
struct Symbol {
|
struct Symbol {
|
||||||
std::string str;
|
std::string str;
|
||||||
sym_idx_t idx;
|
sym_idx_t idx;
|
||||||
|
@ -73,9 +61,6 @@ private:
|
||||||
std::unique_ptr<Symbol> sym[SIZE_PRIME + 1];
|
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);
|
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:
|
public:
|
||||||
|
|
||||||
static constexpr sym_idx_t not_found = 0;
|
static constexpr sym_idx_t not_found = 0;
|
||||||
|
@ -88,22 +73,12 @@ public:
|
||||||
Symbol* operator[](sym_idx_t i) const {
|
Symbol* operator[](sym_idx_t i) const {
|
||||||
return sym[i].get();
|
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 {
|
std::string get_name(sym_idx_t i) const {
|
||||||
return sym[i] ? sym[i]->str : Symbol::unknown_symbol_name(i);
|
return sym[i] ? sym[i]->str : Symbol::unknown_symbol_name(i);
|
||||||
}
|
}
|
||||||
SymbolSubclass get_subclass(sym_idx_t i) const {
|
SymbolSubclass get_subclass(sym_idx_t i) const {
|
||||||
return sym[i] ? sym[i]->subclass : SymbolSubclass::undef;
|
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 {
|
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 {
|
struct SymDef {
|
||||||
int level;
|
int level;
|
||||||
|
@ -133,18 +99,9 @@ struct SymDef {
|
||||||
SymDef(int lvl, sym_idx_t idx, SrcLocation _loc, SymValBase* val = nullptr)
|
SymDef(int lvl, sym_idx_t idx, SrcLocation _loc, SymValBase* val = nullptr)
|
||||||
: level(lvl), sym_idx(idx), value(val), loc(_loc) {
|
: level(lvl), sym_idx(idx), value(val), loc(_loc) {
|
||||||
}
|
}
|
||||||
bool has_name() const {
|
std::string name() const;
|
||||||
return sym_idx;
|
|
||||||
}
|
|
||||||
std::string name() const {
|
|
||||||
return symbols.get_name(sym_idx);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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 open_scope(SrcLocation loc);
|
||||||
void close_scope(SrcLocation loc);
|
void close_scope(SrcLocation loc);
|
||||||
|
|
|
@ -24,16 +24,23 @@
|
||||||
from all source files in the program, then also delete it here.
|
from all source files in the program, then also delete it here.
|
||||||
*/
|
*/
|
||||||
#include "tolk.h"
|
#include "tolk.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
|
#include "td/utils/port/path.h"
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <utility>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include "git.h"
|
#include "git.h"
|
||||||
|
|
||||||
|
using namespace tolk;
|
||||||
|
|
||||||
void usage(const char* progname) {
|
void usage(const char* progname) {
|
||||||
std::cerr
|
std::cerr
|
||||||
<< "usage: " << progname << " [options] <filename.tolk>\n"
|
<< "usage: " << progname << " [options] <filename.tolk>\n"
|
||||||
"\tGenerates Fift TVM assembler code from a .tolk file\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"
|
"-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"
|
"-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"
|
"-O<level>\tSets optimization level (2 by default)\n"
|
||||||
"-S\tDon't include stack layout comments into Fift output\n"
|
"-S\tDon't include stack layout comments into Fift output\n"
|
||||||
"-e\tIncreases verbosity level (extra output into stderr)\n"
|
"-e\tIncreases verbosity level (extra output into stderr)\n"
|
||||||
|
@ -41,28 +48,100 @@ void usage(const char* progname) {
|
||||||
std::exit(2);
|
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 main(int argc, char* const argv[]) {
|
||||||
int i;
|
int i;
|
||||||
std::string output_filename;
|
while ((i = getopt(argc, argv, "o:b:s:O:Sevh")) != -1) {
|
||||||
while ((i = getopt(argc, argv, "o:b:O:Sevh")) != -1) {
|
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 'o':
|
case 'o':
|
||||||
output_filename = optarg;
|
G.settings.output_filename = optarg;
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
tolk::boc_output_filename = optarg;
|
G.settings.boc_output_filename = optarg;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
G.settings.stdlib_filename = optarg;
|
||||||
break;
|
break;
|
||||||
case 'O':
|
case 'O':
|
||||||
tolk::opt_level = std::max(0, atoi(optarg));
|
G.settings.optimization_level = std::max(0, atoi(optarg));
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
tolk::stack_layout_comments = false;
|
G.settings.stack_layout_comments = false;
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
++tolk::verbosity;
|
G.settings.verbosity++;
|
||||||
break;
|
break;
|
||||||
case 'v':
|
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 commit: " << GitMetadata::CommitSHA1() << "\n";
|
||||||
std::cout << "Build date: " << GitMetadata::CommitDate() << "\n";
|
std::cout << "Build date: " << GitMetadata::CommitDate() << "\n";
|
||||||
std::exit(0);
|
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::unique_ptr<std::fstream> fs;
|
std::cerr << "Failed to create output file " << G.settings.output_filename << '\n';
|
||||||
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;
|
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) {
|
if (optind != argc - 1) {
|
||||||
|
@ -89,9 +176,8 @@ int main(int argc, char* const argv[]) {
|
||||||
return 2;
|
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_proceed(G.settings.entrypoint_filename);
|
||||||
|
|
||||||
return tolk::tolk_proceed(entrypoint_file_name, *outs, std::cerr);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,28 +24,34 @@
|
||||||
from all source files in the program, then also delete it here.
|
from all source files in the program, then also delete it here.
|
||||||
*/
|
*/
|
||||||
#include "tolk.h"
|
#include "tolk.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
#include "git.h"
|
#include "git.h"
|
||||||
#include "td/utils/JsonBuilder.h"
|
#include "td/utils/JsonBuilder.h"
|
||||||
#include "fift/utils.h"
|
#include "fift/utils.h"
|
||||||
#include "td/utils/base64.h"
|
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
|
||||||
|
using namespace tolk;
|
||||||
|
|
||||||
td::Result<std::string> compile_internal(char *config_json) {
|
td::Result<std::string> compile_internal(char *config_json) {
|
||||||
TRY_RESULT(input_json, td::json_decode(td::MutableSlice(config_json)))
|
TRY_RESULT(input_json, td::json_decode(td::MutableSlice(config_json)))
|
||||||
td::JsonObject& config = input_json.get_object();
|
td::JsonObject& config = input_json.get_object();
|
||||||
|
|
||||||
TRY_RESULT(opt_level, td::get_json_object_int_field(config, "optimizationLevel", true, 2));
|
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(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));
|
TRY_RESULT(entrypoint_file_name, td::get_json_object_string_field(config, "entrypointFileName", false));
|
||||||
|
|
||||||
tolk::opt_level = std::max(0, opt_level);
|
G.settings.verbosity = 0;
|
||||||
tolk::verbosity = 0;
|
G.settings.optimization_level = std::max(0, opt_level);
|
||||||
tolk::stack_layout_comments = stack_comments;
|
G.settings.stdlib_filename = stdlib_tolk;
|
||||||
|
G.settings.stack_layout_comments = stack_comments;
|
||||||
|
G.settings.entrypoint_filename = entrypoint_file_name;
|
||||||
|
|
||||||
std::ostringstream outs, errs;
|
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) {
|
if (tolk_res != 0) {
|
||||||
return td::Status::Error("Tolk compilation error: " + errs.str());
|
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("fiftCode", fift_res.fiftCode);
|
||||||
obj("codeBoc64", fift_res.codeBoc64);
|
obj("codeBoc64", fift_res.codeBoc64);
|
||||||
obj("codeHashHex", fift_res.codeHashHex);
|
obj("codeHashHex", fift_res.codeHashHex);
|
||||||
|
obj("stderr", errs.str().c_str());
|
||||||
obj.leave();
|
obj.leave();
|
||||||
|
|
||||||
return result_json.string_builder().as_cslice().str();
|
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.
|
/// 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);
|
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) {
|
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* destContents = nullptr;
|
||||||
char* destError = nullptr;
|
char* destError = nullptr;
|
||||||
_readCallback(static_cast<int>(kind), data, &destContents, &destError);
|
_readCallback(static_cast<int>(kind), data, &destContents, &destError);
|
||||||
|
@ -93,7 +100,7 @@ extern "C" {
|
||||||
const char* version() {
|
const char* version() {
|
||||||
auto version_json = td::JsonBuilder();
|
auto version_json = td::JsonBuilder();
|
||||||
auto obj = version_json.enter_object();
|
auto obj = version_json.enter_object();
|
||||||
obj("tolkVersion", tolk::tolk_version);
|
obj("tolkVersion", tolk_version);
|
||||||
obj("tolkFiftLibCommitHash", GitMetadata::CommitSHA1());
|
obj("tolkFiftLibCommitHash", GitMetadata::CommitSHA1());
|
||||||
obj("tolkFiftLibCommitDate", GitMetadata::CommitDate());
|
obj("tolkFiftLibCommitDate", GitMetadata::CommitDate());
|
||||||
obj.leave();
|
obj.leave();
|
||||||
|
@ -101,13 +108,9 @@ const char* version() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *tolk_compile(char *config_json, CStyleReadFileCallback callback) {
|
const char *tolk_compile(char *config_json, CStyleReadFileCallback callback) {
|
||||||
if (callback) {
|
G.settings.read_callback = wrapReadCallback(callback);
|
||||||
tolk::read_callback = wrapReadCallback(callback);
|
|
||||||
} else {
|
|
||||||
tolk::read_callback = tolk::fs_read_callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto res = compile_internal(config_json);
|
td::Result<std::string> res = compile_internal(config_json);
|
||||||
|
|
||||||
if (res.is_error()) {
|
if (res.is_error()) {
|
||||||
auto result = res.move_as_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.
|
from all source files in the program, then also delete it here.
|
||||||
*/
|
*/
|
||||||
#include "tolk.h"
|
#include "tolk.h"
|
||||||
|
#include "compiler-state.h"
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include "git.h"
|
#include "git.h"
|
||||||
|
@ -33,14 +34,6 @@
|
||||||
|
|
||||||
namespace tolk {
|
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
|
// 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)
|
// 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
|
// 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 {
|
bool SymValCodeFunc::does_need_codegen() const {
|
||||||
// when a function is declared, but not referenced from code in any way, don't generate its body
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
// when a function is referenced like `var a = some_fn;` (or in some other non-call way), its continuation should exist
|
// 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
|
// 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_dfs(const std::unique_ptr<Op>& op);
|
||||||
|
|
||||||
void mark_function_used(SymValCodeFunc* func_val) {
|
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() {
|
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);
|
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() ||
|
if (func_val->method_id.not_null() ||
|
||||||
name == "main" || name == "recv_internal" || name == "recv_external" ||
|
name == "main" || name == "recv_internal" || name == "recv_external" ||
|
||||||
name == "run_ticktock" || name == "split_prepare" || name == "split_install") {
|
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);
|
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
||||||
tolk_assert(func_val);
|
tolk_assert(func_val);
|
||||||
std::string name = symbols.get_name(func_sym->sym_idx);
|
std::string name = G.symbols.get_name(func_sym->sym_idx);
|
||||||
if (verbosity >= 2) {
|
if (G.is_verbosity(2)) {
|
||||||
errs << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
|
std::cerr << "\n\n=========================\nfunction " << name << " : " << func_val->get_type() << std::endl;
|
||||||
}
|
}
|
||||||
if (!func_val->code) {
|
if (!func_val->code) {
|
||||||
throw ParseError(func_sym->loc, "function `" + name + "` is just declared, not implemented");
|
throw ParseError(func_sym->loc, "function `" + name + "` is just declared, not implemented");
|
||||||
} else {
|
} else {
|
||||||
CodeBlob& code = *(func_val->code);
|
CodeBlob& code = *(func_val->code);
|
||||||
if (verbosity >= 3) {
|
if (G.is_verbosity(3)) {
|
||||||
code.print(errs, 9);
|
code.print(std::cerr, 9);
|
||||||
}
|
}
|
||||||
code.simplify_var_types();
|
code.simplify_var_types();
|
||||||
if (verbosity >= 5) {
|
if (G.is_verbosity(5)) {
|
||||||
errs << "after simplify_var_types: \n";
|
std::cerr << "after simplify_var_types: \n";
|
||||||
code.print(errs, 0);
|
code.print(std::cerr, 0);
|
||||||
}
|
}
|
||||||
code.prune_unreachable_code();
|
code.prune_unreachable_code();
|
||||||
if (verbosity >= 5) {
|
if (G.is_verbosity(5)) {
|
||||||
errs << "after prune_unreachable: \n";
|
std::cerr << "after prune_unreachable: \n";
|
||||||
code.print(errs, 0);
|
code.print(std::cerr, 0);
|
||||||
}
|
}
|
||||||
code.split_vars(true);
|
code.split_vars(true);
|
||||||
if (verbosity >= 5) {
|
if (G.is_verbosity(5)) {
|
||||||
errs << "after split_vars: \n";
|
std::cerr << "after split_vars: \n";
|
||||||
code.print(errs, 0);
|
code.print(std::cerr, 0);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
code.compute_used_code_vars();
|
code.compute_used_code_vars();
|
||||||
if (verbosity >= 4) {
|
if (G.is_verbosity(4)) {
|
||||||
errs << "after compute_used_vars: \n";
|
std::cerr << "after compute_used_vars: \n";
|
||||||
code.print(errs, 6);
|
code.print(std::cerr, 6);
|
||||||
}
|
}
|
||||||
code.fwd_analyze();
|
code.fwd_analyze();
|
||||||
if (verbosity >= 5) {
|
if (G.is_verbosity(5)) {
|
||||||
errs << "after fwd_analyze: \n";
|
std::cerr << "after fwd_analyze: \n";
|
||||||
code.print(errs, 6);
|
code.print(std::cerr, 6);
|
||||||
}
|
}
|
||||||
code.prune_unreachable_code();
|
code.prune_unreachable_code();
|
||||||
if (verbosity >= 5) {
|
if (G.is_verbosity(5)) {
|
||||||
errs << "after prune_unreachable: \n";
|
std::cerr << "after prune_unreachable: \n";
|
||||||
code.print(errs, 6);
|
code.print(std::cerr, 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
code.mark_noreturn();
|
code.mark_noreturn();
|
||||||
if (verbosity >= 3) {
|
if (G.is_verbosity(3)) {
|
||||||
code.print(errs, 15);
|
code.print(std::cerr, 15);
|
||||||
}
|
}
|
||||||
if (verbosity >= 2) {
|
if (G.is_verbosity(2)) {
|
||||||
errs << "\n---------- resulting code for " << name << " -------------\n";
|
std::cerr << "\n---------- resulting code for " << name << " -------------\n";
|
||||||
}
|
}
|
||||||
const char* modifier = "";
|
const char* modifier = "";
|
||||||
if (func_val->is_inline()) {
|
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()) {
|
} else if (func_val->is_inline_ref()) {
|
||||||
modifier = "REF";
|
modifier = "REF";
|
||||||
}
|
}
|
||||||
outs << std::string(2, ' ') << name << " PROC" << modifier << ":<{\n";
|
std::cout << std::string(2, ' ') << name << " PROC" << modifier << ":<{\n";
|
||||||
int mode = 0;
|
int mode = 0;
|
||||||
if (stack_layout_comments) {
|
if (G.settings.stack_layout_comments) {
|
||||||
mode |= Stack::_StkCmt | Stack::_CptStkCmt;
|
mode |= Stack::_StkCmt | Stack::_CptStkCmt;
|
||||||
}
|
}
|
||||||
if (opt_level < 2) {
|
|
||||||
mode |= Stack::_DisableOpt;
|
|
||||||
}
|
|
||||||
if (func_val->is_inline() && code.ops->noreturn()) {
|
if (func_val->is_inline() && code.ops->noreturn()) {
|
||||||
mode |= Stack::_InlineFunc;
|
mode |= Stack::_InlineFunc;
|
||||||
}
|
}
|
||||||
if (func_val->is_inline() || func_val->is_inline_ref()) {
|
if (func_val->is_inline() || func_val->is_inline_ref()) {
|
||||||
mode |= Stack::_InlineAny;
|
mode |= Stack::_InlineAny;
|
||||||
}
|
}
|
||||||
code.generate_code(outs, mode, 2);
|
code.generate_code(std::cout, mode, 2);
|
||||||
outs << std::string(2, ' ') << "}>\n";
|
std::cout << std::string(2, ' ') << "}>\n";
|
||||||
if (verbosity >= 2) {
|
if (G.is_verbosity(2)) {
|
||||||
errs << "--------------\n";
|
std::cerr << "--------------\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int generate_output(std::ostream &outs, std::ostream &errs) {
|
// this function either throws or successfully prints whole program output to std::cout
|
||||||
outs << "\"Asm.fif\" include\n";
|
void generate_output() {
|
||||||
outs << "// automatically generated from " << generated_from << std::endl;
|
std::cout << "\"Asm.fif\" include\n";
|
||||||
outs << "PROGRAM{\n";
|
std::cout << "// automatically generated from " << G.generated_from << std::endl;
|
||||||
|
std::cout << "PROGRAM{\n";
|
||||||
mark_used_symbols();
|
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);
|
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
||||||
tolk_assert(func_val);
|
tolk_assert(func_val);
|
||||||
if (!func_val->does_need_codegen()) {
|
if (!func_val->does_need_codegen()) {
|
||||||
if (verbosity >= 2) {
|
if (G.is_verbosity(2)) {
|
||||||
errs << func_sym->name() << ": code not generated, function does not need codegen\n";
|
std::cerr << func_sym->name() << ": code not generated, function does not need codegen\n";
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string name = symbols.get_name(func_sym->sym_idx);
|
std::string name = G.symbols.get_name(func_sym->sym_idx);
|
||||||
outs << std::string(2, ' ');
|
std::cout << std::string(2, ' ');
|
||||||
if (func_val->method_id.is_null()) {
|
if (func_val->method_id.is_null()) {
|
||||||
outs << "DECLPROC " << name << "\n";
|
std::cout << "DECLPROC " << name << "\n";
|
||||||
} else {
|
} 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);
|
auto* glob_val = dynamic_cast<SymValGlobVar*>(gvar_sym->value);
|
||||||
tolk_assert(glob_val);
|
tolk_assert(glob_val);
|
||||||
if (!glob_val->is_really_used && pragma_remove_unused_functions.enabled()) {
|
if (!glob_val->is_really_used && G.pragma_remove_unused_functions.enabled()) {
|
||||||
if (verbosity >= 2) {
|
if (G.is_verbosity(2)) {
|
||||||
errs << gvar_sym->name() << ": variable not generated, it's unused\n";
|
std::cerr << gvar_sym->name() << ": variable not generated, it's unused\n";
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
std::string name = symbols.get_name(gvar_sym->sym_idx);
|
std::string name = G.symbols.get_name(gvar_sym->sym_idx);
|
||||||
outs << std::string(2, ' ') << "DECLGLOBVAR " << name << "\n";
|
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);
|
SymValCodeFunc* func_val = dynamic_cast<SymValCodeFunc*>(func_sym->value);
|
||||||
if (!func_val->does_need_codegen()) {
|
if (!func_val->does_need_codegen()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
generate_output_func(func_sym);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "}END>c\n";
|
||||||
|
if (!G.settings.boc_output_filename.empty()) {
|
||||||
|
std::cout << "boc>B \"" << G.settings.boc_output_filename << "\" B>file\n";
|
||||||
}
|
}
|
||||||
outs << "}END>c\n";
|
|
||||||
if (!boc_output_filename.empty()) {
|
|
||||||
outs << "boc>B \"" << 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();
|
define_builtins();
|
||||||
lexer_init();
|
lexer_init();
|
||||||
pragma_allow_post_modification.always_on_and_deprecated("0.5.0");
|
G.pragma_allow_post_modification.always_on_and_deprecated("0.5.0");
|
||||||
pragma_compute_asm_ltr.always_on_and_deprecated("0.5.0");
|
G.pragma_compute_asm_ltr.always_on_and_deprecated("0.5.0");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bool ok = parse_source_file(entrypoint_file_name.c_str(), {});
|
{
|
||||||
if (!ok) {
|
if (G.settings.stdlib_filename.empty()) {
|
||||||
throw Fatal{"output code generation omitted because of errors"};
|
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
|
// todo #ifdef TOLK_PROFILING + comment
|
||||||
// lexer_measure_performance(all_src_files.get_all_files());
|
// lexer_measure_performance(all_src_files.get_all_files());
|
||||||
|
|
||||||
return generate_output(outs, errs);
|
generate_output();
|
||||||
|
return 0;
|
||||||
} catch (Fatal& fatal) {
|
} catch (Fatal& fatal) {
|
||||||
errs << "fatal: " << fatal << std::endl;
|
std::cerr << "fatal: " << fatal << std::endl;
|
||||||
return 2;
|
return 2;
|
||||||
} catch (ParseError& error) {
|
} catch (ParseError& error) {
|
||||||
errs << error << std::endl;
|
std::cerr << error << std::endl;
|
||||||
return 2;
|
return 2;
|
||||||
} catch (UnifyError& unif_err) {
|
} catch (UnifyError& unif_err) {
|
||||||
errs << "fatal: ";
|
std::cerr << "fatal: ";
|
||||||
unif_err.print_message(errs);
|
unif_err.print_message(std::cerr);
|
||||||
errs << std::endl;
|
std::cerr << std::endl;
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace tolk
|
} // 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/>.
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#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 "src-file.h"
|
||||||
#include "lexer.h"
|
#include "lexer.h"
|
||||||
#include "symtable.h"
|
#include "symtable.h"
|
||||||
|
#include "crypto/common/refint.h"
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <stack>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#define tolk_assert(expr) \
|
#define tolk_assert(expr) \
|
||||||
(bool(expr) ? void(0) \
|
(bool(expr) ? void(0) \
|
||||||
|
@ -38,14 +32,6 @@
|
||||||
|
|
||||||
namespace tolk {
|
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
|
* TYPE EXPRESSIONS
|
||||||
|
@ -200,8 +186,6 @@ std::ostream& operator<<(std::ostream& os, const UnifyError& ue);
|
||||||
|
|
||||||
void unify(TypeExpr*& te1, TypeExpr*& te2);
|
void unify(TypeExpr*& te1, TypeExpr*& te2);
|
||||||
|
|
||||||
// extern int TypeExpr::holes;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* ABSTRACT CODE
|
* 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) {
|
CodeBlob(TypeExpr* ret = nullptr) : var_cnt(0), in_var_cnt(0), op_cnt(0), ret_type(ret), cur_ops(&ops) {
|
||||||
}
|
}
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
Op& emplace_back(const Args&... args) {
|
Op& emplace_back(Args&&... args) {
|
||||||
Op& res = *(*cur_ops = std::make_unique<Op>(args...));
|
Op& res = *(*cur_ops = std::make_unique<Op>(args...));
|
||||||
cur_ops = &(res.next);
|
cur_ops = &(res.next);
|
||||||
return res;
|
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
|
// defined in parse-tolk.cpp
|
||||||
void parse_source(const SrcFile* file);
|
td::Result<SrcFile*> locate_source_file(const std::string& rel_filename);
|
||||||
bool parse_source_file(const char* filename, SrcLocation loc_included_from);
|
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
|
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) {
|
inline std::ostream& operator<<(std::ostream& os, const StackTransform& trans) {
|
||||||
trans.show(os);
|
trans.show(os);
|
||||||
return os;
|
return os;
|
||||||
|
@ -1375,14 +1338,14 @@ bool apply_op(StackTransform& trans, const AsmOp& op);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct Optimizer {
|
struct Optimizer {
|
||||||
enum { n = optimize_depth };
|
static constexpr int optimize_depth = 20;
|
||||||
AsmOpConsList code_;
|
AsmOpConsList code_;
|
||||||
int l_{0}, l2_{0}, p_, pb_, q_, indent_;
|
int l_{0}, l2_{0}, p_, pb_, q_, indent_;
|
||||||
bool debug_{false};
|
bool debug_{false};
|
||||||
std::unique_ptr<AsmOp> op_[n], oq_[n];
|
std::unique_ptr<AsmOp> op_[optimize_depth], oq_[optimize_depth];
|
||||||
AsmOpCons* op_cons_[n];
|
AsmOpCons* op_cons_[optimize_depth];
|
||||||
int offs_[n];
|
int offs_[optimize_depth];
|
||||||
StackTransform tr_[n];
|
StackTransform tr_[optimize_depth];
|
||||||
int mode_{0};
|
int mode_{0};
|
||||||
Optimizer() {
|
Optimizer() {
|
||||||
}
|
}
|
||||||
|
@ -1475,7 +1438,7 @@ struct Stack {
|
||||||
StackLayoutExt s;
|
StackLayoutExt s;
|
||||||
AsmOpList& o;
|
AsmOpList& o;
|
||||||
enum {
|
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,
|
_InlineFunc = 512, _NeedRetAlt = 1024, _InlineAny = 2048,
|
||||||
_ModeSave = _InlineFunc | _NeedRetAlt | _InlineAny,
|
_ModeSave = _InlineFunc | _NeedRetAlt | _InlineAny,
|
||||||
_Garbage = -0x10000
|
_Garbage = -0x10000
|
||||||
|
@ -1640,33 +1603,6 @@ AsmOp push_const(td::RefInt256 x);
|
||||||
void define_builtins();
|
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
|
} // namespace tolk
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue