1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-14 12:12:21 +00:00

Add namespaces to Fift (#641)

* Add fift-based disassembler

* Fift improvements: namespaces, hashmaps, flow controls

* Fift: add lib with better block structuring and more

* Minor changes in fift HashMap + tests (#643)

* Minor changes in fift HashMap

* Add tests for extended fift

---------

Co-authored-by: OmicronTau <omicron@ton.org>
Co-authored-by: Tolya <1449561+tolya-yanot@users.noreply.github.com>
Co-authored-by: SpyCheese <mikle98@yandex.ru>
This commit is contained in:
EmelyanenkoK 2023-03-10 14:16:29 +03:00 committed by GitHub
parent 4590ed381b
commit 865ebfce8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 2323 additions and 699 deletions

View file

@ -143,6 +143,7 @@ set(FIFT_SOURCE
fift/Dictionary.cpp
fift/Fift.cpp
fift/IntCtx.cpp
fift/HashMap.cpp
fift/Continuation.cpp
fift/SourceLookup.cpp
fift/utils.cpp
@ -151,6 +152,7 @@ set(FIFT_SOURCE
fift/Dictionary.h
fift/Fift.h
fift/IntCtx.h
fift/HashMap.h
fift/Continuation.h
fift/SourceLookup.h
fift/utils.h

View file

@ -47,6 +47,7 @@
#include "fift/Fift.h"
#include "fift/Dictionary.h"
#include "fift/SourceLookup.h"
#include "fift/IntCtx.h"
#include "fift/words.h"
#include "td/utils/logging.h"
@ -866,8 +867,9 @@ int main(int argc, char* const argv[]) {
case 'v':
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
break;
case 'V':
std::cout << "create-state build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n";
case 'V':
std::cout << "create-state build information: [ Commit: " << GitMetadata::CommitSHA1()
<< ", Date: " << GitMetadata::CommitDate() << "]\n";
std::exit(0);
break;
case 'h':

View file

@ -27,7 +27,7 @@ namespace fift {
//
bool FiftCont::print_dict_name(std::ostream& os, const IntCtx& ctx) const {
std::string word_name;
if (ctx.dictionary && ctx.dictionary->lookup_def(this, &word_name)) {
if (ctx.dictionary.lookup_def(this, &word_name)) {
if (word_name.size() && word_name.back() == ' ') {
word_name.pop_back();
}
@ -39,7 +39,7 @@ bool FiftCont::print_dict_name(std::ostream& os, const IntCtx& ctx) const {
std::string FiftCont::get_dict_name(const IntCtx& ctx) const {
std::string word_name;
if (ctx.dictionary && ctx.dictionary->lookup_def(this, &word_name)) {
if (ctx.dictionary.lookup_def(this, &word_name)) {
if (word_name.size() && word_name.back() == ' ') {
word_name.pop_back();
}
@ -63,6 +63,140 @@ bool FiftCont::dump(std::ostream& os, const IntCtx& ctx) const {
return ok;
}
//
// StackWord
//
Ref<FiftCont> StackWord::run_tail(IntCtx& ctx) const {
f(ctx.stack);
return {};
}
//
// CtxWord
//
Ref<FiftCont> CtxWord::run_tail(IntCtx& ctx) const {
f(ctx);
return {};
}
//
// CtxTailWord
//
Ref<FiftCont> CtxTailWord::run_tail(IntCtx& ctx) const {
return f(ctx);
}
//
// WordList
//
WordList::WordList(std::vector<Ref<FiftCont>>&& _list) : list(std::move(_list)) {
}
WordList::WordList(const std::vector<Ref<FiftCont>>& _list) : list(_list) {
}
WordList& WordList::push_back(Ref<FiftCont> word_def) {
list.push_back(std::move(word_def));
return *this;
}
WordList& WordList::push_back(FiftCont& wd) {
list.emplace_back(&wd);
return *this;
}
Ref<FiftCont> WordList::run_tail(IntCtx& ctx) const {
if (list.empty()) {
return {};
}
if (list.size() > 1) {
ctx.next = td::make_ref<ListCont>(std::move(ctx.next), Ref<WordList>(this), 1);
}
return list[0];
}
void WordList::close() {
list.shrink_to_fit();
}
WordList& WordList::append(const std::vector<Ref<FiftCont>>& other) {
list.insert(list.end(), other.begin(), other.end());
return *this;
}
WordList& WordList::append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end) {
list.insert(list.end(), begin, end);
return *this;
}
bool WordList::dump(std::ostream& os, const IntCtx& ctx) const {
os << "{";
for (auto entry : list) {
os << ' ';
entry->print_name(os, ctx);
}
os << " }" << std::endl;
return true;
}
//
// ListCont
//
Ref<FiftCont> ListCont::run_tail(IntCtx& ctx) const {
auto sz = list->size();
if (pos >= sz) {
return std::move(ctx.next);
} else if (ctx.next.not_null()) {
ctx.next = td::make_ref<ListCont>(SeqCont::seq(next, std::move(ctx.next)), list, pos + 1);
} else if (pos + 1 == sz) {
ctx.next = next;
} else {
ctx.next = td::make_ref<ListCont>(next, list, pos + 1);
}
return list->at(pos);
}
Ref<FiftCont> ListCont::run_modify(IntCtx& ctx) {
auto sz = list->size();
if (pos >= sz) {
return std::move(ctx.next);
}
auto cur = list->at(pos++);
if (ctx.next.not_null()) {
next = SeqCont::seq(next, std::move(ctx.next));
}
if (pos == sz) {
ctx.next = std::move(next);
} else {
ctx.next = self();
}
return cur;
}
bool ListCont::dump(std::ostream& os, const IntCtx& ctx) const {
std::string dict_name = list->get_dict_name(ctx);
if (!dict_name.empty()) {
os << "[in " << dict_name << ":] ";
}
std::size_t sz = list->size(), i, a = (pos >= 16 ? pos - 16 : 0), b = std::min(pos + 16, sz);
if (a > 0) {
os << "... ";
}
for (i = a; i < b; i++) {
if (i == pos) {
os << "**HERE** ";
}
list->at(i)->print_name(os, ctx);
os << ' ';
}
if (b < sz) {
os << "...";
}
os << std::endl;
return true;
}
//
// QuitCont
//
@ -295,12 +429,15 @@ bool GenericLitCont::print_name(std::ostream& os, const IntCtx& ctx) const {
bool sp = false;
for (auto entry : list) {
if (sp) {
os << sp;
os << ' ';
}
sp = true;
int tp = entry.type();
if (entry.is_int() || entry.is(vm::StackEntry::t_string) || entry.is(vm::StackEntry::t_bytes)) {
entry.dump(os);
} else if (entry.is_atom()) {
os << '`';
entry.dump(os);
} else {
auto cont_lit = entry.as_object<FiftCont>();
if (cont_lit.not_null()) {

View file

@ -17,6 +17,7 @@
Copyright 2020 Telegram Systems LLP
*/
#pragma once
#include <functional>
#include "common/refcnt.hpp"
#include "common/refint.h"
#include "vm/stack.hpp"
@ -76,6 +77,101 @@ class FiftCont : public td::CntObject {
}
};
typedef std::function<void(vm::Stack&)> StackWordFunc;
typedef std::function<void(IntCtx&)> CtxWordFunc;
typedef std::function<Ref<FiftCont>(IntCtx&)> CtxTailWordFunc;
class NopWord : public FiftCont {
public:
NopWord() = default;
~NopWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override {
return {};
}
};
class StackWord : public FiftCont {
StackWordFunc f;
public:
StackWord(StackWordFunc _f) : f(std::move(_f)) {
}
~StackWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class CtxWord : public FiftCont {
CtxWordFunc f;
public:
CtxWord(CtxWordFunc _f) : f(std::move(_f)) {
}
~CtxWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class CtxTailWord : public FiftCont {
CtxTailWordFunc f;
public:
CtxTailWord(CtxTailWordFunc _f) : f(std::move(_f)) {
}
~CtxTailWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class WordList : public FiftCont {
std::vector<Ref<FiftCont>> list;
public:
~WordList() override = default;
WordList() = default;
WordList(std::vector<Ref<FiftCont>>&& _list);
WordList(const std::vector<Ref<FiftCont>>& _list);
WordList& push_back(Ref<FiftCont> word_def);
WordList& push_back(FiftCont& wd);
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
void close();
bool is_list() const override {
return true;
}
long long list_size() const override {
return (long long)list.size();
}
std::size_t size() const {
return list.size();
}
const Ref<FiftCont>& at(std::size_t idx) const {
return list.at(idx);
}
const Ref<FiftCont>* get_list() const override {
return list.data();
}
WordList& append(const std::vector<Ref<FiftCont>>& other);
WordList& append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end);
WordList* make_copy() const override {
return new WordList(list);
}
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class ListCont : public FiftCont {
Ref<FiftCont> next;
Ref<WordList> list;
std::size_t pos;
public:
ListCont(Ref<FiftCont> nxt, Ref<WordList> wl, std::size_t p = 0) : next(std::move(nxt)), list(std::move(wl)), pos(p) {
}
~ListCont() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
Ref<FiftCont> up() const override {
return next;
}
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class QuitCont : public FiftCont {
int exit_code;

View file

@ -17,143 +17,10 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#include "Dictionary.h"
#include "IntCtx.h"
namespace fift {
//
// StackWord
//
Ref<FiftCont> StackWord::run_tail(IntCtx& ctx) const {
f(ctx.stack);
return {};
}
//
// CtxWord
//
Ref<FiftCont> CtxWord::run_tail(IntCtx& ctx) const {
f(ctx);
return {};
}
//
// CtxTailWord
//
Ref<FiftCont> CtxTailWord::run_tail(IntCtx& ctx) const {
return f(ctx);
}
//
// WordList
//
WordList::WordList(std::vector<Ref<FiftCont>>&& _list) : list(std::move(_list)) {
}
WordList::WordList(const std::vector<Ref<FiftCont>>& _list) : list(_list) {
}
WordList& WordList::push_back(Ref<FiftCont> word_def) {
list.push_back(std::move(word_def));
return *this;
}
WordList& WordList::push_back(FiftCont& wd) {
list.emplace_back(&wd);
return *this;
}
Ref<FiftCont> WordList::run_tail(IntCtx& ctx) const {
if (list.empty()) {
return {};
}
if (list.size() > 1) {
ctx.next = td::make_ref<ListCont>(std::move(ctx.next), Ref<WordList>(this), 1);
}
return list[0];
}
void WordList::close() {
list.shrink_to_fit();
}
WordList& WordList::append(const std::vector<Ref<FiftCont>>& other) {
list.insert(list.end(), other.begin(), other.end());
return *this;
}
WordList& WordList::append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end) {
list.insert(list.end(), begin, end);
return *this;
}
bool WordList::dump(std::ostream& os, const IntCtx& ctx) const {
os << "{";
for (auto entry : list) {
os << ' ';
entry->print_name(os, ctx);
}
os << " }" << std::endl;
return true;
}
//
// ListCont
//
Ref<FiftCont> ListCont::run_tail(IntCtx& ctx) const {
auto sz = list->size();
if (pos >= sz) {
return std::move(ctx.next);
} else if (ctx.next.not_null()) {
ctx.next = td::make_ref<ListCont>(SeqCont::seq(next, std::move(ctx.next)), list, pos + 1);
} else if (pos + 1 == sz) {
ctx.next = next;
} else {
ctx.next = td::make_ref<ListCont>(next, list, pos + 1);
}
return list->at(pos);
}
Ref<FiftCont> ListCont::run_modify(IntCtx& ctx) {
auto sz = list->size();
if (pos >= sz) {
return std::move(ctx.next);
}
auto cur = list->at(pos++);
if (ctx.next.not_null()) {
next = SeqCont::seq(next, std::move(ctx.next));
}
if (pos == sz) {
ctx.next = std::move(next);
} else {
ctx.next = self();
}
return cur;
}
bool ListCont::dump(std::ostream& os, const IntCtx& ctx) const {
std::string dict_name = list->get_dict_name(ctx);
if (!dict_name.empty()) {
os << "[in " << dict_name << ":] ";
}
std::size_t sz = list->size(), i, a = (pos >= 16 ? pos - 16 : 0), b = std::min(pos + 16, sz);
if (a > 0) {
os << "... ";
}
for (i = a; i < b; i++) {
if (i == pos) {
os << "**HERE** ";
}
list->at(i)->print_name(os, ctx);
os << ' ';
}
if (b < sz) {
os << "...";
}
os << std::endl;
return true;
}
//
// DictEntry
//
@ -167,15 +34,49 @@ DictEntry::DictEntry(CtxWordFunc func, bool _act) : def(Ref<CtxWord>{true, std::
DictEntry::DictEntry(CtxTailWordFunc func, bool _act) : def(Ref<CtxTailWord>{true, std::move(func)}), active(_act) {
}
DictEntry DictEntry::create_from(vm::StackEntry se) {
if (se.is_tuple()) {
auto& tuple = *se.as_tuple();
if (tuple.size() == 1) {
auto def = tuple[0].as_object<FiftCont>();
if (def.not_null()) {
return DictEntry{std::move(def), true};
}
}
} else {
auto def = std::move(se).as_object<FiftCont>();
if (def.not_null()) {
return DictEntry{std::move(def)};
}
}
return {};
}
DictEntry::operator vm::StackEntry() const& {
if (def.is_null()) {
return {};
} else if (active) {
return vm::make_tuple_ref(vm::StackEntry{vm::from_object, def});
} else {
return {vm::from_object, def};
}
}
DictEntry::operator vm::StackEntry() && {
if (def.is_null()) {
return {};
} else if (active) {
return vm::make_tuple_ref(vm::StackEntry{vm::from_object, std::move(def)});
} else {
return {vm::from_object, std::move(def)};
}
}
//
// Dictionary
//
DictEntry* Dictionary::lookup(td::Slice name) {
auto it = words_.find(name);
if (it == words_.end()) {
return nullptr;
}
return &it->second;
DictEntry Dictionary::lookup(std::string name) const {
return DictEntry::create_from(words().get(name));
}
void Dictionary::def_ctx_word(std::string name, CtxWordFunc func) {
@ -196,26 +97,27 @@ void Dictionary::def_ctx_tail_word(std::string name, CtxTailWordFunc func) {
}
void Dictionary::def_word(std::string name, DictEntry word) {
auto res = words_.emplace(name, std::move(word));
LOG_IF(FATAL, !res.second) << "Cannot redefine word: " << name;
auto dict = words();
dict.set(std::move(name), vm::StackEntry(std::move(word)));
set_words(dict);
}
void Dictionary::undef_word(td::Slice name) {
auto it = words_.find(name);
if (it == words_.end()) {
return;
void Dictionary::undef_word(std::string name) {
auto dict = words();
if (dict.remove(name)) {
set_words(dict);
}
words_.erase(it);
}
bool Dictionary::lookup_def(const FiftCont* cont, std::string* word_ptr) const {
if (!cont) {
return false;
}
for (const auto& entry : words_) {
if (entry.second.get_def().get() == cont) {
for (auto entry : words()) {
auto val = DictEntry::create_from(entry.value());
if (val.get_def().get() == cont && entry.key().is_string()) {
if (word_ptr) {
*word_ptr = entry.first;
*word_ptr = vm::StackEntry(entry.key()).as_string();
}
return true;
}
@ -223,35 +125,4 @@ bool Dictionary::lookup_def(const FiftCont* cont, std::string* word_ptr) const {
return false;
}
void interpret_nop(vm::Stack& stack) {
}
Ref<FiftCont> Dictionary::nop_word_def = Ref<StackWord>{true, interpret_nop};
//
// functions for wordef
//
Ref<FiftCont> pop_exec_token(vm::Stack& stack) {
stack.check_underflow(1);
auto wd_ref = stack.pop().as_object<FiftCont>();
if (wd_ref.is_null()) {
throw IntError{"execution token expected"};
}
return wd_ref;
}
Ref<WordList> pop_word_list(vm::Stack& stack) {
stack.check_underflow(1);
auto wl_ref = stack.pop().as_object<WordList>();
if (wl_ref.is_null()) {
throw IntError{"word list expected"};
}
return wl_ref;
}
void push_argcount(vm::Stack& stack, int args) {
stack.push_smallint(args);
stack.push({vm::from_object, Dictionary::nop_word_def});
}
} // namespace fift

View file

@ -17,115 +17,27 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include <functional>
#include <map>
#include "IntCtx.h"
#include "Continuation.h"
#include "HashMap.h"
#include "vm/box.hpp"
namespace fift {
using td::Ref;
struct IntCtx;
/*
*
* WORD CLASSES
*
*/
typedef std::function<void(vm::Stack&)> StackWordFunc;
typedef std::function<void(IntCtx&)> CtxWordFunc;
class StackWord : public FiftCont {
StackWordFunc f;
public:
StackWord(StackWordFunc _f) : f(std::move(_f)) {
}
~StackWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class CtxWord : public FiftCont {
CtxWordFunc f;
public:
CtxWord(CtxWordFunc _f) : f(std::move(_f)) {
}
~CtxWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
typedef std::function<Ref<FiftCont>(IntCtx&)> CtxTailWordFunc;
class CtxTailWord : public FiftCont {
CtxTailWordFunc f;
public:
CtxTailWord(CtxTailWordFunc _f) : f(std::move(_f)) {
}
~CtxTailWord() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class WordList : public FiftCont {
std::vector<Ref<FiftCont>> list;
public:
~WordList() override = default;
WordList() = default;
WordList(std::vector<Ref<FiftCont>>&& _list);
WordList(const std::vector<Ref<FiftCont>>& _list);
WordList& push_back(Ref<FiftCont> word_def);
WordList& push_back(FiftCont& wd);
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
void close();
bool is_list() const override {
return true;
}
long long list_size() const override {
return (long long)list.size();
}
std::size_t size() const {
return list.size();
}
const Ref<FiftCont>& at(std::size_t idx) const {
return list.at(idx);
}
const Ref<FiftCont>* get_list() const override {
return list.data();
}
WordList& append(const std::vector<Ref<FiftCont>>& other);
WordList& append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end);
WordList* make_copy() const override {
return new WordList(list);
}
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class ListCont : public FiftCont {
Ref<FiftCont> next;
Ref<WordList> list;
std::size_t pos;
public:
ListCont(Ref<FiftCont> nxt, Ref<WordList> wl, std::size_t p = 0) : next(std::move(nxt)), list(std::move(wl)), pos(p) {
}
~ListCont() override = default;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
Ref<FiftCont> up() const override {
return next;
}
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class DictEntry {
Ref<FiftCont> def;
bool active;
bool active{false};
public:
DictEntry() = delete;
DictEntry() = default;
DictEntry(const DictEntry& ref) = default;
DictEntry(DictEntry&& ref) = default;
DictEntry(Ref<FiftCont> _def, bool _act = false) : def(std::move(_def)), active(_act) {
@ -137,6 +49,9 @@ class DictEntry {
//DictEntry(std::vector<Ref<FiftCont>>&& word_list);
DictEntry& operator=(const DictEntry&) = default;
DictEntry& operator=(DictEntry&&) = default;
static DictEntry create_from(vm::StackEntry se);
explicit operator vm::StackEntry() const&;
explicit operator vm::StackEntry() &&;
Ref<FiftCont> get_def() const& {
return def;
}
@ -146,16 +61,17 @@ class DictEntry {
bool is_active() const {
return active;
}
bool empty() const {
return def.is_null();
}
explicit operator bool() const {
return def.not_null();
}
bool operator!() const {
return def.is_null();
}
};
/*
DictEntry::DictEntry(const std::vector<Ref<FiftCont>>& word_list) : def(Ref<WordList>{true, word_list}) {
}
DictEntry::DictEntry(std::vector<Ref<FiftCont>>&& word_list) : def(Ref<WordList>{true, std::move(word_list)}) {
}
*/
/*
*
* DICTIONARIES
@ -164,37 +80,52 @@ DictEntry::DictEntry(std::vector<Ref<FiftCont>>&& word_list) : def(Ref<WordList>
class Dictionary {
public:
DictEntry* lookup(td::Slice name);
Dictionary() : box_(true) {
}
Dictionary(Ref<vm::Box> box) : box_(std::move(box)) {
}
Dictionary(Ref<Hashmap> hmap) : box_(true, vm::from_object, std::move(hmap)) {
}
DictEntry lookup(std::string name) const;
void def_ctx_word(std::string name, CtxWordFunc func);
void def_ctx_tail_word(std::string name, CtxTailWordFunc func);
void def_active_word(std::string name, CtxWordFunc func);
void def_stack_word(std::string name, StackWordFunc func);
void def_word(std::string name, DictEntry word);
void undef_word(td::Slice name);
void undef_word(std::string name);
bool lookup_def(const FiftCont* cont, std::string* word_ptr = nullptr) const;
bool lookup_def(Ref<FiftCont> cont, std::string* word_ptr = nullptr) const {
return lookup_def(cont.get(), word_ptr);
}
auto begin() const {
return words_.begin();
return words().begin();
}
auto end() const {
return words_.end();
return words().end();
}
HashmapKeeper words() const {
if (box_->empty()) {
return {};
} else {
return box_->get().as_object<Hashmap>();
}
}
Ref<vm::Box> get_box() const {
return box_;
}
void set_words(Ref<Hashmap> new_words) {
box_->set(vm::StackEntry{vm::from_object, std::move(new_words)});
}
bool operator==(const Dictionary& other) const {
return box_ == other.box_;
}
bool operator!=(const Dictionary& other) const {
return box_ != other.box_;
}
static Ref<FiftCont> nop_word_def;
private:
std::map<std::string, DictEntry, std::less<>> words_;
Ref<vm::Box> box_;
};
/*
*
* AUX FUNCTIONS FOR WORD DEFS
*
*/
Ref<FiftCont> pop_exec_token(vm::Stack& stack);
Ref<WordList> pop_word_list(vm::Stack& stack);
void push_argcount(vm::Stack& stack, int args);
} // namespace fift

View file

@ -17,7 +17,7 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#include "Fift.h"
#include "IntCtx.h"
#include "words.h"
#include "td/utils/PathView.h"
@ -51,7 +51,7 @@ td::Result<int> Fift::interpret_istream(std::istream& stream, std::string curren
td::Result<int> Fift::do_interpret(IntCtx& ctx, bool is_interactive) {
ctx.ton_db = &config_.ton_db;
ctx.source_lookup = &config_.source_lookup;
ctx.dictionary = &config_.dictionary;
ctx.dictionary = ctx.main_dictionary = ctx.context = config_.dictionary;
ctx.output_stream = config_.output_stream;
ctx.error_stream = config_.error_stream;
if (!ctx.output_stream) {
@ -71,7 +71,7 @@ td::Result<int> Fift::do_interpret(IntCtx& ctx, bool is_interactive) {
ctx.top_ctx();
ctx.clear_error();
ctx.stack.clear();
ctx.load_next_line();
ctx.parser->load_next_line();
continue;
}
}

371
crypto/fift/HashMap.cpp Normal file
View 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
View 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

View file

@ -20,7 +20,7 @@
namespace fift {
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx) {
td::StringBuilder& operator<<(td::StringBuilder& os, const ParseCtx& ctx) {
if (ctx.include_depth) {
return os << ctx.filename << ":" << ctx.line_no << ": ";
} else {
@ -28,7 +28,7 @@ td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx) {
}
}
std::ostream& operator<<(std::ostream& os, const IntCtx& ctx) {
std::ostream& operator<<(std::ostream& os, const ParseCtx& ctx) {
return os << (PSLICE() << ctx).c_str();
}
@ -67,73 +67,7 @@ void CharClassifier::set_char_class(int c, int cl) {
*p = static_cast<unsigned char>((*p & ~mask) | cl);
}
IntCtx::Savepoint::Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
std::unique_ptr<std::istream> new_input_stream)
: ctx(_ctx)
, old_line_no(_ctx.line_no)
, old_need_line(_ctx.need_line)
, old_filename(_ctx.filename)
, old_current_dir(_ctx.currentd_dir)
, old_input_stream(_ctx.input_stream)
, old_input_stream_holder(std::move(_ctx.input_stream_holder))
, old_curline(_ctx.str)
, old_curpos(_ctx.input_ptr - _ctx.str.c_str())
, old_word(_ctx.word) {
ctx.line_no = 0;
ctx.filename = new_filename;
ctx.currentd_dir = new_current_dir;
ctx.input_stream = new_input_stream.get();
ctx.input_stream_holder = std::move(new_input_stream);
ctx.str = "";
ctx.input_ptr = 0;
++(ctx.include_depth);
}
bool IntCtx::Savepoint::restore(IntCtx& _ctx) {
if (restored || &ctx != &_ctx) {
return false;
}
ctx.line_no = old_line_no;
ctx.need_line = old_need_line;
ctx.filename = old_filename;
ctx.currentd_dir = old_current_dir;
ctx.input_stream = old_input_stream;
ctx.input_stream_holder = std::move(old_input_stream_holder);
ctx.str = old_curline;
ctx.input_ptr = ctx.str.c_str() + old_curpos;
ctx.word = old_word;
--(ctx.include_depth);
return restored = true;
}
bool IntCtx::enter_ctx(std::string new_filename, std::string new_current_dir,
std::unique_ptr<std::istream> new_input_stream) {
if (!new_input_stream) {
return false;
}
ctx_save_stack.emplace_back(*this, std::move(new_filename), std::move(new_current_dir), std::move(new_input_stream));
return true;
}
bool IntCtx::leave_ctx() {
if (ctx_save_stack.empty()) {
return false;
}
bool ok = ctx_save_stack.back().restore(*this);
ctx_save_stack.pop_back();
return ok;
}
bool IntCtx::top_ctx() {
while (!ctx_save_stack.empty()) {
if (!leave_ctx()) {
return false;
}
}
return true;
}
bool IntCtx::load_next_line() {
bool ParseCtx::load_next_line() {
if (!std::getline(*input_stream, str)) {
return false;
}
@ -145,11 +79,11 @@ bool IntCtx::load_next_line() {
return true;
}
bool IntCtx::is_sb() const {
bool ParseCtx::is_sb() const {
return !eof() && line_no == 1 && *input_ptr == '#' && input_ptr[1] == '!';
}
td::Slice IntCtx::scan_word_to(char delim, bool err_endl) {
td::Slice ParseCtx::scan_word_to(char delim, bool err_endl) {
load_next_line_ifreq();
auto ptr = input_ptr;
while (*ptr && *ptr != delim) {
@ -167,7 +101,7 @@ td::Slice IntCtx::scan_word_to(char delim, bool err_endl) {
}
}
td::Slice IntCtx::scan_word() {
td::Slice ParseCtx::scan_word() {
skipspc(true);
auto ptr = input_ptr;
while (*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '\r') {
@ -179,7 +113,7 @@ td::Slice IntCtx::scan_word() {
return td::Slice{ptr, ptr2};
}
td::Slice IntCtx::scan_word_ext(const CharClassifier& classifier) {
td::Slice ParseCtx::scan_word_ext(const CharClassifier& classifier) {
skipspc(true);
auto ptr = input_ptr;
while (*ptr && *ptr != '\r' && *ptr != '\n') {
@ -196,7 +130,7 @@ td::Slice IntCtx::scan_word_ext(const CharClassifier& classifier) {
return td::Slice{ptr, input_ptr};
}
void IntCtx::skipspc(bool skip_eol) {
void ParseCtx::skipspc(bool skip_eol) {
do {
while (*input_ptr == ' ' || *input_ptr == '\t' || *input_ptr == '\r') {
++input_ptr;
@ -207,6 +141,45 @@ void IntCtx::skipspc(bool skip_eol) {
} while (load_next_line());
}
bool IntCtx::enter_ctx(std::unique_ptr<ParseCtx> new_parser) {
if (!new_parser) {
return false;
}
if (parser) {
parser_save_stack.push_back(std::move(parser));
}
parser = std::move(new_parser);
return true;
}
bool IntCtx::enter_ctx(std::string new_filename, std::string new_current_dir,
std::unique_ptr<std::istream> new_input_stream) {
if (!new_input_stream) {
return false;
} else {
return enter_ctx(
std::make_unique<ParseCtx>(std::move(new_input_stream), new_filename, new_current_dir, include_depth() + 1));
}
}
bool IntCtx::leave_ctx() {
if (parser_save_stack.empty()) {
return false;
} else {
parser = std::move(parser_save_stack.back());
parser_save_stack.pop_back();
return true;
}
}
bool IntCtx::top_ctx() {
if (!parser_save_stack.empty()) {
parser = std::move(parser_save_stack[0]);
parser_save_stack.clear();
}
return true;
}
void IntCtx::check_compile() const {
if (state <= 0) {
throw IntError{"compilation mode only"};
@ -283,15 +256,20 @@ td::Result<int> IntCtx::get_result() {
}
}
std::ostream& ParseCtx::show_context(std::ostream& os) const {
if (include_depth && line_no) {
os << filename << ":" << line_no << ":\t";
}
if (!word.empty()) {
os << word << ":";
}
return os;
}
td::Status IntCtx::add_error_loc(td::Status err) const {
if (err.is_error()) {
if (err.is_error() && parser) {
std::ostringstream os;
if (include_depth && line_no) {
os << filename << ":" << line_no << ":\t";
}
if (!word.empty()) {
os << word << ":";
}
parser->show_context(os);
return err.move_as_error_prefix(os.str());
} else {
return err;

View file

@ -18,8 +18,8 @@
*/
#pragma once
#include "crypto/vm/db/TonDb.h" // FIXME
#include "crypto/vm/stack.hpp"
#include "crypto/vm/box.hpp"
#include "crypto/common/bitstring.h"
#include "td/utils/Status.h"
@ -32,6 +32,11 @@
#include <iostream>
#include <string>
namespace vm {
class TonDbImpl; // from crypto/vm/db/TonDb.h
using TonDb = std::unique_ptr<TonDbImpl>;
} // namespace vm
namespace fift {
class Dictionary;
class SourceLookup;
@ -68,71 +73,36 @@ class CharClassifier {
}
};
struct IntCtx {
vm::Stack stack;
Ref<FiftCont> next, exc_handler;
Ref<FiftCont> exc_cont, exc_next;
int state{0};
struct ParseCtx {
int include_depth{0};
int line_no{0};
int exit_code{0};
td::Status error;
bool need_line{true};
std::string filename;
std::string currentd_dir;
std::istream* input_stream{nullptr};
std::unique_ptr<std::istream> input_stream_holder;
std::ostream* output_stream{nullptr};
std::ostream* error_stream{nullptr};
vm::TonDb* ton_db{nullptr};
Dictionary* dictionary{nullptr};
SourceLookup* source_lookup{nullptr};
int* now{nullptr};
std::string word;
private:
std::string str;
const char* input_ptr = nullptr;
class Savepoint {
IntCtx& ctx;
int old_line_no;
bool old_need_line;
bool restored{false};
std::string old_filename;
std::string old_current_dir;
std::istream* old_input_stream;
std::unique_ptr<std::istream> old_input_stream_holder;
std::string old_curline;
std::ptrdiff_t old_curpos;
std::string old_word;
public:
Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
std::unique_ptr<std::istream> new_input_stream);
bool restore(IntCtx& _ctx);
};
std::vector<Savepoint> ctx_save_stack;
public:
IntCtx() = default;
IntCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0)
ParseCtx() = default;
ParseCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0)
: include_depth(_depth)
, filename(std::move(_filename))
, currentd_dir(std::move(_curdir))
, input_stream(&_istream) {
}
operator vm::Stack&() {
return stack;
ParseCtx(std::unique_ptr<std::istream> _istream_ptr, std::string _filename, std::string _curdir = "", int _depth = 0)
: include_depth(_depth)
, filename(std::move(_filename))
, currentd_dir(std::move(_curdir))
, input_stream(_istream_ptr.get())
, input_stream_holder(std::move(_istream_ptr)) {
}
bool enter_ctx(std::string new_filename, std::string new_current_dir, std::unique_ptr<std::istream> new_input_stream);
bool leave_ctx();
bool top_ctx();
td::Slice scan_word_to(char delim, bool err_endl = true);
td::Slice scan_word();
td::Slice scan_word_ext(const CharClassifier& classifier);
@ -165,6 +135,50 @@ struct IntCtx {
bool is_sb() const;
std::ostream& show_context(std::ostream& os) const;
};
struct IntCtx {
vm::Stack stack;
Ref<FiftCont> next, exc_handler;
Ref<FiftCont> exc_cont, exc_next;
int state{0};
int exit_code{0};
td::Status error;
std::unique_ptr<ParseCtx> parser;
std::vector<std::unique_ptr<ParseCtx>> parser_save_stack;
std::ostream* output_stream{nullptr}; // move to OutCtx?
std::ostream* error_stream{nullptr};
vm::TonDb* ton_db{nullptr};
SourceLookup* source_lookup{nullptr};
int* now{nullptr};
Dictionary dictionary, main_dictionary, context;
public:
IntCtx() = default;
IntCtx(std::istream& _istream, std::string _filename, std::string _curdir = "", int _depth = 0) {
parser = std::make_unique<ParseCtx>(_istream, _filename, _curdir, _depth);
}
IntCtx(std::unique_ptr<std::istream> _istream, std::string _filename, std::string _curdir = "", int _depth = 0) {
parser = std::make_unique<ParseCtx>(std::move(_istream), _filename, _curdir, _depth);
}
bool enter_ctx(std::unique_ptr<ParseCtx> new_ctx);
bool enter_ctx(std::string new_filename, std::string new_current_dir, std::unique_ptr<std::istream> new_input_stream);
bool leave_ctx();
bool top_ctx();
int include_depth() const {
return parser ? parser->include_depth : -1;
}
operator vm::Stack &() {
return stack;
}
void clear() {
state = 0;
stack.clear();
@ -194,6 +208,6 @@ struct IntCtx {
td::Result<int> run(Ref<FiftCont> cont);
};
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx);
std::ostream& operator<<(std::ostream& os, const IntCtx& ctx);
td::StringBuilder& operator<<(td::StringBuilder& os, const ParseCtx& ctx);
std::ostream& operator<<(std::ostream& os, const ParseCtx& ctx);
} // namespace fift

View file

@ -1,12 +1,14 @@
library TVM_Asm
// simple TVM Assembler
namespace Asm
Asm definitions
variable @atend
variable @was-split
false @was-split !
{ "not in asm context" abort } @atend !
{ `normal eq? not abort"must be terminated by }>" } : @normal?
{ @atend @ 1 { @atend ! @normal? } does @atend ! } : @pushatend
{ @pushatend <b } : <{
{ context@ @atend @ 2 { @atend ! context! @normal? } does @atend ! } : @pushatend
{ @pushatend Asm <b } : <{
{ @atend @ execute } : @endblk
{ false @was-split ! `normal @endblk } : }>
{ }> b> } : }>c
@ -1219,7 +1221,10 @@ variable asm-mode 1 asm-mode !
{ 1 'nop does swap 0 (create) } : @declglobvar
{ @proccnt @ 1+ dup @proccnt ! 1 @declproc } : @newproc
{ @gvarcnt @ 1+ dup @gvarcnt ! @declglobvar } : @newglobvar
{ 0 =: main @proclist null! @proccnt 0! @gvarcnt 0!
variable @oldcurrent variable @oldctx
Fift-wordlist dup @oldcurrent ! @oldctx !
{ current@ @oldcurrent ! context@ @oldctx ! Asm definitions
0 =: main @proclist null! @proccnt 0! @gvarcnt 0!
{ bl word @newproc } : NEWPROC
{ bl word dup (def?) ' drop ' @newproc cond } : DECLPROC
{ bl word dup find
@ -1293,6 +1298,7 @@ variable asm-mode 1 asm-mode !
} while
drop @proclist null! @procinfo null! @proccnt 0!
@procdict dup @ swap null!
@oldctx @ context! @oldcurrent @ current!
} : }END
forget @proclist forget @proccnt
{ }END <{ SETCP0 swap @procdictkeylen DICTPUSHCONST DICTIGETJMPZ 11 THROWARG }> } : }END>
@ -1319,3 +1325,8 @@ forget @proclist forget @proccnt
{ <b 2 8 u, swap 256 u, b>spec } : hash>libref
// ( c -- c' )
{ hash hash>libref } : >libref
Fift definitions Asm
' <{ : <{
' PROGRAM{ : PROGRAM{
Fift

141
crypto/fift/lib/Disasm.fif Normal file
View 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 !

View file

@ -76,6 +76,8 @@ variable base
{ def? ' skip-to-eof if } : skip-ifdef
{ bl word dup (def?) { drop skip-to-eof } { 'nop swap 0 (create) } cond } : library
{ bl word dup (def?) { 2drop skip-to-eof } { swap 1 'nop does swap 0 (create) } cond } : library-version
{ hole dup 1 'nop does swap 1 { context! } does bl word tuck 0 (create) +"-wordlist" 0 (create) } : namespace
{ context@ current! } : definitions
{ char ) word "$" swap $+ 1 { find 0= abort"undefined parameter" execute } } ::_ $(
// b s -- ?
{ sbitrefs rot brembitrefs rot >= -rot <= and } : s-fits?

118
crypto/fift/lib/FiftExt.fif Normal file
View 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

View file

@ -59,6 +59,12 @@ td::Result<std::string> load_GetOpt_fif(std::string dir = "") {
td::Result<std::string> load_wallet3_code_fif(std::string dir = "") {
return td::read_file_str(smartcont_dir(dir) + "wallet-v3-code.fif");
}
td::Result<std::string> load_FiftExt_fif(std::string dir = "") {
return load_source("FiftExt.fif", dir);
}
td::Result<std::string> load_Disasm_fif(std::string dir = "") {
return load_source("Disasm.fif", dir);
}
class MemoryFileLoader : public fift::FileLoader {
public:
@ -108,7 +114,8 @@ class MemoryFileLoader : public fift::FileLoader {
td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_preamble = true, bool need_asm = true,
bool need_ton_util = true, bool need_lisp = true,
bool need_w3_code = true, std::string dir = "") {
bool need_w3_code = true, bool need_fift_ext = true,
bool need_disasm = true, std::string dir = "") {
auto loader = std::make_unique<MemoryFileLoader>();
loader->add_file("/main.fif", std::move(main));
if (need_preamble) {
@ -141,6 +148,14 @@ td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_
TRY_RESULT(f, load_wallet3_code_fif(dir));
loader->add_file("/wallet-v3-code.fif", std::move(f));
}
if (need_fift_ext) {
TRY_RESULT(f, load_FiftExt_fif(dir));
loader->add_file("/FiftExt.fif", std::move(f));
}
if (need_disasm) {
TRY_RESULT(f, load_Disasm_fif(dir));
loader->add_file("/Disasm.fif", std::move(f));
}
auto res = fift::SourceLookup(std::move(loader));
res.add_include_path("/");
return std::move(res);
@ -172,7 +187,7 @@ td::Result<fift::SourceLookup> run_fift(fift::SourceLookup source_lookup, std::o
} // namespace
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args, std::string fift_dir) {
std::stringstream ss;
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, true, fift_dir));
TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, true, true, true, fift_dir));
TRY_RESULT_ASSIGN(source_lookup, run_fift(std::move(source_lookup), &ss, true, std::move(args)));
FiftOutput res;
res.source_lookup = std::move(source_lookup);
@ -190,7 +205,8 @@ td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std:
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir, bool need_preamble,
bool need_asm, bool need_ton_util, bool need_lisp,
bool need_w3_code) {
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, fift_dir);
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, need_w3_code, false, false,
fift_dir);
}
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir, bool is_raw) {
@ -198,7 +214,7 @@ td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_d
TRY_RESULT(source_lookup,
create_source_lookup(PSTRING() << "\"Asm.fif\" include\n " << (is_raw ? "<{" : "") << asm_code << "\n"
<< (is_raw ? "}>c" : "") << " boc>B \"res\" B>file",
true, true, true, false, false, fift_dir));
true, true, true, false, false, false, false, fift_dir));
TRY_RESULT(res, run_fift(std::move(source_lookup), &ss));
TRY_RESULT(boc, res.read_file("res"));
return vm::std_boc_deserialize(std::move(boc.data));

View file

@ -21,6 +21,7 @@
#include "Dictionary.h"
#include "IntCtx.h"
#include "SourceLookup.h"
#include "HashMap.h"
#include "common/refcnt.hpp"
#include "common/bigint.hpp"
@ -42,6 +43,8 @@
#include "vm/box.hpp"
#include "vm/atom.h"
#include "vm/db/TonDb.h" // only for interpret_db_run_vm{,_parallel}
#include "block/block.h"
#include "td/utils/filesystem.h"
@ -58,12 +61,31 @@
namespace fift {
void show_total_cells(std::ostream& stream) {
stream << "total cells = " << vm::DataCell::get_total_data_cells() << std::endl;
const Ref<FiftCont> nop_word_def = Ref<NopWord>{true};
//
// functions for wordef
//
Ref<FiftCont> pop_exec_token(vm::Stack& stack) {
auto wd_ref = stack.pop_chk().as_object<FiftCont>();
if (wd_ref.is_null()) {
throw IntError{"execution token expected"};
}
return wd_ref;
}
void do_compile(vm::Stack& stack, Ref<FiftCont> word_def);
void do_compile_literals(vm::Stack& stack, int count);
Ref<WordList> pop_word_list(vm::Stack& stack) {
auto wl_ref = stack.pop_chk().as_object<WordList>();
if (wl_ref.is_null()) {
throw IntError{"word list expected"};
}
return wl_ref;
}
void push_argcount(vm::Stack& stack, int args) {
stack.push_smallint(args);
stack.push_object(nop_word_def);
}
void interpret_dot(IntCtx& ctx, bool space_after) {
*ctx.output_stream << dec_string2(ctx.stack.pop_int()) << (space_after ? " " : "");
@ -121,7 +143,7 @@ void interpret_print_list(IntCtx& ctx) {
}
void interpret_dottc(IntCtx& ctx) {
show_total_cells(*ctx.output_stream);
*ctx.output_stream << "total cells = " << vm::DataCell::get_total_data_cells() << std::endl;
}
void interpret_dot_internal(vm::Stack& stack) {
@ -461,7 +483,7 @@ void interpret_make_xchg(vm::Stack& stack) {
if (x) {
stack.push_object(td::Ref<StackWord>{true, std::bind(interpret_xchg, _1, x, y)});
} else if (y <= 1) {
stack.push_object(y ? swap_word_def : Dictionary::nop_word_def);
stack.push_object(y ? swap_word_def : nop_word_def);
} else {
stack.push_object(td::Ref<StackWord>{true, std::bind(interpret_xchg0, _1, y)});
}
@ -1080,6 +1102,31 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) {
}
}
void interpret_fetch_slice(vm::Stack& stack, int mode) {
unsigned refs = ((mode & 1) ? stack.pop_smallint_range(4) : 0);
unsigned bits = stack.pop_smallint_range(1023);
auto cs = stack.pop_cellslice();
if (!cs->have(bits, refs)) {
if (mode & 2) {
stack.push(std::move(cs));
}
stack.push_bool(false);
if (!(mode & 4)) {
throw IntError{"end of data while fetching subslice from cell"};
}
} else {
if (mode & 2) {
stack.push(cs.write().fetch_subslice(bits, refs));
stack.push(std::move(cs));
} else {
stack.push(cs->prefetch_subslice(bits, refs));
}
if (mode & 4) {
stack.push_bool(true);
}
}
}
void interpret_cell_empty(vm::Stack& stack) {
auto cs = stack.pop_cellslice();
stack.push_bool(cs->empty_ext());
@ -1483,6 +1530,150 @@ void interpret_crc32c(vm::Stack& stack) {
stack.push_smallint(td::crc32c(td::Slice{str}));
}
// Fift hashmaps
void push_hmap(vm::Stack& stack, Ref<Hashmap> hmap) {
if (hmap.not_null()) {
stack.push_object(std::move(hmap));
} else {
stack.push({});
}
}
void push_hmap(vm::Stack& stack, HashmapKeeper hmap_keep) {
push_hmap(stack, hmap_keep.extract());
}
Ref<Hashmap> pop_hmap(vm::Stack& stack) {
stack.check_underflow(1);
auto se = stack.pop();
if (se.is_null()) {
return {};
}
auto hmap_ref = std::move(se).as_object<Hashmap>();
if (hmap_ref.is_null()) {
throw IntError{"hashmap expected"};
}
return hmap_ref;
}
HashmapKeeper pop_hmap_keeper(vm::Stack& stack) {
return HashmapKeeper{pop_hmap(stack)};
}
void interpret_hmap_new(vm::Stack& stack) {
stack.push({});
}
void interpret_hmap_fetch(vm::Stack& stack, int mode) {
auto hmap = pop_hmap(stack);
auto value = Hashmap::get(std::move(hmap), stack.pop_chk());
bool found = !value.is_null();
if ((mode & 8) && !found) {
throw IntError{"hashmap key not found"};
}
if (mode & (2 << (int)found)) {
stack.push(std::move(value));
}
if (mode & 1) {
stack.push_bool(found);
}
}
void interpret_hmap_delete(vm::Stack& stack, int mode) {
auto hmap = pop_hmap(stack);
auto res = Hashmap::get_remove(std::move(hmap), stack.pop_chk());
push_hmap(stack, std::move(res.first));
bool found = !res.second.is_null();
if ((mode & 8) && !found) {
throw IntError{"hashmap key not found"};
}
if (mode & (2 << (int)found)) {
stack.push(std::move(res.second));
}
if (mode & 1) {
stack.push_bool(found);
}
}
void interpret_hmap_store(vm::Stack& stack, int mode) {
stack.check_underflow(3);
auto hmap = pop_hmap_keeper(stack);
auto key = stack.pop(), value = stack.pop();
bool ok = true;
if (mode & 1) {
hmap.set(std::move(key), std::move(value));
} else {
ok = hmap.replace(std::move(key), std::move(value));
}
push_hmap(stack, std::move(hmap));
if (mode & 2) {
stack.push_bool(ok);
}
}
void interpret_hmap_is_empty(vm::Stack& stack) {
stack.push_bool(pop_hmap(stack).is_null());
}
void interpret_hmap_decompose(vm::Stack& stack, int mode) {
auto hmap = pop_hmap(stack);
if (hmap.is_null()) {
if (mode & 1) {
stack.push_bool(false);
} else {
throw IntError{"empty hmap"};
}
return;
}
stack.push(hmap->key());
stack.push(hmap->value());
push_hmap(stack, hmap->left());
push_hmap(stack, hmap->right());
if (mode & 1) {
stack.push_bool(true);
}
}
class HmapIterCont : public LoopCont {
HashmapIterator it;
bool ok;
public:
HmapIterCont(Ref<FiftCont> _func, Ref<FiftCont> _after, HashmapIterator _it)
: LoopCont(std::move(_func), std::move(_after)), it(std::move(_it)), ok(true) {
}
HmapIterCont(const HmapIterCont&) = default;
HmapIterCont* make_copy() const override {
return new HmapIterCont(*this);
}
bool init(IntCtx& ctx) override {
return true;
}
bool pre_exec(IntCtx& ctx) override {
if (it.eof()) {
return false;
} else {
ctx.stack.push(it->key());
ctx.stack.push(it->value());
return true;
}
}
bool post_exec(IntCtx& ctx) override {
ok = ctx.stack.pop_bool();
return ok && it.next();
}
bool finalize(IntCtx& ctx) override {
ctx.stack.push_bool(ok);
return true;
}
};
Ref<FiftCont> interpret_hmap_foreach(IntCtx& ctx, int mode) {
auto func = pop_exec_token(ctx);
return td::make_ref<HmapIterCont>(std::move(func), std::move(ctx.next), pop_hmap_keeper(ctx).begin(mode & 1));
}
// vm dictionaries
void interpret_dict_new(vm::Stack& stack) {
stack.push({});
@ -1882,7 +2073,7 @@ void interpret_pfx_dict_get(vm::Stack& stack) {
}
void interpret_bitstring_hex_literal(IntCtx& ctx) {
auto s = ctx.scan_word_to('}');
auto s = ctx.parser->scan_word_to('}');
unsigned char buff[128];
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), s.begin(), s.end());
if (bits < 0) {
@ -1890,11 +2081,11 @@ void interpret_bitstring_hex_literal(IntCtx& ctx) {
}
auto cs = Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
ctx.stack.push(std::move(cs));
push_argcount(ctx.stack, 1);
push_argcount(ctx, 1);
}
void interpret_bitstring_binary_literal(IntCtx& ctx) {
auto s = ctx.scan_word_to('}');
auto s = ctx.parser->scan_word_to('}');
unsigned char buff[128];
int bits = (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), s.begin(), s.end());
if (bits < 0) {
@ -1902,12 +2093,12 @@ void interpret_bitstring_binary_literal(IntCtx& ctx) {
}
auto cs = Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()};
ctx.stack.push(std::move(cs));
push_argcount(ctx.stack, 1);
push_argcount(ctx, 1);
}
void interpret_word(IntCtx& ctx) {
char sep = (char)ctx.stack.pop_smallint_range(127);
auto word = (sep != ' ' ? ctx.scan_word_to(sep, true) : ctx.scan_word());
auto word = (sep != ' ' ? ctx.parser->scan_word_to(sep, true) : ctx.parser->scan_word());
ctx.stack.push_string(word);
}
@ -1915,17 +2106,17 @@ void interpret_word_ext(IntCtx& ctx) {
int mode = ctx.stack.pop_smallint_range(11);
auto delims = ctx.stack.pop_string();
if (mode & 8) {
ctx.skipspc(mode & 4);
ctx.parser->skipspc(mode & 4);
}
ctx.stack.push_string(ctx.scan_word_ext(CharClassifier{delims, mode & 3}));
ctx.stack.push_string(ctx.parser->scan_word_ext(CharClassifier{delims, mode & 3}));
}
void interpret_skipspc(IntCtx& ctx) {
ctx.skipspc();
ctx.parser->skipspc();
}
void interpret_wordlist_begin_aux(vm::Stack& stack) {
stack.push({vm::from_object, Ref<WordList>{true}});
stack.push_make_object<WordList>();
}
void interpret_wordlist_begin(IntCtx& ctx) {
@ -1938,7 +2129,7 @@ void interpret_wordlist_begin(IntCtx& ctx) {
void interpret_wordlist_end_aux(vm::Stack& stack) {
Ref<WordList> wordlist_ref = pop_word_list(stack);
wordlist_ref.write().close();
stack.push({vm::from_object, Ref<FiftCont>{wordlist_ref}});
stack.push_object(std::move(wordlist_ref));
}
void interpret_wordlist_end(IntCtx& ctx) {
@ -1957,7 +2148,7 @@ void interpret_internal_interpret_begin(IntCtx& ctx) {
void interpret_internal_interpret_end(IntCtx& ctx) {
ctx.check_int_exec();
ctx.state = -ctx.state;
ctx.stack.push({vm::from_object, Dictionary::nop_word_def});
ctx.stack.push_object(nop_word_def);
}
// (create)
@ -1974,18 +2165,12 @@ void interpret_create_aux(IntCtx& ctx, int mode) {
if (!(mode & 2)) {
word += ' ';
}
bool active = (mode & 1);
auto entry = ctx.dictionary->lookup(word);
if (entry) {
*entry = DictEntry{std::move(wd_ref), active}; // redefine word
} else {
ctx.dictionary->def_word(std::move(word), {std::move(wd_ref), active});
}
ctx.dictionary.def_word(std::move(word), {std::move(wd_ref), (bool)(mode & 1)});
}
// { bl word 0 (create) } : create
void interpret_create(IntCtx& ctx) {
auto word = ctx.scan_word();
auto word = ctx.parser->scan_word();
if (!word.size()) {
throw IntError{"non-empty word name expected"};
}
@ -1997,10 +2182,10 @@ Ref<FiftCont> create_aux_wd{Ref<CtxWord>{true, std::bind(interpret_create_aux, s
// { bl word <mode> 2 ' (create) } :: :
void interpret_colon(IntCtx& ctx, int mode) {
ctx.stack.push_string(ctx.scan_word());
ctx.stack.push_string(ctx.parser->scan_word());
ctx.stack.push_smallint(mode);
ctx.stack.push_smallint(2);
ctx.stack.push({vm::from_object, create_aux_wd});
ctx.stack.push_object(create_aux_wd);
//push_argcount(ctx, 2, create_wd);
}
@ -2008,27 +2193,27 @@ void interpret_colon(IntCtx& ctx, int mode) {
void interpret_forget_aux(IntCtx& ctx) {
std::string s = ctx.stack.pop_string();
auto s_copy = s;
auto entry = ctx.dictionary->lookup(s);
auto entry = ctx.dictionary.lookup(s);
if (!entry) {
s += " ";
entry = ctx.dictionary->lookup(s);
entry = ctx.dictionary.lookup(s);
}
if (!entry) {
throw IntError{"`" + s_copy + "` not found"};
} else {
ctx.dictionary->undef_word(s);
ctx.dictionary.undef_word(s);
}
}
// { bl word (forget) } : forget
void interpret_forget(IntCtx& ctx) {
ctx.stack.push_string(ctx.scan_word());
ctx.stack.push_string(ctx.parser->scan_word());
interpret_forget_aux(ctx);
}
void interpret_quote_str(IntCtx& ctx) {
ctx.stack.push_string(ctx.scan_word_to('"'));
push_argcount(ctx.stack, 1);
ctx.stack.push_string(ctx.parser->scan_word_to('"'));
push_argcount(ctx, 1);
}
int str_utf8_code(const char* str, int& len) {
@ -2056,7 +2241,7 @@ int str_utf8_code(const char* str, int& len) {
}
void interpret_char(IntCtx& ctx) {
auto s = ctx.scan_word();
auto s = ctx.parser->scan_word();
int len = (s.size() < 10 ? (int)s.size() : 10);
int code = str_utf8_code(s.data(), len);
if (code < 0 || s.size() != (unsigned)len) {
@ -2197,6 +2382,12 @@ Ref<FiftCont> interpret_execute(IntCtx& ctx) {
return pop_exec_token(ctx);
}
Ref<FiftCont> interpret_call_cc(IntCtx& ctx) {
auto next = pop_exec_token(ctx);
ctx.stack.push_object(std::move(ctx.next));
return next;
}
Ref<FiftCont> interpret_execute_times(IntCtx& ctx) {
int count = ctx.stack.pop_smallint_range(1000000000);
auto body = pop_exec_token(ctx);
@ -2251,30 +2442,46 @@ Ref<FiftCont> interpret_until(IntCtx& ctx) {
return body;
}
void interpret_tick(IntCtx& ctx) {
std::string word = ctx.scan_word().str();
auto entry = ctx.dictionary->lookup(word);
if (!entry) {
entry = ctx.dictionary->lookup(word + ' ');
DictEntry context_lookup(IntCtx& ctx, std::string word, bool append_space = true) {
if (append_space) {
auto entry = context_lookup(ctx, word, false);
if (!entry) {
throw IntError{"word `" + word + "` undefined"};
entry = context_lookup(ctx, word + ' ', false);
}
return entry;
}
ctx.stack.push({vm::from_object, entry->get_def()});
auto entry = ctx.context.lookup(word);
if (!entry && ctx.context != ctx.dictionary) {
entry = ctx.dictionary.lookup(word);
}
if (!entry && ctx.main_dictionary != ctx.context && ctx.main_dictionary != ctx.dictionary) {
entry = ctx.main_dictionary.lookup(word);
}
return entry;
}
void interpret_tick(IntCtx& ctx) {
std::string word = ctx.parser->scan_word().str();
auto entry = context_lookup(ctx, word);
if (!entry) {
throw IntError{"word `" + word + "` undefined"};
}
ctx.stack.push_object(entry.get_def());
push_argcount(ctx, 1);
}
void interpret_find(IntCtx& ctx) {
void interpret_find(IntCtx& ctx, int mode) {
std::string word = ctx.stack.pop_string();
auto entry = ctx.dictionary->lookup(word);
if (!entry) {
entry = ctx.dictionary->lookup(word + ' ');
}
auto entry = context_lookup(ctx, word, !(mode & 2));
if (!entry) {
ctx.stack.push_bool(false);
} else {
ctx.stack.push({vm::from_object, entry->get_def()});
ctx.stack.push_bool(true);
ctx.stack.push_object(entry.get_def());
if (!(mode & 1) || !entry.is_active()) {
ctx.stack.push_bool(true);
} else {
ctx.stack.push_smallint(1);
}
}
}
@ -2284,9 +2491,13 @@ void interpret_leave_source(IntCtx& ctx) {
}
}
void interpret_include_depth(IntCtx& ctx) {
ctx.stack.push_smallint(ctx.include_depth());
}
Ref<FiftCont> interpret_include(IntCtx& ctx) {
auto fname = ctx.stack.pop_string();
auto r_file = ctx.source_lookup->lookup_source(fname, ctx.currentd_dir);
auto r_file = ctx.source_lookup->lookup_source(fname, ctx.parser->currentd_dir);
if (r_file.is_error()) {
throw IntError{"cannot locate file `" + fname + "`"};
}
@ -2314,12 +2525,32 @@ Ref<FiftCont> interpret_skip_source(IntCtx& ctx) {
}
void interpret_words(IntCtx& ctx) {
for (const auto& x : *ctx.dictionary) {
*ctx.output_stream << x.first << " ";
for (const auto& x : ctx.dictionary) {
*ctx.output_stream << vm::StackEntry(x.key()).as_string() << " ";
}
*ctx.output_stream << std::endl;
}
void interpret_get_current(IntCtx& ctx) {
ctx.stack.push(ctx.dictionary.get_box());
}
void interpret_set_current(IntCtx& ctx) {
ctx.dictionary = ctx.stack.pop_box();
}
void interpret_get_context(IntCtx& ctx) {
ctx.stack.push(ctx.context.get_box());
}
void interpret_set_context(IntCtx& ctx) {
ctx.context = ctx.stack.pop_box();
}
void interpret_set_context_to(IntCtx& ctx, Ref<vm::Box> box) {
ctx.context = std::move(box);
}
void interpret_print_backtrace(IntCtx& ctx) {
ctx.print_backtrace(*ctx.output_stream, ctx.next);
}
@ -2465,6 +2696,28 @@ void interpret_run_vm(IntCtx& ctx, int mode) {
}
}
void interpret_vmop_len(vm::Stack& stack) {
int cp = stack.pop_smallint_range(0x7fffffff, -0x80000000);
auto cs = stack.pop_cellslice();
auto dispatch = vm::DispatchTable::get_table(cp);
if (!dispatch) {
throw IntError{"unknown vm codepage"};
}
stack.push_smallint(dispatch->instr_len(*cs));
}
void interpret_vmop_dump(vm::Stack& stack) {
int cp = stack.pop_smallint_range(0x7fffffff, -0x80000000);
auto cs = stack.pop_cellslice();
auto dispatch = vm::DispatchTable::get_table(cp);
if (!dispatch) {
throw IntError{"unknown vm codepage"};
}
auto dump = dispatch->dump_instr(cs.write());
stack.push_cellslice(std::move(cs));
stack.push_string(std::move(dump));
}
void do_interpret_db_run_vm_parallel(std::ostream* stream, vm::Stack& stack, vm::TonDb* ton_db_ptr, int threads_n,
int tasks_n) {
if (!ton_db_ptr || !*ton_db_ptr) {
@ -2623,11 +2876,11 @@ Ref<FiftCont> interpret_get_cmdline_arg(IntCtx& ctx) {
interpret_get_fixed_cmdline_arg(ctx.stack, n);
return {};
}
auto entry = ctx.dictionary->lookup("$0 ");
auto entry = ctx.dictionary.lookup("$0 ");
if (!entry) {
throw IntError{"-?"};
} else {
return entry->get_def();
return entry.get_def();
}
}
@ -2667,29 +2920,19 @@ Ref<FiftCont> interpret_execute_internal(IntCtx& ctx) {
return word_def;
}
// wl x1 .. xn n 'w --> wl'
void interpret_compile_internal(vm::Stack& stack) {
Ref<FiftCont> word_def = pop_exec_token(stack);
int count = stack.pop_smallint_range(255);
do_compile_literals(stack, count);
if (word_def != Dictionary::nop_word_def) {
do_compile(stack, word_def);
}
}
void do_compile(vm::Stack& stack, Ref<FiftCont> word_def) {
Ref<WordList> wl_ref = pop_word_list(stack);
if (word_def != Dictionary::nop_word_def) {
if (word_def != nop_word_def) {
auto list_size = word_def->list_size();
if ((td::uint64)list_size <= 1) {
// inline short definitions
if (list_size >= 0 && (list_size <= 2 || word_def.is_unique())) {
// inline short and unique definitions
auto list = word_def->get_list();
wl_ref.write().append(list, list + list_size);
} else {
wl_ref.write().push_back(word_def);
wl_ref.write().push_back(std::move(word_def));
}
}
stack.push({vm::from_object, wl_ref});
stack.push_object(std::move(wl_ref));
}
void compile_one_literal(WordList& wlist, vm::StackEntry val) {
@ -2717,12 +2960,146 @@ void do_compile_literals(vm::Stack& stack, int count) {
}
}
stack.pop_many(count + 1);
stack.push({vm::from_object, wl_ref});
stack.push_object(std::move(wl_ref));
}
// wl x1 .. xn n 'w --> wl'
void interpret_compile_internal(vm::Stack& stack) {
Ref<FiftCont> word_def = pop_exec_token(stack);
int count = stack.pop_smallint_range(255);
do_compile_literals(stack, count);
if (word_def != nop_word_def) {
do_compile(stack, std::move(word_def));
}
}
Ref<FiftCont> interpret_compile_execute(IntCtx& ctx) {
if (ctx.state > 0) {
interpret_compile_internal(ctx.stack);
return {};
} else {
return interpret_execute_internal(ctx);
}
}
void interpret_seekeof(IntCtx& ctx, int mode) {
if (mode == -1) {
mode = ctx.stack.pop_smallint_range(3, -1);
}
bool eof = true;
if (ctx.parser && (ctx.parser->get_input() || ctx.parser->load_next_line())) {
while (true) {
if (!ctx.parser->is_sb()) {
ctx.parser->skipspc();
if (*ctx.parser->get_input()) {
eof = false;
break;
}
}
if (mode & 1) {
*ctx.output_stream << " ok" << std::endl;
}
if (!ctx.parser->load_next_line()) {
break;
}
}
}
ctx.stack.push_bool(eof);
}
void interpret_word_prefix_find(IntCtx& ctx, int mode) {
const char *ptr = ctx.parser->get_input(), *start = ptr;
if (!ptr) {
ctx.stack.push_string(std::string{});
ctx.stack.push_bool(false);
return;
}
while (*ptr && *ptr != ' ' && *ptr != '\t') {
ptr++;
}
std::string Word{start, ptr};
Word.push_back(' ');
auto entry = context_lookup(ctx, Word, false);
Word.pop_back();
if (entry) {
ctx.parser->set_input(ptr);
ctx.parser->skipspc();
} else {
const char* ptr_end = ptr;
while (true) {
entry = context_lookup(ctx, Word, false);
if (entry) {
ctx.parser->set_input(ptr);
break;
}
if (ptr == start) {
Word = std::string{start, ptr_end};
ctx.parser->set_input(ptr_end);
ctx.parser->skipspc();
break;
}
Word.pop_back();
--ptr;
}
}
ctx.parser->word = Word;
if (!(mode & 2) || !entry) {
ctx.stack.push_string(std::move(Word));
}
if (mode & 1) {
if (!entry) {
ctx.stack.push_bool(false);
} else {
ctx.stack.push_object(entry.get_def());
ctx.stack.push_smallint(entry.is_active() ? 1 : -1);
}
}
}
// equivalent to
// { ?dup { 1+ { execute } { 0 swap } cond } { (number) ?dup 0= abort"-?" 'nop } cond
// } : (interpret-prepare)
Ref<FiftCont> interpret_prepare(IntCtx& ctx) {
int found = ctx.stack.pop_smallint_range(1, -1);
if (!found) {
// numbers
interpret_parse_number(ctx); // (number)
interpret_cond_dup(ctx); // ?dup
auto res = ctx.stack.pop_int(); // 0= abort"-?"
if (res == 0) {
throw IntError{"-?"};
}
ctx.stack.push_object(nop_word_def); // 'nop
return {};
} else if (found == -1) {
// ordinary word
ctx.stack.push_smallint(0); // 0
interpret_swap(ctx); // swap
return {};
} else {
// active word
return pop_exec_token(ctx); // execute
}
}
Ref<FiftCont> InterpretCont::run_tail(IntCtx& ctx) const {
static Ref<FiftCont> interpret_prepare_ref = td::make_ref<CtxTailWord>(interpret_prepare);
static Ref<FiftCont> compile_exec_ref = td::make_ref<CtxTailWord>(interpret_compile_execute);
interpret_seekeof(ctx, !ctx.state && !ctx.include_depth()); // seekeof
if (ctx.stack.pop_bool()) {
exit_interpret->clear();
return {}; // exit loop
}
exit_interpret->set({vm::from_object, ctx.next}); // set 'exit-interpret to current continuation
interpret_word_prefix_find(ctx, 3); // (word-prefix-find)
// (interpet-prepare) (compile-execute) and schedule next loop iteration
ctx.next = SeqCont::seq(compile_exec_ref, SeqCont::seq(self(), std::move(ctx.next)));
return interpret_prepare_ref; // (interpret-prepare)
}
void init_words_common(Dictionary& d) {
using namespace std::placeholders;
d.def_word("nop ", Dictionary::nop_word_def);
d.def_word("nop ", nop_word_def);
// stack print/dump words
d.def_ctx_word(". ", std::bind(interpret_dot, _1, true));
d.def_ctx_word("._ ", std::bind(interpret_dot, _1, false));
@ -2942,6 +3319,14 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("ref@+ ", std::bind(interpret_fetch_ref, _1, 2));
d.def_stack_word("ref@? ", std::bind(interpret_fetch_ref, _1, 4));
d.def_stack_word("ref@?+ ", std::bind(interpret_fetch_ref, _1, 6));
d.def_stack_word("s@ ", std::bind(interpret_fetch_slice, _1, 0));
d.def_stack_word("sr@ ", std::bind(interpret_fetch_slice, _1, 1));
d.def_stack_word("s@+ ", std::bind(interpret_fetch_slice, _1, 2));
d.def_stack_word("sr@+ ", std::bind(interpret_fetch_slice, _1, 3));
d.def_stack_word("s@? ", std::bind(interpret_fetch_slice, _1, 4));
d.def_stack_word("sr@? ", std::bind(interpret_fetch_slice, _1, 5));
d.def_stack_word("s@?+ ", std::bind(interpret_fetch_slice, _1, 6));
d.def_stack_word("sr@?+ ", std::bind(interpret_fetch_slice, _1, 7));
d.def_stack_word("s> ", interpret_cell_check_empty);
d.def_stack_word("empty? ", interpret_cell_empty);
d.def_stack_word("remaining ", interpret_cell_remaining);
@ -2970,6 +3355,18 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("crc16 ", interpret_crc16);
d.def_stack_word("crc32 ", interpret_crc32);
d.def_stack_word("crc32c ", interpret_crc32c);
// hashmaps
d.def_stack_word("hmapnew ", interpret_hmap_new);
d.def_stack_word("hmap@ ", std::bind(interpret_hmap_fetch, _1, 6));
d.def_stack_word("hmap@? ", std::bind(interpret_hmap_fetch, _1, 5));
d.def_stack_word("hmap- ", std::bind(interpret_hmap_delete, _1, 0));
d.def_stack_word("hmap-? ", std::bind(interpret_hmap_delete, _1, 1));
d.def_stack_word("hmap@- ", std::bind(interpret_hmap_delete, _1, 6));
d.def_stack_word("hmap! ", std::bind(interpret_hmap_store, _1, 0));
d.def_stack_word("hmap!+ ", std::bind(interpret_hmap_store, _1, 1));
d.def_stack_word("hmapempty? ", interpret_hmap_is_empty);
d.def_stack_word("hmapunpack ", std::bind(interpret_hmap_decompose, _1, 1));
d.def_ctx_tail_word("hmapforeach ", std::bind(interpret_hmap_foreach, _1, 0));
// vm dictionaries
d.def_stack_word("dictnew ", interpret_dict_new);
d.def_stack_word("dict>s ", interpret_dict_to_slice);
@ -3041,6 +3438,7 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("atom? ", interpret_is_atom);
// execution control
d.def_ctx_tail_word("execute ", interpret_execute);
d.def_ctx_tail_word("call/cc ", interpret_call_cc);
d.def_ctx_tail_word("times ", interpret_execute_times);
d.def_ctx_tail_word("if ", interpret_if);
d.def_ctx_tail_word("ifnot ", interpret_ifnot);
@ -3056,10 +3454,12 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("(}) ", interpret_wordlist_end_aux);
d.def_stack_word("(compile) ", interpret_compile_internal);
d.def_ctx_tail_word("(execute) ", interpret_execute_internal);
d.def_ctx_tail_word("(interpret-prepare) ", interpret_prepare);
d.def_active_word("' ", interpret_tick);
d.def_word("'nop ", LitCont::literal({vm::from_object, Dictionary::nop_word_def}));
d.def_word("'nop ", LitCont::literal({vm::from_object, nop_word_def}));
// dictionary manipulation
d.def_ctx_word("find ", interpret_find);
d.def_ctx_word("find ", std::bind(interpret_find, _1, 1));
d.def_ctx_word("(word-prefix-find) ", std::bind(interpret_word_prefix_find, _1, 3));
d.def_ctx_word("create ", interpret_create);
d.def_ctx_word("(create) ", std::bind(interpret_create_aux, _1, -1));
d.def_active_word(": ", std::bind(interpret_colon, _1, 0));
@ -3069,12 +3469,21 @@ void init_words_common(Dictionary& d) {
d.def_ctx_word("(forget) ", interpret_forget_aux);
d.def_ctx_word("forget ", interpret_forget);
d.def_ctx_word("words ", interpret_words);
d.def_word("Fift-wordlist ", LitCont::literal(d.get_box()));
d.def_ctx_word("Fift ", std::bind(interpret_set_context_to, _1, d.get_box()));
d.def_ctx_word("current@ ", interpret_get_current);
d.def_ctx_word("current! ", interpret_set_current);
d.def_ctx_word("context@ ", interpret_get_context);
d.def_ctx_word("context! ", interpret_set_context);
d.def_ctx_word(".bt ", interpret_print_backtrace);
d.def_ctx_word("cont. ", interpret_print_continuation);
// input parse
d.def_ctx_word("word ", interpret_word);
d.def_ctx_word("(word) ", interpret_word_ext);
d.def_ctx_word("skipspc ", interpret_skipspc);
d.def_ctx_word("seekeof? ", std::bind(interpret_seekeof, _1, 1));
d.def_ctx_word("(seekeof?) ", std::bind(interpret_seekeof, _1, -1));
d.def_ctx_word("include-depth ", interpret_include_depth);
d.def_ctx_tail_word("include ", interpret_include);
d.def_ctx_tail_word("skip-to-eof ", interpret_skip_source);
d.def_word("'exit-interpret ", LitCont::literal(exit_interpret));
@ -3110,6 +3519,8 @@ void init_words_vm(Dictionary& d, bool enable_debug) {
d.def_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel);
d.def_stack_word("vmcont, ", interpret_store_vm_cont);
d.def_stack_word("vmcont@ ", interpret_fetch_vm_cont);
d.def_stack_word("(vmoplen) ", interpret_vmop_len);
d.def_stack_word("(vmopdump) ", interpret_vmop_dump);
}
void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]) {
@ -3128,99 +3539,4 @@ void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* con
}
}
std::pair<td::RefInt256, td::RefInt256> numeric_value_ext(std::string s, bool allow_frac = true) {
td::RefInt256 num, denom;
int res = parse_number(s, num, denom, allow_frac);
if (res <= 0) {
throw IntError{"-?"};
}
return std::make_pair(std::move(num), res == 2 ? std::move(denom) : td::RefInt256{});
}
td::RefInt256 numeric_value(std::string s) {
td::RefInt256 num, denom;
int res = parse_number(s, num, denom, false);
if (res != 1) {
throw IntError{"-?"};
}
return num;
}
Ref<FiftCont> interpret_compile_execute(IntCtx& ctx) {
if (ctx.state > 0) {
interpret_compile_internal(ctx.stack);
return {};
} else {
return interpret_execute_internal(ctx);
}
}
Ref<FiftCont> InterpretCont::run_tail(IntCtx& ctx) const {
if (!ctx.get_input() && !ctx.load_next_line()) {
return {};
}
while (true) {
if (!ctx.is_sb()) {
ctx.skipspc();
if (*ctx.get_input()) {
break;
}
}
if (!ctx.state && !ctx.include_depth) {
*ctx.output_stream << " ok" << std::endl;
}
if (!ctx.load_next_line()) {
return {};
}
}
const char* ptr = ctx.get_input();
std::string Word;
Word.reserve(128);
auto entry = ctx.dictionary->lookup("");
std::string entry_word;
const char* ptr_end = ptr;
while (*ptr && *ptr != ' ' && *ptr != '\t') {
Word += *ptr++;
auto cur = ctx.dictionary->lookup(Word);
if (cur) {
entry = cur;
entry_word = Word;
ptr_end = ptr;
}
}
auto cur = ctx.dictionary->lookup(Word + " ");
if (cur || !entry) {
entry = std::move(cur);
ctx.set_input(ptr);
ctx.skipspc();
} else {
Word = entry_word;
ctx.set_input(ptr_end);
}
ctx.word = Word;
static Ref<FiftCont> compile_exec_ref = td::make_ref<CtxTailWord>(interpret_compile_execute);
if (!entry) {
// numbers
auto res = numeric_value_ext(Word);
ctx.stack.push(std::move(res.first));
if (res.second.not_null()) {
ctx.stack.push(std::move(res.second));
push_argcount(ctx, 2);
} else {
push_argcount(ctx, 1);
}
} else if (!entry->is_active()) {
// ordinary word
ctx.stack.push_smallint(0);
ctx.stack.push({vm::from_object, entry->get_def()});
} else {
// active word
ctx.next = SeqCont::seq(compile_exec_ref, SeqCont::seq(self(), std::move(ctx.next)));
return entry->get_def();
}
exit_interpret->set({vm::from_object, ctx.next});
ctx.next = SeqCont::seq(self(), std::move(ctx.next));
return compile_exec_ref;
}
} // namespace fift

View file

@ -110,3 +110,19 @@ TEST(Fift, test_sort) {
TEST(Fift, test_sort2) {
run_fift("sort2.fif");
}
TEST(Fift, test_hmap) {
run_fift("hmap.fif");
}
TEST(Fift, test_disasm) {
run_fift("disasm.fif");
}
TEST(Fift, test_fiftext) {
run_fift("fift-ext.fif");
}
TEST(Fift, test_namespaces) {
run_fift("namespaces.fif");
}

View 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

View 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
View 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

View 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 "

View file

@ -38,8 +38,8 @@ int exec_push_tinyint4(VmState* st, unsigned args) {
std::string dump_push_tinyint4(CellSlice&, unsigned args) {
int x = (int)((args + 5) & 15) - 5;
std::ostringstream os{"PUSHINT "};
os << x;
std::ostringstream os;
os << "PUSHINT " << x;
return os.str();
}
@ -53,8 +53,8 @@ int exec_push_tinyint8(VmState* st, unsigned args) {
std::string dump_op_tinyint8(const char* op_prefix, CellSlice&, unsigned args) {
int x = (signed char)args;
std::ostringstream os{op_prefix};
os << x;
std::ostringstream os;
os << op_prefix << x;
return os.str();
}
@ -68,8 +68,8 @@ int exec_push_smallint(VmState* st, unsigned args) {
std::string dump_push_smallint(CellSlice&, unsigned args) {
int x = (short)args;
std::ostringstream os{"PUSHINT "};
os << x;
std::ostringstream os;
os << "PUSHINT " << x;
return os.str();
}
@ -93,8 +93,8 @@ std::string dump_push_int(CellSlice& cs, unsigned args, int pfx_bits) {
}
cs.advance(pfx_bits);
td::RefInt256 x = cs.fetch_int256(3 + l * 8);
std::ostringstream os{"PUSHINT "};
os << x;
std::ostringstream os;
os << "PUSHINT " << x;
return os.str();
}
@ -302,7 +302,7 @@ std::string dump_divmod(CellSlice&, unsigned args, bool quiet) {
if (quiet) {
s = "Q" + s;
}
return s + "FRC"[round_mode];
return round_mode ? s + "FRC"[round_mode] : s;
}
int exec_shrmod(VmState* st, unsigned args, int mode) {
@ -352,28 +352,28 @@ std::string dump_shrmod(CellSlice&, unsigned args, int mode) {
if (!(args & 12) || round_mode == 3) {
return "";
}
std::string s;
std::ostringstream os;
if (mode & 1) {
os << 'Q';
}
switch (args & 12) {
case 4:
s = "RSHIFT";
os << "RSHIFT";
break;
case 8:
s = "MODPOW2";
os << "MODPOW2";
break;
case 12:
s = "RSHIFTMOD";
os << "RSHIFTMOD";
break;
}
if (mode & 1) {
s = "Q" + s;
if (round_mode) {
os << "FRC"[round_mode];
}
s += "FRC"[round_mode];
if (mode & 2) {
char buff[8];
sprintf(buff, " %d", y);
s += buff;
os << ' ' << y;
}
return s;
return os.str();
}
int exec_muldivmod(VmState* st, unsigned args, int quiet) {
@ -417,7 +417,7 @@ std::string dump_muldivmod(CellSlice&, unsigned args, bool quiet) {
if (quiet) {
s = "Q" + s;
}
return s + "FRC"[round_mode];
return round_mode ? s + "FRC"[round_mode] : s;
}
int exec_mulshrmod(VmState* st, unsigned args, int mode) {
@ -474,28 +474,28 @@ std::string dump_mulshrmod(CellSlice&, unsigned args, int mode) {
if (!(args & 12) || round_mode == 3) {
return "";
}
std::string s;
std::ostringstream os;
if (mode & 1) {
os << 'Q';
}
switch (args & 12) {
case 4:
s = "MULRSHIFT";
os << "MULRSHIFT";
break;
case 8:
s = "MULMODPOW2";
os << "MULMODPOW2";
break;
case 12:
s = "MULRSHIFTMOD";
os << "MULRSHIFTMOD";
break;
}
if (mode & 1) {
s = "Q" + s;
if (round_mode) {
os << "FRC"[round_mode];
}
s += "FRC"[round_mode];
if (mode & 2) {
char buff[8];
sprintf(buff, " %d", y);
s += buff;
os << ' ' << y;
}
return s;
return os.str();
}
int exec_shldivmod(VmState* st, unsigned args, int mode) {
@ -542,19 +542,25 @@ int exec_shldivmod(VmState* st, unsigned args, int mode) {
return 0;
}
std::string dump_shldivmod(CellSlice&, unsigned args, bool quiet) {
std::string dump_shldivmod(CellSlice&, unsigned args, int mode) {
int y = -1;
if (mode & 2) {
y = (args & 0xff) + 1;
args >>= 8;
}
int round_mode = (int)(args & 3);
if (!(args & 12) || round_mode == 3) {
return "";
}
std::string s = (args & 4) ? "LSHIFTDIV" : "LSHIFT";
if (args & 8) {
s += "MOD";
std::ostringstream os;
os << (mode & 1 ? "Q" : "") << (args & 4 ? "LSHIFTDIV" : "LSHIFT") << (args & 8 ? "MOD" : "");
if (round_mode) {
os << "FRC"[round_mode];
}
if (quiet) {
s = "Q" + s;
if (y >= 0) {
os << ' ' << y;
}
return s + "FRC"[round_mode];
return os.str();
}
void register_div_ops(OpcodeTable& cp0) {

View file

@ -30,7 +30,7 @@ class Box : public td::CntObject {
Box(const Box&) = default;
Box(Box&&) = default;
template <typename... Args>
Box(Args... args) : data_{std::move(args...)} {
Box(Args&&... args) : data_{std::forward<Args>(args)...} {
}
~Box() override = default;
Box(const StackEntry& data) : data_(data) {

View file

@ -103,7 +103,8 @@ std::string dump_push_slice_common(CellSlice& cs, unsigned data_bits, unsigned r
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits, refs);
slice.unique_write().remove_trailing();
std::ostringstream os{name};
std::ostringstream os;
os << name;
slice->dump_hex(os, 1, false);
return os.str();
}
@ -188,7 +189,8 @@ std::string dump_push_cont(CellSlice& cs, unsigned args, int pfx_bits) {
}
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits, refs);
std::ostringstream os{"PUSHCONT "};
std::ostringstream os;
os << "PUSHCONT ";
slice->dump_hex(os, 1, false);
return os.str();
}
@ -219,7 +221,8 @@ std::string dump_push_cont_simple(CellSlice& cs, unsigned args, int pfx_bits) {
}
cs.advance(pfx_bits);
auto slice = cs.fetch_subslice(data_bits);
std::ostringstream os{"PUSHCONT "};
std::ostringstream os;
os << "PUSHCONT ";
slice->dump_hex(os, 1, false);
return os.str();
}
@ -1060,8 +1063,8 @@ int exec_load_int_fixed2(VmState* st, unsigned args) {
}
std::string dump_load_int_fixed2(CellSlice&, unsigned args) {
std::ostringstream os{args & 0x200 ? "PLD" : "LD"};
os << (args & 0x100 ? 'U' : 'I');
std::ostringstream os;
os << (args & 0x200 ? "PLD" : "LD") << (args & 0x100 ? 'U' : 'I');
if (args & 0x400) {
os << 'Q';
}
@ -1081,9 +1084,9 @@ int exec_preload_uint_fixed_0e(VmState* st, unsigned args) {
}
std::string dump_preload_uint_fixed_0e(CellSlice&, unsigned args) {
std::ostringstream os{"PLDUZ "};
std::ostringstream os;
unsigned bits = ((args & 7) + 1) << 5;
os << bits;
os << "PLDUZ " << bits;
return os.str();
}
@ -1108,7 +1111,8 @@ int exec_load_slice_fixed2(VmState* st, unsigned args) {
std::string dump_load_slice_fixed2(CellSlice&, unsigned args) {
unsigned bits = (args & 0xff) + 1;
std::ostringstream os{args & 0x100 ? "PLDSLICE" : "LDSLICE"};
std::ostringstream os;
os << (args & 0x100 ? "PLDSLICE" : "LDSLICE");
if (args & 0x200) {
os << 'Q';
}

View file

@ -378,8 +378,8 @@ int exec_if_bit_jmp(VmState* st, unsigned args) {
}
std::string dump_if_bit_jmp(CellSlice& cs, unsigned args) {
std::ostringstream os{args & 0x20 ? "IFN" : " IF"};
os << "BITJMP " << (args & 0x1f);
std::ostringstream os;
os << "IF" << (args & 0x20 ? "N" : "") << "BITJMP " << (args & 0x1f);
return os.str();
}
@ -408,8 +408,8 @@ std::string dump_if_bit_jmpref(CellSlice& cs, unsigned args, int pfx_bits) {
}
cs.advance(pfx_bits);
cs.advance_refs(1);
std::ostringstream os{args & 0x20 ? "IFN" : " IF"};
os << "BITJMPREF " << (args & 0x1f);
std::ostringstream os;
os << "IF" << (args & 0x20 ? "N" : "") << "BITJMPREF " << (args & 0x1f);
return os.str();
}
@ -597,8 +597,8 @@ int exec_setcontargs(VmState* st, unsigned args) {
std::string dump_setcontargs(CellSlice& cs, unsigned args, const char* name) {
int copy = (args >> 4) & 15, more = ((args + 1) & 15) - 1;
std::ostringstream os{name};
os << ' ' << copy << ',' << more;
std::ostringstream os;
os << name << ' ' << copy << ',' << more;
return os.str();
}
@ -1065,8 +1065,8 @@ std::string dump_throw_any(CellSlice& cs, unsigned args) {
bool has_param = args & 1;
bool has_cond = args & 6;
bool throw_cond = args & 2;
std::ostringstream os{has_param ? "THROWARG" : "THROW"};
os << "ANY";
std::ostringstream os;
os << "THROW" << (has_param ? "ARG" : "") << "ANY";
if (has_cond) {
os << (throw_cond ? "IF" : "IFNOT");
}

View file

@ -172,7 +172,8 @@ int exec_load_dict(VmState* st, unsigned args) {
}
std::string dump_dictop(unsigned args, const char* name) {
std::ostringstream os{"DICT"};
std::ostringstream os;
os << "DICT";
if (args & 4) {
os << (args & 2 ? 'U' : 'I');
}
@ -184,7 +185,8 @@ std::string dump_dictop(unsigned args, const char* name) {
}
std::string dump_dictop2(unsigned args, const char* name) {
std::ostringstream os{"DICT"};
std::ostringstream os;
os << "DICT";
if (args & 2) {
os << (args & 1 ? 'U' : 'I');
}
@ -193,7 +195,8 @@ std::string dump_dictop2(unsigned args, const char* name) {
}
std::string dump_subdictop2(unsigned args, const char* name) {
std::ostringstream os{"SUBDICT"};
std::ostringstream os;
os << "SUBDICT";
if (args & 2) {
os << (args & 1 ? 'U' : 'I');
}
@ -508,7 +511,8 @@ int exec_dict_getmin(VmState* st, unsigned args) {
}
std::string dump_dictop_getnear(CellSlice& cs, unsigned args) {
std::ostringstream os{"DICT"};
std::ostringstream os;
os << "DICT";
if (args & 8) {
os << (args & 4 ? 'U' : 'I');
}
@ -637,8 +641,8 @@ std::string dump_push_const_dict(CellSlice& cs, int pfx_bits, const char* name)
cs.advance(pfx_bits - 11);
auto slice = cs.fetch_subslice(1, 1);
int n = (int)cs.fetch_ulong(10);
std::ostringstream os{name};
os << ' ' << n << " (";
std::ostringstream os;
os << name << ' ' << n << " (";
slice->dump_hex(os, false);
os << ')';
return os.str();

View file

@ -357,32 +357,32 @@ using namespace std::placeholders;
dump_arg_instr_func_t dump_1sr(std::string prefix, std::string suffix) {
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << 's' << (args & 15) << suffix;
std::ostringstream os;
os << prefix << 's' << (args & 15) << suffix;
return os.str();
};
}
dump_arg_instr_func_t dump_1sr_l(std::string prefix, std::string suffix) {
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << 's' << (args & 255) << suffix;
std::ostringstream os;
os << prefix << 's' << (args & 255) << suffix;
return os.str();
};
}
dump_arg_instr_func_t dump_2sr(std::string prefix, std::string suffix) {
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << 's' << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
std::ostringstream os;
os << prefix << 's' << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
return os.str();
};
}
dump_arg_instr_func_t dump_2sr_adj(unsigned adj, std::string prefix, std::string suffix) {
return [adj, prefix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << 's' << (int)((args >> 4) & 15) - (int)((adj >> 4) & 15) << ",s" << (int)(args & 15) - (int)(adj & 15)
std::ostringstream os;
os << prefix << 's' << (int)((args >> 4) & 15) - (int)((adj >> 4) & 15) << ",s" << (int)(args & 15) - (int)(adj & 15)
<< suffix;
return os.str();
};
@ -390,16 +390,16 @@ dump_arg_instr_func_t dump_2sr_adj(unsigned adj, std::string prefix, std::string
dump_arg_instr_func_t dump_3sr(std::string prefix, std::string suffix) {
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << 's' << ((args >> 8) & 15) << ",s" << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
std::ostringstream os;
os << prefix << 's' << ((args >> 8) & 15) << ",s" << ((args >> 4) & 15) << ",s" << (args & 15) << suffix;
return os.str();
};
}
dump_arg_instr_func_t dump_3sr_adj(unsigned adj, std::string prefix, std::string suffix) {
return [adj, prefix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << 's' << (int)((args >> 8) & 15) - (int)((adj >> 8) & 15) << ",s"
std::ostringstream os;
os << prefix << 's' << (int)((args >> 8) & 15) - (int)((adj >> 8) & 15) << ",s"
<< (int)((args >> 4) & 15) - (int)((adj >> 4) & 15) << ",s" << (int)(args & 15) - (int)(adj & 15) << suffix;
return os.str();
};
@ -407,40 +407,40 @@ dump_arg_instr_func_t dump_3sr_adj(unsigned adj, std::string prefix, std::string
dump_arg_instr_func_t dump_1c(std::string prefix, std::string suffix) {
return [prefix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << (args & 15) << suffix;
std::ostringstream os;
os << prefix << (args & 15) << suffix;
return os.str();
};
}
dump_arg_instr_func_t dump_1c_l_add(int adj, std::string prefix, std::string suffix) {
return [adj, prefix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << (int)(args & 255) + adj << suffix;
std::ostringstream os;
os << prefix << (int)(args & 255) + adj << suffix;
return os.str();
};
}
dump_arg_instr_func_t dump_1c_and(unsigned mask, std::string prefix, std::string suffix) {
return [mask, prefix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << (args & mask) << suffix;
std::ostringstream os;
os << prefix << (args & mask) << suffix;
return os.str();
};
}
dump_arg_instr_func_t dump_2c(std::string prefix, std::string interfix, std::string suffix) {
return [prefix, interfix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << ((args >> 4) & 15) << interfix << (args & 15) << suffix;
std::ostringstream os;
os << prefix << ((args >> 4) & 15) << interfix << (args & 15) << suffix;
return os.str();
};
}
dump_arg_instr_func_t dump_2c_add(unsigned add, std::string prefix, std::string interfix, std::string suffix) {
return [add, prefix, interfix, suffix](CellSlice&, unsigned args) -> std::string {
std::ostringstream os{prefix};
os << ((args >> 4) & 15) + ((add >> 4) & 15) << interfix << (args & 15) + (add & 15) << suffix;
std::ostringstream os;
os << prefix << ((args >> 4) & 15) + ((add >> 4) & 15) << interfix << (args & 15) + (add & 15) << suffix;
return os.str();
};
}

View file

@ -103,6 +103,9 @@ class StackEntry {
}
StackEntry(td::RefInt256 int_ref) : ref(std::move(int_ref)), tp(t_int) {
}
StackEntry(Ref<Cnt<std::string>> str_ref, bool bytes = false)
: ref(std::move(str_ref)), tp(bytes ? t_bytes : t_string) {
}
StackEntry(std::string str, bool bytes = false) : ref(), tp(bytes ? t_bytes : t_string) {
ref = Ref<Cnt<std::string>>{true, std::move(str)};
}

View file

@ -75,8 +75,8 @@ std::string dump_xchg(CellSlice&, unsigned args) {
if (!x || x >= y) {
return "";
}
std::ostringstream os{"XCHG s"};
os << x << ",s" << y;
std::ostringstream os;
os << "XCHG s" << x << ",s" << y;
return os.str();
}
@ -92,7 +92,7 @@ int exec_xchg1(VmState* st, unsigned args) {
int exec_dup(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DUP\n";
VM_LOG(st) << "execute DUP";
stack.check_underflow(1);
stack.push(stack.fetch(0));
return 0;
@ -100,7 +100,7 @@ int exec_dup(VmState* st) {
int exec_over(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute OVER\n";
VM_LOG(st) << "execute OVER";
stack.check_underflow(2);
stack.push(stack.fetch(1));
return 0;
@ -126,7 +126,7 @@ int exec_push_l(VmState* st, unsigned args) {
int exec_drop(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute DROP\n";
VM_LOG(st) << "execute DROP";
stack.check_underflow(1);
stack.pop();
return 0;
@ -134,7 +134,7 @@ int exec_drop(VmState* st) {
int exec_nip(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute NIP\n";
VM_LOG(st) << "execute NIP";
stack.check_underflow(2);
stack.pop(stack[1]);
return 0;

View file

@ -8,7 +8,11 @@ Test_Fift_bug_ufits_default 51bf5a9f1ed7633a193f6fdd17a7a3af8e032dfe72a9669c85e8
Test_Fift_contfrac_default 09ebce5c91bcb70696c6fb6981d82dc3b9e3444dab608a7a1b044c0ddd778a96
Test_Fift_test_default 4e44b3382963ec89f7b5c8f2ebd85da3bc8aebad5b49f5b11b14075061477b4d
Test_Fift_test_dict_default a9c8cbcfdece5573185022cea07f59f1bc404e5d879e5157a5745757f8ee0525
Test_Fift_test_disasm_default dacaa555f5f217b2373e01e3bcd59634e480f5759dcc43edbdef35273ae38492
Test_Fift_test_fiftext_default 2b0db5d4d4bfbc705b959cc787540d7b3a21a71469eac54756e76953f0d9afca
Test_Fift_test_fixed_default 278a19d56b773102caf5c1fe2997ea6c8d0d9e720eff8503feede6398a197eec
Test_Fift_test_hmap_default c269246882039824bb5822e896c3e6e82ef8e1251b6b251f5af8ea9fb8d05067
Test_Fift_test_namespaces_default e6419619c51332fb5e8bf22043ef415db686c47fe24f03061e5ad831014e7c6c
Test_Fift_test_sort2_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a
Test_Fift_test_sort_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a
Test_Fift_testvm2_default 8a6e35fc0224398be9d2de39d31c86ea96965ef1eca2aa9e0af2303150ed4a7b