mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-14 12:12:21 +00:00
Add namespaces to Fift (#641)
* Add fift-based disassembler * Fift improvements: namespaces, hashmaps, flow controls * Fift: add lib with better block structuring and more * Minor changes in fift HashMap + tests (#643) * Minor changes in fift HashMap * Add tests for extended fift --------- Co-authored-by: OmicronTau <omicron@ton.org> Co-authored-by: Tolya <1449561+tolya-yanot@users.noreply.github.com> Co-authored-by: SpyCheese <mikle98@yandex.ru>
This commit is contained in:
parent
4590ed381b
commit
865ebfce8d
31 changed files with 2323 additions and 699 deletions
|
@ -143,6 +143,7 @@ set(FIFT_SOURCE
|
|||
fift/Dictionary.cpp
|
||||
fift/Fift.cpp
|
||||
fift/IntCtx.cpp
|
||||
fift/HashMap.cpp
|
||||
fift/Continuation.cpp
|
||||
fift/SourceLookup.cpp
|
||||
fift/utils.cpp
|
||||
|
@ -151,6 +152,7 @@ set(FIFT_SOURCE
|
|||
fift/Dictionary.h
|
||||
fift/Fift.h
|
||||
fift/IntCtx.h
|
||||
fift/HashMap.h
|
||||
fift/Continuation.h
|
||||
fift/SourceLookup.h
|
||||
fift/utils.h
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "fift/Fift.h"
|
||||
#include "fift/Dictionary.h"
|
||||
#include "fift/SourceLookup.h"
|
||||
#include "fift/IntCtx.h"
|
||||
#include "fift/words.h"
|
||||
|
||||
#include "td/utils/logging.h"
|
||||
|
@ -866,8 +867,9 @@ int main(int argc, char* const argv[]) {
|
|||
case 'v':
|
||||
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
|
||||
break;
|
||||
case 'V':
|
||||
std::cout << "create-state build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
|
||||
case 'V':
|
||||
std::cout << "create-state build information: [ Commit: " << GitMetadata::CommitSHA1()
|
||||
<< ", Date: " << GitMetadata::CommitDate() << "]\n";
|
||||
std::exit(0);
|
||||
break;
|
||||
case 'h':
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace fift {
|
|||
//
|
||||
bool FiftCont::print_dict_name(std::ostream& os, const IntCtx& ctx) const {
|
||||
std::string word_name;
|
||||
if (ctx.dictionary && ctx.dictionary->lookup_def(this, &word_name)) {
|
||||
if (ctx.dictionary.lookup_def(this, &word_name)) {
|
||||
if (word_name.size() && word_name.back() == ' ') {
|
||||
word_name.pop_back();
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ bool FiftCont::print_dict_name(std::ostream& os, const IntCtx& ctx) const {
|
|||
|
||||
std::string FiftCont::get_dict_name(const IntCtx& ctx) const {
|
||||
std::string word_name;
|
||||
if (ctx.dictionary && ctx.dictionary->lookup_def(this, &word_name)) {
|
||||
if (ctx.dictionary.lookup_def(this, &word_name)) {
|
||||
if (word_name.size() && word_name.back() == ' ') {
|
||||
word_name.pop_back();
|
||||
}
|
||||
|
@ -63,6 +63,140 @@ bool FiftCont::dump(std::ostream& os, const IntCtx& ctx) const {
|
|||
return ok;
|
||||
}
|
||||
|
||||
//
|
||||
// StackWord
|
||||
//
|
||||
Ref<FiftCont> StackWord::run_tail(IntCtx& ctx) const {
|
||||
f(ctx.stack);
|
||||
return {};
|
||||
}
|
||||
|
||||
//
|
||||
// CtxWord
|
||||
//
|
||||
Ref<FiftCont> CtxWord::run_tail(IntCtx& ctx) const {
|
||||
f(ctx);
|
||||
return {};
|
||||
}
|
||||
|
||||
//
|
||||
// CtxTailWord
|
||||
//
|
||||
Ref<FiftCont> CtxTailWord::run_tail(IntCtx& ctx) const {
|
||||
return f(ctx);
|
||||
}
|
||||
|
||||
//
|
||||
// WordList
|
||||
//
|
||||
WordList::WordList(std::vector<Ref<FiftCont>>&& _list) : list(std::move(_list)) {
|
||||
}
|
||||
|
||||
WordList::WordList(const std::vector<Ref<FiftCont>>& _list) : list(_list) {
|
||||
}
|
||||
|
||||
WordList& WordList::push_back(Ref<FiftCont> word_def) {
|
||||
list.push_back(std::move(word_def));
|
||||
return *this;
|
||||
}
|
||||
|
||||
WordList& WordList::push_back(FiftCont& wd) {
|
||||
list.emplace_back(&wd);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Ref<FiftCont> WordList::run_tail(IntCtx& ctx) const {
|
||||
if (list.empty()) {
|
||||
return {};
|
||||
}
|
||||
if (list.size() > 1) {
|
||||
ctx.next = td::make_ref<ListCont>(std::move(ctx.next), Ref<WordList>(this), 1);
|
||||
}
|
||||
return list[0];
|
||||
}
|
||||
|
||||
void WordList::close() {
|
||||
list.shrink_to_fit();
|
||||
}
|
||||
|
||||
WordList& WordList::append(const std::vector<Ref<FiftCont>>& other) {
|
||||
list.insert(list.end(), other.begin(), other.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
WordList& WordList::append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end) {
|
||||
list.insert(list.end(), begin, end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool WordList::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "{";
|
||||
for (auto entry : list) {
|
||||
os << ' ';
|
||||
entry->print_name(os, ctx);
|
||||
}
|
||||
os << " }" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// ListCont
|
||||
//
|
||||
|
||||
Ref<FiftCont> ListCont::run_tail(IntCtx& ctx) const {
|
||||
auto sz = list->size();
|
||||
if (pos >= sz) {
|
||||
return std::move(ctx.next);
|
||||
} else if (ctx.next.not_null()) {
|
||||
ctx.next = td::make_ref<ListCont>(SeqCont::seq(next, std::move(ctx.next)), list, pos + 1);
|
||||
} else if (pos + 1 == sz) {
|
||||
ctx.next = next;
|
||||
} else {
|
||||
ctx.next = td::make_ref<ListCont>(next, list, pos + 1);
|
||||
}
|
||||
return list->at(pos);
|
||||
}
|
||||
|
||||
Ref<FiftCont> ListCont::run_modify(IntCtx& ctx) {
|
||||
auto sz = list->size();
|
||||
if (pos >= sz) {
|
||||
return std::move(ctx.next);
|
||||
}
|
||||
auto cur = list->at(pos++);
|
||||
if (ctx.next.not_null()) {
|
||||
next = SeqCont::seq(next, std::move(ctx.next));
|
||||
}
|
||||
if (pos == sz) {
|
||||
ctx.next = std::move(next);
|
||||
} else {
|
||||
ctx.next = self();
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
bool ListCont::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
std::string dict_name = list->get_dict_name(ctx);
|
||||
if (!dict_name.empty()) {
|
||||
os << "[in " << dict_name << ":] ";
|
||||
}
|
||||
std::size_t sz = list->size(), i, a = (pos >= 16 ? pos - 16 : 0), b = std::min(pos + 16, sz);
|
||||
if (a > 0) {
|
||||
os << "... ";
|
||||
}
|
||||
for (i = a; i < b; i++) {
|
||||
if (i == pos) {
|
||||
os << "**HERE** ";
|
||||
}
|
||||
list->at(i)->print_name(os, ctx);
|
||||
os << ' ';
|
||||
}
|
||||
if (b < sz) {
|
||||
os << "...";
|
||||
}
|
||||
os << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// QuitCont
|
||||
//
|
||||
|
@ -295,12 +429,15 @@ bool GenericLitCont::print_name(std::ostream& os, const IntCtx& ctx) const {
|
|||
bool sp = false;
|
||||
for (auto entry : list) {
|
||||
if (sp) {
|
||||
os << sp;
|
||||
os << ' ';
|
||||
}
|
||||
sp = true;
|
||||
int tp = entry.type();
|
||||
if (entry.is_int() || entry.is(vm::StackEntry::t_string) || entry.is(vm::StackEntry::t_bytes)) {
|
||||
entry.dump(os);
|
||||
} else if (entry.is_atom()) {
|
||||
os << '`';
|
||||
entry.dump(os);
|
||||
} else {
|
||||
auto cont_lit = entry.as_object<FiftCont>();
|
||||
if (cont_lit.not_null()) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
Copyright 2020 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include "common/refcnt.hpp"
|
||||
#include "common/refint.h"
|
||||
#include "vm/stack.hpp"
|
||||
|
@ -76,6 +77,101 @@ class FiftCont : public td::CntObject {
|
|||
}
|
||||
};
|
||||
|
||||
typedef std::function<void(vm::Stack&)> StackWordFunc;
|
||||
typedef std::function<void(IntCtx&)> CtxWordFunc;
|
||||
typedef std::function<Ref<FiftCont>(IntCtx&)> CtxTailWordFunc;
|
||||
|
||||
class NopWord : public FiftCont {
|
||||
public:
|
||||
NopWord() = default;
|
||||
~NopWord() override = default;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
class StackWord : public FiftCont {
|
||||
StackWordFunc f;
|
||||
|
||||
public:
|
||||
StackWord(StackWordFunc _f) : f(std::move(_f)) {
|
||||
}
|
||||
~StackWord() override = default;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class CtxWord : public FiftCont {
|
||||
CtxWordFunc f;
|
||||
|
||||
public:
|
||||
CtxWord(CtxWordFunc _f) : f(std::move(_f)) {
|
||||
}
|
||||
~CtxWord() override = default;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class CtxTailWord : public FiftCont {
|
||||
CtxTailWordFunc f;
|
||||
|
||||
public:
|
||||
CtxTailWord(CtxTailWordFunc _f) : f(std::move(_f)) {
|
||||
}
|
||||
~CtxTailWord() override = default;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class WordList : public FiftCont {
|
||||
std::vector<Ref<FiftCont>> list;
|
||||
|
||||
public:
|
||||
~WordList() override = default;
|
||||
WordList() = default;
|
||||
WordList(std::vector<Ref<FiftCont>>&& _list);
|
||||
WordList(const std::vector<Ref<FiftCont>>& _list);
|
||||
WordList& push_back(Ref<FiftCont> word_def);
|
||||
WordList& push_back(FiftCont& wd);
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
void close();
|
||||
bool is_list() const override {
|
||||
return true;
|
||||
}
|
||||
long long list_size() const override {
|
||||
return (long long)list.size();
|
||||
}
|
||||
std::size_t size() const {
|
||||
return list.size();
|
||||
}
|
||||
const Ref<FiftCont>& at(std::size_t idx) const {
|
||||
return list.at(idx);
|
||||
}
|
||||
const Ref<FiftCont>* get_list() const override {
|
||||
return list.data();
|
||||
}
|
||||
WordList& append(const std::vector<Ref<FiftCont>>& other);
|
||||
WordList& append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end);
|
||||
WordList* make_copy() const override {
|
||||
return new WordList(list);
|
||||
}
|
||||
bool dump(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class ListCont : public FiftCont {
|
||||
Ref<FiftCont> next;
|
||||
Ref<WordList> list;
|
||||
std::size_t pos;
|
||||
|
||||
public:
|
||||
ListCont(Ref<FiftCont> nxt, Ref<WordList> wl, std::size_t p = 0) : next(std::move(nxt)), list(std::move(wl)), pos(p) {
|
||||
}
|
||||
~ListCont() override = default;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
Ref<FiftCont> up() const override {
|
||||
return next;
|
||||
}
|
||||
bool dump(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class QuitCont : public FiftCont {
|
||||
int exit_code;
|
||||
|
||||
|
|
|
@ -17,143 +17,10 @@
|
|||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "Dictionary.h"
|
||||
#include "IntCtx.h"
|
||||
|
||||
namespace fift {
|
||||
|
||||
//
|
||||
// StackWord
|
||||
//
|
||||
Ref<FiftCont> StackWord::run_tail(IntCtx& ctx) const {
|
||||
f(ctx.stack);
|
||||
return {};
|
||||
}
|
||||
|
||||
//
|
||||
// CtxWord
|
||||
//
|
||||
Ref<FiftCont> CtxWord::run_tail(IntCtx& ctx) const {
|
||||
f(ctx);
|
||||
return {};
|
||||
}
|
||||
|
||||
//
|
||||
// CtxTailWord
|
||||
//
|
||||
Ref<FiftCont> CtxTailWord::run_tail(IntCtx& ctx) const {
|
||||
return f(ctx);
|
||||
}
|
||||
|
||||
//
|
||||
// WordList
|
||||
//
|
||||
WordList::WordList(std::vector<Ref<FiftCont>>&& _list) : list(std::move(_list)) {
|
||||
}
|
||||
|
||||
WordList::WordList(const std::vector<Ref<FiftCont>>& _list) : list(_list) {
|
||||
}
|
||||
|
||||
WordList& WordList::push_back(Ref<FiftCont> word_def) {
|
||||
list.push_back(std::move(word_def));
|
||||
return *this;
|
||||
}
|
||||
|
||||
WordList& WordList::push_back(FiftCont& wd) {
|
||||
list.emplace_back(&wd);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Ref<FiftCont> WordList::run_tail(IntCtx& ctx) const {
|
||||
if (list.empty()) {
|
||||
return {};
|
||||
}
|
||||
if (list.size() > 1) {
|
||||
ctx.next = td::make_ref<ListCont>(std::move(ctx.next), Ref<WordList>(this), 1);
|
||||
}
|
||||
return list[0];
|
||||
}
|
||||
|
||||
void WordList::close() {
|
||||
list.shrink_to_fit();
|
||||
}
|
||||
|
||||
WordList& WordList::append(const std::vector<Ref<FiftCont>>& other) {
|
||||
list.insert(list.end(), other.begin(), other.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
WordList& WordList::append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end) {
|
||||
list.insert(list.end(), begin, end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool WordList::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
os << "{";
|
||||
for (auto entry : list) {
|
||||
os << ' ';
|
||||
entry->print_name(os, ctx);
|
||||
}
|
||||
os << " }" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// ListCont
|
||||
//
|
||||
|
||||
Ref<FiftCont> ListCont::run_tail(IntCtx& ctx) const {
|
||||
auto sz = list->size();
|
||||
if (pos >= sz) {
|
||||
return std::move(ctx.next);
|
||||
} else if (ctx.next.not_null()) {
|
||||
ctx.next = td::make_ref<ListCont>(SeqCont::seq(next, std::move(ctx.next)), list, pos + 1);
|
||||
} else if (pos + 1 == sz) {
|
||||
ctx.next = next;
|
||||
} else {
|
||||
ctx.next = td::make_ref<ListCont>(next, list, pos + 1);
|
||||
}
|
||||
return list->at(pos);
|
||||
}
|
||||
|
||||
Ref<FiftCont> ListCont::run_modify(IntCtx& ctx) {
|
||||
auto sz = list->size();
|
||||
if (pos >= sz) {
|
||||
return std::move(ctx.next);
|
||||
}
|
||||
auto cur = list->at(pos++);
|
||||
if (ctx.next.not_null()) {
|
||||
next = SeqCont::seq(next, std::move(ctx.next));
|
||||
}
|
||||
if (pos == sz) {
|
||||
ctx.next = std::move(next);
|
||||
} else {
|
||||
ctx.next = self();
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
bool ListCont::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||
std::string dict_name = list->get_dict_name(ctx);
|
||||
if (!dict_name.empty()) {
|
||||
os << "[in " << dict_name << ":] ";
|
||||
}
|
||||
std::size_t sz = list->size(), i, a = (pos >= 16 ? pos - 16 : 0), b = std::min(pos + 16, sz);
|
||||
if (a > 0) {
|
||||
os << "... ";
|
||||
}
|
||||
for (i = a; i < b; i++) {
|
||||
if (i == pos) {
|
||||
os << "**HERE** ";
|
||||
}
|
||||
list->at(i)->print_name(os, ctx);
|
||||
os << ' ';
|
||||
}
|
||||
if (b < sz) {
|
||||
os << "...";
|
||||
}
|
||||
os << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// DictEntry
|
||||
//
|
||||
|
@ -167,15 +34,49 @@ DictEntry::DictEntry(CtxWordFunc func, bool _act) : def(Ref<CtxWord>{true, std::
|
|||
DictEntry::DictEntry(CtxTailWordFunc func, bool _act) : def(Ref<CtxTailWord>{true, std::move(func)}), active(_act) {
|
||||
}
|
||||
|
||||
DictEntry DictEntry::create_from(vm::StackEntry se) {
|
||||
if (se.is_tuple()) {
|
||||
auto& tuple = *se.as_tuple();
|
||||
if (tuple.size() == 1) {
|
||||
auto def = tuple[0].as_object<FiftCont>();
|
||||
if (def.not_null()) {
|
||||
return DictEntry{std::move(def), true};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto def = std::move(se).as_object<FiftCont>();
|
||||
if (def.not_null()) {
|
||||
return DictEntry{std::move(def)};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
DictEntry::operator vm::StackEntry() const& {
|
||||
if (def.is_null()) {
|
||||
return {};
|
||||
} else if (active) {
|
||||
return vm::make_tuple_ref(vm::StackEntry{vm::from_object, def});
|
||||
} else {
|
||||
return {vm::from_object, def};
|
||||
}
|
||||
}
|
||||
|
||||
DictEntry::operator vm::StackEntry() && {
|
||||
if (def.is_null()) {
|
||||
return {};
|
||||
} else if (active) {
|
||||
return vm::make_tuple_ref(vm::StackEntry{vm::from_object, std::move(def)});
|
||||
} else {
|
||||
return {vm::from_object, std::move(def)};
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Dictionary
|
||||
//
|
||||
DictEntry* Dictionary::lookup(td::Slice name) {
|
||||
auto it = words_.find(name);
|
||||
if (it == words_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
DictEntry Dictionary::lookup(std::string name) const {
|
||||
return DictEntry::create_from(words().get(name));
|
||||
}
|
||||
|
||||
void Dictionary::def_ctx_word(std::string name, CtxWordFunc func) {
|
||||
|
@ -196,26 +97,27 @@ void Dictionary::def_ctx_tail_word(std::string name, CtxTailWordFunc func) {
|
|||
}
|
||||
|
||||
void Dictionary::def_word(std::string name, DictEntry word) {
|
||||
auto res = words_.emplace(name, std::move(word));
|
||||
LOG_IF(FATAL, !res.second) << "Cannot redefine word: " << name;
|
||||
auto dict = words();
|
||||
dict.set(std::move(name), vm::StackEntry(std::move(word)));
|
||||
set_words(dict);
|
||||
}
|
||||
|
||||
void Dictionary::undef_word(td::Slice name) {
|
||||
auto it = words_.find(name);
|
||||
if (it == words_.end()) {
|
||||
return;
|
||||
void Dictionary::undef_word(std::string name) {
|
||||
auto dict = words();
|
||||
if (dict.remove(name)) {
|
||||
set_words(dict);
|
||||
}
|
||||
words_.erase(it);
|
||||
}
|
||||
|
||||
bool Dictionary::lookup_def(const FiftCont* cont, std::string* word_ptr) const {
|
||||
if (!cont) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& entry : words_) {
|
||||
if (entry.second.get_def().get() == cont) {
|
||||
for (auto entry : words()) {
|
||||
auto val = DictEntry::create_from(entry.value());
|
||||
if (val.get_def().get() == cont && entry.key().is_string()) {
|
||||
if (word_ptr) {
|
||||
*word_ptr = entry.first;
|
||||
*word_ptr = vm::StackEntry(entry.key()).as_string();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -223,35 +125,4 @@ bool Dictionary::lookup_def(const FiftCont* cont, std::string* word_ptr) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void interpret_nop(vm::Stack& stack) {
|
||||
}
|
||||
|
||||
Ref<FiftCont> Dictionary::nop_word_def = Ref<StackWord>{true, interpret_nop};
|
||||
|
||||
//
|
||||
// functions for wordef
|
||||
//
|
||||
Ref<FiftCont> pop_exec_token(vm::Stack& stack) {
|
||||
stack.check_underflow(1);
|
||||
auto wd_ref = stack.pop().as_object<FiftCont>();
|
||||
if (wd_ref.is_null()) {
|
||||
throw IntError{"execution token expected"};
|
||||
}
|
||||
return wd_ref;
|
||||
}
|
||||
|
||||
Ref<WordList> pop_word_list(vm::Stack& stack) {
|
||||
stack.check_underflow(1);
|
||||
auto wl_ref = stack.pop().as_object<WordList>();
|
||||
if (wl_ref.is_null()) {
|
||||
throw IntError{"word list expected"};
|
||||
}
|
||||
return wl_ref;
|
||||
}
|
||||
|
||||
void push_argcount(vm::Stack& stack, int args) {
|
||||
stack.push_smallint(args);
|
||||
stack.push({vm::from_object, Dictionary::nop_word_def});
|
||||
}
|
||||
|
||||
} // namespace fift
|
||||
|
|
|
@ -17,115 +17,27 @@
|
|||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#include "IntCtx.h"
|
||||
#include "Continuation.h"
|
||||
#include "HashMap.h"
|
||||
#include "vm/box.hpp"
|
||||
|
||||
namespace fift {
|
||||
using td::Ref;
|
||||
|
||||
struct IntCtx;
|
||||
|
||||
/*
|
||||
*
|
||||
* WORD CLASSES
|
||||
*
|
||||
*/
|
||||
|
||||
typedef std::function<void(vm::Stack&)> StackWordFunc;
|
||||
typedef std::function<void(IntCtx&)> CtxWordFunc;
|
||||
|
||||
class StackWord : public FiftCont {
|
||||
StackWordFunc f;
|
||||
|
||||
public:
|
||||
StackWord(StackWordFunc _f) : f(std::move(_f)) {
|
||||
}
|
||||
~StackWord() override = default;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class CtxWord : public FiftCont {
|
||||
CtxWordFunc f;
|
||||
|
||||
public:
|
||||
CtxWord(CtxWordFunc _f) : f(std::move(_f)) {
|
||||
}
|
||||
~CtxWord() override = default;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
typedef std::function<Ref<FiftCont>(IntCtx&)> CtxTailWordFunc;
|
||||
|
||||
class CtxTailWord : public FiftCont {
|
||||
CtxTailWordFunc f;
|
||||
|
||||
public:
|
||||
CtxTailWord(CtxTailWordFunc _f) : f(std::move(_f)) {
|
||||
}
|
||||
~CtxTailWord() override = default;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class WordList : public FiftCont {
|
||||
std::vector<Ref<FiftCont>> list;
|
||||
|
||||
public:
|
||||
~WordList() override = default;
|
||||
WordList() = default;
|
||||
WordList(std::vector<Ref<FiftCont>>&& _list);
|
||||
WordList(const std::vector<Ref<FiftCont>>& _list);
|
||||
WordList& push_back(Ref<FiftCont> word_def);
|
||||
WordList& push_back(FiftCont& wd);
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
void close();
|
||||
bool is_list() const override {
|
||||
return true;
|
||||
}
|
||||
long long list_size() const override {
|
||||
return (long long)list.size();
|
||||
}
|
||||
std::size_t size() const {
|
||||
return list.size();
|
||||
}
|
||||
const Ref<FiftCont>& at(std::size_t idx) const {
|
||||
return list.at(idx);
|
||||
}
|
||||
const Ref<FiftCont>* get_list() const override {
|
||||
return list.data();
|
||||
}
|
||||
WordList& append(const std::vector<Ref<FiftCont>>& other);
|
||||
WordList& append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end);
|
||||
WordList* make_copy() const override {
|
||||
return new WordList(list);
|
||||
}
|
||||
bool dump(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class ListCont : public FiftCont {
|
||||
Ref<FiftCont> next;
|
||||
Ref<WordList> list;
|
||||
std::size_t pos;
|
||||
|
||||
public:
|
||||
ListCont(Ref<FiftCont> nxt, Ref<WordList> wl, std::size_t p = 0) : next(std::move(nxt)), list(std::move(wl)), pos(p) {
|
||||
}
|
||||
~ListCont() override = default;
|
||||
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
|
||||
Ref<FiftCont> run_modify(IntCtx& ctx) override;
|
||||
Ref<FiftCont> up() const override {
|
||||
return next;
|
||||
}
|
||||
bool dump(std::ostream& os, const IntCtx& ctx) const override;
|
||||
};
|
||||
|
||||
class DictEntry {
|
||||
Ref<FiftCont> def;
|
||||
bool active;
|
||||
bool active{false};
|
||||
|
||||
public:
|
||||
DictEntry() = delete;
|
||||
DictEntry() = default;
|
||||
DictEntry(const DictEntry& ref) = default;
|
||||
DictEntry(DictEntry&& ref) = default;
|
||||
DictEntry(Ref<FiftCont> _def, bool _act = false) : def(std::move(_def)), active(_act) {
|
||||
|
@ -137,6 +49,9 @@ class DictEntry {
|
|||
//DictEntry(std::vector<Ref<FiftCont>>&& word_list);
|
||||
DictEntry& operator=(const DictEntry&) = default;
|
||||
DictEntry& operator=(DictEntry&&) = default;
|
||||
static DictEntry create_from(vm::StackEntry se);
|
||||
explicit operator vm::StackEntry() const&;
|
||||
explicit operator vm::StackEntry() &&;
|
||||
Ref<FiftCont> get_def() const& {
|
||||
return def;
|
||||
}
|
||||
|
@ -146,16 +61,17 @@ class DictEntry {
|
|||
bool is_active() const {
|
||||
return active;
|
||||
}
|
||||
bool empty() const {
|
||||
return def.is_null();
|
||||
}
|
||||
explicit operator bool() const {
|
||||
return def.not_null();
|
||||
}
|
||||
bool operator!() const {
|
||||
return def.is_null();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
DictEntry::DictEntry(const std::vector<Ref<FiftCont>>& word_list) : def(Ref<WordList>{true, word_list}) {
|
||||
}
|
||||
|
||||
DictEntry::DictEntry(std::vector<Ref<FiftCont>>&& word_list) : def(Ref<WordList>{true, std::move(word_list)}) {
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* DICTIONARIES
|
||||
|
@ -164,37 +80,52 @@ DictEntry::DictEntry(std::vector<Ref<FiftCont>>&& word_list) : def(Ref<WordList>
|
|||
|
||||
class Dictionary {
|
||||
public:
|
||||
DictEntry* lookup(td::Slice name);
|
||||
Dictionary() : box_(true) {
|
||||
}
|
||||
Dictionary(Ref<vm::Box> box) : box_(std::move(box)) {
|
||||
}
|
||||
Dictionary(Ref<Hashmap> hmap) : box_(true, vm::from_object, std::move(hmap)) {
|
||||
}
|
||||
|
||||
DictEntry lookup(std::string name) const;
|
||||
void def_ctx_word(std::string name, CtxWordFunc func);
|
||||
void def_ctx_tail_word(std::string name, CtxTailWordFunc func);
|
||||
void def_active_word(std::string name, CtxWordFunc func);
|
||||
void def_stack_word(std::string name, StackWordFunc func);
|
||||
void def_word(std::string name, DictEntry word);
|
||||
void undef_word(td::Slice name);
|
||||
void undef_word(std::string name);
|
||||
bool lookup_def(const FiftCont* cont, std::string* word_ptr = nullptr) const;
|
||||
bool lookup_def(Ref<FiftCont> cont, std::string* word_ptr = nullptr) const {
|
||||
return lookup_def(cont.get(), word_ptr);
|
||||
}
|
||||
auto begin() const {
|
||||
return words_.begin();
|
||||
return words().begin();
|
||||
}
|
||||
auto end() const {
|
||||
return words_.end();
|
||||
return words().end();
|
||||
}
|
||||
HashmapKeeper words() const {
|
||||
if (box_->empty()) {
|
||||
return {};
|
||||
} else {
|
||||
return box_->get().as_object<Hashmap>();
|
||||
}
|
||||
}
|
||||
Ref<vm::Box> get_box() const {
|
||||
return box_;
|
||||
}
|
||||
void set_words(Ref<Hashmap> new_words) {
|
||||
box_->set(vm::StackEntry{vm::from_object, std::move(new_words)});
|
||||
}
|
||||
bool operator==(const Dictionary& other) const {
|
||||
return box_ == other.box_;
|
||||
}
|
||||
bool operator!=(const Dictionary& other) const {
|
||||
return box_ != other.box_;
|
||||
}
|
||||
|
||||
static Ref<FiftCont> nop_word_def;
|
||||
|
||||
private:
|
||||
std::map<std::string, DictEntry, std::less<>> words_;
|
||||
Ref<vm::Box> box_;
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
* AUX FUNCTIONS FOR WORD DEFS
|
||||
*
|
||||
*/
|
||||
|
||||
Ref<FiftCont> pop_exec_token(vm::Stack& stack);
|
||||
Ref<WordList> pop_word_list(vm::Stack& stack);
|
||||
void push_argcount(vm::Stack& stack, int args);
|
||||
} // namespace fift
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "Fift.h"
|
||||
|
||||
#include "IntCtx.h"
|
||||
#include "words.h"
|
||||
|
||||
#include "td/utils/PathView.h"
|
||||
|
@ -51,7 +51,7 @@ td::Result<int> Fift::interpret_istream(std::istream& stream, std::string curren
|
|||
td::Result<int> Fift::do_interpret(IntCtx& ctx, bool is_interactive) {
|
||||
ctx.ton_db = &config_.ton_db;
|
||||
ctx.source_lookup = &config_.source_lookup;
|
||||
ctx.dictionary = &config_.dictionary;
|
||||
ctx.dictionary = ctx.main_dictionary = ctx.context = config_.dictionary;
|
||||
ctx.output_stream = config_.output_stream;
|
||||
ctx.error_stream = config_.error_stream;
|
||||
if (!ctx.output_stream) {
|
||||
|
@ -71,7 +71,7 @@ td::Result<int> Fift::do_interpret(IntCtx& ctx, bool is_interactive) {
|
|||
ctx.top_ctx();
|
||||
ctx.clear_error();
|
||||
ctx.stack.clear();
|
||||
ctx.load_next_line();
|
||||
ctx.parser->load_next_line();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
371
crypto/fift/HashMap.cpp
Normal file
371
crypto/fift/HashMap.cpp
Normal file
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
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 "HashMap.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "IntCtx.h"
|
||||
|
||||
namespace fift {
|
||||
using td::Ref;
|
||||
|
||||
DictKey::DictKey(vm::StackEntry se) {
|
||||
auto tp = tp_ = se.type();
|
||||
switch (tp) {
|
||||
case Type::t_int:
|
||||
ref_ = se.as_int();
|
||||
break;
|
||||
case Type::t_atom:
|
||||
ref_ = se.as_atom();
|
||||
break;
|
||||
case Type::t_string:
|
||||
ref_ = se.as_string_ref();
|
||||
break;
|
||||
case Type::t_bytes:
|
||||
ref_ = se.as_bytes_ref();
|
||||
break;
|
||||
case Type::t_null:
|
||||
break;
|
||||
default:
|
||||
throw IntError{"unsupported key type"};
|
||||
}
|
||||
compute_hash();
|
||||
}
|
||||
|
||||
DictKey::operator vm::StackEntry() const& {
|
||||
switch (tp_) {
|
||||
case Type::t_int:
|
||||
return value<td::CntInt256>();
|
||||
case Type::t_atom:
|
||||
return value<vm::Atom>();
|
||||
case Type::t_string:
|
||||
case Type::t_bytes:
|
||||
return {value<td::Cnt<std::string>>(), tp_ == Type::t_bytes};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
DictKey::operator vm::StackEntry() && {
|
||||
switch (tp_) {
|
||||
case Type::t_int:
|
||||
return move_value<td::CntInt256>();
|
||||
case Type::t_atom:
|
||||
return move_value<vm::Atom>();
|
||||
case Type::t_string:
|
||||
case Type::t_bytes:
|
||||
return {move_value<td::Cnt<std::string>>(), tp_ == Type::t_bytes};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DictKey& dkey) {
|
||||
return os << vm::StackEntry(dkey).to_string();
|
||||
}
|
||||
|
||||
int DictKey::cmp_internal(const DictKey& other) const {
|
||||
if (tp_ != other.tp_) {
|
||||
return tp_ < other.tp_ ? -1 : 1;
|
||||
}
|
||||
switch (tp_) {
|
||||
case Type::t_int:
|
||||
return td::cmp(value<td::CntInt256>(), other.value<td::CntInt256>());
|
||||
case Type::t_atom: {
|
||||
int u = value<vm::Atom>()->index(), v = other.value<vm::Atom>()->index();
|
||||
return u == v ? 0 : (u < v ? -1 : 1);
|
||||
}
|
||||
case Type::t_string:
|
||||
case Type::t_bytes:
|
||||
return value<td::Cnt<std::string>>()->compare(*other.value<td::Cnt<std::string>>());
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int DictKey::cmp(const DictKey& other) const {
|
||||
if (hash_ < other.hash_) {
|
||||
return -1;
|
||||
} else if (hash_ > other.hash_) {
|
||||
return 1;
|
||||
} else {
|
||||
return cmp_internal(other);
|
||||
}
|
||||
}
|
||||
|
||||
DictKey::keyhash_t DictKey::compute_str_hash(DictKey::keyhash_t h, const char* str, std::size_t len) {
|
||||
const char* end = str + len;
|
||||
while (str < end) {
|
||||
h = h * StrHash + (unsigned char)*str++;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
DictKey::keyhash_t DictKey::compute_int_hash(td::AnyIntView<> x) {
|
||||
keyhash_t h = IntHash0;
|
||||
for (int i = 0; i < x.size(); i++) {
|
||||
h = h * MixConst3 + x.digits[i];
|
||||
}
|
||||
return h * MixConst4;
|
||||
}
|
||||
|
||||
DictKey::keyhash_t DictKey::compute_hash() {
|
||||
switch (tp_) {
|
||||
case Type::t_int:
|
||||
return hash_ = compute_int_hash(value<td::CntInt256>()->as_any_int());
|
||||
case Type::t_atom:
|
||||
return hash_ = value<vm::Atom>()->index() * MixConst1 + MixConst2;
|
||||
case Type::t_string:
|
||||
case Type::t_bytes: {
|
||||
auto ref = value<td::Cnt<std::string>>();
|
||||
return hash_ = compute_str_hash(tp_, ref->data(), ref->size());
|
||||
}
|
||||
default:
|
||||
return hash_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const Hashmap* Hashmap::lookup_key_aux(const Hashmap* root, const DictKey& key) {
|
||||
if (key.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
while (root) {
|
||||
int r = key.cmp(root->key_);
|
||||
if (!r) {
|
||||
break;
|
||||
}
|
||||
root = (r < 0 ? root->left_.get() : root->right_.get());
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
Ref<Hashmap> Hashmap::lookup_key(Ref<Hashmap> root, const DictKey& key) {
|
||||
return Ref<Hashmap>(lookup_key_aux(root.get(), key));
|
||||
}
|
||||
|
||||
vm::StackEntry Hashmap::get_key(Ref<Hashmap> root, const DictKey& key) {
|
||||
auto node = lookup_key_aux(root.get(), key);
|
||||
if (node) {
|
||||
return node->value_;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<Ref<Hashmap>, vm::StackEntry> Hashmap::get_remove_key(Ref<Hashmap> root, const DictKey& key) {
|
||||
if (root.is_null() || key.is_null()) {
|
||||
return {std::move(root), {}};
|
||||
}
|
||||
vm::StackEntry val;
|
||||
auto res = root->get_remove_internal(key, val);
|
||||
if (val.is_null()) {
|
||||
return {std::move(root), {}};
|
||||
} else {
|
||||
return {std::move(res), std::move(val)};
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Hashmap> Hashmap::remove_key(Ref<Hashmap> root, const DictKey& key) {
|
||||
if (root.is_null() || key.is_null()) {
|
||||
return root;
|
||||
}
|
||||
vm::StackEntry val;
|
||||
auto res = root->get_remove_internal(key, val);
|
||||
if (val.is_null()) {
|
||||
return root;
|
||||
} else {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Hashmap> Hashmap::get_remove_internal(const DictKey& key, vm::StackEntry& val) const {
|
||||
int r = key.cmp(key_);
|
||||
if (!r) {
|
||||
val = value_;
|
||||
return merge(left_, right_);
|
||||
} else if (r < 0) {
|
||||
if (left_.is_null()) {
|
||||
return {};
|
||||
} else {
|
||||
auto res = left_->get_remove_internal(key, val);
|
||||
if (val.is_null()) {
|
||||
return res;
|
||||
} else {
|
||||
return td::make_ref<Hashmap>(key_, value_, std::move(res), right_, y_);
|
||||
}
|
||||
}
|
||||
} else if (right_.is_null()) {
|
||||
return {};
|
||||
} else {
|
||||
auto res = right_->get_remove_internal(key, val);
|
||||
if (val.is_null()) {
|
||||
return res;
|
||||
} else {
|
||||
return td::make_ref<Hashmap>(key_, value_, left_, std::move(res), y_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Hashmap> Hashmap::merge(Ref<Hashmap> a, Ref<Hashmap> b) {
|
||||
if (a.is_null()) {
|
||||
return b;
|
||||
} else if (b.is_null()) {
|
||||
return a;
|
||||
} else if (a->y_ > b->y_) {
|
||||
auto& aa = a.write();
|
||||
aa.right_ = merge(std::move(aa.right_), std::move(b));
|
||||
return a;
|
||||
} else {
|
||||
auto& bb = b.write();
|
||||
bb.left_ = merge(std::move(a), std::move(bb.left_));
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Hashmap> Hashmap::set(Ref<Hashmap> root, const DictKey& key, vm::StackEntry value) {
|
||||
if (!key.is_null() && !replace(root, key, value) && !value.is_null()) {
|
||||
insert(root, key, value, new_y());
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
bool Hashmap::replace(Ref<Hashmap>& root, const DictKey& key, vm::StackEntry value) {
|
||||
if (root.is_null() || key.is_null()) {
|
||||
return false;
|
||||
}
|
||||
if (value.is_null()) {
|
||||
auto res = root->get_remove_internal(key, value);
|
||||
if (value.is_null()) {
|
||||
return false;
|
||||
} else {
|
||||
root = std::move(res);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bool found = false;
|
||||
auto res = root->replace_internal(key, std::move(value), found);
|
||||
if (found) {
|
||||
root = std::move(res);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
Ref<Hashmap> Hashmap::replace_internal(const DictKey& key, const vm::StackEntry& value, bool& found) const {
|
||||
int r = key.cmp(key_);
|
||||
if (!r) {
|
||||
found = true;
|
||||
return td::make_ref<Hashmap>(key_, value, left_, right_, y_);
|
||||
} else if (r < 0) {
|
||||
if (left_.is_null()) {
|
||||
found = false;
|
||||
return {};
|
||||
}
|
||||
auto res = left_->replace_internal(key, value, found);
|
||||
if (!found) {
|
||||
return {};
|
||||
}
|
||||
return td::make_ref<Hashmap>(key_, value_, std::move(res), right_, y_);
|
||||
} else {
|
||||
if (right_.is_null()) {
|
||||
found = false;
|
||||
return {};
|
||||
}
|
||||
auto res = right_->replace_internal(key, value, found);
|
||||
if (!found) {
|
||||
return {};
|
||||
}
|
||||
return td::make_ref<Hashmap>(key_, value_, left_, std::move(res), y_);
|
||||
}
|
||||
}
|
||||
|
||||
void Hashmap::insert(Ref<Hashmap>& root, const DictKey& key, vm::StackEntry value, long long y) {
|
||||
if (root.is_null()) {
|
||||
root = td::make_ref<Hashmap>(key, std::move(value), empty(), empty(), y);
|
||||
return;
|
||||
}
|
||||
if (root->y_ <= y) {
|
||||
auto res = split(std::move(root), key);
|
||||
root = td::make_ref<Hashmap>(key, std::move(value), std::move(res.first), std::move(res.second), y);
|
||||
return;
|
||||
}
|
||||
int r = key.cmp(root->key_);
|
||||
CHECK(r);
|
||||
insert(r < 0 ? root.write().left_ : root.write().right_, key, std::move(value), y);
|
||||
}
|
||||
|
||||
std::pair<Ref<Hashmap>, Ref<Hashmap>> Hashmap::split(Ref<Hashmap> root, const DictKey& key, bool cmpv) {
|
||||
if (root.is_null()) {
|
||||
return {{}, {}};
|
||||
}
|
||||
int r = key.cmp(root->key_);
|
||||
if (r < (int)cmpv) {
|
||||
if (root->left_.is_null()) {
|
||||
return {{}, std::move(root)};
|
||||
}
|
||||
auto res = split(root->left_, key, cmpv);
|
||||
return {std::move(res.first),
|
||||
td::make_ref<Hashmap>(root->key_, root->value_, std::move(res.second), root->right_, root->y_)};
|
||||
} else {
|
||||
if (root->right_.is_null()) {
|
||||
return {std::move(root), {}};
|
||||
}
|
||||
auto res = split(root->right_, key, cmpv);
|
||||
return {td::make_ref<Hashmap>(root->key_, root->value_, root->left_, std::move(res.first), root->y_),
|
||||
std::move(res.second)};
|
||||
}
|
||||
}
|
||||
|
||||
long long Hashmap::new_y() {
|
||||
return td::Random::fast_uint64();
|
||||
}
|
||||
|
||||
bool HashmapIterator::unwind(Ref<Hashmap> root) {
|
||||
if (root.is_null()) {
|
||||
return false;
|
||||
}
|
||||
while (true) {
|
||||
auto left = root->lr(down_);
|
||||
if (left.is_null()) {
|
||||
cur_ = std::move(root);
|
||||
return true;
|
||||
}
|
||||
stack_.push_back(std::move(root));
|
||||
root = std::move(left);
|
||||
}
|
||||
}
|
||||
|
||||
bool HashmapIterator::next() {
|
||||
if (cur_.not_null()) {
|
||||
cur_ = cur_->rl(down_);
|
||||
if (cur_.not_null()) {
|
||||
while (true) {
|
||||
auto left = cur_->lr(down_);
|
||||
if (left.is_null()) {
|
||||
return true;
|
||||
}
|
||||
stack_.push_back(std::move(cur_));
|
||||
cur_ = std::move(left);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (stack_.empty()) {
|
||||
return false;
|
||||
}
|
||||
cur_ = std::move(stack_.back());
|
||||
stack_.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fift
|
306
crypto/fift/HashMap.h
Normal file
306
crypto/fift/HashMap.h
Normal file
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
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 "common/refcnt.hpp"
|
||||
#include "vm/stack.hpp"
|
||||
#include "vm/atom.h"
|
||||
|
||||
namespace fift {
|
||||
|
||||
using td::Ref;
|
||||
using td::RefAny;
|
||||
|
||||
class DictKey {
|
||||
public:
|
||||
typedef vm::StackEntry::Type Type;
|
||||
typedef unsigned long long keyhash_t;
|
||||
|
||||
private:
|
||||
RefAny ref_;
|
||||
Type tp_ = Type::t_null;
|
||||
keyhash_t hash_ = 0;
|
||||
|
||||
static constexpr keyhash_t IntHash0 = 0xce6ab89d724409ed, MixConst1 = 0xcd5c126501510979,
|
||||
MixConst2 = 0xb8f44d7fd6274ad1, MixConst3 = 0xd08726ea2422e405,
|
||||
MixConst4 = 0x6407d2aeb5039dfb, StrHash = 0x93ff128344add06d;
|
||||
keyhash_t compute_hash();
|
||||
static keyhash_t compute_str_hash(DictKey::keyhash_t h, const char* str, std::size_t len);
|
||||
static keyhash_t compute_int_hash(td::AnyIntView<> x);
|
||||
int cmp_internal(const DictKey& other) const;
|
||||
template <typename T>
|
||||
Ref<T> value() const {
|
||||
return Ref<T>{td::static_cast_ref(), ref_};
|
||||
}
|
||||
template <typename T>
|
||||
Ref<T> move_value() {
|
||||
return Ref<T>{td::static_cast_ref(), std::move(ref_)};
|
||||
}
|
||||
|
||||
public:
|
||||
DictKey() : ref_(), tp_(Type::t_null) {
|
||||
}
|
||||
DictKey(const DictKey& other) = default;
|
||||
DictKey(DictKey&& other) = default;
|
||||
DictKey& operator=(const DictKey& other) = default;
|
||||
DictKey& operator=(DictKey&& other) = default;
|
||||
DictKey(Ref<vm::Atom> atom_ref) : ref_(std::move(atom_ref)), tp_(Type::t_atom) {
|
||||
compute_hash();
|
||||
}
|
||||
DictKey(td::RefInt256 int_ref) : ref_(std::move(int_ref)), tp_(Type::t_int) {
|
||||
compute_hash();
|
||||
}
|
||||
explicit DictKey(vm::StackEntry se);
|
||||
DictKey(std::string str, bool bytes = false) : ref_(), tp_(bytes ? Type::t_bytes : Type::t_string) {
|
||||
ref_ = Ref<td::Cnt<std::string>>{true, std::move(str)};
|
||||
compute_hash();
|
||||
}
|
||||
Type type() const {
|
||||
return tp_;
|
||||
}
|
||||
void swap(DictKey& other) {
|
||||
ref_.swap(other.ref_);
|
||||
std::swap(tp_, other.tp_);
|
||||
}
|
||||
|
||||
operator vm::StackEntry() const&;
|
||||
operator vm::StackEntry() &&;
|
||||
int cmp(const DictKey& other) const;
|
||||
bool operator==(const DictKey& other) const {
|
||||
return hash_ == other.hash_ && !cmp_internal(other);
|
||||
}
|
||||
bool operator!=(const DictKey& other) const {
|
||||
return hash_ != other.hash_ || cmp_internal(other);
|
||||
}
|
||||
bool operator<(const DictKey& other) const {
|
||||
return hash_ < other.hash_ || (hash_ == other.hash_ && cmp_internal(other) < 0);
|
||||
}
|
||||
bool is_null() const {
|
||||
return tp_ == Type::t_null;
|
||||
}
|
||||
bool is_string() const {
|
||||
return tp_ == Type::t_string;
|
||||
}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const DictKey& dkey);
|
||||
|
||||
class Hashmap : public td::CntObject {
|
||||
DictKey key_;
|
||||
vm::StackEntry value_;
|
||||
Ref<Hashmap> left_;
|
||||
Ref<Hashmap> right_;
|
||||
long long y_;
|
||||
|
||||
public:
|
||||
Hashmap(DictKey key, vm::StackEntry value, Ref<Hashmap> left, Ref<Hashmap> right, long long y)
|
||||
: key_(std::move(key)), value_(std::move(value)), left_(std::move(left)), right_(std::move(right)), y_(y) {
|
||||
}
|
||||
Hashmap(const Hashmap& other) = default;
|
||||
Hashmap(Hashmap&& other) = default;
|
||||
virtual ~Hashmap() {
|
||||
}
|
||||
Hashmap* make_copy() const override {
|
||||
return new Hashmap(*this);
|
||||
}
|
||||
const DictKey& key() const& {
|
||||
return key_;
|
||||
}
|
||||
DictKey key() && {
|
||||
return std::move(key_);
|
||||
}
|
||||
const vm::StackEntry& value() const& {
|
||||
return value_;
|
||||
}
|
||||
vm::StackEntry value() && {
|
||||
return std::move(value_);
|
||||
}
|
||||
Ref<Hashmap> left() const {
|
||||
return left_;
|
||||
}
|
||||
Ref<Hashmap> right() const {
|
||||
return right_;
|
||||
}
|
||||
Ref<Hashmap> lr(bool branch) const {
|
||||
return branch ? right_ : left_;
|
||||
}
|
||||
Ref<Hashmap> rl(bool branch) const {
|
||||
return branch ? left_ : right_;
|
||||
}
|
||||
static Ref<Hashmap> lookup_key(Ref<Hashmap> root, const DictKey& key);
|
||||
template <typename... Args>
|
||||
static Ref<Hashmap> lookup(Ref<Hashmap> root, Args&&... args) {
|
||||
return lookup_key(std::move(root), DictKey{std::forward<Args>(args)...});
|
||||
}
|
||||
static vm::StackEntry get_key(Ref<Hashmap> root, const DictKey& key);
|
||||
template <typename... Args>
|
||||
static vm::StackEntry get(Ref<Hashmap> root, Args&&... args) {
|
||||
return get_key(std::move(root), DictKey{std::forward<Args>(args)...});
|
||||
}
|
||||
static Ref<Hashmap> remove_key(Ref<Hashmap> root, const DictKey& key);
|
||||
template <typename... Args>
|
||||
static Ref<Hashmap> remove(Ref<Hashmap> root, Args&&... args) {
|
||||
return remove_key(std::move(root), DictKey{std::forward<Args>(args)...});
|
||||
}
|
||||
static std::pair<Ref<Hashmap>, vm::StackEntry> get_remove_key(Ref<Hashmap> root, const DictKey& key);
|
||||
template <typename... Args>
|
||||
static std::pair<Ref<Hashmap>, vm::StackEntry> get_remove(Ref<Hashmap> root, Args&&... args) {
|
||||
return get_remove_key(std::move(root), DictKey{std::forward<Args>(args)...});
|
||||
}
|
||||
static Ref<Hashmap> set(Ref<Hashmap> root, const DictKey& key, vm::StackEntry value);
|
||||
static bool replace(Ref<Hashmap>& root, const DictKey& key, vm::StackEntry value);
|
||||
static std::pair<Ref<Hashmap>, Ref<Hashmap>> split(Ref<Hashmap> root, const DictKey& key, bool eq_left = false);
|
||||
static Ref<Hashmap> empty() {
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
static Ref<Hashmap> merge(Ref<Hashmap> a, Ref<Hashmap> b);
|
||||
static const Hashmap* lookup_key_aux(const Hashmap* root, const DictKey& key);
|
||||
Ref<Hashmap> get_remove_internal(const DictKey& key, vm::StackEntry& val) const;
|
||||
Ref<Hashmap> replace_internal(const DictKey& key, const vm::StackEntry& value, bool& found) const;
|
||||
static void insert(Ref<Hashmap>& root, const DictKey& key, vm::StackEntry value, long long y);
|
||||
static long long new_y();
|
||||
};
|
||||
|
||||
struct HashmapIdx {
|
||||
Ref<Hashmap>& root_;
|
||||
DictKey idx_;
|
||||
template <typename... Args>
|
||||
HashmapIdx(Ref<Hashmap>& root, Args&&... args) : root_(root), idx_(std::forward<Args>(args)...) {
|
||||
}
|
||||
operator vm::StackEntry() const {
|
||||
return Hashmap::get(root_, idx_);
|
||||
}
|
||||
template <typename T>
|
||||
HashmapIdx& operator=(T&& value) {
|
||||
root_ = Hashmap::set(root_, idx_, vm::StackEntry(std::forward<T>(value)));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class HashmapIterator {
|
||||
std::vector<Ref<Hashmap>> stack_;
|
||||
Ref<Hashmap> cur_;
|
||||
const bool down_{false};
|
||||
bool unwind(Ref<Hashmap> root);
|
||||
|
||||
public:
|
||||
HashmapIterator() = default;
|
||||
HashmapIterator(Ref<Hashmap> root, bool down = false) : down_(down) {
|
||||
unwind(std::move(root));
|
||||
}
|
||||
const Hashmap& operator*() const {
|
||||
return *cur_;
|
||||
}
|
||||
const Hashmap* operator->() const {
|
||||
return cur_.get();
|
||||
}
|
||||
bool eof() {
|
||||
return cur_.is_null();
|
||||
}
|
||||
bool next();
|
||||
bool operator<(const HashmapIterator& other) const {
|
||||
if (other.cur_.is_null()) {
|
||||
return cur_.not_null();
|
||||
} else if (cur_.is_null()) {
|
||||
return false;
|
||||
} else {
|
||||
return cur_->key().cmp(other.cur_->key()) * (down_ ? -1 : 1) < 0;
|
||||
}
|
||||
}
|
||||
bool operator==(const HashmapIterator& other) const {
|
||||
return other.cur_.is_null() ? cur_.is_null() : (cur_.not_null() && cur_->key() == other.cur_->key());
|
||||
}
|
||||
bool operator!=(const HashmapIterator& other) const {
|
||||
return other.cur_.is_null() ? cur_.not_null() : (cur_.is_null() || cur_->key() != other.cur_->key());
|
||||
}
|
||||
HashmapIterator& operator++() {
|
||||
next();
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct HashmapKeeper {
|
||||
Ref<Hashmap> root;
|
||||
HashmapKeeper() = default;
|
||||
HashmapKeeper(Ref<Hashmap> _root) : root(std::move(_root)) {
|
||||
}
|
||||
Ref<Hashmap> extract() {
|
||||
return std::move(root);
|
||||
}
|
||||
operator Ref<Hashmap>() const& {
|
||||
return root;
|
||||
}
|
||||
operator Ref<Hashmap>() && {
|
||||
return std::move(root);
|
||||
}
|
||||
template <typename... Args>
|
||||
HashmapIdx operator[](Args&&... args) {
|
||||
return HashmapIdx{root, DictKey{std::forward<Args>(args)...}};
|
||||
}
|
||||
template <typename... Args>
|
||||
vm::StackEntry operator[](Args&&... args) const {
|
||||
return Hashmap::get(root, DictKey{std::forward<Args>(args)...});
|
||||
}
|
||||
vm::StackEntry get_key(const DictKey& key) const {
|
||||
return Hashmap::get(root, key);
|
||||
}
|
||||
template <typename... Args>
|
||||
vm::StackEntry get(Args&&... args) const {
|
||||
return Hashmap::get(root, DictKey{std::forward<Args>(args)...});
|
||||
}
|
||||
vm::StackEntry get_remove_key(const DictKey& key) {
|
||||
auto res = Hashmap::get_remove_key(root, key);
|
||||
root = std::move(res.first);
|
||||
return std::move(res.second);
|
||||
}
|
||||
template <typename... Args>
|
||||
vm::StackEntry get_remove(Args&&... args) {
|
||||
return get_remove_key(DictKey{std::forward<Args>(args)...});
|
||||
}
|
||||
bool remove_key(const DictKey& key) {
|
||||
auto res = Hashmap::get_remove(root, key);
|
||||
root = std::move(res.first);
|
||||
return !res.second.is_null();
|
||||
}
|
||||
template <typename... Args>
|
||||
bool remove(Args&&... args) {
|
||||
return remove_key(DictKey{std::forward<Args>(args)...});
|
||||
}
|
||||
template <typename T>
|
||||
void set(T key, vm::StackEntry value) {
|
||||
root = Hashmap::set(root, DictKey(key), std::move(value));
|
||||
}
|
||||
template <typename T>
|
||||
bool replace(T key, vm::StackEntry value) {
|
||||
return Hashmap::replace(root, DictKey(key), std::move(value));
|
||||
}
|
||||
HashmapIterator begin(bool reverse = false) const {
|
||||
return HashmapIterator{root, reverse};
|
||||
}
|
||||
HashmapIterator end() const {
|
||||
return HashmapIterator{};
|
||||
}
|
||||
HashmapIterator rbegin() const {
|
||||
return HashmapIterator{root, true};
|
||||
}
|
||||
HashmapIterator rend() const {
|
||||
return HashmapIterator{};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fift
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
namespace fift {
|
||||
|
||||
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx) {
|
||||
td::StringBuilder& operator<<(td::StringBuilder& os, const ParseCtx& ctx) {
|
||||
if (ctx.include_depth) {
|
||||
return os << ctx.filename << ":" << ctx.line_no << ": ";
|
||||
} else {
|
||||
|
@ -28,7 +28,7 @@ td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const IntCtx& ctx) {
|
||||
std::ostream& operator<<(std::ostream& os, const ParseCtx& ctx) {
|
||||
return os << (PSLICE() << ctx).c_str();
|
||||
}
|
||||
|
||||
|
@ -67,73 +67,7 @@ void CharClassifier::set_char_class(int c, int cl) {
|
|||
*p = static_cast<unsigned char>((*p & ~mask) | cl);
|
||||
}
|
||||
|
||||
IntCtx::Savepoint::Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
|
||||
std::unique_ptr<std::istream> new_input_stream)
|
||||
: ctx(_ctx)
|
||||
, old_line_no(_ctx.line_no)
|
||||
, old_need_line(_ctx.need_line)
|
||||
, old_filename(_ctx.filename)
|
||||
, old_current_dir(_ctx.currentd_dir)
|
||||
, old_input_stream(_ctx.input_stream)
|
||||
, old_input_stream_holder(std::move(_ctx.input_stream_holder))
|
||||
, old_curline(_ctx.str)
|
||||
, old_curpos(_ctx.input_ptr - _ctx.str.c_str())
|
||||
, old_word(_ctx.word) {
|
||||
ctx.line_no = 0;
|
||||
ctx.filename = new_filename;
|
||||
ctx.currentd_dir = new_current_dir;
|
||||
ctx.input_stream = new_input_stream.get();
|
||||
ctx.input_stream_holder = std::move(new_input_stream);
|
||||
ctx.str = "";
|
||||
ctx.input_ptr = 0;
|
||||
++(ctx.include_depth);
|
||||
}
|
||||
|
||||
bool IntCtx::Savepoint::restore(IntCtx& _ctx) {
|
||||
if (restored || &ctx != &_ctx) {
|
||||
return false;
|
||||
}
|
||||
ctx.line_no = old_line_no;
|
||||
ctx.need_line = old_need_line;
|
||||
ctx.filename = old_filename;
|
||||
ctx.currentd_dir = old_current_dir;
|
||||
ctx.input_stream = old_input_stream;
|
||||
ctx.input_stream_holder = std::move(old_input_stream_holder);
|
||||
ctx.str = old_curline;
|
||||
ctx.input_ptr = ctx.str.c_str() + old_curpos;
|
||||
ctx.word = old_word;
|
||||
--(ctx.include_depth);
|
||||
return restored = true;
|
||||
}
|
||||
|
||||
bool IntCtx::enter_ctx(std::string new_filename, std::string new_current_dir,
|
||||
std::unique_ptr<std::istream> new_input_stream) {
|
||||
if (!new_input_stream) {
|
||||
return false;
|
||||
}
|
||||
ctx_save_stack.emplace_back(*this, std::move(new_filename), std::move(new_current_dir), std::move(new_input_stream));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IntCtx::leave_ctx() {
|
||||
if (ctx_save_stack.empty()) {
|
||||
return false;
|
||||
}
|
||||
bool ok = ctx_save_stack.back().restore(*this);
|
||||
ctx_save_stack.pop_back();
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool IntCtx::top_ctx() {
|
||||
while (!ctx_save_stack.empty()) {
|
||||
if (!leave_ctx()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IntCtx::load_next_line() {
|
||||
bool ParseCtx::load_next_line() {
|
||||
if (!std::getline(*input_stream, str)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -145,11 +79,11 @@ bool IntCtx::load_next_line() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool IntCtx::is_sb() const {
|
||||
bool ParseCtx::is_sb() const {
|
||||
return !eof() && line_no == 1 && *input_ptr == '#' && input_ptr[1] == '!';
|
||||
}
|
||||
|
||||
td::Slice IntCtx::scan_word_to(char delim, bool err_endl) {
|
||||
td::Slice ParseCtx::scan_word_to(char delim, bool err_endl) {
|
||||
load_next_line_ifreq();
|
||||
auto ptr = input_ptr;
|
||||
while (*ptr && *ptr != delim) {
|
||||
|
@ -167,7 +101,7 @@ td::Slice IntCtx::scan_word_to(char delim, bool err_endl) {
|
|||
}
|
||||
}
|
||||
|
||||
td::Slice IntCtx::scan_word() {
|
||||
td::Slice ParseCtx::scan_word() {
|
||||
skipspc(true);
|
||||
auto ptr = input_ptr;
|
||||
while (*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '\r') {
|
||||
|
@ -179,7 +113,7 @@ td::Slice IntCtx::scan_word() {
|
|||
return td::Slice{ptr, ptr2};
|
||||
}
|
||||
|
||||
td::Slice IntCtx::scan_word_ext(const CharClassifier& classifier) {
|
||||
td::Slice ParseCtx::scan_word_ext(const CharClassifier& classifier) {
|
||||
skipspc(true);
|
||||
auto ptr = input_ptr;
|
||||
while (*ptr && *ptr != '\r' && *ptr != '\n') {
|
||||
|
@ -196,7 +130,7 @@ td::Slice IntCtx::scan_word_ext(const CharClassifier& classifier) {
|
|||
return td::Slice{ptr, input_ptr};
|
||||
}
|
||||
|
||||
void IntCtx::skipspc(bool skip_eol) {
|
||||
void ParseCtx::skipspc(bool skip_eol) {
|
||||
do {
|
||||
while (*input_ptr == ' ' || *input_ptr == '\t' || *input_ptr == '\r') {
|
||||
++input_ptr;
|
||||
|
@ -207,6 +141,45 @@ void IntCtx::skipspc(bool skip_eol) {
|
|||
} while (load_next_line());
|
||||
}
|
||||
|
||||
bool IntCtx::enter_ctx(std::unique_ptr<ParseCtx> new_parser) {
|
||||
if (!new_parser) {
|
||||
return false;
|
||||
}
|
||||
if (parser) {
|
||||
parser_save_stack.push_back(std::move(parser));
|
||||
}
|
||||
parser = std::move(new_parser);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IntCtx::enter_ctx(std::string new_filename, std::string new_current_dir,
|
||||
std::unique_ptr<std::istream> new_input_stream) {
|
||||
if (!new_input_stream) {
|
||||
return false;
|
||||
} else {
|
||||
return enter_ctx(
|
||||
std::make_unique<ParseCtx>(std::move(new_input_stream), new_filename, new_current_dir, include_depth() + 1));
|
||||
}
|
||||
}
|
||||
|
||||
bool IntCtx::leave_ctx() {
|
||||
if (parser_save_stack.empty()) {
|
||||
return false;
|
||||
} else {
|
||||
parser = std::move(parser_save_stack.back());
|
||||
parser_save_stack.pop_back();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool IntCtx::top_ctx() {
|
||||
if (!parser_save_stack.empty()) {
|
||||
parser = std::move(parser_save_stack[0]);
|
||||
parser_save_stack.clear();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void IntCtx::check_compile() const {
|
||||
if (state <= 0) {
|
||||
throw IntError{"compilation mode only"};
|
||||
|
@ -283,15 +256,20 @@ td::Result<int> IntCtx::get_result() {
|
|||
}
|
||||
}
|
||||
|
||||
std::ostream& ParseCtx::show_context(std::ostream& os) const {
|
||||
if (include_depth && line_no) {
|
||||
os << filename << ":" << line_no << ":\t";
|
||||
}
|
||||
if (!word.empty()) {
|
||||
os << word << ":";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
td::Status IntCtx::add_error_loc(td::Status err) const {
|
||||
if (err.is_error()) {
|
||||
if (err.is_error() && parser) {
|
||||
std::ostringstream os;
|
||||
if (include_depth && line_no) {
|
||||
os << filename << ":" << line_no << ":\t";
|
||||
}
|
||||
if (!word.empty()) {
|
||||
os << word << ":";
|
||||
}
|
||||
parser->show_context(os);
|
||||
return err.move_as_error_prefix(os.str());
|
||||
} else {
|
||||
return err;
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "crypto/vm/db/TonDb.h" // FIXME
|
||||
#include "crypto/vm/stack.hpp"
|
||||
#include "crypto/vm/box.hpp"
|
||||
#include "crypto/common/bitstring.h"
|
||||
|
||||
#include "td/utils/Status.h"
|
||||
|
@ -32,6 +32,11 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace vm {
|
||||
class TonDbImpl; // from crypto/vm/db/TonDb.h
|
||||
using TonDb = std::unique_ptr<TonDbImpl>;
|
||||
} // namespace vm
|
||||
|
||||
namespace fift {
|
||||
class Dictionary;
|
||||
class SourceLookup;
|
||||
|
@ -68,71 +73,36 @@ class CharClassifier {
|
|||
}
|
||||
};
|
||||
|
||||
struct IntCtx {
|
||||
vm::Stack stack;
|
||||
Ref<FiftCont> next, exc_handler;
|
||||
Ref<FiftCont> exc_cont, exc_next;
|
||||
int state{0};
|
||||
struct ParseCtx {
|
||||
int include_depth{0};
|
||||
int line_no{0};
|
||||
int exit_code{0};
|
||||
td::Status error;
|
||||
bool need_line{true};
|
||||
std::string filename;
|
||||
std::string currentd_dir;
|
||||
std::istream* input_stream{nullptr};
|
||||
std::unique_ptr<std::istream> input_stream_holder;
|
||||
std::ostream* output_stream{nullptr};
|
||||
std::ostream* error_stream{nullptr};
|
||||
|
||||
vm::TonDb* ton_db{nullptr};
|
||||
Dictionary* dictionary{nullptr};
|
||||
SourceLookup* source_lookup{nullptr};
|
||||
int* now{nullptr};
|
||||
std::string word;
|
||||
|
||||
private:
|
||||
std::string str;
|
||||
const char* input_ptr = nullptr;
|
||||
|
||||
class Savepoint {
|
||||
IntCtx& ctx;
|
||||
int old_line_no;
|
||||
bool old_need_line;
|
||||
bool restored{false};
|
||||
std::string old_filename;
|
||||
std::string old_current_dir;
|
||||
std::istream* old_input_stream;
|
||||
std::unique_ptr<std::istream> old_input_stream_holder;
|
||||
std::string old_curline;
|
||||
std::ptrdiff_t old_curpos;
|
||||
std::string old_word;
|
||||
|
||||
public:
|
||||
Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
|
||||
std::unique_ptr<std::istream> new_input_stream);
|
||||
bool restore(IntCtx& _ctx);
|
||||
};
|
||||
|
||||
std::vector<Savepoint> ctx_save_stack;
|
||||
|
||||
public:
|
||||
IntCtx() = default;
|
||||
IntCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0)
|
||||
ParseCtx() = default;
|
||||
ParseCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0)
|
||||
: include_depth(_depth)
|
||||
, filename(std::move(_filename))
|
||||
, currentd_dir(std::move(_curdir))
|
||||
, input_stream(&_istream) {
|
||||
}
|
||||
|
||||
operator vm::Stack&() {
|
||||
return stack;
|
||||
ParseCtx(std::unique_ptr<std::istream> _istream_ptr, std::string _filename, std::string _curdir = "", int _depth = 0)
|
||||
: include_depth(_depth)
|
||||
, filename(std::move(_filename))
|
||||
, currentd_dir(std::move(_curdir))
|
||||
, input_stream(_istream_ptr.get())
|
||||
, input_stream_holder(std::move(_istream_ptr)) {
|
||||
}
|
||||
|
||||
bool enter_ctx(std::string new_filename, std::string new_current_dir, std::unique_ptr<std::istream> new_input_stream);
|
||||
bool leave_ctx();
|
||||
bool top_ctx();
|
||||
|
||||
td::Slice scan_word_to(char delim, bool err_endl = true);
|
||||
td::Slice scan_word();
|
||||
td::Slice scan_word_ext(const CharClassifier& classifier);
|
||||
|
@ -165,6 +135,50 @@ struct IntCtx {
|
|||
|
||||
bool is_sb() const;
|
||||
|
||||
std::ostream& show_context(std::ostream& os) const;
|
||||
};
|
||||
|
||||
struct IntCtx {
|
||||
vm::Stack stack;
|
||||
Ref<FiftCont> next, exc_handler;
|
||||
Ref<FiftCont> exc_cont, exc_next;
|
||||
int state{0};
|
||||
int exit_code{0};
|
||||
td::Status error;
|
||||
|
||||
std::unique_ptr<ParseCtx> parser;
|
||||
std::vector<std::unique_ptr<ParseCtx>> parser_save_stack;
|
||||
|
||||
std::ostream* output_stream{nullptr}; // move to OutCtx?
|
||||
std::ostream* error_stream{nullptr};
|
||||
|
||||
vm::TonDb* ton_db{nullptr};
|
||||
SourceLookup* source_lookup{nullptr};
|
||||
int* now{nullptr};
|
||||
|
||||
Dictionary dictionary, main_dictionary, context;
|
||||
|
||||
public:
|
||||
IntCtx() = default;
|
||||
IntCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0) {
|
||||
parser = std::make_unique<ParseCtx>(_istream, _filename, _curdir, _depth);
|
||||
}
|
||||
IntCtx(std::unique_ptr<std::istream> _istream, std::string _filename, std::string _curdir = "", int _depth = 0) {
|
||||
parser = std::make_unique<ParseCtx>(std::move(_istream), _filename, _curdir, _depth);
|
||||
}
|
||||
|
||||
bool enter_ctx(std::unique_ptr<ParseCtx> new_ctx);
|
||||
bool enter_ctx(std::string new_filename, std::string new_current_dir, std::unique_ptr<std::istream> new_input_stream);
|
||||
bool leave_ctx();
|
||||
bool top_ctx();
|
||||
int include_depth() const {
|
||||
return parser ? parser->include_depth : -1;
|
||||
}
|
||||
|
||||
operator vm::Stack &() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
state = 0;
|
||||
stack.clear();
|
||||
|
@ -194,6 +208,6 @@ struct IntCtx {
|
|||
td::Result<int> run(Ref<FiftCont> cont);
|
||||
};
|
||||
|
||||
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx);
|
||||
std::ostream& operator<<(std::ostream& os, const IntCtx& ctx);
|
||||
td::StringBuilder& operator<<(td::StringBuilder& os, const ParseCtx& ctx);
|
||||
std::ostream& operator<<(std::ostream& os, const ParseCtx& ctx);
|
||||
} // namespace fift
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
library TVM_Asm
|
||||
// simple TVM Assembler
|
||||
namespace Asm
|
||||
Asm definitions
|
||||
variable @atend
|
||||
variable @was-split
|
||||
false @was-split !
|
||||
{ "not in asm context" abort } @atend !
|
||||
{ `normal eq? not abort"must be terminated by }>" } : @normal?
|
||||
{ @atend @ 1 { @atend ! @normal? } does @atend ! } : @pushatend
|
||||
{ @pushatend <b } : <{
|
||||
{ context@ @atend @ 2 { @atend ! context! @normal? } does @atend ! } : @pushatend
|
||||
{ @pushatend Asm <b } : <{
|
||||
{ @atend @ execute } : @endblk
|
||||
{ false @was-split ! `normal @endblk } : }>
|
||||
{ }> b> } : }>c
|
||||
|
@ -1219,7 +1221,10 @@ variable asm-mode 1 asm-mode !
|
|||
{ 1 'nop does swap 0 (create) } : @declglobvar
|
||||
{ @proccnt @ 1+ dup @proccnt ! 1 @declproc } : @newproc
|
||||
{ @gvarcnt @ 1+ dup @gvarcnt ! @declglobvar } : @newglobvar
|
||||
{ 0 =: main @proclist null! @proccnt 0! @gvarcnt 0!
|
||||
variable @oldcurrent variable @oldctx
|
||||
Fift-wordlist dup @oldcurrent ! @oldctx !
|
||||
{ current@ @oldcurrent ! context@ @oldctx ! Asm definitions
|
||||
0 =: main @proclist null! @proccnt 0! @gvarcnt 0!
|
||||
{ bl word @newproc } : NEWPROC
|
||||
{ bl word dup (def?) ' drop ' @newproc cond } : DECLPROC
|
||||
{ bl word dup find
|
||||
|
@ -1293,6 +1298,7 @@ variable asm-mode 1 asm-mode !
|
|||
} while
|
||||
drop @proclist null! @procinfo null! @proccnt 0!
|
||||
@procdict dup @ swap null!
|
||||
@oldctx @ context! @oldcurrent @ current!
|
||||
} : }END
|
||||
forget @proclist forget @proccnt
|
||||
{ }END <{ SETCP0 swap @procdictkeylen DICTPUSHCONST DICTIGETJMPZ 11 THROWARG }> } : }END>
|
||||
|
@ -1319,3 +1325,8 @@ forget @proclist forget @proccnt
|
|||
{ <b 2 8 u, swap 256 u, b>spec } : hash>libref
|
||||
// ( c -- c' )
|
||||
{ hash hash>libref } : >libref
|
||||
|
||||
Fift definitions Asm
|
||||
' <{ : <{
|
||||
' PROGRAM{ : PROGRAM{
|
||||
Fift
|
||||
|
|
141
crypto/fift/lib/Disasm.fif
Normal file
141
crypto/fift/lib/Disasm.fif
Normal file
|
@ -0,0 +1,141 @@
|
|||
library TVM_Disasm
|
||||
// simple TVM Disassembler
|
||||
"Lists.fif" include
|
||||
|
||||
variable 'disasm
|
||||
{ 'disasm @ execute } : disasm // disassemble a slice
|
||||
// usage: x{74B0} disasm
|
||||
|
||||
variable @dismode @dismode 0!
|
||||
{ rot over @ and rot xor swap ! } : andxor!
|
||||
{ -2 0 @dismode andxor! } : stack-disasm // output 's1 s4 XCHG'
|
||||
{ -2 1 @dismode andxor! } : std-disasm // output 'XCHG s1, s4'
|
||||
{ -3 2 @dismode andxor! } : show-vm-code
|
||||
{ -3 0 @dismode andxor! } : hide-vm-code
|
||||
{ @dismode @ 1 and 0= } : stack-disasm?
|
||||
|
||||
variable @indent @indent 0!
|
||||
{ ' space @indent @ 2* times } : .indent
|
||||
{ @indent 1+! } : +indent
|
||||
{ @indent 1-! } : -indent
|
||||
|
||||
{ " " $pos } : spc-pos
|
||||
{ dup " " $pos swap "," $pos dup 0< { drop } {
|
||||
over 0< { nip } { min } cond } cond
|
||||
} : spc-comma-pos
|
||||
{ { dup spc-pos 0= } { 1 $| nip } while } : -leading
|
||||
{ -leading -trailing dup spc-pos dup 0< {
|
||||
drop dup $len { atom single } { drop nil } cond } {
|
||||
$| swap atom swap -leading 2 { over spc-comma-pos dup 0>= } {
|
||||
swap 1+ -rot $| 1 $| nip -leading rot
|
||||
} while drop tuple
|
||||
} cond
|
||||
} : parse-op
|
||||
{ dup "s-1" $= { drop "s(-1)" true } {
|
||||
dup "s-2" $= { drop "s(-2)" true } {
|
||||
dup 1 $| swap "x" $= { nip "x{" swap $+ +"}" true } {
|
||||
2drop false } cond } cond } cond
|
||||
} : adj-op-arg
|
||||
{ over count over <= { drop } { 2dup [] adj-op-arg { swap []= } { drop } cond } cond } : adj-arg[]
|
||||
{ 1 adj-arg[] 2 adj-arg[] 3 adj-arg[]
|
||||
dup first
|
||||
dup `XCHG eq? {
|
||||
drop dup count 2 = { tpop swap "s0" , swap , } if } {
|
||||
dup `LSHIFT eq? {
|
||||
drop dup count 2 = stack-disasm? and { second `LSHIFT# swap pair } if } {
|
||||
dup `RSHIFT eq? {
|
||||
drop dup count 2 = stack-disasm? and { second `RSHIFT# swap pair } if } {
|
||||
drop
|
||||
} cond } cond } cond
|
||||
} : adjust-op
|
||||
|
||||
variable @cp @cp 0!
|
||||
variable @curop
|
||||
variable @contX variable @contY variable @cdict
|
||||
|
||||
{ atom>$ type } : .atom
|
||||
{ dup first .atom dup count 1 > { space 0 over count 2- { 1+ 2dup [] type .", " } swap times 1+ [] type } { drop } cond } : std-show-op
|
||||
{ 0 over count 1- { 1+ 2dup [] type space } swap times drop first .atom } : stk-show-op
|
||||
{ @dismode @ 2 and { .indent ."// " @curop @ csr. } if } : .curop?
|
||||
{ .curop? .indent @dismode @ 1 and ' std-show-op ' stk-show-op cond cr
|
||||
} : show-simple-op
|
||||
{ dup 4 u@ 9 = { 8 u@+ swap 15 and 3 << s@ } {
|
||||
dup 7 u@ 0x47 = { 7 u@+ nip 2 u@+ 7 u@+ -rot 3 << swap sr@ } {
|
||||
dup 8 u@ 0x8A = { ref@ <s } {
|
||||
abort"invalid PUSHCONT"
|
||||
} cond } cond } cond
|
||||
} : get-cont-body
|
||||
{ 14 u@+ nip 10 u@+ ref@ dup rot pair swap <s empty? { drop null } if } : get-const-dict
|
||||
{ @contX @ @contY @ @contX ! @contY ! } : scont-swap
|
||||
{ .indent swap type type cr @contY @ @contY null! @contX @ @contX null!
|
||||
+indent disasm -indent @contY !
|
||||
} : show-cont-bodyx
|
||||
{ ":<{" show-cont-bodyx .indent ."}>" cr } : show-cont-op
|
||||
{ swap scont-swap ":<{" show-cont-bodyx scont-swap
|
||||
"" show-cont-bodyx .indent ."}>" cr } : show-cont2-op
|
||||
|
||||
{ @contX @ null? { "CONT" show-cont-op } ifnot
|
||||
} : flush-contX
|
||||
{ @contY @ null? { scont-swap "CONT" show-cont-op scont-swap } ifnot
|
||||
} : flush-contY
|
||||
{ flush-contY flush-contX } : flush-cont
|
||||
{ @contX @ null? not } : have-cont?
|
||||
{ @contY @ null? not } : have-cont2?
|
||||
{ flush-contY @contY ! scont-swap } : save-cont-body
|
||||
|
||||
{ @cdict ! } : save-const-dict
|
||||
{ @cdict null! } : flush-dict
|
||||
{ @cdict @ null? not } : have-dict?
|
||||
|
||||
{ flush-cont .indent type .":<{" cr
|
||||
@curop @ ref@ <s +indent disasm -indent .indent ."}>" cr
|
||||
} : show-ref-op
|
||||
{ flush-contY .indent rot type .":<{" cr
|
||||
@curop @ ref@ <s @contX @ @contX null! rot ' swap if
|
||||
+indent disasm -indent .indent swap type cr
|
||||
+indent disasm -indent .indent ."}>" cr
|
||||
} : show-cont-ref-op
|
||||
{ flush-cont .indent swap type .":<{" cr
|
||||
@curop @ ref@+ <s +indent disasm -indent .indent swap type cr
|
||||
ref@ <s +indent disasm -indent .indent ."}>" cr
|
||||
} : show-ref2-op
|
||||
|
||||
{ flush-cont first atom>$ dup 5 $| drop "DICTI" $= swap
|
||||
.indent type ." {" cr +indent @cdict @ @cdict null! unpair
|
||||
rot {
|
||||
swap .indent . ."=> <{" cr +indent disasm -indent .indent ."}>" cr true
|
||||
} swap ' idictforeach ' dictforeach cond drop
|
||||
-indent .indent ."}" cr
|
||||
} : show-const-dict-op
|
||||
|
||||
( `PUSHCONT `PUSHREFCONT ) constant @PushContL
|
||||
( `REPEAT `UNTIL `IF `IFNOT `IFJMP `IFNOTJMP ) constant @CmdC1
|
||||
( `IFREF `IFNOTREF `IFJMPREF `IFNOTJMPREF `CALLREF `JMPREF ) constant @CmdR1
|
||||
( `DICTIGETJMP `DICTIGETJMPZ `DICTUGETJMP `DICTUGETJMPZ `DICTIGETEXEC `DICTUGETEXEC ) constant @JmpDictL
|
||||
{ dup first `DICTPUSHCONST eq? {
|
||||
flush-cont @curop @ get-const-dict save-const-dict show-simple-op } {
|
||||
dup first @JmpDictL list-member? have-dict? and {
|
||||
flush-cont show-const-dict-op } {
|
||||
flush-dict
|
||||
dup first @PushContL list-member? {
|
||||
drop @curop @ get-cont-body save-cont-body } {
|
||||
dup first @CmdC1 list-member? have-cont? and {
|
||||
flush-contY first atom>$ .curop? show-cont-op } {
|
||||
dup first @CmdR1 list-member? {
|
||||
flush-cont first atom>$ dup $len 3 - $| drop .curop? show-ref-op } {
|
||||
dup first `WHILE eq? have-cont2? and {
|
||||
drop "WHILE" "}>DO<{" .curop? show-cont2-op } {
|
||||
dup first `IFELSE eq? have-cont2? and {
|
||||
drop "IF" "}>ELSE<{" .curop? show-cont2-op } {
|
||||
dup first dup `IFREFELSE eq? swap `IFELSEREF eq? or have-cont? and {
|
||||
first `IFREFELSE eq? "IF" "}>ELSE<{" rot .curop? show-cont-ref-op } {
|
||||
dup first `IFREFELSEREF eq? {
|
||||
drop "IF" "}>ELSE<{" .curop? show-ref2-op } {
|
||||
flush-cont show-simple-op
|
||||
} cond } cond } cond } cond } cond } cond } cond } cond } cond
|
||||
} : show-op
|
||||
{ dup @cp @ (vmoplen) dup 0> { 65536 /mod swap sr@+ swap dup @cp @ (vmopdump) parse-op swap s> true } { drop false } cond } : fetch-one-op
|
||||
{ { fetch-one-op } { swap @curop ! adjust-op show-op } while } : disasm-slice
|
||||
{ { disasm-slice dup sbitrefs 1- or 0= } { ref@ <s } while flush-dict flush-cont } : disasm-chain
|
||||
{ @curop @ swap disasm-chain dup sbitrefs or { .indent ."Cannot disassemble: " csr. } { drop } cond @curop ! }
|
||||
'disasm !
|
|
@ -76,6 +76,8 @@ variable base
|
|||
{ def? ' skip-to-eof if } : skip-ifdef
|
||||
{ bl word dup (def?) { drop skip-to-eof } { 'nop swap 0 (create) } cond } : library
|
||||
{ bl word dup (def?) { 2drop skip-to-eof } { swap 1 'nop does swap 0 (create) } cond } : library-version
|
||||
{ hole dup 1 'nop does swap 1 { context! } does bl word tuck 0 (create) +"-wordlist" 0 (create) } : namespace
|
||||
{ context@ current! } : definitions
|
||||
{ char ) word "$" swap $+ 1 { find 0= abort"undefined parameter" execute } } ::_ $(
|
||||
// b s -- ?
|
||||
{ sbitrefs rot brembitrefs rot >= -rot <= and } : s-fits?
|
||||
|
|
118
crypto/fift/lib/FiftExt.fif
Normal file
118
crypto/fift/lib/FiftExt.fif
Normal file
|
@ -0,0 +1,118 @@
|
|||
{ ?dup { 1+ { execute } { 0 swap } cond }
|
||||
{ (number) ?dup 0= abort"-?" 'nop } cond
|
||||
} : (interpret-prepare)
|
||||
{ { include-depth 0= (seekeof?) not } {
|
||||
(word-prefix-find) (interpret-prepare) (execute)
|
||||
} while
|
||||
} : interpret
|
||||
{ ({)
|
||||
{ 0 (seekeof?) abort"no }" (word-prefix-find) (interpret-prepare) (compile) over atom? not } until
|
||||
(}) swap execute
|
||||
} : begin-block
|
||||
{ swap 0 'nop } : end-block
|
||||
{ { 1 'nop } `{ begin-block }
|
||||
{ { swap `{ eq? not abort"} without {" swap execute } end-block }
|
||||
:: } :: {
|
||||
|
||||
// if{ ... }then{ ... }elseif{ ... }then{ ... }else{ ... }
|
||||
{ eq? not abort"unexpected" } : ?pairs
|
||||
{ dup `if eq? swap `ifnot eq? over or not abort"without if{" } : if-ifnot?
|
||||
// cond then ? -- exec
|
||||
{ { ' if } { ' ifnot } cond rot ({) 0 rot (compile) -rot 1 swap (compile) (})
|
||||
} : (make-if)
|
||||
// cond then else -- exec
|
||||
{ rot ({) 0 rot (compile) -rot 2 ' cond (compile) (})
|
||||
} : (make-cond)
|
||||
{ `noelse `if begin-block } :: if{
|
||||
{ `noelse `ifnot begin-block } :: ifnot{
|
||||
{ 1 ' end-block does } : end-block-does
|
||||
{ { over `else eq? } {
|
||||
nip rot if-ifnot? ' swap ifnot (make-cond)
|
||||
} while
|
||||
swap `noelse ?pairs 0 swap
|
||||
} : finish-else-chain
|
||||
{ swap dup if-ifnot? drop `then {
|
||||
swap `then ?pairs
|
||||
swap if-ifnot? (make-if) finish-else-chain
|
||||
} `{ begin-block
|
||||
} end-block-does :: }then{
|
||||
{ swap `{ ?pairs nip
|
||||
swap `then eq? not abort"without }then{" `else
|
||||
} : ?else-ok
|
||||
{ ?else-ok { finish-else-chain } `{ begin-block } end-block-does :: }else{
|
||||
{ ?else-ok `if begin-block } end-block-does :: }elseif{
|
||||
{ ?else-ok `ifnot begin-block } end-block-does :: }elseifnot{
|
||||
|
||||
// while{ ... }do{ ... }
|
||||
{ 2 ' while does } : (make-while)
|
||||
{ `while begin-block } :: while{
|
||||
{ swap `while eq? not abort"without while{" `while-do {
|
||||
swap `while-do ?pairs (make-while) 0 swap
|
||||
} `{ begin-block
|
||||
} end-block-does :: }do{
|
||||
|
||||
// repeat{ ... }until{ ... }
|
||||
{ swap ({) 0 rot (compile) 0 rot (compile) (}) 1 ' until does } : (make-until)
|
||||
{ `repeat begin-block } :: repeat{
|
||||
{ swap `repeat eq? not abort"without repeat{" `until {
|
||||
swap `until ?pairs (make-until) 0 swap
|
||||
} `{ begin-block
|
||||
} end-block-does :: }until{
|
||||
|
||||
// def <name> { ... } instead of { ... } : <name>
|
||||
{ bl word swap bl word "{" $cmp abort"{ expected" `def {
|
||||
swap `def ?pairs -rot 3 ' (create)
|
||||
} `{ begin-block
|
||||
} : (def)
|
||||
{ 0 (def) } :: def
|
||||
{ 1 (def) } :: def::
|
||||
|
||||
// defrec <name> { ... } instead of recursive <name> { ... } swap !
|
||||
{ recursive bl word "{" $cmp abort"{ expected" `defrec {
|
||||
swap `defrec ?pairs swap ! 0 'nop
|
||||
} `{ begin-block
|
||||
} :: defrec
|
||||
|
||||
def .sgn {
|
||||
if{ ?dup 0= }then{
|
||||
."zero"
|
||||
}elseif{ 0> }then{
|
||||
."positive"
|
||||
}else{
|
||||
."negative"
|
||||
}
|
||||
cr
|
||||
}
|
||||
// equivalent to: { ?dup 0= { ."zero" } { 0> { ."positive" } { ."negative" } cond } cond cr } : .sgn
|
||||
|
||||
defrec fact {
|
||||
if{ dup }then{
|
||||
dup 1- fact *
|
||||
}else{
|
||||
drop 1
|
||||
}
|
||||
}
|
||||
// equivalent to: recursive fact { dup { dup 1- fact * } { drop 1 } cond } swap !
|
||||
|
||||
// [[ ... ]] computes arbitrary constants inside definitions
|
||||
// { [[ 5 dup * ]] + } : add25
|
||||
// is equivalent to
|
||||
// { 25 + } : add25
|
||||
{ "without [[" abort } box constant ']]
|
||||
{ ']] @ execute } : ]]
|
||||
{ { ']] @ 2 { ']] ! call/cc } does ']] !
|
||||
interpret 'nop ']] ! "]] not found" abort
|
||||
} call/cc
|
||||
drop 1 'nop
|
||||
} :: [[
|
||||
|
||||
{ { over @ swap 2 { call/cc } does swap !
|
||||
interpret "literal to eof" abort
|
||||
} call/cc
|
||||
drop execute 1 'nop
|
||||
} : interpret-literal-to
|
||||
// use next line only if Lists.fif is loaded (or move it to Lists.fif if FiftExt.fif becomes part of Fift.fif)
|
||||
// { ( ') interpret-literal-to } :: '(
|
||||
// then you can use list literals '( a b c ... ) inside definitions:
|
||||
// { '( 1 2 3 ) } : test
|
||||
// { '( ( `a { ."A" } ) ( `b { ."B" } ) ) assoc { cadr execute } { ."???" } cond } : test2
|
|
@ -59,6 +59,12 @@ td::Result<std::string> load_GetOpt_fif(std::string dir = "") {
|
|||
td::Result<std::string> load_wallet3_code_fif(std::string dir = "") {
|
||||
return td::read_file_str(smartcont_dir(dir) + "wallet-v3-code.fif");
|
||||
}
|
||||
td::Result<std::string> load_FiftExt_fif(std::string dir = "") {
|
||||
return load_source("FiftExt.fif", dir);
|
||||
}
|
||||
td::Result<std::string> load_Disasm_fif(std::string dir = "") {
|
||||
return load_source("Disasm.fif", dir);
|
||||
}
|
||||
|
||||
class MemoryFileLoader : public fift::FileLoader {
|
||||
public:
|
||||
|
@ -108,7 +114,8 @@ class MemoryFileLoader : public fift::FileLoader {
|
|||
|
||||
td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_preamble = true, bool need_asm = true,
|
||||
bool need_ton_util = true, bool need_lisp = true,
|
||||
bool need_w3_code = true, std::string dir = "") {
|
||||
bool need_w3_code = true, bool need_fift_ext = true,
|
||||
bool need_disasm = true, std::string dir = "") {
|
||||
auto loader = std::make_unique<MemoryFileLoader>();
|
||||
loader->add_file("/main.fif", std::move(main));
|
||||
if (need_preamble) {
|
||||
|
@ -141,6 +148,14 @@ td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_
|
|||
TRY_RESULT(f, load_wallet3_code_fif(dir));
|
||||
loader->add_file("/wallet-v3-code.fif", std::move(f));
|
||||
}
|
||||
if (need_fift_ext) {
|
||||
TRY_RESULT(f, load_FiftExt_fif(dir));
|
||||
loader->add_file("/FiftExt.fif", std::move(f));
|
||||
}
|
||||
if (need_disasm) {
|
||||
TRY_RESULT(f, load_Disasm_fif(dir));
|
||||
loader->add_file("/Disasm.fif", std::move(f));
|
||||
}
|
||||
auto res = fift::SourceLookup(std::move(loader));
|
||||
res.add_include_path("/");
|
||||
return std::move(res);
|
||||
|
@ -172,7 +187,7 @@ td::Result<fift::SourceLookup> run_fift(fift::SourceLookup source_lookup, std::o
|
|||
} // namespace
|
||||
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args, std::string fift_dir) {
|
||||
std::stringstream ss;
|
||||
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, true, fift_dir));
|
||||
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, true, true, true, fift_dir));
|
||||
TRY_RESULT_ASSIGN(source_lookup, run_fift(std::move(source_lookup), &ss, true, std::move(args)));
|
||||
FiftOutput res;
|
||||
res.source_lookup = std::move(source_lookup);
|
||||
|
@ -190,7 +205,8 @@ td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std:
|
|||
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir, bool need_preamble,
|
||||
bool need_asm, bool need_ton_util, bool need_lisp,
|
||||
bool need_w3_code) {
|
||||
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, fift_dir);
|
||||
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, false, false,
|
||||
fift_dir);
|
||||
}
|
||||
|
||||
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir, bool is_raw) {
|
||||
|
@ -198,7 +214,7 @@ td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_d
|
|||
TRY_RESULT(source_lookup,
|
||||
create_source_lookup(PSTRING() << "\"Asm.fif\" include\n " << (is_raw ? "<{" : "") << asm_code << "\n"
|
||||
<< (is_raw ? "}>c" : "") << " boc>B \"res\" B>file",
|
||||
true, true, true, false, false, fift_dir));
|
||||
true, true, true, false, false, false, false, fift_dir));
|
||||
TRY_RESULT(res, run_fift(std::move(source_lookup), &ss));
|
||||
TRY_RESULT(boc, res.read_file("res"));
|
||||
return vm::std_boc_deserialize(std::move(boc.data));
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "Dictionary.h"
|
||||
#include "IntCtx.h"
|
||||
#include "SourceLookup.h"
|
||||
#include "HashMap.h"
|
||||
|
||||
#include "common/refcnt.hpp"
|
||||
#include "common/bigint.hpp"
|
||||
|
@ -42,6 +43,8 @@
|
|||
#include "vm/box.hpp"
|
||||
#include "vm/atom.h"
|
||||
|
||||
#include "vm/db/TonDb.h" // only for interpret_db_run_vm{,_parallel}
|
||||
|
||||
#include "block/block.h"
|
||||
|
||||
#include "td/utils/filesystem.h"
|
||||
|
@ -58,12 +61,31 @@
|
|||
|
||||
namespace fift {
|
||||
|
||||
void show_total_cells(std::ostream& stream) {
|
||||
stream << "total cells = " << vm::DataCell::get_total_data_cells() << std::endl;
|
||||
const Ref<FiftCont> nop_word_def = Ref<NopWord>{true};
|
||||
|
||||
//
|
||||
// functions for wordef
|
||||
//
|
||||
Ref<FiftCont> pop_exec_token(vm::Stack& stack) {
|
||||
auto wd_ref = stack.pop_chk().as_object<FiftCont>();
|
||||
if (wd_ref.is_null()) {
|
||||
throw IntError{"execution token expected"};
|
||||
}
|
||||
return wd_ref;
|
||||
}
|
||||
|
||||
void do_compile(vm::Stack& stack, Ref<FiftCont> word_def);
|
||||
void do_compile_literals(vm::Stack& stack, int count);
|
||||
Ref<WordList> pop_word_list(vm::Stack& stack) {
|
||||
auto wl_ref = stack.pop_chk().as_object<WordList>();
|
||||
if (wl_ref.is_null()) {
|
||||
throw IntError{"word list expected"};
|
||||
}
|
||||
return wl_ref;
|
||||
}
|
||||
|
||||
void push_argcount(vm::Stack& stack, int args) {
|
||||
stack.push_smallint(args);
|
||||
stack.push_object(nop_word_def);
|
||||
}
|
||||
|
||||
void interpret_dot(IntCtx& ctx, bool space_after) {
|
||||
*ctx.output_stream << dec_string2(ctx.stack.pop_int()) << (space_after ? " " : "");
|
||||
|
@ -121,7 +143,7 @@ void interpret_print_list(IntCtx& ctx) {
|
|||
}
|
||||
|
||||
void interpret_dottc(IntCtx& ctx) {
|
||||
show_total_cells(*ctx.output_stream);
|
||||
*ctx.output_stream << "total cells = " << vm::DataCell::get_total_data_cells() << std::endl;
|
||||
}
|
||||
|
||||
void interpret_dot_internal(vm::Stack& stack) {
|
||||
|
@ -461,7 +483,7 @@ void interpret_make_xchg(vm::Stack& stack) {
|
|||
if (x) {
|
||||
stack.push_object(td::Ref<StackWord>{true, std::bind(interpret_xchg, _1, x, y)});
|
||||
} else if (y <= 1) {
|
||||
stack.push_object(y ? swap_word_def : Dictionary::nop_word_def);
|
||||
stack.push_object(y ? swap_word_def : nop_word_def);
|
||||
} else {
|
||||
stack.push_object(td::Ref<StackWord>{true, std::bind(interpret_xchg0, _1, y)});
|
||||
}
|
||||
|
@ -1080,6 +1102,31 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) {
|
|||
}
|
||||
}
|
||||
|
||||
void interpret_fetch_slice(vm::Stack& stack, int mode) {
|
||||
unsigned refs = ((mode & 1) ? stack.pop_smallint_range(4) : 0);
|
||||
unsigned bits = stack.pop_smallint_range(1023);
|
||||
auto cs = stack.pop_cellslice();
|
||||
if (!cs->have(bits, refs)) {
|
||||
if (mode & 2) {
|
||||
stack.push(std::move(cs));
|
||||
}
|
||||
stack.push_bool(false);
|
||||
if (!(mode & 4)) {
|
||||
throw IntError{"end of data while fetching subslice from cell"};
|
||||
}
|
||||
} else {
|
||||
if (mode & 2) {
|
||||
stack.push(cs.write().fetch_subslice(bits, refs));
|
||||
stack.push(std::move(cs));
|
||||
} else {
|
||||
stack.push(cs->prefetch_subslice(bits, refs));
|
||||
}
|
||||
if (mode & 4) {
|
||||
stack.push_bool(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_cell_empty(vm::Stack& stack) {
|
||||
auto cs = stack.pop_cellslice();
|
||||
stack.push_bool(cs->empty_ext());
|
||||
|
@ -1483,6 +1530,150 @@ void interpret_crc32c(vm::Stack& stack) {
|
|||
stack.push_smallint(td::crc32c(td::Slice{str}));
|
||||
}
|
||||
|
||||
// Fift hashmaps
|
||||
|
||||
void push_hmap(vm::Stack& stack, Ref<Hashmap> hmap) {
|
||||
if (hmap.not_null()) {
|
||||
stack.push_object(std::move(hmap));
|
||||
} else {
|
||||
stack.push({});
|
||||
}
|
||||
}
|
||||
|
||||
void push_hmap(vm::Stack& stack, HashmapKeeper hmap_keep) {
|
||||
push_hmap(stack, hmap_keep.extract());
|
||||
}
|
||||
|
||||
Ref<Hashmap> pop_hmap(vm::Stack& stack) {
|
||||
stack.check_underflow(1);
|
||||
auto se = stack.pop();
|
||||
if (se.is_null()) {
|
||||
return {};
|
||||
}
|
||||
auto hmap_ref = std::move(se).as_object<Hashmap>();
|
||||
if (hmap_ref.is_null()) {
|
||||
throw IntError{"hashmap expected"};
|
||||
}
|
||||
return hmap_ref;
|
||||
}
|
||||
|
||||
HashmapKeeper pop_hmap_keeper(vm::Stack& stack) {
|
||||
return HashmapKeeper{pop_hmap(stack)};
|
||||
}
|
||||
|
||||
void interpret_hmap_new(vm::Stack& stack) {
|
||||
stack.push({});
|
||||
}
|
||||
|
||||
void interpret_hmap_fetch(vm::Stack& stack, int mode) {
|
||||
auto hmap = pop_hmap(stack);
|
||||
auto value = Hashmap::get(std::move(hmap), stack.pop_chk());
|
||||
bool found = !value.is_null();
|
||||
if ((mode & 8) && !found) {
|
||||
throw IntError{"hashmap key not found"};
|
||||
}
|
||||
if (mode & (2 << (int)found)) {
|
||||
stack.push(std::move(value));
|
||||
}
|
||||
if (mode & 1) {
|
||||
stack.push_bool(found);
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_hmap_delete(vm::Stack& stack, int mode) {
|
||||
auto hmap = pop_hmap(stack);
|
||||
auto res = Hashmap::get_remove(std::move(hmap), stack.pop_chk());
|
||||
push_hmap(stack, std::move(res.first));
|
||||
bool found = !res.second.is_null();
|
||||
if ((mode & 8) && !found) {
|
||||
throw IntError{"hashmap key not found"};
|
||||
}
|
||||
if (mode & (2 << (int)found)) {
|
||||
stack.push(std::move(res.second));
|
||||
}
|
||||
if (mode & 1) {
|
||||
stack.push_bool(found);
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_hmap_store(vm::Stack& stack, int mode) {
|
||||
stack.check_underflow(3);
|
||||
auto hmap = pop_hmap_keeper(stack);
|
||||
auto key = stack.pop(), value = stack.pop();
|
||||
bool ok = true;
|
||||
if (mode & 1) {
|
||||
hmap.set(std::move(key), std::move(value));
|
||||
} else {
|
||||
ok = hmap.replace(std::move(key), std::move(value));
|
||||
}
|
||||
push_hmap(stack, std::move(hmap));
|
||||
if (mode & 2) {
|
||||
stack.push_bool(ok);
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_hmap_is_empty(vm::Stack& stack) {
|
||||
stack.push_bool(pop_hmap(stack).is_null());
|
||||
}
|
||||
|
||||
void interpret_hmap_decompose(vm::Stack& stack, int mode) {
|
||||
auto hmap = pop_hmap(stack);
|
||||
if (hmap.is_null()) {
|
||||
if (mode & 1) {
|
||||
stack.push_bool(false);
|
||||
} else {
|
||||
throw IntError{"empty hmap"};
|
||||
}
|
||||
return;
|
||||
}
|
||||
stack.push(hmap->key());
|
||||
stack.push(hmap->value());
|
||||
push_hmap(stack, hmap->left());
|
||||
push_hmap(stack, hmap->right());
|
||||
if (mode & 1) {
|
||||
stack.push_bool(true);
|
||||
}
|
||||
}
|
||||
|
||||
class HmapIterCont : public LoopCont {
|
||||
HashmapIterator it;
|
||||
bool ok;
|
||||
|
||||
public:
|
||||
HmapIterCont(Ref<FiftCont> _func, Ref<FiftCont> _after, HashmapIterator _it)
|
||||
: LoopCont(std::move(_func), std::move(_after)), it(std::move(_it)), ok(true) {
|
||||
}
|
||||
HmapIterCont(const HmapIterCont&) = default;
|
||||
HmapIterCont* make_copy() const override {
|
||||
return new HmapIterCont(*this);
|
||||
}
|
||||
bool init(IntCtx& ctx) override {
|
||||
return true;
|
||||
}
|
||||
bool pre_exec(IntCtx& ctx) override {
|
||||
if (it.eof()) {
|
||||
return false;
|
||||
} else {
|
||||
ctx.stack.push(it->key());
|
||||
ctx.stack.push(it->value());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
bool post_exec(IntCtx& ctx) override {
|
||||
ok = ctx.stack.pop_bool();
|
||||
return ok && it.next();
|
||||
}
|
||||
bool finalize(IntCtx& ctx) override {
|
||||
ctx.stack.push_bool(ok);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
Ref<FiftCont> interpret_hmap_foreach(IntCtx& ctx, int mode) {
|
||||
auto func = pop_exec_token(ctx);
|
||||
return td::make_ref<HmapIterCont>(std::move(func), std::move(ctx.next), pop_hmap_keeper(ctx).begin(mode & 1));
|
||||
}
|
||||
|
||||
// vm dictionaries
|
||||
void interpret_dict_new(vm::Stack& stack) {
|
||||
stack.push({});
|
||||
|
@ -1882,7 +2073,7 @@ void interpret_pfx_dict_get(vm::Stack& stack) {
|
|||
}
|
||||
|
||||
void interpret_bitstring_hex_literal(IntCtx& ctx) {
|
||||
auto s = ctx.scan_word_to('}');
|
||||
auto s = ctx.parser->scan_word_to('}');
|
||||
unsigned char buff[128];
|
||||
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), s.begin(), s.end());
|
||||
if (bits < 0) {
|
||||
|
@ -1890,11 +2081,11 @@ void interpret_bitstring_hex_literal(IntCtx& ctx) {
|
|||
}
|
||||
auto cs = Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
|
||||
ctx.stack.push(std::move(cs));
|
||||
push_argcount(ctx.stack, 1);
|
||||
push_argcount(ctx, 1);
|
||||
}
|
||||
|
||||
void interpret_bitstring_binary_literal(IntCtx& ctx) {
|
||||
auto s = ctx.scan_word_to('}');
|
||||
auto s = ctx.parser->scan_word_to('}');
|
||||
unsigned char buff[128];
|
||||
int bits = (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), s.begin(), s.end());
|
||||
if (bits < 0) {
|
||||
|
@ -1902,12 +2093,12 @@ void interpret_bitstring_binary_literal(IntCtx& ctx) {
|
|||
}
|
||||
auto cs = Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
|
||||
ctx.stack.push(std::move(cs));
|
||||
push_argcount(ctx.stack, 1);
|
||||
push_argcount(ctx, 1);
|
||||
}
|
||||
|
||||
void interpret_word(IntCtx& ctx) {
|
||||
char sep = (char)ctx.stack.pop_smallint_range(127);
|
||||
auto word = (sep != ' ' ? ctx.scan_word_to(sep, true) : ctx.scan_word());
|
||||
auto word = (sep != ' ' ? ctx.parser->scan_word_to(sep, true) : ctx.parser->scan_word());
|
||||
ctx.stack.push_string(word);
|
||||
}
|
||||
|
||||
|
@ -1915,17 +2106,17 @@ void interpret_word_ext(IntCtx& ctx) {
|
|||
int mode = ctx.stack.pop_smallint_range(11);
|
||||
auto delims = ctx.stack.pop_string();
|
||||
if (mode & 8) {
|
||||
ctx.skipspc(mode & 4);
|
||||
ctx.parser->skipspc(mode & 4);
|
||||
}
|
||||
ctx.stack.push_string(ctx.scan_word_ext(CharClassifier{delims, mode & 3}));
|
||||
ctx.stack.push_string(ctx.parser->scan_word_ext(CharClassifier{delims, mode & 3}));
|
||||
}
|
||||
|
||||
void interpret_skipspc(IntCtx& ctx) {
|
||||
ctx.skipspc();
|
||||
ctx.parser->skipspc();
|
||||
}
|
||||
|
||||
void interpret_wordlist_begin_aux(vm::Stack& stack) {
|
||||
stack.push({vm::from_object, Ref<WordList>{true}});
|
||||
stack.push_make_object<WordList>();
|
||||
}
|
||||
|
||||
void interpret_wordlist_begin(IntCtx& ctx) {
|
||||
|
@ -1938,7 +2129,7 @@ void interpret_wordlist_begin(IntCtx& ctx) {
|
|||
void interpret_wordlist_end_aux(vm::Stack& stack) {
|
||||
Ref<WordList> wordlist_ref = pop_word_list(stack);
|
||||
wordlist_ref.write().close();
|
||||
stack.push({vm::from_object, Ref<FiftCont>{wordlist_ref}});
|
||||
stack.push_object(std::move(wordlist_ref));
|
||||
}
|
||||
|
||||
void interpret_wordlist_end(IntCtx& ctx) {
|
||||
|
@ -1957,7 +2148,7 @@ void interpret_internal_interpret_begin(IntCtx& ctx) {
|
|||
void interpret_internal_interpret_end(IntCtx& ctx) {
|
||||
ctx.check_int_exec();
|
||||
ctx.state = -ctx.state;
|
||||
ctx.stack.push({vm::from_object, Dictionary::nop_word_def});
|
||||
ctx.stack.push_object(nop_word_def);
|
||||
}
|
||||
|
||||
// (create)
|
||||
|
@ -1974,18 +2165,12 @@ void interpret_create_aux(IntCtx& ctx, int mode) {
|
|||
if (!(mode & 2)) {
|
||||
word += ' ';
|
||||
}
|
||||
bool active = (mode & 1);
|
||||
auto entry = ctx.dictionary->lookup(word);
|
||||
if (entry) {
|
||||
*entry = DictEntry{std::move(wd_ref), active}; // redefine word
|
||||
} else {
|
||||
ctx.dictionary->def_word(std::move(word), {std::move(wd_ref), active});
|
||||
}
|
||||
ctx.dictionary.def_word(std::move(word), {std::move(wd_ref), (bool)(mode & 1)});
|
||||
}
|
||||
|
||||
// { bl word 0 (create) } : create
|
||||
void interpret_create(IntCtx& ctx) {
|
||||
auto word = ctx.scan_word();
|
||||
auto word = ctx.parser->scan_word();
|
||||
if (!word.size()) {
|
||||
throw IntError{"non-empty word name expected"};
|
||||
}
|
||||
|
@ -1997,10 +2182,10 @@ Ref<FiftCont> create_aux_wd{Ref<CtxWord>{true, std::bind(interpret_create_aux, s
|
|||
|
||||
// { bl word <mode> 2 ' (create) } :: :
|
||||
void interpret_colon(IntCtx& ctx, int mode) {
|
||||
ctx.stack.push_string(ctx.scan_word());
|
||||
ctx.stack.push_string(ctx.parser->scan_word());
|
||||
ctx.stack.push_smallint(mode);
|
||||
ctx.stack.push_smallint(2);
|
||||
ctx.stack.push({vm::from_object, create_aux_wd});
|
||||
ctx.stack.push_object(create_aux_wd);
|
||||
//push_argcount(ctx, 2, create_wd);
|
||||
}
|
||||
|
||||
|
@ -2008,27 +2193,27 @@ void interpret_colon(IntCtx& ctx, int mode) {
|
|||
void interpret_forget_aux(IntCtx& ctx) {
|
||||
std::string s = ctx.stack.pop_string();
|
||||
auto s_copy = s;
|
||||
auto entry = ctx.dictionary->lookup(s);
|
||||
auto entry = ctx.dictionary.lookup(s);
|
||||
if (!entry) {
|
||||
s += " ";
|
||||
entry = ctx.dictionary->lookup(s);
|
||||
entry = ctx.dictionary.lookup(s);
|
||||
}
|
||||
if (!entry) {
|
||||
throw IntError{"`" + s_copy + "` not found"};
|
||||
} else {
|
||||
ctx.dictionary->undef_word(s);
|
||||
ctx.dictionary.undef_word(s);
|
||||
}
|
||||
}
|
||||
|
||||
// { bl word (forget) } : forget
|
||||
void interpret_forget(IntCtx& ctx) {
|
||||
ctx.stack.push_string(ctx.scan_word());
|
||||
ctx.stack.push_string(ctx.parser->scan_word());
|
||||
interpret_forget_aux(ctx);
|
||||
}
|
||||
|
||||
void interpret_quote_str(IntCtx& ctx) {
|
||||
ctx.stack.push_string(ctx.scan_word_to('"'));
|
||||
push_argcount(ctx.stack, 1);
|
||||
ctx.stack.push_string(ctx.parser->scan_word_to('"'));
|
||||
push_argcount(ctx, 1);
|
||||
}
|
||||
|
||||
int str_utf8_code(const char* str, int& len) {
|
||||
|
@ -2056,7 +2241,7 @@ int str_utf8_code(const char* str, int& len) {
|
|||
}
|
||||
|
||||
void interpret_char(IntCtx& ctx) {
|
||||
auto s = ctx.scan_word();
|
||||
auto s = ctx.parser->scan_word();
|
||||
int len = (s.size() < 10 ? (int)s.size() : 10);
|
||||
int code = str_utf8_code(s.data(), len);
|
||||
if (code < 0 || s.size() != (unsigned)len) {
|
||||
|
@ -2197,6 +2382,12 @@ Ref<FiftCont> interpret_execute(IntCtx& ctx) {
|
|||
return pop_exec_token(ctx);
|
||||
}
|
||||
|
||||
Ref<FiftCont> interpret_call_cc(IntCtx& ctx) {
|
||||
auto next = pop_exec_token(ctx);
|
||||
ctx.stack.push_object(std::move(ctx.next));
|
||||
return next;
|
||||
}
|
||||
|
||||
Ref<FiftCont> interpret_execute_times(IntCtx& ctx) {
|
||||
int count = ctx.stack.pop_smallint_range(1000000000);
|
||||
auto body = pop_exec_token(ctx);
|
||||
|
@ -2251,30 +2442,46 @@ Ref<FiftCont> interpret_until(IntCtx& ctx) {
|
|||
return body;
|
||||
}
|
||||
|
||||
void interpret_tick(IntCtx& ctx) {
|
||||
std::string word = ctx.scan_word().str();
|
||||
auto entry = ctx.dictionary->lookup(word);
|
||||
if (!entry) {
|
||||
entry = ctx.dictionary->lookup(word + ' ');
|
||||
DictEntry context_lookup(IntCtx& ctx, std::string word, bool append_space = true) {
|
||||
if (append_space) {
|
||||
auto entry = context_lookup(ctx, word, false);
|
||||
if (!entry) {
|
||||
throw IntError{"word `" + word + "` undefined"};
|
||||
entry = context_lookup(ctx, word + ' ', false);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
ctx.stack.push({vm::from_object, entry->get_def()});
|
||||
auto entry = ctx.context.lookup(word);
|
||||
if (!entry && ctx.context != ctx.dictionary) {
|
||||
entry = ctx.dictionary.lookup(word);
|
||||
}
|
||||
if (!entry && ctx.main_dictionary != ctx.context && ctx.main_dictionary != ctx.dictionary) {
|
||||
entry = ctx.main_dictionary.lookup(word);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
void interpret_tick(IntCtx& ctx) {
|
||||
std::string word = ctx.parser->scan_word().str();
|
||||
auto entry = context_lookup(ctx, word);
|
||||
if (!entry) {
|
||||
throw IntError{"word `" + word + "` undefined"};
|
||||
}
|
||||
ctx.stack.push_object(entry.get_def());
|
||||
push_argcount(ctx, 1);
|
||||
}
|
||||
|
||||
void interpret_find(IntCtx& ctx) {
|
||||
void interpret_find(IntCtx& ctx, int mode) {
|
||||
std::string word = ctx.stack.pop_string();
|
||||
auto entry = ctx.dictionary->lookup(word);
|
||||
if (!entry) {
|
||||
entry = ctx.dictionary->lookup(word + ' ');
|
||||
}
|
||||
auto entry = context_lookup(ctx, word, !(mode & 2));
|
||||
if (!entry) {
|
||||
ctx.stack.push_bool(false);
|
||||
} else {
|
||||
ctx.stack.push({vm::from_object, entry->get_def()});
|
||||
ctx.stack.push_bool(true);
|
||||
ctx.stack.push_object(entry.get_def());
|
||||
if (!(mode & 1) || !entry.is_active()) {
|
||||
ctx.stack.push_bool(true);
|
||||
} else {
|
||||
ctx.stack.push_smallint(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2284,9 +2491,13 @@ void interpret_leave_source(IntCtx& ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
void interpret_include_depth(IntCtx& ctx) {
|
||||
ctx.stack.push_smallint(ctx.include_depth());
|
||||
}
|
||||
|
||||
Ref<FiftCont> interpret_include(IntCtx& ctx) {
|
||||
auto fname = ctx.stack.pop_string();
|
||||
auto r_file = ctx.source_lookup->lookup_source(fname, ctx.currentd_dir);
|
||||
auto r_file = ctx.source_lookup->lookup_source(fname, ctx.parser->currentd_dir);
|
||||
if (r_file.is_error()) {
|
||||
throw IntError{"cannot locate file `" + fname + "`"};
|
||||
}
|
||||
|
@ -2314,12 +2525,32 @@ Ref<FiftCont> interpret_skip_source(IntCtx& ctx) {
|
|||
}
|
||||
|
||||
void interpret_words(IntCtx& ctx) {
|
||||
for (const auto& x : *ctx.dictionary) {
|
||||
*ctx.output_stream << x.first << " ";
|
||||
for (const auto& x : ctx.dictionary) {
|
||||
*ctx.output_stream << vm::StackEntry(x.key()).as_string() << " ";
|
||||
}
|
||||
*ctx.output_stream << std::endl;
|
||||
}
|
||||
|
||||
void interpret_get_current(IntCtx& ctx) {
|
||||
ctx.stack.push(ctx.dictionary.get_box());
|
||||
}
|
||||
|
||||
void interpret_set_current(IntCtx& ctx) {
|
||||
ctx.dictionary = ctx.stack.pop_box();
|
||||
}
|
||||
|
||||
void interpret_get_context(IntCtx& ctx) {
|
||||
ctx.stack.push(ctx.context.get_box());
|
||||
}
|
||||
|
||||
void interpret_set_context(IntCtx& ctx) {
|
||||
ctx.context = ctx.stack.pop_box();
|
||||
}
|
||||
|
||||
void interpret_set_context_to(IntCtx& ctx, Ref<vm::Box> box) {
|
||||
ctx.context = std::move(box);
|
||||
}
|
||||
|
||||
void interpret_print_backtrace(IntCtx& ctx) {
|
||||
ctx.print_backtrace(*ctx.output_stream, ctx.next);
|
||||
}
|
||||
|
@ -2465,6 +2696,28 @@ void interpret_run_vm(IntCtx& ctx, int mode) {
|
|||
}
|
||||
}
|
||||
|
||||
void interpret_vmop_len(vm::Stack& stack) {
|
||||
int cp = stack.pop_smallint_range(0x7fffffff, -0x80000000);
|
||||
auto cs = stack.pop_cellslice();
|
||||
auto dispatch = vm::DispatchTable::get_table(cp);
|
||||
if (!dispatch) {
|
||||
throw IntError{"unknown vm codepage"};
|
||||
}
|
||||
stack.push_smallint(dispatch->instr_len(*cs));
|
||||
}
|
||||
|
||||
void interpret_vmop_dump(vm::Stack& stack) {
|
||||
int cp = stack.pop_smallint_range(0x7fffffff, -0x80000000);
|
||||
auto cs = stack.pop_cellslice();
|
||||
auto dispatch = vm::DispatchTable::get_table(cp);
|
||||
if (!dispatch) {
|
||||
throw IntError{"unknown vm codepage"};
|
||||
}
|
||||
auto dump = dispatch->dump_instr(cs.write());
|
||||
stack.push_cellslice(std::move(cs));
|
||||
stack.push_string(std::move(dump));
|
||||
}
|
||||
|
||||
void do_interpret_db_run_vm_parallel(std::ostream* stream, vm::Stack& stack, vm::TonDb* ton_db_ptr, int threads_n,
|
||||
int tasks_n) {
|
||||
if (!ton_db_ptr || !*ton_db_ptr) {
|
||||
|
@ -2623,11 +2876,11 @@ Ref<FiftCont> interpret_get_cmdline_arg(IntCtx& ctx) {
|
|||
interpret_get_fixed_cmdline_arg(ctx.stack, n);
|
||||
return {};
|
||||
}
|
||||
auto entry = ctx.dictionary->lookup("$0 ");
|
||||
auto entry = ctx.dictionary.lookup("$0 ");
|
||||
if (!entry) {
|
||||
throw IntError{"-?"};
|
||||
} else {
|
||||
return entry->get_def();
|
||||
return entry.get_def();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2667,29 +2920,19 @@ Ref<FiftCont> interpret_execute_internal(IntCtx& ctx) {
|
|||
return word_def;
|
||||
}
|
||||
|
||||
// wl x1 .. xn n 'w --> wl'
|
||||
void interpret_compile_internal(vm::Stack& stack) {
|
||||
Ref<FiftCont> word_def = pop_exec_token(stack);
|
||||
int count = stack.pop_smallint_range(255);
|
||||
do_compile_literals(stack, count);
|
||||
if (word_def != Dictionary::nop_word_def) {
|
||||
do_compile(stack, word_def);
|
||||
}
|
||||
}
|
||||
|
||||
void do_compile(vm::Stack& stack, Ref<FiftCont> word_def) {
|
||||
Ref<WordList> wl_ref = pop_word_list(stack);
|
||||
if (word_def != Dictionary::nop_word_def) {
|
||||
if (word_def != nop_word_def) {
|
||||
auto list_size = word_def->list_size();
|
||||
if ((td::uint64)list_size <= 1) {
|
||||
// inline short definitions
|
||||
if (list_size >= 0 && (list_size <= 2 || word_def.is_unique())) {
|
||||
// inline short and unique definitions
|
||||
auto list = word_def->get_list();
|
||||
wl_ref.write().append(list, list + list_size);
|
||||
} else {
|
||||
wl_ref.write().push_back(word_def);
|
||||
wl_ref.write().push_back(std::move(word_def));
|
||||
}
|
||||
}
|
||||
stack.push({vm::from_object, wl_ref});
|
||||
stack.push_object(std::move(wl_ref));
|
||||
}
|
||||
|
||||
void compile_one_literal(WordList& wlist, vm::StackEntry val) {
|
||||
|
@ -2717,12 +2960,146 @@ void do_compile_literals(vm::Stack& stack, int count) {
|
|||
}
|
||||
}
|
||||
stack.pop_many(count + 1);
|
||||
stack.push({vm::from_object, wl_ref});
|
||||
stack.push_object(std::move(wl_ref));
|
||||
}
|
||||
|
||||
// wl x1 .. xn n 'w --> wl'
|
||||
void interpret_compile_internal(vm::Stack& stack) {
|
||||
Ref<FiftCont> word_def = pop_exec_token(stack);
|
||||
int count = stack.pop_smallint_range(255);
|
||||
do_compile_literals(stack, count);
|
||||
if (word_def != nop_word_def) {
|
||||
do_compile(stack, std::move(word_def));
|
||||
}
|
||||
}
|
||||
|
||||
Ref<FiftCont> interpret_compile_execute(IntCtx& ctx) {
|
||||
if (ctx.state > 0) {
|
||||
interpret_compile_internal(ctx.stack);
|
||||
return {};
|
||||
} else {
|
||||
return interpret_execute_internal(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void interpret_seekeof(IntCtx& ctx, int mode) {
|
||||
if (mode == -1) {
|
||||
mode = ctx.stack.pop_smallint_range(3, -1);
|
||||
}
|
||||
bool eof = true;
|
||||
if (ctx.parser && (ctx.parser->get_input() || ctx.parser->load_next_line())) {
|
||||
while (true) {
|
||||
if (!ctx.parser->is_sb()) {
|
||||
ctx.parser->skipspc();
|
||||
if (*ctx.parser->get_input()) {
|
||||
eof = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mode & 1) {
|
||||
*ctx.output_stream << " ok" << std::endl;
|
||||
}
|
||||
if (!ctx.parser->load_next_line()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.stack.push_bool(eof);
|
||||
}
|
||||
|
||||
void interpret_word_prefix_find(IntCtx& ctx, int mode) {
|
||||
const char *ptr = ctx.parser->get_input(), *start = ptr;
|
||||
if (!ptr) {
|
||||
ctx.stack.push_string(std::string{});
|
||||
ctx.stack.push_bool(false);
|
||||
return;
|
||||
}
|
||||
while (*ptr && *ptr != ' ' && *ptr != '\t') {
|
||||
ptr++;
|
||||
}
|
||||
std::string Word{start, ptr};
|
||||
Word.push_back(' ');
|
||||
auto entry = context_lookup(ctx, Word, false);
|
||||
Word.pop_back();
|
||||
if (entry) {
|
||||
ctx.parser->set_input(ptr);
|
||||
ctx.parser->skipspc();
|
||||
} else {
|
||||
const char* ptr_end = ptr;
|
||||
while (true) {
|
||||
entry = context_lookup(ctx, Word, false);
|
||||
if (entry) {
|
||||
ctx.parser->set_input(ptr);
|
||||
break;
|
||||
}
|
||||
if (ptr == start) {
|
||||
Word = std::string{start, ptr_end};
|
||||
ctx.parser->set_input(ptr_end);
|
||||
ctx.parser->skipspc();
|
||||
break;
|
||||
}
|
||||
Word.pop_back();
|
||||
--ptr;
|
||||
}
|
||||
}
|
||||
ctx.parser->word = Word;
|
||||
if (!(mode & 2) || !entry) {
|
||||
ctx.stack.push_string(std::move(Word));
|
||||
}
|
||||
if (mode & 1) {
|
||||
if (!entry) {
|
||||
ctx.stack.push_bool(false);
|
||||
} else {
|
||||
ctx.stack.push_object(entry.get_def());
|
||||
ctx.stack.push_smallint(entry.is_active() ? 1 : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// equivalent to
|
||||
// { ?dup { 1+ { execute } { 0 swap } cond } { (number) ?dup 0= abort"-?" 'nop } cond
|
||||
// } : (interpret-prepare)
|
||||
Ref<FiftCont> interpret_prepare(IntCtx& ctx) {
|
||||
int found = ctx.stack.pop_smallint_range(1, -1);
|
||||
if (!found) {
|
||||
// numbers
|
||||
interpret_parse_number(ctx); // (number)
|
||||
interpret_cond_dup(ctx); // ?dup
|
||||
auto res = ctx.stack.pop_int(); // 0= abort"-?"
|
||||
if (res == 0) {
|
||||
throw IntError{"-?"};
|
||||
}
|
||||
ctx.stack.push_object(nop_word_def); // 'nop
|
||||
return {};
|
||||
} else if (found == -1) {
|
||||
// ordinary word
|
||||
ctx.stack.push_smallint(0); // 0
|
||||
interpret_swap(ctx); // swap
|
||||
return {};
|
||||
} else {
|
||||
// active word
|
||||
return pop_exec_token(ctx); // execute
|
||||
}
|
||||
}
|
||||
|
||||
Ref<FiftCont> InterpretCont::run_tail(IntCtx& ctx) const {
|
||||
static Ref<FiftCont> interpret_prepare_ref = td::make_ref<CtxTailWord>(interpret_prepare);
|
||||
static Ref<FiftCont> compile_exec_ref = td::make_ref<CtxTailWord>(interpret_compile_execute);
|
||||
interpret_seekeof(ctx, !ctx.state && !ctx.include_depth()); // seekeof
|
||||
if (ctx.stack.pop_bool()) {
|
||||
exit_interpret->clear();
|
||||
return {}; // exit loop
|
||||
}
|
||||
exit_interpret->set({vm::from_object, ctx.next}); // set 'exit-interpret to current continuation
|
||||
interpret_word_prefix_find(ctx, 3); // (word-prefix-find)
|
||||
// (interpet-prepare) (compile-execute) and schedule next loop iteration
|
||||
ctx.next = SeqCont::seq(compile_exec_ref, SeqCont::seq(self(), std::move(ctx.next)));
|
||||
return interpret_prepare_ref; // (interpret-prepare)
|
||||
}
|
||||
|
||||
void init_words_common(Dictionary& d) {
|
||||
using namespace std::placeholders;
|
||||
d.def_word("nop ", Dictionary::nop_word_def);
|
||||
d.def_word("nop ", nop_word_def);
|
||||
// stack print/dump words
|
||||
d.def_ctx_word(". ", std::bind(interpret_dot, _1, true));
|
||||
d.def_ctx_word("._ ", std::bind(interpret_dot, _1, false));
|
||||
|
@ -2942,6 +3319,14 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("ref@+ ", std::bind(interpret_fetch_ref, _1, 2));
|
||||
d.def_stack_word("ref@? ", std::bind(interpret_fetch_ref, _1, 4));
|
||||
d.def_stack_word("ref@?+ ", std::bind(interpret_fetch_ref, _1, 6));
|
||||
d.def_stack_word("s@ ", std::bind(interpret_fetch_slice, _1, 0));
|
||||
d.def_stack_word("sr@ ", std::bind(interpret_fetch_slice, _1, 1));
|
||||
d.def_stack_word("s@+ ", std::bind(interpret_fetch_slice, _1, 2));
|
||||
d.def_stack_word("sr@+ ", std::bind(interpret_fetch_slice, _1, 3));
|
||||
d.def_stack_word("s@? ", std::bind(interpret_fetch_slice, _1, 4));
|
||||
d.def_stack_word("sr@? ", std::bind(interpret_fetch_slice, _1, 5));
|
||||
d.def_stack_word("s@?+ ", std::bind(interpret_fetch_slice, _1, 6));
|
||||
d.def_stack_word("sr@?+ ", std::bind(interpret_fetch_slice, _1, 7));
|
||||
d.def_stack_word("s> ", interpret_cell_check_empty);
|
||||
d.def_stack_word("empty? ", interpret_cell_empty);
|
||||
d.def_stack_word("remaining ", interpret_cell_remaining);
|
||||
|
@ -2970,6 +3355,18 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("crc16 ", interpret_crc16);
|
||||
d.def_stack_word("crc32 ", interpret_crc32);
|
||||
d.def_stack_word("crc32c ", interpret_crc32c);
|
||||
// hashmaps
|
||||
d.def_stack_word("hmapnew ", interpret_hmap_new);
|
||||
d.def_stack_word("hmap@ ", std::bind(interpret_hmap_fetch, _1, 6));
|
||||
d.def_stack_word("hmap@? ", std::bind(interpret_hmap_fetch, _1, 5));
|
||||
d.def_stack_word("hmap- ", std::bind(interpret_hmap_delete, _1, 0));
|
||||
d.def_stack_word("hmap-? ", std::bind(interpret_hmap_delete, _1, 1));
|
||||
d.def_stack_word("hmap@- ", std::bind(interpret_hmap_delete, _1, 6));
|
||||
d.def_stack_word("hmap! ", std::bind(interpret_hmap_store, _1, 0));
|
||||
d.def_stack_word("hmap!+ ", std::bind(interpret_hmap_store, _1, 1));
|
||||
d.def_stack_word("hmapempty? ", interpret_hmap_is_empty);
|
||||
d.def_stack_word("hmapunpack ", std::bind(interpret_hmap_decompose, _1, 1));
|
||||
d.def_ctx_tail_word("hmapforeach ", std::bind(interpret_hmap_foreach, _1, 0));
|
||||
// vm dictionaries
|
||||
d.def_stack_word("dictnew ", interpret_dict_new);
|
||||
d.def_stack_word("dict>s ", interpret_dict_to_slice);
|
||||
|
@ -3041,6 +3438,7 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("atom? ", interpret_is_atom);
|
||||
// execution control
|
||||
d.def_ctx_tail_word("execute ", interpret_execute);
|
||||
d.def_ctx_tail_word("call/cc ", interpret_call_cc);
|
||||
d.def_ctx_tail_word("times ", interpret_execute_times);
|
||||
d.def_ctx_tail_word("if ", interpret_if);
|
||||
d.def_ctx_tail_word("ifnot ", interpret_ifnot);
|
||||
|
@ -3056,10 +3454,12 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_stack_word("(}) ", interpret_wordlist_end_aux);
|
||||
d.def_stack_word("(compile) ", interpret_compile_internal);
|
||||
d.def_ctx_tail_word("(execute) ", interpret_execute_internal);
|
||||
d.def_ctx_tail_word("(interpret-prepare) ", interpret_prepare);
|
||||
d.def_active_word("' ", interpret_tick);
|
||||
d.def_word("'nop ", LitCont::literal({vm::from_object, Dictionary::nop_word_def}));
|
||||
d.def_word("'nop ", LitCont::literal({vm::from_object, nop_word_def}));
|
||||
// dictionary manipulation
|
||||
d.def_ctx_word("find ", interpret_find);
|
||||
d.def_ctx_word("find ", std::bind(interpret_find, _1, 1));
|
||||
d.def_ctx_word("(word-prefix-find) ", std::bind(interpret_word_prefix_find, _1, 3));
|
||||
d.def_ctx_word("create ", interpret_create);
|
||||
d.def_ctx_word("(create) ", std::bind(interpret_create_aux, _1, -1));
|
||||
d.def_active_word(": ", std::bind(interpret_colon, _1, 0));
|
||||
|
@ -3069,12 +3469,21 @@ void init_words_common(Dictionary& d) {
|
|||
d.def_ctx_word("(forget) ", interpret_forget_aux);
|
||||
d.def_ctx_word("forget ", interpret_forget);
|
||||
d.def_ctx_word("words ", interpret_words);
|
||||
d.def_word("Fift-wordlist ", LitCont::literal(d.get_box()));
|
||||
d.def_ctx_word("Fift ", std::bind(interpret_set_context_to, _1, d.get_box()));
|
||||
d.def_ctx_word("current@ ", interpret_get_current);
|
||||
d.def_ctx_word("current! ", interpret_set_current);
|
||||
d.def_ctx_word("context@ ", interpret_get_context);
|
||||
d.def_ctx_word("context! ", interpret_set_context);
|
||||
d.def_ctx_word(".bt ", interpret_print_backtrace);
|
||||
d.def_ctx_word("cont. ", interpret_print_continuation);
|
||||
// input parse
|
||||
d.def_ctx_word("word ", interpret_word);
|
||||
d.def_ctx_word("(word) ", interpret_word_ext);
|
||||
d.def_ctx_word("skipspc ", interpret_skipspc);
|
||||
d.def_ctx_word("seekeof? ", std::bind(interpret_seekeof, _1, 1));
|
||||
d.def_ctx_word("(seekeof?) ", std::bind(interpret_seekeof, _1, -1));
|
||||
d.def_ctx_word("include-depth ", interpret_include_depth);
|
||||
d.def_ctx_tail_word("include ", interpret_include);
|
||||
d.def_ctx_tail_word("skip-to-eof ", interpret_skip_source);
|
||||
d.def_word("'exit-interpret ", LitCont::literal(exit_interpret));
|
||||
|
@ -3110,6 +3519,8 @@ void init_words_vm(Dictionary& d, bool enable_debug) {
|
|||
d.def_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel);
|
||||
d.def_stack_word("vmcont, ", interpret_store_vm_cont);
|
||||
d.def_stack_word("vmcont@ ", interpret_fetch_vm_cont);
|
||||
d.def_stack_word("(vmoplen) ", interpret_vmop_len);
|
||||
d.def_stack_word("(vmopdump) ", interpret_vmop_dump);
|
||||
}
|
||||
|
||||
void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]) {
|
||||
|
@ -3128,99 +3539,4 @@ void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* con
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<td::RefInt256, td::RefInt256> numeric_value_ext(std::string s, bool allow_frac = true) {
|
||||
td::RefInt256 num, denom;
|
||||
int res = parse_number(s, num, denom, allow_frac);
|
||||
if (res <= 0) {
|
||||
throw IntError{"-?"};
|
||||
}
|
||||
return std::make_pair(std::move(num), res == 2 ? std::move(denom) : td::RefInt256{});
|
||||
}
|
||||
|
||||
td::RefInt256 numeric_value(std::string s) {
|
||||
td::RefInt256 num, denom;
|
||||
int res = parse_number(s, num, denom, false);
|
||||
if (res != 1) {
|
||||
throw IntError{"-?"};
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
Ref<FiftCont> interpret_compile_execute(IntCtx& ctx) {
|
||||
if (ctx.state > 0) {
|
||||
interpret_compile_internal(ctx.stack);
|
||||
return {};
|
||||
} else {
|
||||
return interpret_execute_internal(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<FiftCont> InterpretCont::run_tail(IntCtx& ctx) const {
|
||||
if (!ctx.get_input() && !ctx.load_next_line()) {
|
||||
return {};
|
||||
}
|
||||
while (true) {
|
||||
if (!ctx.is_sb()) {
|
||||
ctx.skipspc();
|
||||
if (*ctx.get_input()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ctx.state && !ctx.include_depth) {
|
||||
*ctx.output_stream << " ok" << std::endl;
|
||||
}
|
||||
if (!ctx.load_next_line()) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
const char* ptr = ctx.get_input();
|
||||
std::string Word;
|
||||
Word.reserve(128);
|
||||
auto entry = ctx.dictionary->lookup("");
|
||||
std::string entry_word;
|
||||
const char* ptr_end = ptr;
|
||||
while (*ptr && *ptr != ' ' && *ptr != '\t') {
|
||||
Word += *ptr++;
|
||||
auto cur = ctx.dictionary->lookup(Word);
|
||||
if (cur) {
|
||||
entry = cur;
|
||||
entry_word = Word;
|
||||
ptr_end = ptr;
|
||||
}
|
||||
}
|
||||
auto cur = ctx.dictionary->lookup(Word + " ");
|
||||
if (cur || !entry) {
|
||||
entry = std::move(cur);
|
||||
ctx.set_input(ptr);
|
||||
ctx.skipspc();
|
||||
} else {
|
||||
Word = entry_word;
|
||||
ctx.set_input(ptr_end);
|
||||
}
|
||||
ctx.word = Word;
|
||||
static Ref<FiftCont> compile_exec_ref = td::make_ref<CtxTailWord>(interpret_compile_execute);
|
||||
if (!entry) {
|
||||
// numbers
|
||||
auto res = numeric_value_ext(Word);
|
||||
ctx.stack.push(std::move(res.first));
|
||||
if (res.second.not_null()) {
|
||||
ctx.stack.push(std::move(res.second));
|
||||
push_argcount(ctx, 2);
|
||||
} else {
|
||||
push_argcount(ctx, 1);
|
||||
}
|
||||
} else if (!entry->is_active()) {
|
||||
// ordinary word
|
||||
ctx.stack.push_smallint(0);
|
||||
ctx.stack.push({vm::from_object, entry->get_def()});
|
||||
} else {
|
||||
// active word
|
||||
ctx.next = SeqCont::seq(compile_exec_ref, SeqCont::seq(self(), std::move(ctx.next)));
|
||||
return entry->get_def();
|
||||
}
|
||||
exit_interpret->set({vm::from_object, ctx.next});
|
||||
ctx.next = SeqCont::seq(self(), std::move(ctx.next));
|
||||
return compile_exec_ref;
|
||||
}
|
||||
|
||||
} // namespace fift
|
||||
|
|
|
@ -110,3 +110,19 @@ TEST(Fift, test_sort) {
|
|||
TEST(Fift, test_sort2) {
|
||||
run_fift("sort2.fif");
|
||||
}
|
||||
|
||||
TEST(Fift, test_hmap) {
|
||||
run_fift("hmap.fif");
|
||||
}
|
||||
|
||||
TEST(Fift, test_disasm) {
|
||||
run_fift("disasm.fif");
|
||||
}
|
||||
|
||||
TEST(Fift, test_fiftext) {
|
||||
run_fift("fift-ext.fif");
|
||||
}
|
||||
|
||||
TEST(Fift, test_namespaces) {
|
||||
run_fift("namespaces.fif");
|
||||
}
|
||||
|
|
70
crypto/test/fift/disasm.fif
Normal file
70
crypto/test/fift/disasm.fif
Normal file
|
@ -0,0 +1,70 @@
|
|||
"Disasm.fif" include
|
||||
"Asm.fif" include
|
||||
|
||||
<{
|
||||
IF:<{
|
||||
123456789 PUSHINT
|
||||
}>ELSE<{
|
||||
x{12345} PUSHSLICE
|
||||
}>
|
||||
WHILE:<{ ADD }>DO<{
|
||||
10 PUSHINT REPEAT:<{
|
||||
CONT:<{ NOP }>
|
||||
CONT:<{ }>
|
||||
}>
|
||||
}>
|
||||
}>s
|
||||
disasm cr
|
||||
|
||||
x{007A7} disasm cr // Cannot disassemble: x{7}
|
||||
|
||||
<{
|
||||
SWAP
|
||||
s0 s10 XCHG s0 100 s() XCHG
|
||||
s5 PUSH s6 POP
|
||||
s4 s10 PUSH2
|
||||
5 10 BLKSWAP
|
||||
c4 PUSH c5 POP
|
||||
}>s dup dup
|
||||
disasm cr
|
||||
std-disasm disasm cr
|
||||
stack-disasm show-vm-code disasm cr
|
||||
hide-vm-code
|
||||
|
||||
<{
|
||||
1 INT 123456789 INT 123456789123456789123456789 INT
|
||||
<b x{1234} s, b> PUSHREF
|
||||
x{567} PUSHSLICE
|
||||
10 ADDCONST
|
||||
DIV DIVR DIVC
|
||||
RSHIFTC 10 RSHIFTC#
|
||||
20 MODPOW2#
|
||||
30 MULRSHIFTR#
|
||||
LSHIFTDIVC 40 LSHIFT#DIVR
|
||||
}>s
|
||||
disasm cr
|
||||
|
||||
PROGRAM{
|
||||
11 DECLMETHOD foo1
|
||||
12 DECLMETHOD foo2
|
||||
13 DECLMETHOD foo3
|
||||
DECLPROC main
|
||||
foo1 PROC:<{
|
||||
123 ADDCONST
|
||||
}>
|
||||
foo2 PROC:<{
|
||||
OVER
|
||||
5 EQINT
|
||||
IFJMP:<{
|
||||
NIP
|
||||
}>
|
||||
MUL
|
||||
}>
|
||||
foo3 PROC:<{
|
||||
ADD
|
||||
foo2 CALLDICT
|
||||
}>
|
||||
main PROC:<{
|
||||
}>
|
||||
}END>s
|
||||
disasm
|
107
crypto/test/fift/fift-ext.fif
Normal file
107
crypto/test/fift/fift-ext.fif
Normal file
|
@ -0,0 +1,107 @@
|
|||
"FiftExt.fif" include
|
||||
|
||||
// if then
|
||||
{
|
||||
if{ 0> }then{ "Yes" }
|
||||
} : run
|
||||
5 run type cr // Yes
|
||||
-5 run .s // nothing
|
||||
|
||||
// if then else
|
||||
{
|
||||
if{ 0> }then{ "Yes" }else{ "No" }
|
||||
} : run
|
||||
5 run type ." " // Yes
|
||||
-5 run type cr // No
|
||||
|
||||
// if then elseif else
|
||||
{
|
||||
dup dup if{ 20 > }then{ 2drop "AAA" }elseif{ 10 > }then{ drop "BBB" }elseif{ 0> }then{ "CCC" }else{ "DDD" }
|
||||
} : run
|
||||
25 run type ." " // AAA
|
||||
15 run type ." " // BBB
|
||||
5 run type ." " // CCC
|
||||
-5 run type cr // DDD
|
||||
|
||||
// while do
|
||||
1
|
||||
while{ dup 100 < }do{
|
||||
dup .
|
||||
2 *
|
||||
}
|
||||
cr // 1 2 4 8 16 32 64
|
||||
drop
|
||||
|
||||
// repeat until
|
||||
1
|
||||
repeat{
|
||||
dup .
|
||||
3 *
|
||||
}until{ dup 500 > }
|
||||
cr // 1 3 9 27 81 243
|
||||
drop
|
||||
|
||||
// def
|
||||
def foo1 { * + }
|
||||
5 10 20 foo1 . // 205
|
||||
6 100 100 foo1 . cr // 10006
|
||||
|
||||
// defrec
|
||||
defrec fib {
|
||||
if{ dup 2 < }then{
|
||||
drop 1
|
||||
}else{
|
||||
dup 1- fib swap 2- fib +
|
||||
}
|
||||
}
|
||||
8 fib . // 34
|
||||
9 fib . // 55
|
||||
20 fib . cr // 10946
|
||||
|
||||
// [[ ... ]]
|
||||
def foo2 { [[ ."Exec" cr 5 5 * ]] + }
|
||||
."Calling foo2: "
|
||||
100 foo2 . 200 foo2 . cr
|
||||
|
||||
// Nested blocks
|
||||
def sqr { dup * }
|
||||
def power {
|
||||
1 -rot
|
||||
while{ dup 0> }do{
|
||||
if{ dup 1 and }then{
|
||||
-rot tuck * swap rot
|
||||
}
|
||||
2/
|
||||
if{ dup 0> }then{
|
||||
swap sqr swap
|
||||
}
|
||||
}
|
||||
2drop
|
||||
}
|
||||
|
||||
3 0 power . // 1
|
||||
3 1 power . // 3
|
||||
3 4 power . // 81
|
||||
3 12 power . // 531441
|
||||
3 50 power . // 717897987691852588770249
|
||||
cr
|
||||
|
||||
0 while{ dup 2 < }do{
|
||||
."A"
|
||||
0 while{ dup 2 < }do{
|
||||
."B"
|
||||
0 while{ dup 2 < }do{
|
||||
."C"
|
||||
0 while{ dup 2 < }do{
|
||||
."D"
|
||||
0 while{ dup 2 < }do{
|
||||
."E"
|
||||
0 while{ dup 2 < }do{
|
||||
."F"
|
||||
1+ } drop
|
||||
1+ } drop
|
||||
1+ } drop
|
||||
1+ } drop
|
||||
1+ } drop
|
||||
1+ } drop
|
||||
cr // ABCDEFFEFFDEFFEFFCDEFFEFFDEFFEFFBCDEFFEFFDEFFEFFCDEFFEFFDEFFEFFABCDEFFEFFDEFFEFFCDEFFEFFDEFFEFFBCDEFFEFFDEFFEFFCDEFFEFFDEFFEFF
|
69
crypto/test/fift/hmap.fif
Normal file
69
crypto/test/fift/hmap.fif
Normal file
|
@ -0,0 +1,69 @@
|
|||
hmapnew .s // (null)
|
||||
|
||||
// Get from empty hmap
|
||||
dup 10 swap hmap@ null? not abort"expected null"
|
||||
dup "abc" swap hmap@ null? not abort"expected null"
|
||||
dup 10 swap hmap@? abort"expected 0"
|
||||
dup "abc" swap hmap@? abort"expected 0"
|
||||
dup hmapempty? not abort"expected -1"
|
||||
.s cr // (null)
|
||||
|
||||
// Insert values
|
||||
"value-1" 123 rot hmap!+
|
||||
"value-2" "Abc" rot hmap!+
|
||||
"value-3" "xyz" rot hmap!+
|
||||
"value-4" B{78797A} rot hmap!+
|
||||
"value-5" `xyz rot hmap!+
|
||||
"abcdefg" null rot hmap!+ // No effect!
|
||||
dup hmapempty? abort"expected 0"
|
||||
|
||||
// Get values
|
||||
dup 123 swap hmap@ type cr // value-1
|
||||
dup "Abc" swap hmap@ type cr // value-2
|
||||
dup "xyz" swap hmap@ type cr // value-3
|
||||
dup B{78797A} swap hmap@ type cr // value-4
|
||||
dup `xyz swap hmap@ type cr // value-5
|
||||
dup 123 swap hmap@? . type cr // -1 value-1
|
||||
dup "Abc" swap hmap@? . type cr // -1 value-2
|
||||
dup "xyz" swap hmap@? . type cr // -1 value-3
|
||||
dup B{78797A} swap hmap@? . type cr // -1 value-4
|
||||
dup `xyz swap hmap@? . type cr // -1 value-5
|
||||
// Get null
|
||||
dup 1234 swap hmap@ null? not abort"expected null"
|
||||
dup null swap hmap@ null? not abort"expected null"
|
||||
dup 1234 swap hmap@? abort"expected 0"
|
||||
dup null swap hmap@? abort"expected 0"
|
||||
cr
|
||||
|
||||
// hmapforeach
|
||||
constant hmap
|
||||
hmap { .s drop drop -1 } hmapforeach
|
||||
.s drop
|
||||
3 hmap { .s drop drop 1- dup } hmapforeach
|
||||
.s cr drop drop hmap
|
||||
|
||||
// hmap! (insert) and hmap!+ (replace)
|
||||
"value-11" 123 rot hmap!+
|
||||
"value-66" 567 rot hmap!+
|
||||
"value-33" "xyz" rot hmap!
|
||||
"value-77" "qwe" rot hmap! // No effect!
|
||||
dup 123 swap hmap@ type cr // value-11
|
||||
dup 567 swap hmap@ type cr // value-66
|
||||
dup "xyz" swap hmap@ type cr // value-33
|
||||
dup "qwe" swap hmap@ null? not abort"null expected"
|
||||
cr
|
||||
|
||||
// Delete
|
||||
567 swap hmap-
|
||||
789 swap hmap-
|
||||
"qwe" swap hmap-? . // -1
|
||||
"rty" swap hmap-? . // 0
|
||||
`xyz swap hmap@- type // value-5
|
||||
`zyx swap hmap@- null? not abort"null expected"
|
||||
null 123 rot hmap! // same as hmap-
|
||||
null 321 rot hmap!
|
||||
null `xyz rot hmap!+
|
||||
cr
|
||||
constant hmap
|
||||
hmap { .s drop drop -1 } hmapforeach
|
||||
drop
|
29
crypto/test/fift/namespaces.fif
Normal file
29
crypto/test/fift/namespaces.fif
Normal file
|
@ -0,0 +1,29 @@
|
|||
namespace My
|
||||
|
||||
"a" constant a
|
||||
"b" constant b
|
||||
"c" constant c
|
||||
|
||||
a b c .s { drop } 3 times // "a" "b" "c"
|
||||
|
||||
My definitions
|
||||
"b-my" constant b
|
||||
"c-my" constant c
|
||||
"d-my" constant d
|
||||
|
||||
a b c d .s { drop } 4 times // "a" "b-my" "c-my" "d-my"
|
||||
|
||||
Fift definitions
|
||||
a b c .s { drop } 3 times // "a" "b-my" "c-my" "d-my"
|
||||
|
||||
My b My c My d .s { drop } 3 times // "b-my" "c-my" "d-my"
|
||||
a b c .s { drop } 3 times // "a" "b" "c" "d"
|
||||
|
||||
My definitions
|
||||
a b c d .s { drop } 4 times // "a" "b-my" "c-my" "d-my"
|
||||
Fift a Fift b Fift c d .s { drop } 4 times // "a" "b" "c" "d-my"
|
||||
|
||||
Fift definitions
|
||||
cr
|
||||
My-wordlist @
|
||||
{ drop type -1 } hmapforeach drop cr // "b " "d " "c "
|
|
@ -38,8 +38,8 @@ int exec_push_tinyint4(VmState* st, unsigned args) {
|
|||
|
||||
std::string dump_push_tinyint4(CellSlice&, unsigned args) {
|
||||
int x = (int)((args + 5) & 15) - 5;
|
||||
std::ostringstream os{"PUSHINT "};
|
||||
os << x;
|
||||
std::ostringstream os;
|
||||
os << "PUSHINT " << x;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
@ -53,8 +53,8 @@ int exec_push_tinyint8(VmState* st, unsigned args) {
|
|||
|
||||
std::string dump_op_tinyint8(const char* op_prefix, CellSlice&, unsigned args) {
|
||||
int x = (signed char)args;
|
||||
std::ostringstream os{op_prefix};
|
||||
os << x;
|
||||
std::ostringstream os;
|
||||
os << op_prefix << x;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,8 @@ int exec_push_smallint(VmState* st, unsigned args) {
|
|||
|
||||
std::string dump_push_smallint(CellSlice&, unsigned args) {
|
||||
int x = (short)args;
|
||||
std::ostringstream os{"PUSHINT "};
|
||||
os << x;
|
||||
std::ostringstream os;
|
||||
os << "PUSHINT " << x;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
@ -93,8 +93,8 @@ std::string dump_push_int(CellSlice& cs, unsigned args, int pfx_bits) {
|
|||
}
|
||||
cs.advance(pfx_bits);
|
||||
td::RefInt256 x = cs.fetch_int256(3 + l * 8);
|
||||
std::ostringstream os{"PUSHINT "};
|
||||
os << x;
|
||||
std::ostringstream os;
|
||||
os << "PUSHINT " << x;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
@ -302,7 +302,7 @@ std::string dump_divmod(CellSlice&, unsigned args, bool quiet) {
|
|||
if (quiet) {
|
||||
s = "Q" + s;
|
||||
}
|
||||
return s + "FRC"[round_mode];
|
||||
return round_mode ? s + "FRC"[round_mode] : s;
|
||||
}
|
||||
|
||||
int exec_shrmod(VmState* st, unsigned args, int mode) {
|
||||
|
@ -352,28 +352,28 @@ std::string dump_shrmod(CellSlice&, unsigned args, int mode) {
|
|||
if (!(args & 12) || round_mode == 3) {
|
||||
return "";
|
||||
}
|
||||
std::string s;
|
||||
std::ostringstream os;
|
||||
if (mode & 1) {
|
||||
os << 'Q';
|
||||
}
|
||||
switch (args & 12) {
|
||||
case 4:
|
||||
s = "RSHIFT";
|
||||
os << "RSHIFT";
|
||||
break;
|
||||
case 8:
|
||||
s = "MODPOW2";
|
||||
os << "MODPOW2";
|
||||
break;
|
||||
case 12:
|
||||
s = "RSHIFTMOD";
|
||||
os << "RSHIFTMOD";
|
||||
break;
|
||||
}
|
||||
if (mode & 1) {
|
||||
s = "Q" + s;
|
||||
if (round_mode) {
|
||||
os << "FRC"[round_mode];
|
||||
}
|
||||
s += "FRC"[round_mode];
|
||||
if (mode & 2) {
|
||||
char buff[8];
|
||||
sprintf(buff, " %d", y);
|
||||
s += buff;
|
||||
os << ' ' << y;
|
||||
}
|
||||
return s;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
int exec_muldivmod(VmState* st, unsigned args, int quiet) {
|
||||
|
@ -417,7 +417,7 @@ std::string dump_muldivmod(CellSlice&, unsigned args, bool quiet) {
|
|||
if (quiet) {
|
||||
s = "Q" + s;
|
||||
}
|
||||
return s + "FRC"[round_mode];
|
||||
return round_mode ? s + "FRC"[round_mode] : s;
|
||||
}
|
||||
|
||||
int exec_mulshrmod(VmState* st, unsigned args, int mode) {
|
||||
|
@ -474,28 +474,28 @@ std::string dump_mulshrmod(CellSlice&, unsigned args, int mode) {
|
|||
if (!(args & 12) || round_mode == 3) {
|
||||
return "";
|
||||
}
|
||||
std::string s;
|
||||
std::ostringstream os;
|
||||
if (mode & 1) {
|
||||
os << 'Q';
|
||||
}
|
||||
switch (args & 12) {
|
||||
case 4:
|
||||
s = "MULRSHIFT";
|
||||
os << "MULRSHIFT";
|
||||
break;
|
||||
case 8:
|
||||
s = "MULMODPOW2";
|
||||
os << "MULMODPOW2";
|
||||
break;
|
||||
case 12:
|
||||
s = "MULRSHIFTMOD";
|
||||
os << "MULRSHIFTMOD";
|
||||
break;
|
||||
}
|
||||
if (mode & 1) {
|
||||
s = "Q" + s;
|
||||
if (round_mode) {
|
||||
os << "FRC"[round_mode];
|
||||
}
|
||||
s += "FRC"[round_mode];
|
||||
if (mode & 2) {
|
||||
char buff[8];
|
||||
sprintf(buff, " %d", y);
|
||||
s += buff;
|
||||
os << ' ' << y;
|
||||
}
|
||||
return s;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
int exec_shldivmod(VmState* st, unsigned args, int mode) {
|
||||
|
@ -542,19 +542,25 @@ int exec_shldivmod(VmState* st, unsigned args, int mode) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::string dump_shldivmod(CellSlice&, unsigned args, bool quiet) {
|
||||
std::string dump_shldivmod(CellSlice&, unsigned args, int mode) {
|
||||
int y = -1;
|
||||
if (mode & 2) {
|
||||
y = (args & 0xff) + 1;
|
||||
args >>= 8;
|
||||
}
|
||||
int round_mode = (int)(args & 3);
|
||||
if (!(args & 12) || round_mode == 3) {
|
||||
return "";
|
||||
}
|
||||
std::string s = (args & 4) ? "LSHIFTDIV" : "LSHIFT";
|
||||
if (args & 8) {
|
||||
s += "MOD";
|
||||
std::ostringstream os;
|
||||
os << (mode & 1 ? "Q" : "") << (args & 4 ? "LSHIFTDIV" : "LSHIFT") << (args & 8 ? "MOD" : "");
|
||||
if (round_mode) {
|
||||
os << "FRC"[round_mode];
|
||||
}
|
||||
if (quiet) {
|
||||
s = "Q" + s;
|
||||
if (y >= 0) {
|
||||
os << ' ' << y;
|
||||
}
|
||||
return s + "FRC"[round_mode];
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void register_div_ops(OpcodeTable& cp0) {
|
||||
|
|
|
@ -30,7 +30,7 @@ class Box : public td::CntObject {
|
|||
Box(const Box&) = default;
|
||||
Box(Box&&) = default;
|
||||
template <typename... Args>
|
||||
Box(Args... args) : data_{std::move(args...)} {
|
||||
Box(Args&&... args) : data_{std::forward<Args>(args)...} {
|
||||
}
|
||||
~Box() override = default;
|
||||
Box(const StackEntry& data) : data_(data) {
|
||||
|
|
|
@ -103,7 +103,8 @@ std::string dump_push_slice_common(CellSlice& cs, unsigned data_bits, unsigned r
|
|||
cs.advance(pfx_bits);
|
||||
auto slice = cs.fetch_subslice(data_bits, refs);
|
||||
slice.unique_write().remove_trailing();
|
||||
std::ostringstream os{name};
|
||||
std::ostringstream os;
|
||||
os << name;
|
||||
slice->dump_hex(os, 1, false);
|
||||
return os.str();
|
||||
}
|
||||
|
@ -188,7 +189,8 @@ std::string dump_push_cont(CellSlice& cs, unsigned args, int pfx_bits) {
|
|||
}
|
||||
cs.advance(pfx_bits);
|
||||
auto slice = cs.fetch_subslice(data_bits, refs);
|
||||
std::ostringstream os{"PUSHCONT "};
|
||||
std::ostringstream os;
|
||||
os << "PUSHCONT ";
|
||||
slice->dump_hex(os, 1, false);
|
||||
return os.str();
|
||||
}
|
||||
|
@ -219,7 +221,8 @@ std::string dump_push_cont_simple(CellSlice& cs, unsigned args, int pfx_bits) {
|
|||
}
|
||||
cs.advance(pfx_bits);
|
||||
auto slice = cs.fetch_subslice(data_bits);
|
||||
std::ostringstream os{"PUSHCONT "};
|
||||
std::ostringstream os;
|
||||
os << "PUSHCONT ";
|
||||
slice->dump_hex(os, 1, false);
|
||||
return os.str();
|
||||
}
|
||||
|
@ -1060,8 +1063,8 @@ int exec_load_int_fixed2(VmState* st, unsigned args) {
|
|||
}
|
||||
|
||||
std::string dump_load_int_fixed2(CellSlice&, unsigned args) {
|
||||
std::ostringstream os{args & 0x200 ? "PLD" : "LD"};
|
||||
os << (args & 0x100 ? 'U' : 'I');
|
||||
std::ostringstream os;
|
||||
os << (args & 0x200 ? "PLD" : "LD") << (args & 0x100 ? 'U' : 'I');
|
||||
if (args & 0x400) {
|
||||
os << 'Q';
|
||||
}
|
||||
|
@ -1081,9 +1084,9 @@ int exec_preload_uint_fixed_0e(VmState* st, unsigned args) {
|
|||
}
|
||||
|
||||
std::string dump_preload_uint_fixed_0e(CellSlice&, unsigned args) {
|
||||
std::ostringstream os{"PLDUZ "};
|
||||
std::ostringstream os;
|
||||
unsigned bits = ((args & 7) + 1) << 5;
|
||||
os << bits;
|
||||
os << "PLDUZ " << bits;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
@ -1108,7 +1111,8 @@ int exec_load_slice_fixed2(VmState* st, unsigned args) {
|
|||
|
||||
std::string dump_load_slice_fixed2(CellSlice&, unsigned args) {
|
||||
unsigned bits = (args & 0xff) + 1;
|
||||
std::ostringstream os{args & 0x100 ? "PLDSLICE" : "LDSLICE"};
|
||||
std::ostringstream os;
|
||||
os << (args & 0x100 ? "PLDSLICE" : "LDSLICE");
|
||||
if (args & 0x200) {
|
||||
os << 'Q';
|
||||
}
|
||||
|
|
|
@ -378,8 +378,8 @@ int exec_if_bit_jmp(VmState* st, unsigned args) {
|
|||
}
|
||||
|
||||
std::string dump_if_bit_jmp(CellSlice& cs, unsigned args) {
|
||||
std::ostringstream os{args & 0x20 ? "IFN" : " IF"};
|
||||
os << "BITJMP " << (args & 0x1f);
|
||||
std::ostringstream os;
|
||||
os << "IF" << (args & 0x20 ? "N" : "") << "BITJMP " << (args & 0x1f);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
@ -408,8 +408,8 @@ std::string dump_if_bit_jmpref(CellSlice& cs, unsigned args, int pfx_bits) {
|
|||
}
|
||||
cs.advance(pfx_bits);
|
||||
cs.advance_refs(1);
|
||||
std::ostringstream os{args & 0x20 ? "IFN" : " IF"};
|
||||
os << "BITJMPREF " << (args & 0x1f);
|
||||
std::ostringstream os;
|
||||
os << "IF" << (args & 0x20 ? "N" : "") << "BITJMPREF " << (args & 0x1f);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
@ -597,8 +597,8 @@ int exec_setcontargs(VmState* st, unsigned args) {
|
|||
|
||||
std::string dump_setcontargs(CellSlice& cs, unsigned args, const char* name) {
|
||||
int copy = (args >> 4) & 15, more = ((args + 1) & 15) - 1;
|
||||
std::ostringstream os{name};
|
||||
os << ' ' << copy << ',' << more;
|
||||
std::ostringstream os;
|
||||
os << name << ' ' << copy << ',' << more;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
@ -1065,8 +1065,8 @@ std::string dump_throw_any(CellSlice& cs, unsigned args) {
|
|||
bool has_param = args & 1;
|
||||
bool has_cond = args & 6;
|
||||
bool throw_cond = args & 2;
|
||||
std::ostringstream os{has_param ? "THROWARG" : "THROW"};
|
||||
os << "ANY";
|
||||
std::ostringstream os;
|
||||
os << "THROW" << (has_param ? "ARG" : "") << "ANY";
|
||||
if (has_cond) {
|
||||
os << (throw_cond ? "IF" : "IFNOT");
|
||||
}
|
||||
|
|
|
@ -172,7 +172,8 @@ int exec_load_dict(VmState* st, unsigned args) {
|
|||
}
|
||||
|
||||
std::string dump_dictop(unsigned args, const char* name) {
|
||||
std::ostringstream os{"DICT"};
|
||||
std::ostringstream os;
|
||||
os << "DICT";
|
||||
if (args & 4) {
|
||||
os << (args & 2 ? 'U' : 'I');
|
||||
}
|
||||
|
@ -184,7 +185,8 @@ std::string dump_dictop(unsigned args, const char* name) {
|
|||
}
|
||||
|
||||
std::string dump_dictop2(unsigned args, const char* name) {
|
||||
std::ostringstream os{"DICT"};
|
||||
std::ostringstream os;
|
||||
os << "DICT";
|
||||
if (args & 2) {
|
||||
os << (args & 1 ? 'U' : 'I');
|
||||
}
|
||||
|
@ -193,7 +195,8 @@ std::string dump_dictop2(unsigned args, const char* name) {
|
|||
}
|
||||
|
||||
std::string dump_subdictop2(unsigned args, const char* name) {
|
||||
std::ostringstream os{"SUBDICT"};
|
||||
std::ostringstream os;
|
||||
os << "SUBDICT";
|
||||
if (args & 2) {
|
||||
os << (args & 1 ? 'U' : 'I');
|
||||
}
|
||||
|
@ -508,7 +511,8 @@ int exec_dict_getmin(VmState* st, unsigned args) {
|
|||
}
|
||||
|
||||
std::string dump_dictop_getnear(CellSlice& cs, unsigned args) {
|
||||
std::ostringstream os{"DICT"};
|
||||
std::ostringstream os;
|
||||
os << "DICT";
|
||||
if (args & 8) {
|
||||
os << (args & 4 ? 'U' : 'I');
|
||||
}
|
||||
|
@ -637,8 +641,8 @@ std::string dump_push_const_dict(CellSlice& cs, int pfx_bits, const char* name)
|
|||
cs.advance(pfx_bits - 11);
|
||||
auto slice = cs.fetch_subslice(1, 1);
|
||||
int n = (int)cs.fetch_ulong(10);
|
||||
std::ostringstream os{name};
|
||||
os << ' ' << n << " (";
|
||||
std::ostringstream os;
|
||||
os << name << ' ' << n << " (";
|
||||
slice->dump_hex(os, false);
|
||||
os << ')';
|
||||
return os.str();
|
||||
|
|
|
@ -357,32 +357,32 @@ using namespace std::placeholders;
|
|||
|
||||
dump_arg_instr_func_t dump_1sr(std::string prefix, std::string suffix) {
|
||||
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << 's' << (args & 15) << suffix;
|
||||
std::ostringstream os;
|
||||
os << prefix << 's' << (args & 15) << suffix;
|
||||
return os.str();
|
||||
};
|
||||
}
|
||||
|
||||
dump_arg_instr_func_t dump_1sr_l(std::string prefix, std::string suffix) {
|
||||
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << 's' << (args & 255) << suffix;
|
||||
std::ostringstream os;
|
||||
os << prefix << 's' << (args & 255) << suffix;
|
||||
return os.str();
|
||||
};
|
||||
}
|
||||
|
||||
dump_arg_instr_func_t dump_2sr(std::string prefix, std::string suffix) {
|
||||
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << 's' << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
|
||||
std::ostringstream os;
|
||||
os << prefix << 's' << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
|
||||
return os.str();
|
||||
};
|
||||
}
|
||||
|
||||
dump_arg_instr_func_t dump_2sr_adj(unsigned adj, std::string prefix, std::string suffix) {
|
||||
return [adj, prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << 's' << (int)((args >> 4) & 15) - (int)((adj >> 4) & 15) << ",s" << (int)(args & 15) - (int)(adj & 15)
|
||||
std::ostringstream os;
|
||||
os << prefix << 's' << (int)((args >> 4) & 15) - (int)((adj >> 4) & 15) << ",s" << (int)(args & 15) - (int)(adj & 15)
|
||||
<< suffix;
|
||||
return os.str();
|
||||
};
|
||||
|
@ -390,16 +390,16 @@ dump_arg_instr_func_t dump_2sr_adj(unsigned adj, std::string prefix, std::string
|
|||
|
||||
dump_arg_instr_func_t dump_3sr(std::string prefix, std::string suffix) {
|
||||
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << 's' << ((args >> 8) & 15) << ",s" << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
|
||||
std::ostringstream os;
|
||||
os << prefix << 's' << ((args >> 8) & 15) << ",s" << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
|
||||
return os.str();
|
||||
};
|
||||
}
|
||||
|
||||
dump_arg_instr_func_t dump_3sr_adj(unsigned adj, std::string prefix, std::string suffix) {
|
||||
return [adj, prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << 's' << (int)((args >> 8) & 15) - (int)((adj >> 8) & 15) << ",s"
|
||||
std::ostringstream os;
|
||||
os << prefix << 's' << (int)((args >> 8) & 15) - (int)((adj >> 8) & 15) << ",s"
|
||||
<< (int)((args >> 4) & 15) - (int)((adj >> 4) & 15) << ",s" << (int)(args & 15) - (int)(adj & 15) << suffix;
|
||||
return os.str();
|
||||
};
|
||||
|
@ -407,40 +407,40 @@ dump_arg_instr_func_t dump_3sr_adj(unsigned adj, std::string prefix, std::string
|
|||
|
||||
dump_arg_instr_func_t dump_1c(std::string prefix, std::string suffix) {
|
||||
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << (args & 15) << suffix;
|
||||
std::ostringstream os;
|
||||
os << prefix << (args & 15) << suffix;
|
||||
return os.str();
|
||||
};
|
||||
}
|
||||
|
||||
dump_arg_instr_func_t dump_1c_l_add(int adj, std::string prefix, std::string suffix) {
|
||||
return [adj, prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << (int)(args & 255) + adj << suffix;
|
||||
std::ostringstream os;
|
||||
os << prefix << (int)(args & 255) + adj << suffix;
|
||||
return os.str();
|
||||
};
|
||||
}
|
||||
|
||||
dump_arg_instr_func_t dump_1c_and(unsigned mask, std::string prefix, std::string suffix) {
|
||||
return [mask, prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << (args & mask) << suffix;
|
||||
std::ostringstream os;
|
||||
os << prefix << (args & mask) << suffix;
|
||||
return os.str();
|
||||
};
|
||||
}
|
||||
|
||||
dump_arg_instr_func_t dump_2c(std::string prefix, std::string interfix, std::string suffix) {
|
||||
return [prefix, interfix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << ((args >> 4) & 15) << interfix << (args & 15) << suffix;
|
||||
std::ostringstream os;
|
||||
os << prefix << ((args >> 4) & 15) << interfix << (args & 15) << suffix;
|
||||
return os.str();
|
||||
};
|
||||
}
|
||||
|
||||
dump_arg_instr_func_t dump_2c_add(unsigned add, std::string prefix, std::string interfix, std::string suffix) {
|
||||
return [add, prefix, interfix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||
std::ostringstream os{prefix};
|
||||
os << ((args >> 4) & 15) + ((add >> 4) & 15) << interfix << (args & 15) + (add & 15) << suffix;
|
||||
std::ostringstream os;
|
||||
os << prefix << ((args >> 4) & 15) + ((add >> 4) & 15) << interfix << (args & 15) + (add & 15) << suffix;
|
||||
return os.str();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -103,6 +103,9 @@ class StackEntry {
|
|||
}
|
||||
StackEntry(td::RefInt256 int_ref) : ref(std::move(int_ref)), tp(t_int) {
|
||||
}
|
||||
StackEntry(Ref<Cnt<std::string>> str_ref, bool bytes = false)
|
||||
: ref(std::move(str_ref)), tp(bytes ? t_bytes : t_string) {
|
||||
}
|
||||
StackEntry(std::string str, bool bytes = false) : ref(), tp(bytes ? t_bytes : t_string) {
|
||||
ref = Ref<Cnt<std::string>>{true, std::move(str)};
|
||||
}
|
||||
|
|
|
@ -75,8 +75,8 @@ std::string dump_xchg(CellSlice&, unsigned args) {
|
|||
if (!x || x >= y) {
|
||||
return "";
|
||||
}
|
||||
std::ostringstream os{"XCHG s"};
|
||||
os << x << ",s" << y;
|
||||
std::ostringstream os;
|
||||
os << "XCHG s" << x << ",s" << y;
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ int exec_xchg1(VmState* st, unsigned args) {
|
|||
|
||||
int exec_dup(VmState* st) {
|
||||
Stack& stack = st->get_stack();
|
||||
VM_LOG(st) << "execute DUP\n";
|
||||
VM_LOG(st) << "execute DUP";
|
||||
stack.check_underflow(1);
|
||||
stack.push(stack.fetch(0));
|
||||
return 0;
|
||||
|
@ -100,7 +100,7 @@ int exec_dup(VmState* st) {
|
|||
|
||||
int exec_over(VmState* st) {
|
||||
Stack& stack = st->get_stack();
|
||||
VM_LOG(st) << "execute OVER\n";
|
||||
VM_LOG(st) << "execute OVER";
|
||||
stack.check_underflow(2);
|
||||
stack.push(stack.fetch(1));
|
||||
return 0;
|
||||
|
@ -126,7 +126,7 @@ int exec_push_l(VmState* st, unsigned args) {
|
|||
|
||||
int exec_drop(VmState* st) {
|
||||
Stack& stack = st->get_stack();
|
||||
VM_LOG(st) << "execute DROP\n";
|
||||
VM_LOG(st) << "execute DROP";
|
||||
stack.check_underflow(1);
|
||||
stack.pop();
|
||||
return 0;
|
||||
|
@ -134,7 +134,7 @@ int exec_drop(VmState* st) {
|
|||
|
||||
int exec_nip(VmState* st) {
|
||||
Stack& stack = st->get_stack();
|
||||
VM_LOG(st) << "execute NIP\n";
|
||||
VM_LOG(st) << "execute NIP";
|
||||
stack.check_underflow(2);
|
||||
stack.pop(stack[1]);
|
||||
return 0;
|
||||
|
|
|
@ -8,7 +8,11 @@ Test_Fift_bug_ufits_default 51bf5a9f1ed7633a193f6fdd17a7a3af8e032dfe72a9669c85e8
|
|||
Test_Fift_contfrac_default 09ebce5c91bcb70696c6fb6981d82dc3b9e3444dab608a7a1b044c0ddd778a96
|
||||
Test_Fift_test_default 4e44b3382963ec89f7b5c8f2ebd85da3bc8aebad5b49f5b11b14075061477b4d
|
||||
Test_Fift_test_dict_default a9c8cbcfdece5573185022cea07f59f1bc404e5d879e5157a5745757f8ee0525
|
||||
Test_Fift_test_disasm_default dacaa555f5f217b2373e01e3bcd59634e480f5759dcc43edbdef35273ae38492
|
||||
Test_Fift_test_fiftext_default 2b0db5d4d4bfbc705b959cc787540d7b3a21a71469eac54756e76953f0d9afca
|
||||
Test_Fift_test_fixed_default 278a19d56b773102caf5c1fe2997ea6c8d0d9e720eff8503feede6398a197eec
|
||||
Test_Fift_test_hmap_default c269246882039824bb5822e896c3e6e82ef8e1251b6b251f5af8ea9fb8d05067
|
||||
Test_Fift_test_namespaces_default e6419619c51332fb5e8bf22043ef415db686c47fe24f03061e5ad831014e7c6c
|
||||
Test_Fift_test_sort2_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a
|
||||
Test_Fift_test_sort_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a
|
||||
Test_Fift_testvm2_default 8a6e35fc0224398be9d2de39d31c86ea96965ef1eca2aa9e0af2303150ed4a7b
|
||||
|
|
Loading…
Reference in a new issue