mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +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/Dictionary.cpp
|
||||||
fift/Fift.cpp
|
fift/Fift.cpp
|
||||||
fift/IntCtx.cpp
|
fift/IntCtx.cpp
|
||||||
|
fift/HashMap.cpp
|
||||||
fift/Continuation.cpp
|
fift/Continuation.cpp
|
||||||
fift/SourceLookup.cpp
|
fift/SourceLookup.cpp
|
||||||
fift/utils.cpp
|
fift/utils.cpp
|
||||||
|
@ -151,6 +152,7 @@ set(FIFT_SOURCE
|
||||||
fift/Dictionary.h
|
fift/Dictionary.h
|
||||||
fift/Fift.h
|
fift/Fift.h
|
||||||
fift/IntCtx.h
|
fift/IntCtx.h
|
||||||
|
fift/HashMap.h
|
||||||
fift/Continuation.h
|
fift/Continuation.h
|
||||||
fift/SourceLookup.h
|
fift/SourceLookup.h
|
||||||
fift/utils.h
|
fift/utils.h
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include "fift/Fift.h"
|
#include "fift/Fift.h"
|
||||||
#include "fift/Dictionary.h"
|
#include "fift/Dictionary.h"
|
||||||
#include "fift/SourceLookup.h"
|
#include "fift/SourceLookup.h"
|
||||||
|
#include "fift/IntCtx.h"
|
||||||
#include "fift/words.h"
|
#include "fift/words.h"
|
||||||
|
|
||||||
#include "td/utils/logging.h"
|
#include "td/utils/logging.h"
|
||||||
|
@ -866,8 +867,9 @@ int main(int argc, char* const argv[]) {
|
||||||
case 'v':
|
case 'v':
|
||||||
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
|
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
std::cout << "create-state build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
|
std::cout << "create-state build information: [ Commit: " << GitMetadata::CommitSHA1()
|
||||||
|
<< ", Date: " << GitMetadata::CommitDate() << "]\n";
|
||||||
std::exit(0);
|
std::exit(0);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace fift {
|
||||||
//
|
//
|
||||||
bool FiftCont::print_dict_name(std::ostream& os, const IntCtx& ctx) const {
|
bool FiftCont::print_dict_name(std::ostream& os, const IntCtx& ctx) const {
|
||||||
std::string word_name;
|
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() == ' ') {
|
if (word_name.size() && word_name.back() == ' ') {
|
||||||
word_name.pop_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 FiftCont::get_dict_name(const IntCtx& ctx) const {
|
||||||
std::string word_name;
|
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() == ' ') {
|
if (word_name.size() && word_name.back() == ' ') {
|
||||||
word_name.pop_back();
|
word_name.pop_back();
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,140 @@ bool FiftCont::dump(std::ostream& os, const IntCtx& ctx) const {
|
||||||
return ok;
|
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
|
// QuitCont
|
||||||
//
|
//
|
||||||
|
@ -295,12 +429,15 @@ bool GenericLitCont::print_name(std::ostream& os, const IntCtx& ctx) const {
|
||||||
bool sp = false;
|
bool sp = false;
|
||||||
for (auto entry : list) {
|
for (auto entry : list) {
|
||||||
if (sp) {
|
if (sp) {
|
||||||
os << sp;
|
os << ' ';
|
||||||
}
|
}
|
||||||
sp = true;
|
sp = true;
|
||||||
int tp = entry.type();
|
int tp = entry.type();
|
||||||
if (entry.is_int() || entry.is(vm::StackEntry::t_string) || entry.is(vm::StackEntry::t_bytes)) {
|
if (entry.is_int() || entry.is(vm::StackEntry::t_string) || entry.is(vm::StackEntry::t_bytes)) {
|
||||||
entry.dump(os);
|
entry.dump(os);
|
||||||
|
} else if (entry.is_atom()) {
|
||||||
|
os << '`';
|
||||||
|
entry.dump(os);
|
||||||
} else {
|
} else {
|
||||||
auto cont_lit = entry.as_object<FiftCont>();
|
auto cont_lit = entry.as_object<FiftCont>();
|
||||||
if (cont_lit.not_null()) {
|
if (cont_lit.not_null()) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
Copyright 2020 Telegram Systems LLP
|
Copyright 2020 Telegram Systems LLP
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <functional>
|
||||||
#include "common/refcnt.hpp"
|
#include "common/refcnt.hpp"
|
||||||
#include "common/refint.h"
|
#include "common/refint.h"
|
||||||
#include "vm/stack.hpp"
|
#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 {
|
class QuitCont : public FiftCont {
|
||||||
int exit_code;
|
int exit_code;
|
||||||
|
|
||||||
|
|
|
@ -17,143 +17,10 @@
|
||||||
Copyright 2017-2020 Telegram Systems LLP
|
Copyright 2017-2020 Telegram Systems LLP
|
||||||
*/
|
*/
|
||||||
#include "Dictionary.h"
|
#include "Dictionary.h"
|
||||||
|
#include "IntCtx.h"
|
||||||
|
|
||||||
namespace fift {
|
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
|
// 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(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
|
// Dictionary
|
||||||
//
|
//
|
||||||
DictEntry* Dictionary::lookup(td::Slice name) {
|
DictEntry Dictionary::lookup(std::string name) const {
|
||||||
auto it = words_.find(name);
|
return DictEntry::create_from(words().get(name));
|
||||||
if (it == words_.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return &it->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dictionary::def_ctx_word(std::string name, CtxWordFunc func) {
|
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) {
|
void Dictionary::def_word(std::string name, DictEntry word) {
|
||||||
auto res = words_.emplace(name, std::move(word));
|
auto dict = words();
|
||||||
LOG_IF(FATAL, !res.second) << "Cannot redefine word: " << name;
|
dict.set(std::move(name), vm::StackEntry(std::move(word)));
|
||||||
|
set_words(dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dictionary::undef_word(td::Slice name) {
|
void Dictionary::undef_word(std::string name) {
|
||||||
auto it = words_.find(name);
|
auto dict = words();
|
||||||
if (it == words_.end()) {
|
if (dict.remove(name)) {
|
||||||
return;
|
set_words(dict);
|
||||||
}
|
}
|
||||||
words_.erase(it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Dictionary::lookup_def(const FiftCont* cont, std::string* word_ptr) const {
|
bool Dictionary::lookup_def(const FiftCont* cont, std::string* word_ptr) const {
|
||||||
if (!cont) {
|
if (!cont) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (const auto& entry : words_) {
|
for (auto entry : words()) {
|
||||||
if (entry.second.get_def().get() == cont) {
|
auto val = DictEntry::create_from(entry.value());
|
||||||
|
if (val.get_def().get() == cont && entry.key().is_string()) {
|
||||||
if (word_ptr) {
|
if (word_ptr) {
|
||||||
*word_ptr = entry.first;
|
*word_ptr = vm::StackEntry(entry.key()).as_string();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -223,35 +125,4 @@ bool Dictionary::lookup_def(const FiftCont* cont, std::string* word_ptr) const {
|
||||||
return false;
|
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
|
} // namespace fift
|
||||||
|
|
|
@ -17,115 +17,27 @@
|
||||||
Copyright 2017-2020 Telegram Systems LLP
|
Copyright 2017-2020 Telegram Systems LLP
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "IntCtx.h"
|
|
||||||
#include "Continuation.h"
|
#include "Continuation.h"
|
||||||
|
#include "HashMap.h"
|
||||||
|
#include "vm/box.hpp"
|
||||||
|
|
||||||
namespace fift {
|
namespace fift {
|
||||||
using td::Ref;
|
using td::Ref;
|
||||||
|
|
||||||
|
struct IntCtx;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* WORD CLASSES
|
* 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 {
|
class DictEntry {
|
||||||
Ref<FiftCont> def;
|
Ref<FiftCont> def;
|
||||||
bool active;
|
bool active{false};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DictEntry() = delete;
|
DictEntry() = default;
|
||||||
DictEntry(const DictEntry& ref) = default;
|
DictEntry(const DictEntry& ref) = default;
|
||||||
DictEntry(DictEntry&& ref) = default;
|
DictEntry(DictEntry&& ref) = default;
|
||||||
DictEntry(Ref<FiftCont> _def, bool _act = false) : def(std::move(_def)), active(_act) {
|
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(std::vector<Ref<FiftCont>>&& word_list);
|
||||||
DictEntry& operator=(const DictEntry&) = default;
|
DictEntry& operator=(const DictEntry&) = default;
|
||||||
DictEntry& operator=(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& {
|
Ref<FiftCont> get_def() const& {
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
@ -146,16 +61,17 @@ class DictEntry {
|
||||||
bool is_active() const {
|
bool is_active() const {
|
||||||
return active;
|
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
|
* DICTIONARIES
|
||||||
|
@ -164,37 +80,52 @@ DictEntry::DictEntry(std::vector<Ref<FiftCont>>&& word_list) : def(Ref<WordList>
|
||||||
|
|
||||||
class Dictionary {
|
class Dictionary {
|
||||||
public:
|
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_word(std::string name, CtxWordFunc func);
|
||||||
void def_ctx_tail_word(std::string name, CtxTailWordFunc func);
|
void def_ctx_tail_word(std::string name, CtxTailWordFunc func);
|
||||||
void def_active_word(std::string name, CtxWordFunc func);
|
void def_active_word(std::string name, CtxWordFunc func);
|
||||||
void def_stack_word(std::string name, StackWordFunc func);
|
void def_stack_word(std::string name, StackWordFunc func);
|
||||||
void def_word(std::string name, DictEntry word);
|
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(const FiftCont* cont, std::string* word_ptr = nullptr) const;
|
||||||
bool lookup_def(Ref<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);
|
return lookup_def(cont.get(), word_ptr);
|
||||||
}
|
}
|
||||||
auto begin() const {
|
auto begin() const {
|
||||||
return words_.begin();
|
return words().begin();
|
||||||
}
|
}
|
||||||
auto end() const {
|
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:
|
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
|
} // namespace fift
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
Copyright 2017-2020 Telegram Systems LLP
|
Copyright 2017-2020 Telegram Systems LLP
|
||||||
*/
|
*/
|
||||||
#include "Fift.h"
|
#include "Fift.h"
|
||||||
|
#include "IntCtx.h"
|
||||||
#include "words.h"
|
#include "words.h"
|
||||||
|
|
||||||
#include "td/utils/PathView.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) {
|
td::Result<int> Fift::do_interpret(IntCtx& ctx, bool is_interactive) {
|
||||||
ctx.ton_db = &config_.ton_db;
|
ctx.ton_db = &config_.ton_db;
|
||||||
ctx.source_lookup = &config_.source_lookup;
|
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.output_stream = config_.output_stream;
|
||||||
ctx.error_stream = config_.error_stream;
|
ctx.error_stream = config_.error_stream;
|
||||||
if (!ctx.output_stream) {
|
if (!ctx.output_stream) {
|
||||||
|
@ -71,7 +71,7 @@ td::Result<int> Fift::do_interpret(IntCtx& ctx, bool is_interactive) {
|
||||||
ctx.top_ctx();
|
ctx.top_ctx();
|
||||||
ctx.clear_error();
|
ctx.clear_error();
|
||||||
ctx.stack.clear();
|
ctx.stack.clear();
|
||||||
ctx.load_next_line();
|
ctx.parser->load_next_line();
|
||||||
continue;
|
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 {
|
namespace fift {
|
||||||
|
|
||||||
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx) {
|
td::StringBuilder& operator<<(td::StringBuilder& os, const ParseCtx& ctx) {
|
||||||
if (ctx.include_depth) {
|
if (ctx.include_depth) {
|
||||||
return os << ctx.filename << ":" << ctx.line_no << ": ";
|
return os << ctx.filename << ":" << ctx.line_no << ": ";
|
||||||
} else {
|
} 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();
|
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);
|
*p = static_cast<unsigned char>((*p & ~mask) | cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
IntCtx::Savepoint::Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
|
bool ParseCtx::load_next_line() {
|
||||||
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() {
|
|
||||||
if (!std::getline(*input_stream, str)) {
|
if (!std::getline(*input_stream, str)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -145,11 +79,11 @@ bool IntCtx::load_next_line() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IntCtx::is_sb() const {
|
bool ParseCtx::is_sb() const {
|
||||||
return !eof() && line_no == 1 && *input_ptr == '#' && input_ptr[1] == '!';
|
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();
|
load_next_line_ifreq();
|
||||||
auto ptr = input_ptr;
|
auto ptr = input_ptr;
|
||||||
while (*ptr && *ptr != delim) {
|
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);
|
skipspc(true);
|
||||||
auto ptr = input_ptr;
|
auto ptr = input_ptr;
|
||||||
while (*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '\r') {
|
while (*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '\r') {
|
||||||
|
@ -179,7 +113,7 @@ td::Slice IntCtx::scan_word() {
|
||||||
return td::Slice{ptr, ptr2};
|
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);
|
skipspc(true);
|
||||||
auto ptr = input_ptr;
|
auto ptr = input_ptr;
|
||||||
while (*ptr && *ptr != '\r' && *ptr != '\n') {
|
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};
|
return td::Slice{ptr, input_ptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntCtx::skipspc(bool skip_eol) {
|
void ParseCtx::skipspc(bool skip_eol) {
|
||||||
do {
|
do {
|
||||||
while (*input_ptr == ' ' || *input_ptr == '\t' || *input_ptr == '\r') {
|
while (*input_ptr == ' ' || *input_ptr == '\t' || *input_ptr == '\r') {
|
||||||
++input_ptr;
|
++input_ptr;
|
||||||
|
@ -207,6 +141,45 @@ void IntCtx::skipspc(bool skip_eol) {
|
||||||
} while (load_next_line());
|
} 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 {
|
void IntCtx::check_compile() const {
|
||||||
if (state <= 0) {
|
if (state <= 0) {
|
||||||
throw IntError{"compilation mode only"};
|
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 {
|
td::Status IntCtx::add_error_loc(td::Status err) const {
|
||||||
if (err.is_error()) {
|
if (err.is_error() && parser) {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
if (include_depth && line_no) {
|
parser->show_context(os);
|
||||||
os << filename << ":" << line_no << ":\t";
|
|
||||||
}
|
|
||||||
if (!word.empty()) {
|
|
||||||
os << word << ":";
|
|
||||||
}
|
|
||||||
return err.move_as_error_prefix(os.str());
|
return err.move_as_error_prefix(os.str());
|
||||||
} else {
|
} else {
|
||||||
return err;
|
return err;
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "crypto/vm/db/TonDb.h" // FIXME
|
|
||||||
#include "crypto/vm/stack.hpp"
|
#include "crypto/vm/stack.hpp"
|
||||||
|
#include "crypto/vm/box.hpp"
|
||||||
#include "crypto/common/bitstring.h"
|
#include "crypto/common/bitstring.h"
|
||||||
|
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
|
@ -32,6 +32,11 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
namespace vm {
|
||||||
|
class TonDbImpl; // from crypto/vm/db/TonDb.h
|
||||||
|
using TonDb = std::unique_ptr<TonDbImpl>;
|
||||||
|
} // namespace vm
|
||||||
|
|
||||||
namespace fift {
|
namespace fift {
|
||||||
class Dictionary;
|
class Dictionary;
|
||||||
class SourceLookup;
|
class SourceLookup;
|
||||||
|
@ -68,71 +73,36 @@ class CharClassifier {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IntCtx {
|
struct ParseCtx {
|
||||||
vm::Stack stack;
|
|
||||||
Ref<FiftCont> next, exc_handler;
|
|
||||||
Ref<FiftCont> exc_cont, exc_next;
|
|
||||||
int state{0};
|
|
||||||
int include_depth{0};
|
int include_depth{0};
|
||||||
int line_no{0};
|
int line_no{0};
|
||||||
int exit_code{0};
|
|
||||||
td::Status error;
|
|
||||||
bool need_line{true};
|
bool need_line{true};
|
||||||
std::string filename;
|
std::string filename;
|
||||||
std::string currentd_dir;
|
std::string currentd_dir;
|
||||||
std::istream* input_stream{nullptr};
|
std::istream* input_stream{nullptr};
|
||||||
std::unique_ptr<std::istream> input_stream_holder;
|
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;
|
std::string word;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string str;
|
std::string str;
|
||||||
const char* input_ptr = nullptr;
|
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:
|
public:
|
||||||
IntCtx() = default;
|
ParseCtx() = default;
|
||||||
IntCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0)
|
ParseCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0)
|
||||||
: include_depth(_depth)
|
: include_depth(_depth)
|
||||||
, filename(std::move(_filename))
|
, filename(std::move(_filename))
|
||||||
, currentd_dir(std::move(_curdir))
|
, currentd_dir(std::move(_curdir))
|
||||||
, input_stream(&_istream) {
|
, input_stream(&_istream) {
|
||||||
}
|
}
|
||||||
|
ParseCtx(std::unique_ptr<std::istream> _istream_ptr, std::string _filename, std::string _curdir = "", int _depth = 0)
|
||||||
operator vm::Stack&() {
|
: include_depth(_depth)
|
||||||
return stack;
|
, 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_to(char delim, bool err_endl = true);
|
||||||
td::Slice scan_word();
|
td::Slice scan_word();
|
||||||
td::Slice scan_word_ext(const CharClassifier& classifier);
|
td::Slice scan_word_ext(const CharClassifier& classifier);
|
||||||
|
@ -165,6 +135,50 @@ struct IntCtx {
|
||||||
|
|
||||||
bool is_sb() const;
|
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() {
|
void clear() {
|
||||||
state = 0;
|
state = 0;
|
||||||
stack.clear();
|
stack.clear();
|
||||||
|
@ -194,6 +208,6 @@ struct IntCtx {
|
||||||
td::Result<int> run(Ref<FiftCont> cont);
|
td::Result<int> run(Ref<FiftCont> cont);
|
||||||
};
|
};
|
||||||
|
|
||||||
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx);
|
td::StringBuilder& operator<<(td::StringBuilder& os, const ParseCtx& ctx);
|
||||||
std::ostream& operator<<(std::ostream& os, const IntCtx& ctx);
|
std::ostream& operator<<(std::ostream& os, const ParseCtx& ctx);
|
||||||
} // namespace fift
|
} // namespace fift
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
library TVM_Asm
|
library TVM_Asm
|
||||||
// simple TVM Assembler
|
// simple TVM Assembler
|
||||||
|
namespace Asm
|
||||||
|
Asm definitions
|
||||||
variable @atend
|
variable @atend
|
||||||
variable @was-split
|
variable @was-split
|
||||||
false @was-split !
|
false @was-split !
|
||||||
{ "not in asm context" abort } @atend !
|
{ "not in asm context" abort } @atend !
|
||||||
{ `normal eq? not abort"must be terminated by }>" } : @normal?
|
{ `normal eq? not abort"must be terminated by }>" } : @normal?
|
||||||
{ @atend @ 1 { @atend ! @normal? } does @atend ! } : @pushatend
|
{ context@ @atend @ 2 { @atend ! context! @normal? } does @atend ! } : @pushatend
|
||||||
{ @pushatend <b } : <{
|
{ @pushatend Asm <b } : <{
|
||||||
{ @atend @ execute } : @endblk
|
{ @atend @ execute } : @endblk
|
||||||
{ false @was-split ! `normal @endblk } : }>
|
{ false @was-split ! `normal @endblk } : }>
|
||||||
{ }> b> } : }>c
|
{ }> b> } : }>c
|
||||||
|
@ -1219,7 +1221,10 @@ variable asm-mode 1 asm-mode !
|
||||||
{ 1 'nop does swap 0 (create) } : @declglobvar
|
{ 1 'nop does swap 0 (create) } : @declglobvar
|
||||||
{ @proccnt @ 1+ dup @proccnt ! 1 @declproc } : @newproc
|
{ @proccnt @ 1+ dup @proccnt ! 1 @declproc } : @newproc
|
||||||
{ @gvarcnt @ 1+ dup @gvarcnt ! @declglobvar } : @newglobvar
|
{ @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 @newproc } : NEWPROC
|
||||||
{ bl word dup (def?) ' drop ' @newproc cond } : DECLPROC
|
{ bl word dup (def?) ' drop ' @newproc cond } : DECLPROC
|
||||||
{ bl word dup find
|
{ bl word dup find
|
||||||
|
@ -1293,6 +1298,7 @@ variable asm-mode 1 asm-mode !
|
||||||
} while
|
} while
|
||||||
drop @proclist null! @procinfo null! @proccnt 0!
|
drop @proclist null! @procinfo null! @proccnt 0!
|
||||||
@procdict dup @ swap null!
|
@procdict dup @ swap null!
|
||||||
|
@oldctx @ context! @oldcurrent @ current!
|
||||||
} : }END
|
} : }END
|
||||||
forget @proclist forget @proccnt
|
forget @proclist forget @proccnt
|
||||||
{ }END <{ SETCP0 swap @procdictkeylen DICTPUSHCONST DICTIGETJMPZ 11 THROWARG }> } : }END>
|
{ }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
|
{ <b 2 8 u, swap 256 u, b>spec } : hash>libref
|
||||||
// ( c -- c' )
|
// ( c -- c' )
|
||||||
{ hash hash>libref } : >libref
|
{ 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
|
{ 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?) { 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
|
{ 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 } } ::_ $(
|
{ char ) word "$" swap $+ 1 { find 0= abort"undefined parameter" execute } } ::_ $(
|
||||||
// b s -- ?
|
// b s -- ?
|
||||||
{ sbitrefs rot brembitrefs rot >= -rot <= and } : s-fits?
|
{ 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 = "") {
|
td::Result<std::string> load_wallet3_code_fif(std::string dir = "") {
|
||||||
return td::read_file_str(smartcont_dir(dir) + "wallet-v3-code.fif");
|
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 {
|
class MemoryFileLoader : public fift::FileLoader {
|
||||||
public:
|
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,
|
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_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>();
|
auto loader = std::make_unique<MemoryFileLoader>();
|
||||||
loader->add_file("/main.fif", std::move(main));
|
loader->add_file("/main.fif", std::move(main));
|
||||||
if (need_preamble) {
|
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));
|
TRY_RESULT(f, load_wallet3_code_fif(dir));
|
||||||
loader->add_file("/wallet-v3-code.fif", std::move(f));
|
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));
|
auto res = fift::SourceLookup(std::move(loader));
|
||||||
res.add_include_path("/");
|
res.add_include_path("/");
|
||||||
return std::move(res);
|
return std::move(res);
|
||||||
|
@ -172,7 +187,7 @@ td::Result<fift::SourceLookup> run_fift(fift::SourceLookup source_lookup, std::o
|
||||||
} // namespace
|
} // namespace
|
||||||
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args, std::string fift_dir) {
|
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args, std::string fift_dir) {
|
||||||
std::stringstream ss;
|
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)));
|
TRY_RESULT_ASSIGN(source_lookup, run_fift(std::move(source_lookup), &ss, true, std::move(args)));
|
||||||
FiftOutput res;
|
FiftOutput res;
|
||||||
res.source_lookup = std::move(source_lookup);
|
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,
|
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_asm, bool need_ton_util, bool need_lisp,
|
||||||
bool need_w3_code) {
|
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) {
|
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,
|
TRY_RESULT(source_lookup,
|
||||||
create_source_lookup(PSTRING() << "\"Asm.fif\" include\n " << (is_raw ? "<{" : "") << asm_code << "\n"
|
create_source_lookup(PSTRING() << "\"Asm.fif\" include\n " << (is_raw ? "<{" : "") << asm_code << "\n"
|
||||||
<< (is_raw ? "}>c" : "") << " boc>B \"res\" B>file",
|
<< (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(res, run_fift(std::move(source_lookup), &ss));
|
||||||
TRY_RESULT(boc, res.read_file("res"));
|
TRY_RESULT(boc, res.read_file("res"));
|
||||||
return vm::std_boc_deserialize(std::move(boc.data));
|
return vm::std_boc_deserialize(std::move(boc.data));
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "Dictionary.h"
|
#include "Dictionary.h"
|
||||||
#include "IntCtx.h"
|
#include "IntCtx.h"
|
||||||
#include "SourceLookup.h"
|
#include "SourceLookup.h"
|
||||||
|
#include "HashMap.h"
|
||||||
|
|
||||||
#include "common/refcnt.hpp"
|
#include "common/refcnt.hpp"
|
||||||
#include "common/bigint.hpp"
|
#include "common/bigint.hpp"
|
||||||
|
@ -42,6 +43,8 @@
|
||||||
#include "vm/box.hpp"
|
#include "vm/box.hpp"
|
||||||
#include "vm/atom.h"
|
#include "vm/atom.h"
|
||||||
|
|
||||||
|
#include "vm/db/TonDb.h" // only for interpret_db_run_vm{,_parallel}
|
||||||
|
|
||||||
#include "block/block.h"
|
#include "block/block.h"
|
||||||
|
|
||||||
#include "td/utils/filesystem.h"
|
#include "td/utils/filesystem.h"
|
||||||
|
@ -58,12 +61,31 @@
|
||||||
|
|
||||||
namespace fift {
|
namespace fift {
|
||||||
|
|
||||||
void show_total_cells(std::ostream& stream) {
|
const Ref<FiftCont> nop_word_def = Ref<NopWord>{true};
|
||||||
stream << "total cells = " << vm::DataCell::get_total_data_cells() << std::endl;
|
|
||||||
|
//
|
||||||
|
// 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);
|
Ref<WordList> pop_word_list(vm::Stack& stack) {
|
||||||
void do_compile_literals(vm::Stack& stack, int count);
|
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) {
|
void interpret_dot(IntCtx& ctx, bool space_after) {
|
||||||
*ctx.output_stream << dec_string2(ctx.stack.pop_int()) << (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) {
|
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) {
|
void interpret_dot_internal(vm::Stack& stack) {
|
||||||
|
@ -461,7 +483,7 @@ void interpret_make_xchg(vm::Stack& stack) {
|
||||||
if (x) {
|
if (x) {
|
||||||
stack.push_object(td::Ref<StackWord>{true, std::bind(interpret_xchg, _1, x, y)});
|
stack.push_object(td::Ref<StackWord>{true, std::bind(interpret_xchg, _1, x, y)});
|
||||||
} else if (y <= 1) {
|
} 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 {
|
} else {
|
||||||
stack.push_object(td::Ref<StackWord>{true, std::bind(interpret_xchg0, _1, y)});
|
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) {
|
void interpret_cell_empty(vm::Stack& stack) {
|
||||||
auto cs = stack.pop_cellslice();
|
auto cs = stack.pop_cellslice();
|
||||||
stack.push_bool(cs->empty_ext());
|
stack.push_bool(cs->empty_ext());
|
||||||
|
@ -1483,6 +1530,150 @@ void interpret_crc32c(vm::Stack& stack) {
|
||||||
stack.push_smallint(td::crc32c(td::Slice{str}));
|
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
|
// vm dictionaries
|
||||||
void interpret_dict_new(vm::Stack& stack) {
|
void interpret_dict_new(vm::Stack& stack) {
|
||||||
stack.push({});
|
stack.push({});
|
||||||
|
@ -1882,7 +2073,7 @@ void interpret_pfx_dict_get(vm::Stack& stack) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void interpret_bitstring_hex_literal(IntCtx& ctx) {
|
void interpret_bitstring_hex_literal(IntCtx& ctx) {
|
||||||
auto s = ctx.scan_word_to('}');
|
auto s = ctx.parser->scan_word_to('}');
|
||||||
unsigned char buff[128];
|
unsigned char buff[128];
|
||||||
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), s.begin(), s.end());
|
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), s.begin(), s.end());
|
||||||
if (bits < 0) {
|
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()};
|
auto cs = Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
|
||||||
ctx.stack.push(std::move(cs));
|
ctx.stack.push(std::move(cs));
|
||||||
push_argcount(ctx.stack, 1);
|
push_argcount(ctx, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void interpret_bitstring_binary_literal(IntCtx& ctx) {
|
void interpret_bitstring_binary_literal(IntCtx& ctx) {
|
||||||
auto s = ctx.scan_word_to('}');
|
auto s = ctx.parser->scan_word_to('}');
|
||||||
unsigned char buff[128];
|
unsigned char buff[128];
|
||||||
int bits = (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), s.begin(), s.end());
|
int bits = (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), s.begin(), s.end());
|
||||||
if (bits < 0) {
|
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()};
|
auto cs = Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
|
||||||
ctx.stack.push(std::move(cs));
|
ctx.stack.push(std::move(cs));
|
||||||
push_argcount(ctx.stack, 1);
|
push_argcount(ctx, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void interpret_word(IntCtx& ctx) {
|
void interpret_word(IntCtx& ctx) {
|
||||||
char sep = (char)ctx.stack.pop_smallint_range(127);
|
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);
|
ctx.stack.push_string(word);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1915,17 +2106,17 @@ void interpret_word_ext(IntCtx& ctx) {
|
||||||
int mode = ctx.stack.pop_smallint_range(11);
|
int mode = ctx.stack.pop_smallint_range(11);
|
||||||
auto delims = ctx.stack.pop_string();
|
auto delims = ctx.stack.pop_string();
|
||||||
if (mode & 8) {
|
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) {
|
void interpret_skipspc(IntCtx& ctx) {
|
||||||
ctx.skipspc();
|
ctx.parser->skipspc();
|
||||||
}
|
}
|
||||||
|
|
||||||
void interpret_wordlist_begin_aux(vm::Stack& stack) {
|
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) {
|
void interpret_wordlist_begin(IntCtx& ctx) {
|
||||||
|
@ -1938,7 +2129,7 @@ void interpret_wordlist_begin(IntCtx& ctx) {
|
||||||
void interpret_wordlist_end_aux(vm::Stack& stack) {
|
void interpret_wordlist_end_aux(vm::Stack& stack) {
|
||||||
Ref<WordList> wordlist_ref = pop_word_list(stack);
|
Ref<WordList> wordlist_ref = pop_word_list(stack);
|
||||||
wordlist_ref.write().close();
|
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) {
|
void interpret_wordlist_end(IntCtx& ctx) {
|
||||||
|
@ -1957,7 +2148,7 @@ void interpret_internal_interpret_begin(IntCtx& ctx) {
|
||||||
void interpret_internal_interpret_end(IntCtx& ctx) {
|
void interpret_internal_interpret_end(IntCtx& ctx) {
|
||||||
ctx.check_int_exec();
|
ctx.check_int_exec();
|
||||||
ctx.state = -ctx.state;
|
ctx.state = -ctx.state;
|
||||||
ctx.stack.push({vm::from_object, Dictionary::nop_word_def});
|
ctx.stack.push_object(nop_word_def);
|
||||||
}
|
}
|
||||||
|
|
||||||
// (create)
|
// (create)
|
||||||
|
@ -1974,18 +2165,12 @@ void interpret_create_aux(IntCtx& ctx, int mode) {
|
||||||
if (!(mode & 2)) {
|
if (!(mode & 2)) {
|
||||||
word += ' ';
|
word += ' ';
|
||||||
}
|
}
|
||||||
bool active = (mode & 1);
|
ctx.dictionary.def_word(std::move(word), {std::move(wd_ref), (bool)(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});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// { bl word 0 (create) } : create
|
// { bl word 0 (create) } : create
|
||||||
void interpret_create(IntCtx& ctx) {
|
void interpret_create(IntCtx& ctx) {
|
||||||
auto word = ctx.scan_word();
|
auto word = ctx.parser->scan_word();
|
||||||
if (!word.size()) {
|
if (!word.size()) {
|
||||||
throw IntError{"non-empty word name expected"};
|
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) } :: :
|
// { bl word <mode> 2 ' (create) } :: :
|
||||||
void interpret_colon(IntCtx& ctx, int mode) {
|
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(mode);
|
||||||
ctx.stack.push_smallint(2);
|
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);
|
//push_argcount(ctx, 2, create_wd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2008,27 +2193,27 @@ void interpret_colon(IntCtx& ctx, int mode) {
|
||||||
void interpret_forget_aux(IntCtx& ctx) {
|
void interpret_forget_aux(IntCtx& ctx) {
|
||||||
std::string s = ctx.stack.pop_string();
|
std::string s = ctx.stack.pop_string();
|
||||||
auto s_copy = s;
|
auto s_copy = s;
|
||||||
auto entry = ctx.dictionary->lookup(s);
|
auto entry = ctx.dictionary.lookup(s);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
s += " ";
|
s += " ";
|
||||||
entry = ctx.dictionary->lookup(s);
|
entry = ctx.dictionary.lookup(s);
|
||||||
}
|
}
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
throw IntError{"`" + s_copy + "` not found"};
|
throw IntError{"`" + s_copy + "` not found"};
|
||||||
} else {
|
} else {
|
||||||
ctx.dictionary->undef_word(s);
|
ctx.dictionary.undef_word(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// { bl word (forget) } : forget
|
// { bl word (forget) } : forget
|
||||||
void interpret_forget(IntCtx& ctx) {
|
void interpret_forget(IntCtx& ctx) {
|
||||||
ctx.stack.push_string(ctx.scan_word());
|
ctx.stack.push_string(ctx.parser->scan_word());
|
||||||
interpret_forget_aux(ctx);
|
interpret_forget_aux(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void interpret_quote_str(IntCtx& ctx) {
|
void interpret_quote_str(IntCtx& ctx) {
|
||||||
ctx.stack.push_string(ctx.scan_word_to('"'));
|
ctx.stack.push_string(ctx.parser->scan_word_to('"'));
|
||||||
push_argcount(ctx.stack, 1);
|
push_argcount(ctx, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int str_utf8_code(const char* str, int& len) {
|
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) {
|
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 len = (s.size() < 10 ? (int)s.size() : 10);
|
||||||
int code = str_utf8_code(s.data(), len);
|
int code = str_utf8_code(s.data(), len);
|
||||||
if (code < 0 || s.size() != (unsigned)len) {
|
if (code < 0 || s.size() != (unsigned)len) {
|
||||||
|
@ -2197,6 +2382,12 @@ Ref<FiftCont> interpret_execute(IntCtx& ctx) {
|
||||||
return pop_exec_token(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) {
|
Ref<FiftCont> interpret_execute_times(IntCtx& ctx) {
|
||||||
int count = ctx.stack.pop_smallint_range(1000000000);
|
int count = ctx.stack.pop_smallint_range(1000000000);
|
||||||
auto body = pop_exec_token(ctx);
|
auto body = pop_exec_token(ctx);
|
||||||
|
@ -2251,30 +2442,46 @@ Ref<FiftCont> interpret_until(IntCtx& ctx) {
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
void interpret_tick(IntCtx& ctx) {
|
DictEntry context_lookup(IntCtx& ctx, std::string word, bool append_space = true) {
|
||||||
std::string word = ctx.scan_word().str();
|
if (append_space) {
|
||||||
auto entry = ctx.dictionary->lookup(word);
|
auto entry = context_lookup(ctx, word, false);
|
||||||
if (!entry) {
|
|
||||||
entry = ctx.dictionary->lookup(word + ' ');
|
|
||||||
if (!entry) {
|
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);
|
push_argcount(ctx, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void interpret_find(IntCtx& ctx) {
|
void interpret_find(IntCtx& ctx, int mode) {
|
||||||
std::string word = ctx.stack.pop_string();
|
std::string word = ctx.stack.pop_string();
|
||||||
auto entry = ctx.dictionary->lookup(word);
|
auto entry = context_lookup(ctx, word, !(mode & 2));
|
||||||
if (!entry) {
|
|
||||||
entry = ctx.dictionary->lookup(word + ' ');
|
|
||||||
}
|
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
ctx.stack.push_bool(false);
|
ctx.stack.push_bool(false);
|
||||||
} else {
|
} else {
|
||||||
ctx.stack.push({vm::from_object, entry->get_def()});
|
ctx.stack.push_object(entry.get_def());
|
||||||
ctx.stack.push_bool(true);
|
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) {
|
Ref<FiftCont> interpret_include(IntCtx& ctx) {
|
||||||
auto fname = ctx.stack.pop_string();
|
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()) {
|
if (r_file.is_error()) {
|
||||||
throw IntError{"cannot locate file `" + fname + "`"};
|
throw IntError{"cannot locate file `" + fname + "`"};
|
||||||
}
|
}
|
||||||
|
@ -2314,12 +2525,32 @@ Ref<FiftCont> interpret_skip_source(IntCtx& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void interpret_words(IntCtx& ctx) {
|
void interpret_words(IntCtx& ctx) {
|
||||||
for (const auto& x : *ctx.dictionary) {
|
for (const auto& x : ctx.dictionary) {
|
||||||
*ctx.output_stream << x.first << " ";
|
*ctx.output_stream << vm::StackEntry(x.key()).as_string() << " ";
|
||||||
}
|
}
|
||||||
*ctx.output_stream << std::endl;
|
*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) {
|
void interpret_print_backtrace(IntCtx& ctx) {
|
||||||
ctx.print_backtrace(*ctx.output_stream, ctx.next);
|
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,
|
void do_interpret_db_run_vm_parallel(std::ostream* stream, vm::Stack& stack, vm::TonDb* ton_db_ptr, int threads_n,
|
||||||
int tasks_n) {
|
int tasks_n) {
|
||||||
if (!ton_db_ptr || !*ton_db_ptr) {
|
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);
|
interpret_get_fixed_cmdline_arg(ctx.stack, n);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto entry = ctx.dictionary->lookup("$0 ");
|
auto entry = ctx.dictionary.lookup("$0 ");
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
throw IntError{"-?"};
|
throw IntError{"-?"};
|
||||||
} else {
|
} else {
|
||||||
return entry->get_def();
|
return entry.get_def();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2667,29 +2920,19 @@ Ref<FiftCont> interpret_execute_internal(IntCtx& ctx) {
|
||||||
return word_def;
|
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) {
|
void do_compile(vm::Stack& stack, Ref<FiftCont> word_def) {
|
||||||
Ref<WordList> wl_ref = pop_word_list(stack);
|
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();
|
auto list_size = word_def->list_size();
|
||||||
if ((td::uint64)list_size <= 1) {
|
if (list_size >= 0 && (list_size <= 2 || word_def.is_unique())) {
|
||||||
// inline short definitions
|
// inline short and unique definitions
|
||||||
auto list = word_def->get_list();
|
auto list = word_def->get_list();
|
||||||
wl_ref.write().append(list, list + list_size);
|
wl_ref.write().append(list, list + list_size);
|
||||||
} else {
|
} 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) {
|
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.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) {
|
void init_words_common(Dictionary& d) {
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
d.def_word("nop ", Dictionary::nop_word_def);
|
d.def_word("nop ", nop_word_def);
|
||||||
// stack print/dump words
|
// stack print/dump words
|
||||||
d.def_ctx_word(". ", std::bind(interpret_dot, _1, true));
|
d.def_ctx_word(". ", std::bind(interpret_dot, _1, true));
|
||||||
d.def_ctx_word("._ ", std::bind(interpret_dot, _1, false));
|
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, 2));
|
||||||
d.def_stack_word("ref@? ", std::bind(interpret_fetch_ref, _1, 4));
|
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("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("s> ", interpret_cell_check_empty);
|
||||||
d.def_stack_word("empty? ", interpret_cell_empty);
|
d.def_stack_word("empty? ", interpret_cell_empty);
|
||||||
d.def_stack_word("remaining ", interpret_cell_remaining);
|
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("crc16 ", interpret_crc16);
|
||||||
d.def_stack_word("crc32 ", interpret_crc32);
|
d.def_stack_word("crc32 ", interpret_crc32);
|
||||||
d.def_stack_word("crc32c ", interpret_crc32c);
|
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
|
// vm dictionaries
|
||||||
d.def_stack_word("dictnew ", interpret_dict_new);
|
d.def_stack_word("dictnew ", interpret_dict_new);
|
||||||
d.def_stack_word("dict>s ", interpret_dict_to_slice);
|
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);
|
d.def_stack_word("atom? ", interpret_is_atom);
|
||||||
// execution control
|
// execution control
|
||||||
d.def_ctx_tail_word("execute ", interpret_execute);
|
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("times ", interpret_execute_times);
|
||||||
d.def_ctx_tail_word("if ", interpret_if);
|
d.def_ctx_tail_word("if ", interpret_if);
|
||||||
d.def_ctx_tail_word("ifnot ", interpret_ifnot);
|
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("(}) ", interpret_wordlist_end_aux);
|
||||||
d.def_stack_word("(compile) ", interpret_compile_internal);
|
d.def_stack_word("(compile) ", interpret_compile_internal);
|
||||||
d.def_ctx_tail_word("(execute) ", interpret_execute_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_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
|
// 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 ", interpret_create);
|
||||||
d.def_ctx_word("(create) ", std::bind(interpret_create_aux, _1, -1));
|
d.def_ctx_word("(create) ", std::bind(interpret_create_aux, _1, -1));
|
||||||
d.def_active_word(": ", std::bind(interpret_colon, _1, 0));
|
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_aux);
|
||||||
d.def_ctx_word("forget ", interpret_forget);
|
d.def_ctx_word("forget ", interpret_forget);
|
||||||
d.def_ctx_word("words ", interpret_words);
|
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(".bt ", interpret_print_backtrace);
|
||||||
d.def_ctx_word("cont. ", interpret_print_continuation);
|
d.def_ctx_word("cont. ", interpret_print_continuation);
|
||||||
// input parse
|
// input parse
|
||||||
d.def_ctx_word("word ", interpret_word);
|
d.def_ctx_word("word ", interpret_word);
|
||||||
d.def_ctx_word("(word) ", interpret_word_ext);
|
d.def_ctx_word("(word) ", interpret_word_ext);
|
||||||
d.def_ctx_word("skipspc ", interpret_skipspc);
|
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("include ", interpret_include);
|
||||||
d.def_ctx_tail_word("skip-to-eof ", interpret_skip_source);
|
d.def_ctx_tail_word("skip-to-eof ", interpret_skip_source);
|
||||||
d.def_word("'exit-interpret ", LitCont::literal(exit_interpret));
|
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_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel);
|
||||||
d.def_stack_word("vmcont, ", interpret_store_vm_cont);
|
d.def_stack_word("vmcont, ", interpret_store_vm_cont);
|
||||||
d.def_stack_word("vmcont@ ", interpret_fetch_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[]) {
|
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
|
} // namespace fift
|
||||||
|
|
|
@ -110,3 +110,19 @@ TEST(Fift, test_sort) {
|
||||||
TEST(Fift, test_sort2) {
|
TEST(Fift, test_sort2) {
|
||||||
run_fift("sort2.fif");
|
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) {
|
std::string dump_push_tinyint4(CellSlice&, unsigned args) {
|
||||||
int x = (int)((args + 5) & 15) - 5;
|
int x = (int)((args + 5) & 15) - 5;
|
||||||
std::ostringstream os{"PUSHINT "};
|
std::ostringstream os;
|
||||||
os << x;
|
os << "PUSHINT " << x;
|
||||||
return os.str();
|
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) {
|
std::string dump_op_tinyint8(const char* op_prefix, CellSlice&, unsigned args) {
|
||||||
int x = (signed char)args;
|
int x = (signed char)args;
|
||||||
std::ostringstream os{op_prefix};
|
std::ostringstream os;
|
||||||
os << x;
|
os << op_prefix << x;
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,8 +68,8 @@ int exec_push_smallint(VmState* st, unsigned args) {
|
||||||
|
|
||||||
std::string dump_push_smallint(CellSlice&, unsigned args) {
|
std::string dump_push_smallint(CellSlice&, unsigned args) {
|
||||||
int x = (short)args;
|
int x = (short)args;
|
||||||
std::ostringstream os{"PUSHINT "};
|
std::ostringstream os;
|
||||||
os << x;
|
os << "PUSHINT " << x;
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,8 +93,8 @@ std::string dump_push_int(CellSlice& cs, unsigned args, int pfx_bits) {
|
||||||
}
|
}
|
||||||
cs.advance(pfx_bits);
|
cs.advance(pfx_bits);
|
||||||
td::RefInt256 x = cs.fetch_int256(3 + l * 8);
|
td::RefInt256 x = cs.fetch_int256(3 + l * 8);
|
||||||
std::ostringstream os{"PUSHINT "};
|
std::ostringstream os;
|
||||||
os << x;
|
os << "PUSHINT " << x;
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +302,7 @@ std::string dump_divmod(CellSlice&, unsigned args, bool quiet) {
|
||||||
if (quiet) {
|
if (quiet) {
|
||||||
s = "Q" + s;
|
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) {
|
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) {
|
if (!(args & 12) || round_mode == 3) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
std::string s;
|
std::ostringstream os;
|
||||||
|
if (mode & 1) {
|
||||||
|
os << 'Q';
|
||||||
|
}
|
||||||
switch (args & 12) {
|
switch (args & 12) {
|
||||||
case 4:
|
case 4:
|
||||||
s = "RSHIFT";
|
os << "RSHIFT";
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
s = "MODPOW2";
|
os << "MODPOW2";
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
s = "RSHIFTMOD";
|
os << "RSHIFTMOD";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (mode & 1) {
|
if (round_mode) {
|
||||||
s = "Q" + s;
|
os << "FRC"[round_mode];
|
||||||
}
|
}
|
||||||
s += "FRC"[round_mode];
|
|
||||||
if (mode & 2) {
|
if (mode & 2) {
|
||||||
char buff[8];
|
os << ' ' << y;
|
||||||
sprintf(buff, " %d", y);
|
|
||||||
s += buff;
|
|
||||||
}
|
}
|
||||||
return s;
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
int exec_muldivmod(VmState* st, unsigned args, int quiet) {
|
int exec_muldivmod(VmState* st, unsigned args, int quiet) {
|
||||||
|
@ -417,7 +417,7 @@ std::string dump_muldivmod(CellSlice&, unsigned args, bool quiet) {
|
||||||
if (quiet) {
|
if (quiet) {
|
||||||
s = "Q" + s;
|
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) {
|
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) {
|
if (!(args & 12) || round_mode == 3) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
std::string s;
|
std::ostringstream os;
|
||||||
|
if (mode & 1) {
|
||||||
|
os << 'Q';
|
||||||
|
}
|
||||||
switch (args & 12) {
|
switch (args & 12) {
|
||||||
case 4:
|
case 4:
|
||||||
s = "MULRSHIFT";
|
os << "MULRSHIFT";
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
s = "MULMODPOW2";
|
os << "MULMODPOW2";
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
s = "MULRSHIFTMOD";
|
os << "MULRSHIFTMOD";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (mode & 1) {
|
if (round_mode) {
|
||||||
s = "Q" + s;
|
os << "FRC"[round_mode];
|
||||||
}
|
}
|
||||||
s += "FRC"[round_mode];
|
|
||||||
if (mode & 2) {
|
if (mode & 2) {
|
||||||
char buff[8];
|
os << ' ' << y;
|
||||||
sprintf(buff, " %d", y);
|
|
||||||
s += buff;
|
|
||||||
}
|
}
|
||||||
return s;
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
int exec_shldivmod(VmState* st, unsigned args, int mode) {
|
int exec_shldivmod(VmState* st, unsigned args, int mode) {
|
||||||
|
@ -542,19 +542,25 @@ int exec_shldivmod(VmState* st, unsigned args, int mode) {
|
||||||
return 0;
|
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);
|
int round_mode = (int)(args & 3);
|
||||||
if (!(args & 12) || round_mode == 3) {
|
if (!(args & 12) || round_mode == 3) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
std::string s = (args & 4) ? "LSHIFTDIV" : "LSHIFT";
|
std::ostringstream os;
|
||||||
if (args & 8) {
|
os << (mode & 1 ? "Q" : "") << (args & 4 ? "LSHIFTDIV" : "LSHIFT") << (args & 8 ? "MOD" : "");
|
||||||
s += "MOD";
|
if (round_mode) {
|
||||||
|
os << "FRC"[round_mode];
|
||||||
}
|
}
|
||||||
if (quiet) {
|
if (y >= 0) {
|
||||||
s = "Q" + s;
|
os << ' ' << y;
|
||||||
}
|
}
|
||||||
return s + "FRC"[round_mode];
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_div_ops(OpcodeTable& cp0) {
|
void register_div_ops(OpcodeTable& cp0) {
|
||||||
|
|
|
@ -30,7 +30,7 @@ class Box : public td::CntObject {
|
||||||
Box(const Box&) = default;
|
Box(const Box&) = default;
|
||||||
Box(Box&&) = default;
|
Box(Box&&) = default;
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
Box(Args... args) : data_{std::move(args...)} {
|
Box(Args&&... args) : data_{std::forward<Args>(args)...} {
|
||||||
}
|
}
|
||||||
~Box() override = default;
|
~Box() override = default;
|
||||||
Box(const StackEntry& data) : data_(data) {
|
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);
|
cs.advance(pfx_bits);
|
||||||
auto slice = cs.fetch_subslice(data_bits, refs);
|
auto slice = cs.fetch_subslice(data_bits, refs);
|
||||||
slice.unique_write().remove_trailing();
|
slice.unique_write().remove_trailing();
|
||||||
std::ostringstream os{name};
|
std::ostringstream os;
|
||||||
|
os << name;
|
||||||
slice->dump_hex(os, 1, false);
|
slice->dump_hex(os, 1, false);
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
@ -188,7 +189,8 @@ std::string dump_push_cont(CellSlice& cs, unsigned args, int pfx_bits) {
|
||||||
}
|
}
|
||||||
cs.advance(pfx_bits);
|
cs.advance(pfx_bits);
|
||||||
auto slice = cs.fetch_subslice(data_bits, refs);
|
auto slice = cs.fetch_subslice(data_bits, refs);
|
||||||
std::ostringstream os{"PUSHCONT "};
|
std::ostringstream os;
|
||||||
|
os << "PUSHCONT ";
|
||||||
slice->dump_hex(os, 1, false);
|
slice->dump_hex(os, 1, false);
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
@ -219,7 +221,8 @@ std::string dump_push_cont_simple(CellSlice& cs, unsigned args, int pfx_bits) {
|
||||||
}
|
}
|
||||||
cs.advance(pfx_bits);
|
cs.advance(pfx_bits);
|
||||||
auto slice = cs.fetch_subslice(data_bits);
|
auto slice = cs.fetch_subslice(data_bits);
|
||||||
std::ostringstream os{"PUSHCONT "};
|
std::ostringstream os;
|
||||||
|
os << "PUSHCONT ";
|
||||||
slice->dump_hex(os, 1, false);
|
slice->dump_hex(os, 1, false);
|
||||||
return os.str();
|
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::string dump_load_int_fixed2(CellSlice&, unsigned args) {
|
||||||
std::ostringstream os{args & 0x200 ? "PLD" : "LD"};
|
std::ostringstream os;
|
||||||
os << (args & 0x100 ? 'U' : 'I');
|
os << (args & 0x200 ? "PLD" : "LD") << (args & 0x100 ? 'U' : 'I');
|
||||||
if (args & 0x400) {
|
if (args & 0x400) {
|
||||||
os << 'Q';
|
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::string dump_preload_uint_fixed_0e(CellSlice&, unsigned args) {
|
||||||
std::ostringstream os{"PLDUZ "};
|
std::ostringstream os;
|
||||||
unsigned bits = ((args & 7) + 1) << 5;
|
unsigned bits = ((args & 7) + 1) << 5;
|
||||||
os << bits;
|
os << "PLDUZ " << bits;
|
||||||
return os.str();
|
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) {
|
std::string dump_load_slice_fixed2(CellSlice&, unsigned args) {
|
||||||
unsigned bits = (args & 0xff) + 1;
|
unsigned bits = (args & 0xff) + 1;
|
||||||
std::ostringstream os{args & 0x100 ? "PLDSLICE" : "LDSLICE"};
|
std::ostringstream os;
|
||||||
|
os << (args & 0x100 ? "PLDSLICE" : "LDSLICE");
|
||||||
if (args & 0x200) {
|
if (args & 0x200) {
|
||||||
os << 'Q';
|
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::string dump_if_bit_jmp(CellSlice& cs, unsigned args) {
|
||||||
std::ostringstream os{args & 0x20 ? "IFN" : " IF"};
|
std::ostringstream os;
|
||||||
os << "BITJMP " << (args & 0x1f);
|
os << "IF" << (args & 0x20 ? "N" : "") << "BITJMP " << (args & 0x1f);
|
||||||
return os.str();
|
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(pfx_bits);
|
||||||
cs.advance_refs(1);
|
cs.advance_refs(1);
|
||||||
std::ostringstream os{args & 0x20 ? "IFN" : " IF"};
|
std::ostringstream os;
|
||||||
os << "BITJMPREF " << (args & 0x1f);
|
os << "IF" << (args & 0x20 ? "N" : "") << "BITJMPREF " << (args & 0x1f);
|
||||||
return os.str();
|
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) {
|
std::string dump_setcontargs(CellSlice& cs, unsigned args, const char* name) {
|
||||||
int copy = (args >> 4) & 15, more = ((args + 1) & 15) - 1;
|
int copy = (args >> 4) & 15, more = ((args + 1) & 15) - 1;
|
||||||
std::ostringstream os{name};
|
std::ostringstream os;
|
||||||
os << ' ' << copy << ',' << more;
|
os << name << ' ' << copy << ',' << more;
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1065,8 +1065,8 @@ std::string dump_throw_any(CellSlice& cs, unsigned args) {
|
||||||
bool has_param = args & 1;
|
bool has_param = args & 1;
|
||||||
bool has_cond = args & 6;
|
bool has_cond = args & 6;
|
||||||
bool throw_cond = args & 2;
|
bool throw_cond = args & 2;
|
||||||
std::ostringstream os{has_param ? "THROWARG" : "THROW"};
|
std::ostringstream os;
|
||||||
os << "ANY";
|
os << "THROW" << (has_param ? "ARG" : "") << "ANY";
|
||||||
if (has_cond) {
|
if (has_cond) {
|
||||||
os << (throw_cond ? "IF" : "IFNOT");
|
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::string dump_dictop(unsigned args, const char* name) {
|
||||||
std::ostringstream os{"DICT"};
|
std::ostringstream os;
|
||||||
|
os << "DICT";
|
||||||
if (args & 4) {
|
if (args & 4) {
|
||||||
os << (args & 2 ? 'U' : 'I');
|
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::string dump_dictop2(unsigned args, const char* name) {
|
||||||
std::ostringstream os{"DICT"};
|
std::ostringstream os;
|
||||||
|
os << "DICT";
|
||||||
if (args & 2) {
|
if (args & 2) {
|
||||||
os << (args & 1 ? 'U' : 'I');
|
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::string dump_subdictop2(unsigned args, const char* name) {
|
||||||
std::ostringstream os{"SUBDICT"};
|
std::ostringstream os;
|
||||||
|
os << "SUBDICT";
|
||||||
if (args & 2) {
|
if (args & 2) {
|
||||||
os << (args & 1 ? 'U' : 'I');
|
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::string dump_dictop_getnear(CellSlice& cs, unsigned args) {
|
||||||
std::ostringstream os{"DICT"};
|
std::ostringstream os;
|
||||||
|
os << "DICT";
|
||||||
if (args & 8) {
|
if (args & 8) {
|
||||||
os << (args & 4 ? 'U' : 'I');
|
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);
|
cs.advance(pfx_bits - 11);
|
||||||
auto slice = cs.fetch_subslice(1, 1);
|
auto slice = cs.fetch_subslice(1, 1);
|
||||||
int n = (int)cs.fetch_ulong(10);
|
int n = (int)cs.fetch_ulong(10);
|
||||||
std::ostringstream os{name};
|
std::ostringstream os;
|
||||||
os << ' ' << n << " (";
|
os << name << ' ' << n << " (";
|
||||||
slice->dump_hex(os, false);
|
slice->dump_hex(os, false);
|
||||||
os << ')';
|
os << ')';
|
||||||
return os.str();
|
return os.str();
|
||||||
|
|
|
@ -357,32 +357,32 @@ using namespace std::placeholders;
|
||||||
|
|
||||||
dump_arg_instr_func_t dump_1sr(std::string prefix, std::string suffix) {
|
dump_arg_instr_func_t dump_1sr(std::string prefix, std::string suffix) {
|
||||||
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << 's' << (args & 15) << suffix;
|
os << prefix << 's' << (args & 15) << suffix;
|
||||||
return os.str();
|
return os.str();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_arg_instr_func_t dump_1sr_l(std::string prefix, std::string suffix) {
|
dump_arg_instr_func_t dump_1sr_l(std::string prefix, std::string suffix) {
|
||||||
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << 's' << (args & 255) << suffix;
|
os << prefix << 's' << (args & 255) << suffix;
|
||||||
return os.str();
|
return os.str();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_arg_instr_func_t dump_2sr(std::string prefix, std::string suffix) {
|
dump_arg_instr_func_t dump_2sr(std::string prefix, std::string suffix) {
|
||||||
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << 's' << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
|
os << prefix << 's' << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
|
||||||
return os.str();
|
return os.str();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_arg_instr_func_t dump_2sr_adj(unsigned adj, std::string prefix, std::string suffix) {
|
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 {
|
return [adj, prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << 's' << (int)((args >> 4) & 15) - (int)((adj >> 4) & 15) << ",s" << (int)(args & 15) - (int)(adj & 15)
|
os << prefix << 's' << (int)((args >> 4) & 15) - (int)((adj >> 4) & 15) << ",s" << (int)(args & 15) - (int)(adj & 15)
|
||||||
<< suffix;
|
<< suffix;
|
||||||
return os.str();
|
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) {
|
dump_arg_instr_func_t dump_3sr(std::string prefix, std::string suffix) {
|
||||||
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << 's' << ((args >> 8) & 15) << ",s" << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
|
os << prefix << 's' << ((args >> 8) & 15) << ",s" << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
|
||||||
return os.str();
|
return os.str();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_arg_instr_func_t dump_3sr_adj(unsigned adj, std::string prefix, std::string suffix) {
|
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 {
|
return [adj, prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << 's' << (int)((args >> 8) & 15) - (int)((adj >> 8) & 15) << ",s"
|
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;
|
<< (int)((args >> 4) & 15) - (int)((adj >> 4) & 15) << ",s" << (int)(args & 15) - (int)(adj & 15) << suffix;
|
||||||
return os.str();
|
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) {
|
dump_arg_instr_func_t dump_1c(std::string prefix, std::string suffix) {
|
||||||
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << (args & 15) << suffix;
|
os << prefix << (args & 15) << suffix;
|
||||||
return os.str();
|
return os.str();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_arg_instr_func_t dump_1c_l_add(int adj, std::string prefix, std::string suffix) {
|
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 {
|
return [adj, prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << (int)(args & 255) + adj << suffix;
|
os << prefix << (int)(args & 255) + adj << suffix;
|
||||||
return os.str();
|
return os.str();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_arg_instr_func_t dump_1c_and(unsigned mask, std::string prefix, std::string suffix) {
|
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 {
|
return [mask, prefix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << (args & mask) << suffix;
|
os << prefix << (args & mask) << suffix;
|
||||||
return os.str();
|
return os.str();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_arg_instr_func_t dump_2c(std::string prefix, std::string interfix, std::string suffix) {
|
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 {
|
return [prefix, interfix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << ((args >> 4) & 15) << interfix << (args & 15) << suffix;
|
os << prefix << ((args >> 4) & 15) << interfix << (args & 15) << suffix;
|
||||||
return os.str();
|
return os.str();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_arg_instr_func_t dump_2c_add(unsigned add, std::string prefix, std::string interfix, std::string suffix) {
|
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 {
|
return [add, prefix, interfix, suffix](CellSlice&, unsigned args) -> std::string {
|
||||||
std::ostringstream os{prefix};
|
std::ostringstream os;
|
||||||
os << ((args >> 4) & 15) + ((add >> 4) & 15) << interfix << (args & 15) + (add & 15) << suffix;
|
os << prefix << ((args >> 4) & 15) + ((add >> 4) & 15) << interfix << (args & 15) + (add & 15) << suffix;
|
||||||
return os.str();
|
return os.str();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,6 +103,9 @@ class StackEntry {
|
||||||
}
|
}
|
||||||
StackEntry(td::RefInt256 int_ref) : ref(std::move(int_ref)), tp(t_int) {
|
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) {
|
StackEntry(std::string str, bool bytes = false) : ref(), tp(bytes ? t_bytes : t_string) {
|
||||||
ref = Ref<Cnt<std::string>>{true, std::move(str)};
|
ref = Ref<Cnt<std::string>>{true, std::move(str)};
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,8 +75,8 @@ std::string dump_xchg(CellSlice&, unsigned args) {
|
||||||
if (!x || x >= y) {
|
if (!x || x >= y) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
std::ostringstream os{"XCHG s"};
|
std::ostringstream os;
|
||||||
os << x << ",s" << y;
|
os << "XCHG s" << x << ",s" << y;
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ int exec_xchg1(VmState* st, unsigned args) {
|
||||||
|
|
||||||
int exec_dup(VmState* st) {
|
int exec_dup(VmState* st) {
|
||||||
Stack& stack = st->get_stack();
|
Stack& stack = st->get_stack();
|
||||||
VM_LOG(st) << "execute DUP\n";
|
VM_LOG(st) << "execute DUP";
|
||||||
stack.check_underflow(1);
|
stack.check_underflow(1);
|
||||||
stack.push(stack.fetch(0));
|
stack.push(stack.fetch(0));
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -100,7 +100,7 @@ int exec_dup(VmState* st) {
|
||||||
|
|
||||||
int exec_over(VmState* st) {
|
int exec_over(VmState* st) {
|
||||||
Stack& stack = st->get_stack();
|
Stack& stack = st->get_stack();
|
||||||
VM_LOG(st) << "execute OVER\n";
|
VM_LOG(st) << "execute OVER";
|
||||||
stack.check_underflow(2);
|
stack.check_underflow(2);
|
||||||
stack.push(stack.fetch(1));
|
stack.push(stack.fetch(1));
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -126,7 +126,7 @@ int exec_push_l(VmState* st, unsigned args) {
|
||||||
|
|
||||||
int exec_drop(VmState* st) {
|
int exec_drop(VmState* st) {
|
||||||
Stack& stack = st->get_stack();
|
Stack& stack = st->get_stack();
|
||||||
VM_LOG(st) << "execute DROP\n";
|
VM_LOG(st) << "execute DROP";
|
||||||
stack.check_underflow(1);
|
stack.check_underflow(1);
|
||||||
stack.pop();
|
stack.pop();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -134,7 +134,7 @@ int exec_drop(VmState* st) {
|
||||||
|
|
||||||
int exec_nip(VmState* st) {
|
int exec_nip(VmState* st) {
|
||||||
Stack& stack = st->get_stack();
|
Stack& stack = st->get_stack();
|
||||||
VM_LOG(st) << "execute NIP\n";
|
VM_LOG(st) << "execute NIP";
|
||||||
stack.check_underflow(2);
|
stack.check_underflow(2);
|
||||||
stack.pop(stack[1]);
|
stack.pop(stack[1]);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -8,7 +8,11 @@ Test_Fift_bug_ufits_default 51bf5a9f1ed7633a193f6fdd17a7a3af8e032dfe72a9669c85e8
|
||||||
Test_Fift_contfrac_default 09ebce5c91bcb70696c6fb6981d82dc3b9e3444dab608a7a1b044c0ddd778a96
|
Test_Fift_contfrac_default 09ebce5c91bcb70696c6fb6981d82dc3b9e3444dab608a7a1b044c0ddd778a96
|
||||||
Test_Fift_test_default 4e44b3382963ec89f7b5c8f2ebd85da3bc8aebad5b49f5b11b14075061477b4d
|
Test_Fift_test_default 4e44b3382963ec89f7b5c8f2ebd85da3bc8aebad5b49f5b11b14075061477b4d
|
||||||
Test_Fift_test_dict_default a9c8cbcfdece5573185022cea07f59f1bc404e5d879e5157a5745757f8ee0525
|
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_fixed_default 278a19d56b773102caf5c1fe2997ea6c8d0d9e720eff8503feede6398a197eec
|
||||||
|
Test_Fift_test_hmap_default c269246882039824bb5822e896c3e6e82ef8e1251b6b251f5af8ea9fb8d05067
|
||||||
|
Test_Fift_test_namespaces_default e6419619c51332fb5e8bf22043ef415db686c47fe24f03061e5ad831014e7c6c
|
||||||
Test_Fift_test_sort2_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a
|
Test_Fift_test_sort2_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a
|
||||||
Test_Fift_test_sort_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a
|
Test_Fift_test_sort_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a
|
||||||
Test_Fift_testvm2_default 8a6e35fc0224398be9d2de39d31c86ea96965ef1eca2aa9e0af2303150ed4a7b
|
Test_Fift_testvm2_default 8a6e35fc0224398be9d2de39d31c86ea96965ef1eca2aa9e0af2303150ed4a7b
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue