1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

integrating the existing state of TON Storage / TON Payments / CPS Fift development branches

This commit is contained in:
ton 2020-05-27 22:10:46 +04:00
parent 040df63c98
commit 4e2624459b
153 changed files with 10760 additions and 1695 deletions

View file

@ -0,0 +1,398 @@
/*
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/>.
Copyright 2020 Telegram Systems LLP
*/
#include "Continuation.h"
#include "IntCtx.h"
#include "Dictionary.h"
namespace fift {
//
// FiftCont
//
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 (word_name.size() && word_name.back() == ' ') {
word_name.pop_back();
}
os << word_name;
return true;
}
return false;
}
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 (word_name.size() && word_name.back() == ' ') {
word_name.pop_back();
}
return word_name;
}
return {};
}
bool FiftCont::print_dummy_name(std::ostream& os, const IntCtx& ctx) const {
os << "<continuation " << (const void*)this << ">";
return false;
}
bool FiftCont::print_name(std::ostream& os, const IntCtx& ctx) const {
return print_dict_name(os, ctx) || print_dummy_name(os, ctx);
}
bool FiftCont::dump(std::ostream& os, const IntCtx& ctx) const {
bool ok = print_name(os, ctx);
os << std::endl;
return ok;
}
//
// QuitCont
//
Ref<FiftCont> QuitCont::run_tail(IntCtx& ctx) const {
ctx.set_exit_code(exit_code);
ctx.next.clear();
return {};
}
bool QuitCont::print_name(std::ostream& os, const IntCtx& ctx) const {
os << "<quit " << exit_code << ">";
return true;
}
//
// SeqCont
//
Ref<FiftCont> SeqCont::run_tail(IntCtx& ctx) const {
ctx.next = seq(second, std::move(ctx.next));
return first;
}
Ref<FiftCont> SeqCont::run_modify(IntCtx& ctx) {
if (ctx.next.is_null()) {
ctx.next = std::move(second);
return std::move(first);
} else {
auto res = std::move(first);
first = std::move(second);
second = std::move(ctx.next);
ctx.next = self();
return res;
}
}
bool SeqCont::print_name(std::ostream& os, const IntCtx& ctx) const {
if (first.not_null()) {
return first->print_name(os, ctx);
} else {
return true;
}
}
bool SeqCont::dump(std::ostream& os, const IntCtx& ctx) const {
if (first.not_null()) {
return first->dump(os, ctx);
} else {
return true;
}
}
//
// TimesCont
//
Ref<FiftCont> TimesCont::run_tail(IntCtx& ctx) const {
if (count > 1) {
ctx.next = td::make_ref<TimesCont>(body, SeqCont::seq(after, std::move(ctx.next)), count - 1);
} else {
ctx.next = SeqCont::seq(after, std::move(ctx.next));
}
return body;
}
Ref<FiftCont> TimesCont::run_modify(IntCtx& ctx) {
if (ctx.next.not_null()) {
after = SeqCont::seq(std::move(after), std::move(ctx.next));
}
if (count > 1) {
--count;
ctx.next = self();
return body;
} else {
ctx.next = std::move(after);
return std::move(body);
}
}
bool TimesCont::print_name(std::ostream& os, const IntCtx& ctx) const {
os << "<repeat " << count << " times>";
return true;
}
bool TimesCont::dump(std::ostream& os, const IntCtx& ctx) const {
os << "<repeat " << count << " times:> ";
return body->dump(os, ctx);
}
//
// UntilCont
//
Ref<FiftCont> UntilCont::run_tail(IntCtx& ctx) const {
if (ctx.stack.pop_bool()) {
return after;
} else if (ctx.next.not_null()) {
ctx.next = td::make_ref<UntilCont>(body, SeqCont::seq(after, std::move(ctx.next)));
return body;
} else {
ctx.next = self();
return body;
}
}
Ref<FiftCont> UntilCont::run_modify(IntCtx& ctx) {
if (ctx.stack.pop_bool()) {
return std::move(after);
} else {
if (ctx.next.not_null()) {
after = SeqCont::seq(after, std::move(ctx.next));
}
ctx.next = self();
return body;
}
}
bool UntilCont::print_name(std::ostream& os, const IntCtx& ctx) const {
os << "<until loop continuation>";
return true;
}
bool UntilCont::dump(std::ostream& os, const IntCtx& ctx) const {
os << "<until loop continuation:> ";
return body->dump(os, ctx);
}
//
// WhileCont
//
Ref<FiftCont> WhileCont::run_tail(IntCtx& ctx) const {
if (!stage) {
ctx.next = td::make_ref<WhileCont>(cond, body, SeqCont::seq(after, std::move(ctx.next)), true);
return cond;
}
if (!ctx.stack.pop_bool()) {
return after;
} else {
ctx.next = td::make_ref<WhileCont>(cond, body, SeqCont::seq(after, std::move(ctx.next)));
return body;
}
}
Ref<FiftCont> WhileCont::run_modify(IntCtx& ctx) {
if (!stage) {
if (ctx.next.not_null()) {
after = SeqCont::seq(std::move(after), std::move(ctx.next));
}
stage = true;
ctx.next = self();
return cond;
}
if (!ctx.stack.pop_bool()) {
return std::move(after);
} else {
if (ctx.next.not_null()) {
after = SeqCont::seq(std::move(after), std::move(ctx.next));
}
stage = false;
ctx.next = self();
return body;
}
}
bool WhileCont::print_name(std::ostream& os, const IntCtx& ctx) const {
os << "<while loop " << (stage ? "body" : "condition") << ">";
return true;
}
bool WhileCont::dump(std::ostream& os, const IntCtx& ctx) const {
os << "<while loop " << (stage ? "body" : "condition") << ":> ";
return (stage ? body : cond)->dump(os, ctx);
}
//
// LoopCont
//
Ref<FiftCont> LoopCont::run_tail(IntCtx& ctx) const {
return Ref<FiftCont>(clone());
}
Ref<FiftCont> LoopCont::run_modify(IntCtx& ctx) {
if (ctx.next.not_null()) {
after = SeqCont::seq(std::move(after), std::move(ctx.next));
}
switch (state) {
case 0:
if (!init(ctx)) {
return std::move(after);
}
state = 1;
// fallthrough
case 1:
if (!pre_exec(ctx)) {
state = 3;
if (finalize(ctx)) {
return std::move(after);
} else {
return {};
}
}
state = 2;
ctx.next = self();
return func;
case 2:
if (post_exec(ctx)) {
state = 1;
return self();
}
state = 3;
// fallthrough
case 3:
if (finalize(ctx)) {
return std::move(after);
} else {
return {};
}
default:
throw IntError{"invalid LoopCont state"};
}
}
bool LoopCont::print_name(std::ostream& os, const IntCtx& ctx) const {
os << "<generic loop continuation state " << state << ">";
return true;
}
//
// GenericLitCont
//
bool GenericLitCont::print_name(std::ostream& os, const IntCtx& ctx) const {
auto list = get_literals();
bool sp = false;
for (auto entry : list) {
if (sp) {
os << sp;
}
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 {
auto cont_lit = entry.as_object<FiftCont>();
if (cont_lit.not_null()) {
os << "{ ";
cont_lit->print_name(os, ctx);
os << " }";
} else {
os << "<literal of type " << tp << ">";
}
}
}
return true;
}
//
// SmallIntLitCont
//
Ref<FiftCont> SmallIntLitCont::run_tail(IntCtx& ctx) const {
ctx.stack.push_smallint(value_);
return {};
}
std::vector<vm::StackEntry> SmallIntLitCont::get_literals() const {
return {td::make_refint(value_)};
}
//
// IntLitCont
//
Ref<FiftCont> IntLitCont::run_tail(IntCtx& ctx) const {
ctx.stack.push_int(value_);
return {};
}
Ref<FiftCont> IntLitCont::run_modify(IntCtx& ctx) {
ctx.stack.push_int(std::move(value_));
return {};
}
Ref<FiftCont> IntLitCont::literal(td::RefInt256 int_value) {
if (int_value->signed_fits_bits(64)) {
return literal(int_value->to_long());
} else {
return td::make_ref<IntLitCont>(std::move(int_value));
}
}
//
// LitCont
//
Ref<FiftCont> LitCont::run_tail(IntCtx& ctx) const {
ctx.stack.push(value_);
return {};
}
Ref<FiftCont> LitCont::run_modify(IntCtx& ctx) {
ctx.stack.push(std::move(value_));
return {};
}
Ref<FiftCont> LitCont::literal(vm::StackEntry value) {
if (value.is_int()) {
return literal(std::move(value).as_int());
} else {
return td::make_ref<LitCont>(std::move(value));
}
}
//
// MultiLitCont
//
Ref<FiftCont> MultiLitCont::run_tail(IntCtx& ctx) const {
for (auto& value : values_) {
ctx.stack.push(value);
}
return {};
}
Ref<FiftCont> MultiLitCont::run_modify(IntCtx& ctx) {
for (auto& value : values_) {
ctx.stack.push(std::move(value));
}
return {};
}
MultiLitCont& MultiLitCont::push_back(vm::StackEntry new_literal) {
values_.push_back(std::move(new_literal));
return *this;
}
vm::StackEntry MultiLitCont::at(int idx) const {
return values_.at(idx);
}
} // namespace fift

272
crypto/fift/Continuation.h Normal file
View file

@ -0,0 +1,272 @@
/*
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/>.
Copyright 2020 Telegram Systems LLP
*/
#pragma once
#include "common/refcnt.hpp"
#include "common/refint.h"
#include "vm/stack.hpp"
namespace fift {
using td::Ref;
struct IntCtx;
/*
*
* FIFT CONTINUATIONS
*
*/
class FiftCont : public td::CntObject {
public:
FiftCont() = default;
virtual ~FiftCont() override = default;
virtual Ref<FiftCont> run_tail(IntCtx& ctx) const = 0;
virtual Ref<FiftCont> run_modify(IntCtx& ctx) {
return run_tail(ctx);
}
virtual Ref<FiftCont> handle_tail(IntCtx& ctx) const {
return {};
}
virtual Ref<FiftCont> handle_modify(IntCtx& ctx) {
return handle_tail(ctx);
}
virtual Ref<FiftCont> up() const {
return {};
}
virtual bool is_list() const {
return false;
}
virtual long long list_size() const {
return -1;
}
virtual const Ref<FiftCont>* get_list() const {
return nullptr;
}
virtual bool is_literal() const {
return false;
}
virtual int literal_count() const {
return -1;
}
virtual std::vector<vm::StackEntry> get_literals() const {
return {};
}
std::string get_dict_name(const IntCtx& ctx) const;
bool print_dict_name(std::ostream& os, const IntCtx& ctx) const;
bool print_dummy_name(std::ostream& os, const IntCtx& ctx) const;
virtual bool print_name(std::ostream& os, const IntCtx& ctx) const;
virtual bool dump(std::ostream& os, const IntCtx& ctx) const;
Ref<FiftCont> self() const {
return Ref<FiftCont>{this};
}
};
class QuitCont : public FiftCont {
int exit_code;
public:
QuitCont(int _exit_code = -1) : exit_code(_exit_code) {
}
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
};
class SeqCont : public FiftCont {
Ref<FiftCont> first, second;
public:
SeqCont(Ref<FiftCont> _first, Ref<FiftCont> _second) : first(std::move(_first)), second(std::move(_second)) {
}
static Ref<FiftCont> seq(Ref<FiftCont> _first, Ref<FiftCont> _second) {
return _second.is_null() ? std::move(_first) : td::make_ref<SeqCont>(std::move(_first), std::move(_second));
}
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
Ref<FiftCont> up() const override {
return second;
}
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class TimesCont : public FiftCont {
Ref<FiftCont> body, after;
int count;
public:
TimesCont(Ref<FiftCont> _body, Ref<FiftCont> _after, int _count)
: body(std::move(_body)), after(std::move(_after)), count(_count) {
}
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
Ref<FiftCont> up() const override {
return after;
}
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class UntilCont : public FiftCont {
Ref<FiftCont> body, after;
public:
UntilCont(Ref<FiftCont> _body, Ref<FiftCont> _after) : body(std::move(_body)), after(std::move(_after)) {
}
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class WhileCont : public FiftCont {
Ref<FiftCont> cond, body, after;
bool stage;
public:
WhileCont(Ref<FiftCont> _cond, Ref<FiftCont> _body, Ref<FiftCont> _after, bool _stage = false)
: cond(std::move(_cond)), body(std::move(_body)), after(std::move(_after)), stage(_stage) {
}
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
Ref<FiftCont> up() const override {
return after;
}
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
bool dump(std::ostream& os, const IntCtx& ctx) const override;
};
class LoopCont : public FiftCont {
Ref<FiftCont> func, after;
int state;
public:
LoopCont(Ref<FiftCont> _func, Ref<FiftCont> _after, int _state = 0)
: func(std::move(_func)), after(std::move(_after)), state(_state) {
}
LoopCont(const LoopCont&) = default;
virtual bool init(IntCtx& ctx) {
return true;
}
virtual bool pre_exec(IntCtx& ctx) {
return true;
}
virtual bool post_exec(IntCtx& ctx) {
return true;
}
virtual bool finalize(IntCtx& ctx) {
return true;
}
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
Ref<FiftCont> up() const override {
return after;
}
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
};
class GenericLitCont : public FiftCont {
public:
bool is_literal() const override {
return true;
}
std::vector<vm::StackEntry> get_literals() const override = 0;
bool print_name(std::ostream& os, const IntCtx& ctx) const override;
};
class SmallIntLitCont : public GenericLitCont {
long long value_;
public:
SmallIntLitCont(long long value) : value_(value) {
}
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
std::vector<vm::StackEntry> get_literals() const override;
static Ref<FiftCont> literal(long long int_value) {
return td::make_ref<SmallIntLitCont>(int_value);
}
};
class IntLitCont : public GenericLitCont {
td::RefInt256 value_;
public:
IntLitCont(td::RefInt256 value) : value_(std::move(value)) {
}
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
std::vector<vm::StackEntry> get_literals() const override {
return {vm::StackEntry(value_)};
}
static Ref<FiftCont> literal(td::RefInt256 int_value);
static Ref<FiftCont> literal(long long int_value) {
return SmallIntLitCont::literal(int_value);
}
};
class LitCont : public GenericLitCont {
vm::StackEntry value_;
public:
LitCont(const vm::StackEntry& value) : value_(value) {
}
LitCont(vm::StackEntry&& value) : value_(std::move(value)) {
}
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
std::vector<vm::StackEntry> get_literals() const override {
return {value_};
}
static Ref<FiftCont> literal(vm::StackEntry value);
static Ref<FiftCont> literal(td::RefInt256 int_value) {
return IntLitCont::literal(std::move(int_value));
}
static Ref<FiftCont> literal(long long int_value) {
return SmallIntLitCont::literal(int_value);
}
};
class MultiLitCont : public GenericLitCont {
std::vector<vm::StackEntry> values_;
public:
MultiLitCont(const std::vector<vm::StackEntry>& values) : values_(values) {
}
MultiLitCont(std::vector<vm::StackEntry>&& values) : values_(std::move(values)) {
}
MultiLitCont(std::initializer_list<vm::StackEntry> value_list) : values_(value_list) {
}
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_modify(IntCtx& ctx) override;
std::vector<vm::StackEntry> get_literals() const override {
return values_;
}
MultiLitCont& push_back(vm::StackEntry new_literal);
vm::StackEntry at(int idx) const;
};
class InterpretCont : public FiftCont {
public:
InterpretCont() = default;
Ref<FiftCont> run_tail(IntCtx&) const override; // text interpreter, defined in words.cpp
bool print_name(std::ostream& os, const IntCtx& ctx) const override {
os << "<text interpreter continuation>";
return true;
}
};
} // namespace fift

View file

@ -20,20 +20,10 @@
namespace fift {
//
// WordDef
//
void WordDef::run(IntCtx& ctx) const {
auto next = run_tail(ctx);
while (next.not_null()) {
next = next->run_tail(ctx);
}
}
//
// StackWord
//
Ref<WordDef> StackWord::run_tail(IntCtx& ctx) const {
Ref<FiftCont> StackWord::run_tail(IntCtx& ctx) const {
f(ctx.stack);
return {};
}
@ -41,7 +31,7 @@ Ref<WordDef> StackWord::run_tail(IntCtx& ctx) const {
//
// CtxWord
//
Ref<WordDef> CtxWord::run_tail(IntCtx& ctx) const {
Ref<FiftCont> CtxWord::run_tail(IntCtx& ctx) const {
f(ctx);
return {};
}
@ -49,57 +39,125 @@ Ref<WordDef> CtxWord::run_tail(IntCtx& ctx) const {
//
// CtxTailWord
//
Ref<WordDef> CtxTailWord::run_tail(IntCtx& ctx) const {
Ref<FiftCont> CtxTailWord::run_tail(IntCtx& ctx) const {
return f(ctx);
}
//
// WordList
//
WordList::WordList(std::vector<Ref<WordDef>>&& _list) : list(std::move(_list)) {
WordList::WordList(std::vector<Ref<FiftCont>>&& _list) : list(std::move(_list)) {
}
WordList::WordList(const std::vector<Ref<WordDef>>& _list) : list(_list) {
WordList::WordList(const std::vector<Ref<FiftCont>>& _list) : list(_list) {
}
WordList& WordList::push_back(Ref<WordDef> word_def) {
WordList& WordList::push_back(Ref<FiftCont> word_def) {
list.push_back(std::move(word_def));
return *this;
}
WordList& WordList::push_back(WordDef& wd) {
WordList& WordList::push_back(FiftCont& wd) {
list.emplace_back(&wd);
return *this;
}
Ref<WordDef> WordList::run_tail(IntCtx& ctx) const {
Ref<FiftCont> WordList::run_tail(IntCtx& ctx) const {
if (list.empty()) {
return {};
}
auto it = list.cbegin(), it2 = list.cend() - 1;
while (it < it2) {
(*it)->run(ctx);
++it;
if (list.size() > 1) {
ctx.next = td::make_ref<ListCont>(std::move(ctx.next), Ref<WordList>(this), 1);
}
return *it;
return list[0];
}
void WordList::close() {
list.shrink_to_fit();
}
WordList& WordList::append(const std::vector<Ref<WordDef>>& other) {
WordList& WordList::append(const std::vector<Ref<FiftCont>>& other) {
list.insert(list.end(), other.begin(), other.end());
return *this;
}
WordList& WordList::append(const Ref<FiftCont>* begin, const Ref<FiftCont>* end) {
list.insert(list.end(), begin, end);
return *this;
}
bool WordList::dump(std::ostream& os, const IntCtx& ctx) const {
os << "{";
for (auto entry : list) {
os << ' ';
entry->print_name(os, ctx);
}
os << " }" << std::endl;
return true;
}
//
// ListCont
//
Ref<FiftCont> ListCont::run_tail(IntCtx& ctx) const {
auto sz = list->size();
if (pos >= sz) {
return std::move(ctx.next);
} else if (ctx.next.not_null()) {
ctx.next = td::make_ref<ListCont>(SeqCont::seq(next, std::move(ctx.next)), list, pos + 1);
} else if (pos + 1 == sz) {
ctx.next = next;
} else {
ctx.next = td::make_ref<ListCont>(next, list, pos + 1);
}
return list->at(pos);
}
Ref<FiftCont> ListCont::run_modify(IntCtx& ctx) {
auto sz = list->size();
if (pos >= sz) {
return std::move(ctx.next);
}
auto cur = list->at(pos++);
if (ctx.next.not_null()) {
next = SeqCont::seq(next, std::move(ctx.next));
}
if (pos == sz) {
ctx.next = std::move(next);
} else {
ctx.next = self();
}
return cur;
}
bool ListCont::dump(std::ostream& os, const IntCtx& ctx) const {
std::string dict_name = list->get_dict_name(ctx);
if (!dict_name.empty()) {
os << "[in " << dict_name << ":] ";
}
std::size_t sz = list->size(), i, a = (pos >= 16 ? pos - 16 : 0), b = std::min(pos + 16, sz);
if (a > 0) {
os << "... ";
}
for (i = a; i < b; i++) {
if (i == pos) {
os << "**HERE** ";
}
list->at(i)->print_name(os, ctx);
os << ' ';
}
if (b < sz) {
os << "...";
}
os << std::endl;
return true;
}
//
// DictEntry
//
DictEntry::DictEntry(Ref<WordDef> _def, bool _act) : def(std::move(_def)), active(_act) {
}
DictEntry::DictEntry(StackWordFunc func) : def(Ref<StackWord>{true, std::move(func)}), active(false) {
}
@ -109,22 +167,6 @@ 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) {
}
Ref<WordDef> DictEntry::get_def() const& {
return def;
}
Ref<WordDef> DictEntry::get_def() && {
return std::move(def);
}
void DictEntry::operator()(IntCtx& ctx) const {
def->run(ctx);
}
bool DictEntry::is_active() const {
return active;
}
//
// Dictionary
//
@ -141,7 +183,7 @@ void Dictionary::def_ctx_word(std::string name, CtxWordFunc func) {
}
void Dictionary::def_active_word(std::string name, CtxWordFunc func) {
Ref<WordDef> wdef = Ref<CtxWord>{true, std::move(func)};
Ref<FiftCont> wdef = Ref<CtxWord>{true, std::move(func)};
def_word(std::move(name), {std::move(wdef), true});
}
@ -166,17 +208,32 @@ void Dictionary::undef_word(td::Slice name) {
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) {
if (word_ptr) {
*word_ptr = entry.first;
}
return true;
}
}
return false;
}
void interpret_nop(vm::Stack& stack) {
}
Ref<WordDef> Dictionary::nop_word_def = Ref<StackWord>{true, interpret_nop};
Ref<FiftCont> Dictionary::nop_word_def = Ref<StackWord>{true, interpret_nop};
//
// functions for wordef
//
Ref<WordDef> pop_exec_token(vm::Stack& stack) {
Ref<FiftCont> pop_exec_token(vm::Stack& stack) {
stack.check_underflow(1);
auto wd_ref = stack.pop().as_object<WordDef>();
auto wd_ref = stack.pop().as_object<FiftCont>();
if (wd_ref.is_null()) {
throw IntError{"execution token expected"};
}

View file

@ -22,9 +22,11 @@
#include <map>
#include "IntCtx.h"
#include "Continuation.h"
namespace fift {
using td::Ref;
/*
*
* WORD CLASSES
@ -34,66 +36,49 @@ using td::Ref;
typedef std::function<void(vm::Stack&)> StackWordFunc;
typedef std::function<void(IntCtx&)> CtxWordFunc;
class WordDef : public td::CntObject {
public:
WordDef() = default;
virtual ~WordDef() override = default;
virtual Ref<WordDef> run_tail(IntCtx& ctx) const = 0;
void run(IntCtx& ctx) const;
virtual bool is_list() const {
return false;
}
virtual long long list_size() const {
return -1;
}
virtual const std::vector<Ref<WordDef>>* get_list() const {
return nullptr;
}
};
class StackWord : public WordDef {
class StackWord : public FiftCont {
StackWordFunc f;
public:
StackWord(StackWordFunc _f) : f(std::move(_f)) {
}
~StackWord() override = default;
Ref<WordDef> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class CtxWord : public WordDef {
class CtxWord : public FiftCont {
CtxWordFunc f;
public:
CtxWord(CtxWordFunc _f) : f(std::move(_f)) {
}
~CtxWord() override = default;
Ref<WordDef> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
typedef std::function<Ref<WordDef>(IntCtx&)> CtxTailWordFunc;
typedef std::function<Ref<FiftCont>(IntCtx&)> CtxTailWordFunc;
class CtxTailWord : public WordDef {
class CtxTailWord : public FiftCont {
CtxTailWordFunc f;
public:
CtxTailWord(CtxTailWordFunc _f) : f(std::move(_f)) {
}
~CtxTailWord() override = default;
Ref<WordDef> run_tail(IntCtx& ctx) const override;
Ref<FiftCont> run_tail(IntCtx& ctx) const override;
};
class WordList : public WordDef {
std::vector<Ref<WordDef>> list;
class WordList : public FiftCont {
std::vector<Ref<FiftCont>> list;
public:
~WordList() override = default;
WordList() = default;
WordList(std::vector<Ref<WordDef>>&& _list);
WordList(const std::vector<Ref<WordDef>>& _list);
WordList& push_back(Ref<WordDef> word_def);
WordList& push_back(WordDef& wd);
Ref<WordDef> run_tail(IntCtx& ctx) const override;
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;
@ -101,43 +86,73 @@ class WordList : public WordDef {
long long list_size() const override {
return (long long)list.size();
}
const std::vector<Ref<WordDef>>* get_list() const override {
return &list;
std::size_t size() const {
return list.size();
}
WordList& append(const std::vector<Ref<WordDef>>& other);
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<WordDef> def;
Ref<FiftCont> def;
bool active;
public:
DictEntry() = delete;
DictEntry(const DictEntry& ref) = default;
DictEntry(DictEntry&& ref) = default;
DictEntry(Ref<WordDef> _def, bool _act = false);
DictEntry(Ref<FiftCont> _def, bool _act = false) : def(std::move(_def)), active(_act) {
}
DictEntry(StackWordFunc func);
DictEntry(CtxWordFunc func, bool _act = false);
DictEntry(CtxTailWordFunc func, bool _act = false);
//DictEntry(const std::vector<Ref<WordDef>>& word_list);
//DictEntry(std::vector<Ref<WordDef>>&& word_list);
//DictEntry(const std::vector<Ref<FiftCont>>& word_list);
//DictEntry(std::vector<Ref<FiftCont>>&& word_list);
DictEntry& operator=(const DictEntry&) = default;
DictEntry& operator=(DictEntry&&) = default;
Ref<WordDef> get_def() const&;
Ref<WordDef> get_def() &&;
void operator()(IntCtx& ctx) const;
bool is_active() const;
~DictEntry() = default;
Ref<FiftCont> get_def() const& {
return def;
}
Ref<FiftCont> get_def() && {
return std::move(def);
}
bool is_active() const {
return active;
}
};
/*
DictEntry::DictEntry(const std::vector<Ref<WordDef>>& word_list) : def(Ref<WordList>{true, word_list}) {
DictEntry::DictEntry(const std::vector<Ref<FiftCont>>& word_list) : def(Ref<WordList>{true, word_list}) {
}
DictEntry::DictEntry(std::vector<Ref<WordDef>>&& word_list) : def(Ref<WordList>{true, std::move(word_list)}) {
DictEntry::DictEntry(std::vector<Ref<FiftCont>>&& word_list) : def(Ref<WordList>{true, std::move(word_list)}) {
}
*/
@ -156,7 +171,10 @@ class Dictionary {
void def_stack_word(std::string name, StackWordFunc func);
void def_word(std::string name, DictEntry word);
void undef_word(td::Slice 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();
}
@ -164,7 +182,7 @@ class Dictionary {
return words_.end();
}
static Ref<WordDef> nop_word_def;
static Ref<FiftCont> nop_word_def;
private:
std::map<std::string, DictEntry, std::less<>> words_;
@ -176,7 +194,7 @@ class Dictionary {
*
*/
Ref<WordDef> pop_exec_token(vm::Stack& stack);
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

@ -37,25 +37,18 @@ td::Result<int> Fift::interpret_file(std::string fname, std::string current_dir,
return td::Status::Error("cannot locate file `" + fname + "`");
}
auto file = r_file.move_as_ok();
IntCtx ctx;
std::stringstream ss(file.data);
ctx.input_stream = &ss;
ctx.filename = td::PathView(file.path).file_name().str();
ctx.currentd_dir = td::PathView(file.path).parent_dir().str();
ctx.include_depth = is_interactive ? 0 : 1;
return do_interpret(ctx);
IntCtx ctx{ss, td::PathView(file.path).file_name().str(), td::PathView(file.path).parent_dir().str(),
(int)!is_interactive};
return do_interpret(ctx, is_interactive);
}
td::Result<int> Fift::interpret_istream(std::istream& stream, std::string current_dir, bool is_interactive) {
IntCtx ctx;
ctx.input_stream = &stream;
ctx.filename = "stdin";
ctx.currentd_dir = current_dir;
ctx.include_depth = is_interactive ? 0 : 1;
return do_interpret(ctx);
IntCtx ctx{stream, "stdin", current_dir, (int)!is_interactive};
return do_interpret(ctx, is_interactive);
}
td::Result<int> Fift::do_interpret(IntCtx& ctx) {
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;
@ -64,13 +57,26 @@ td::Result<int> Fift::do_interpret(IntCtx& ctx) {
if (!ctx.output_stream) {
return td::Status::Error("Cannot run interpreter without output_stream");
}
try {
return funny_interpret_loop(ctx);
} catch (fift::IntError ab) {
return td::Status::Error(ab.msg);
} catch (fift::Quit q) {
return q.res;
while (true) {
auto res = ctx.run(td::make_ref<InterpretCont>());
if (res.is_error()) {
res = ctx.add_error_loc(res.move_as_error());
if (config_.show_backtrace) {
std::ostringstream os;
ctx.print_error_backtrace(os);
LOG(ERROR) << os.str();
}
if (is_interactive) {
LOG(ERROR) << res.move_as_error().message();
ctx.top_ctx();
ctx.clear_error();
ctx.stack.clear();
ctx.load_next_line();
continue;
}
}
return res;
}
return 0;
}
} // namespace fift

View file

@ -26,7 +26,6 @@
namespace fift {
struct IntCtx;
int funny_interpret_loop(IntCtx& ctx);
struct Fift {
public:
@ -36,6 +35,7 @@ struct Fift {
fift::Dictionary dictionary;
std::ostream* output_stream{&std::cout};
std::ostream* error_stream{&std::cerr};
bool show_backtrace{true};
};
// Fift must own ton_db and dictionary, no concurrent access is allowed
explicit Fift(Config config);
@ -48,6 +48,6 @@ struct Fift {
private:
Config config_;
td::Result<int> do_interpret(IntCtx& ctx);
td::Result<int> do_interpret(IntCtx& ctx, bool is_interactive = false);
};
} // namespace fift

View file

@ -68,33 +68,69 @@ void CharClassifier::set_char_class(int c, int cl) {
}
IntCtx::Savepoint::Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir,
std::istream* new_input_stream)
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_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;
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);
}
IntCtx::Savepoint::~Savepoint() {
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() {
@ -194,4 +230,99 @@ void IntCtx::check_int_exec() const {
throw IntError{"internal interpret mode only"};
}
}
bool IntCtx::print_error_backtrace(std::ostream& os) const {
if (exc_cont.is_null() && exc_next.is_null()) {
os << "(no backtrace)\n";
return false;
}
if (exc_cont.not_null()) {
os << "top: ";
exc_cont->dump(os, *this);
}
return print_backtrace(os, exc_next);
}
bool IntCtx::print_backtrace(std::ostream& os, Ref<FiftCont> cont) const {
for (int i = 1; cont.not_null() && i <= 16; i++) {
os << "level " << i << ": ";
cont->dump(os, *this);
cont = cont->up();
}
if (cont.not_null()) {
os << "... more levels ...\n";
}
return true;
}
Ref<FiftCont> IntCtx::throw_exception(td::Status err, Ref<FiftCont> cur) {
exc_cont = std::move(cur);
exc_next = std::move(next);
error = std::move(err);
next.clear();
auto cont = std::move(exc_handler);
if (cont.is_null()) {
return {}; // no Fift exception handler set
} else if (cont.is_unique()) {
return cont.unique_write().handle_modify(*this);
} else {
return cont->handle_tail(*this);
}
}
void IntCtx::clear_error() {
error = td::Status::OK();
exit_code = 0;
}
td::Result<int> IntCtx::get_result() {
if (error.is_error()) {
return error.move_as_error();
} else {
return exit_code;
}
}
td::Status IntCtx::add_error_loc(td::Status err) const {
if (err.is_error()) {
std::ostringstream os;
if (include_depth && line_no) {
os << filename << ":" << line_no << ":\t";
}
if (!word.empty()) {
os << word << ":";
}
return err.move_as_error_prefix(os.str());
} else {
return err;
}
}
td::Result<int> IntCtx::run(Ref<FiftCont> cont) {
clear_error();
while (cont.not_null()) {
try {
if (cont.is_unique()) {
cont = cont.unique_write().run_modify(*this);
} else {
cont = cont->run_tail(*this);
}
} catch (IntError& err) {
cont = throw_exception(td::Status::Error(err.msg), std::move(cont));
} catch (vm::VmError& err) {
cont = throw_exception(err.as_status(), std::move(cont));
} catch (vm::VmVirtError& err) {
cont = throw_exception(err.as_status(), std::move(cont));
} catch (vm::CellBuilder::CellWriteError&) {
cont = throw_exception(td::Status::Error("Cell builder write error"), std::move(cont));
} catch (vm::VmFatal&) {
cont = throw_exception(td::Status::Error("fatal vm error"), std::move(cont));
}
if (cont.is_null()) {
cont = std::move(next);
}
}
return get_result();
}
} // namespace fift

View file

@ -22,6 +22,11 @@
#include "crypto/vm/stack.hpp"
#include "crypto/common/bitstring.h"
#include "td/utils/Status.h"
#include "Dictionary.h"
#include "Continuation.h"
#include <cstdint>
#include <cstring>
#include <iostream>
@ -65,13 +70,18 @@ class CharClassifier {
struct IntCtx {
vm::Stack stack;
Ref<FiftCont> next, exc_handler;
Ref<FiftCont> exc_cont, exc_next;
int state{0};
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};
@ -79,18 +89,50 @@ struct IntCtx {
Dictionary* dictionary{nullptr};
SourceLookup* source_lookup{nullptr};
int* now{nullptr};
std::string word;
private:
std::string str;
const char* input_ptr;
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)
: include_depth(_depth)
, filename(std::move(_filename))
, currentd_dir(std::move(_curdir))
, input_stream(&_istream) {
}
operator vm::Stack&() {
return stack;
}
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);
@ -127,25 +169,29 @@ struct IntCtx {
state = 0;
stack.clear();
}
class Savepoint {
IntCtx& ctx;
int old_line_no;
bool old_need_line;
std::string old_filename;
std::string old_current_dir;
std::istream* old_input_stream;
std::string old_curline;
std::ptrdiff_t old_curpos;
public:
Savepoint(IntCtx& _ctx, std::string new_filename, std::string new_current_dir, std::istream* new_input_stream);
~Savepoint();
};
void check_compile() const;
void check_execute() const;
void check_not_int_exec() const;
void check_int_exec() const;
bool print_error_backtrace(std::ostream& os) const;
bool print_backtrace(std::ostream& os, Ref<FiftCont> cont) const;
td::Status add_error_loc(td::Status err) const;
void set_exit_code(int err_code) {
exit_code = err_code;
}
int get_exit_code() const {
return exit_code;
}
void clear_error();
td::Result<int> get_result();
Ref<FiftCont> throw_exception(td::Status err, Ref<FiftCont> cur = {});
td::Result<int> run(Ref<FiftCont> cont);
};
td::StringBuilder& operator<<(td::StringBuilder& os, const IntCtx& ctx);

View file

@ -28,6 +28,12 @@ namespace {
std::string fift_dir(std::string dir) {
return dir.size() > 0 ? dir : td::PathView(td::realpath(__FILE__).move_as_ok()).parent_dir().str() + "lib/";
}
std::string smartcont_dir(std::string dir) {
return dir.size() > 0
? dir
: td::PathView(td::PathView(td::realpath(__FILE__).move_as_ok()).parent_dir_noslash()).parent_dir().str() +
"smartcont/";
}
td::Result<std::string> load_source(std::string name, std::string dir = "") {
return td::read_file_str(fift_dir(dir) + name);
}
@ -49,6 +55,9 @@ td::Result<std::string> load_Lisp_fif(std::string dir = "") {
td::Result<std::string> load_GetOpt_fif(std::string dir = "") {
return load_source("GetOpt.fif", dir);
}
td::Result<std::string> load_wallet3_code_fif(std::string dir = "") {
return td::read_file_str(smartcont_dir(dir) + "wallet-v3-code.fif");
}
class MemoryFileLoader : public fift::FileLoader {
public:
@ -98,7 +107,7 @@ 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,
std::string dir = "") {
bool need_w3_code = true, std::string dir = "") {
auto loader = std::make_unique<MemoryFileLoader>();
loader->add_file("/main.fif", std::move(main));
if (need_preamble) {
@ -127,6 +136,10 @@ td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_
TRY_RESULT(f, load_Lisp_fif(dir));
loader->add_file("/Lisp.fif", std::move(f));
}
if (need_w3_code) {
TRY_RESULT(f, load_wallet3_code_fif(dir));
loader->add_file("/wallet-v3-code.fif", std::move(f));
}
auto res = fift::SourceLookup(std::move(loader));
res.add_include_path("/");
return std::move(res);
@ -158,7 +171,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, fift_dir));
TRY_RESULT(source_lookup, create_source_lookup(source, 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);
@ -174,8 +187,9 @@ td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std:
return std::move(res);
}
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) {
return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, fift_dir);
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);
}
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir, bool is_raw) {
@ -183,7 +197,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, fift_dir));
true, true, true, 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

@ -28,7 +28,8 @@ struct FiftOutput {
};
td::Result<fift::SourceLookup> create_mem_source_lookup(std::string main, std::string fift_dir = "",
bool need_preamble = true, bool need_asm = true,
bool need_ton_util = true, bool need_lisp = true);
bool need_ton_util = true, bool need_lisp = true,
bool need_w3_code = true);
td::Result<FiftOutput> mem_run_fift(std::string source, std::vector<std::string> args = {}, std::string fift_dir = "");
td::Result<FiftOutput> mem_run_fift(SourceLookup source_lookup, std::vector<std::string> args);
td::Result<td::Ref<vm::Cell>> compile_asm(td::Slice asm_code, std::string fift_dir = "", bool is_raw = true);

View file

@ -62,7 +62,7 @@ void show_total_cells(std::ostream& stream) {
stream << "total cells = " << vm::DataCell::get_total_data_cells() << std::endl;
}
void do_compile(vm::Stack& stack, Ref<WordDef> word_def);
void do_compile(vm::Stack& stack, Ref<FiftCont> word_def);
void do_compile_literals(vm::Stack& stack, int count);
void interpret_dot(IntCtx& ctx, bool space_after) {
@ -204,18 +204,6 @@ void interpret_negate(vm::Stack& stack) {
stack.push_int(-stack.pop_int());
}
void interpret_const(vm::Stack& stack, long long val) {
stack.push_smallint(val);
}
void interpret_big_const(vm::Stack& stack, td::RefInt256 val) {
stack.push_int(std::move(val));
}
void interpret_literal(vm::Stack& stack, vm::StackEntry se) {
stack.push(std::move(se));
}
void interpret_cmp(vm::Stack& stack, const char opt[3]) {
auto y = stack.pop_int();
auto x = stack.pop_int();
@ -537,7 +525,8 @@ void interpret_hold(vm::Stack& stack) {
stack.check_underflow(2);
char buffer[8];
unsigned len = make_utf8_char(buffer, stack.pop_smallint_range(0x10ffff, -128));
std::string s = stack.pop_string() + std::string{buffer, len};
std::string s = stack.pop_string();
s.append(buffer, len);
stack.push_string(std::move(s));
}
@ -1567,78 +1556,183 @@ void interpret_dict_get(vm::Stack& stack, int sgnd, int mode) {
}
}
void interpret_dict_map(IntCtx& ctx, bool ext, bool sgnd) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n}, dict2{n};
for (auto entry : dict.range(false, sgnd)) {
ctx.stack.push_builder(Ref<vm::CellBuilder>{true});
if (ext) {
ctx.stack.push_int(dict.key_as_integer(entry.first, sgnd));
}
ctx.stack.push_cellslice(std::move(entry.second));
func->run(ctx);
if (ctx.stack.pop_bool()) {
if (!dict2.set_builder(entry.first, n, ctx.stack.pop_builder())) {
throw IntError{"cannot insert value into dictionary"};
}
}
};
ctx.stack.push_maybe_cell(std::move(dict2).extract_root_cell());
class DictMapCont : public LoopCont {
int n;
bool ext;
bool sgnd;
vm::Dictionary dict, dict2;
vm::DictIterator it;
public:
DictMapCont(Ref<FiftCont> _func, Ref<FiftCont> _after, int _n, Ref<vm::Cell> dict_root, bool _ext, bool _sgnd)
: LoopCont(std::move(_func), std::move(_after))
, n(_n)
, ext(_ext)
, sgnd(_sgnd)
, dict(std::move(dict_root), n)
, dict2(n) {
}
DictMapCont(const DictMapCont&) = default;
DictMapCont* make_copy() const override {
return new DictMapCont(*this);
}
bool init(IntCtx& ctx) override {
it = dict.init_iterator(false, sgnd);
return true;
}
bool pre_exec(IntCtx& ctx) override;
bool post_exec(IntCtx& ctx) override;
bool finalize(IntCtx& ctx) override;
};
bool DictMapCont::pre_exec(IntCtx& ctx) {
if (it.eof()) {
return false;
}
ctx.stack.push_builder(td::make_ref<vm::CellBuilder>());
if (ext) {
ctx.stack.push_int(dict.key_as_integer(it.cur_pos(), sgnd));
}
ctx.stack.push_cellslice(it.cur_value());
return true;
}
void interpret_dict_foreach(IntCtx& ctx, bool reverse, bool sgnd) {
bool DictMapCont::post_exec(IntCtx& ctx) {
if (ctx.stack.pop_bool()) {
if (!dict2.set_builder(it.cur_pos(), n, ctx.stack.pop_builder())) {
throw IntError{"cannot insert value into dictionary"};
}
}
return !(++it).eof();
}
bool DictMapCont::finalize(IntCtx& ctx) {
ctx.stack.push_maybe_cell(std::move(dict2).extract_root_cell());
return true;
}
Ref<FiftCont> interpret_dict_map(IntCtx& ctx, bool ext, bool sgnd) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n};
for (auto entry : dict.range(reverse, sgnd)) {
ctx.stack.push_int(dict.key_as_integer(entry.first, sgnd));
ctx.stack.push_cellslice(std::move(entry.second));
func->run(ctx);
if (!ctx.stack.pop_bool()) {
ctx.stack.push_bool(false);
return;
}
};
ctx.stack.push_bool(true);
return td::make_ref<DictMapCont>(std::move(func), std::move(ctx.next), n, ctx.stack.pop_maybe_cell(), ext, sgnd);
}
class DictIterCont : public LoopCont {
int n;
bool reverse;
bool sgnd;
bool ok;
bool inited{false};
vm::Dictionary dict;
vm::DictIterator it;
public:
DictIterCont(Ref<FiftCont> _func, Ref<FiftCont> _after, int _n, Ref<vm::Cell> dict_root, bool _reverse, bool _sgnd)
: LoopCont(std::move(_func), std::move(_after))
, n(_n)
, reverse(_reverse)
, sgnd(_sgnd)
, ok(true)
, dict(std::move(dict_root), n) {
}
DictIterCont(const DictIterCont&) = default;
DictIterCont* make_copy() const override {
return new DictIterCont(*this);
}
bool do_init();
bool init(IntCtx& ctx) override {
return do_init();
}
bool pre_exec(IntCtx& ctx) override;
bool post_exec(IntCtx& ctx) override;
bool finalize(IntCtx& ctx) override;
template <typename T>
bool lookup(const T& key, bool strict, bool backw) {
return do_init() && it.lookup(key, strict, backw);
}
};
bool DictIterCont::do_init() {
if (!inited) {
it = dict.init_iterator(reverse, sgnd);
inited = true;
}
return true;
}
bool DictIterCont::pre_exec(IntCtx& ctx) {
if (it.eof()) {
return false;
}
ctx.stack.push_int(dict.key_as_integer(it.cur_pos(), sgnd));
ctx.stack.push_cellslice(it.cur_value());
return true;
}
bool DictIterCont::post_exec(IntCtx& ctx) {
ok = ctx.stack.pop_bool();
return ok && !(++it).eof();
}
bool DictIterCont::finalize(IntCtx& ctx) {
ctx.stack.push_bool(ok);
return true;
}
Ref<FiftCont> interpret_dict_foreach(IntCtx& ctx, bool reverse, bool sgnd) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
return td::make_ref<DictIterCont>(std::move(func), std::move(ctx.next), n, ctx.stack.pop_maybe_cell(), reverse, sgnd);
}
// mode: +1 = reverse, +2 = signed, +4 = strict, +8 = lookup backwards, +16 = with hint
void interpret_dict_foreach_from(IntCtx& ctx, int mode) {
Ref<FiftCont> interpret_dict_foreach_from(IntCtx& ctx, int mode) {
if (mode < 0) {
mode = ctx.stack.pop_smallint_range(31);
}
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n};
vm::DictIterator it{dict, mode & 3};
unsigned char buffer[vm::Dictionary::max_key_bytes];
auto it_cont = td::make_ref<DictIterCont>(std::move(func), std::move(ctx.next), n, ctx.stack.pop_maybe_cell(),
mode & 1, mode & 2);
for (int s = (mode >> 4) & 1; s >= 0; --s) {
auto key = dict.integer_key(ctx.stack.pop_int(), n, mode & 2, buffer);
unsigned char buffer[vm::Dictionary::max_key_bytes];
auto key = vm::Dictionary::integer_key(ctx.stack.pop_int(), n, mode & 2, buffer);
if (!key.is_valid()) {
throw IntError{"not enough bits for a dictionary key"};
}
it.lookup(key, mode & 4, mode & 8);
it_cont.write().lookup(key, mode & 4, mode & 8);
}
for (; !it.eof(); ++it) {
ctx.stack.push_int(dict.key_as_integer(it.cur_pos(), mode & 2));
ctx.stack.push_cellslice(it.cur_value());
func->run(ctx);
if (!ctx.stack.pop_bool()) {
ctx.stack.push_bool(false);
return;
}
};
ctx.stack.push_bool(true);
return it_cont;
}
void interpret_dict_merge(IntCtx& ctx) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict2{ctx.stack.pop_maybe_cell(), n};
vm::Dictionary dict1{ctx.stack.pop_maybe_cell(), n};
vm::Dictionary dict3{n};
auto it1 = dict1.begin(), it2 = dict2.begin();
class DictMergeCont : public LoopCont {
int n;
vm::Dictionary dict1, dict2, dict3;
vm::DictIterator it1, it2;
public:
DictMergeCont(Ref<FiftCont> _func, Ref<FiftCont> _after, int _n, Ref<vm::Cell> dict1_root, Ref<vm::Cell> dict2_root)
: LoopCont(std::move(_func), std::move(_after))
, n(_n)
, dict1(std::move(dict1_root), n)
, dict2(std::move(dict2_root), n)
, dict3(n) {
}
DictMergeCont(const DictMergeCont&) = default;
DictMergeCont* make_copy() const override {
return new DictMergeCont(*this);
}
bool init(IntCtx& ctx) override {
it1 = dict1.begin();
it2 = dict2.begin();
return true;
}
bool pre_exec(IntCtx& ctx) override;
bool post_exec(IntCtx& ctx) override;
bool finalize(IntCtx& ctx) override;
};
bool DictMergeCont::pre_exec(IntCtx& ctx) {
while (!it1.eof() || !it2.eof()) {
int c = it1.eof() ? 1 : (it2.eof() ? -1 : it1.cur_pos().compare(it2.cur_pos(), n));
bool ok = true;
@ -1652,29 +1746,67 @@ void interpret_dict_merge(IntCtx& ctx) {
ctx.stack.push_builder(Ref<vm::CellBuilder>{true});
ctx.stack.push_cellslice(it1.cur_value());
ctx.stack.push_cellslice(it2.cur_value());
func->run(ctx);
if (ctx.stack.pop_bool()) {
ok = dict3.set_builder(it1.cur_pos(), n, ctx.stack.pop_builder());
}
++it1;
++it2;
return true;
}
if (!ok) {
throw IntError{"cannot insert value into dictionary"};
}
}
ctx.stack.push_maybe_cell(std::move(dict3).extract_root_cell());
return false;
}
void interpret_dict_diff(IntCtx& ctx) {
bool DictMergeCont::post_exec(IntCtx& ctx) {
if (ctx.stack.pop_bool() && !dict3.set_builder(it1.cur_pos(), n, ctx.stack.pop_builder())) {
throw IntError{"cannot insert value into dictionary"};
}
++it1;
++it2;
return true;
}
bool DictMergeCont::finalize(IntCtx& ctx) {
ctx.stack.push_maybe_cell(std::move(dict3).extract_root_cell());
return true;
}
Ref<FiftCont> interpret_dict_merge(IntCtx& ctx) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict2{ctx.stack.pop_maybe_cell(), n};
vm::Dictionary dict1{ctx.stack.pop_maybe_cell(), n};
auto it1 = dict1.begin(), it2 = dict2.begin();
auto dict2_root = ctx.stack.pop_maybe_cell();
return td::make_ref<DictMergeCont>(std::move(func), std::move(ctx.next), n, ctx.stack.pop_maybe_cell(),
std::move(dict2_root));
}
class DictDiffCont : public LoopCont {
int n;
bool ok{true};
vm::Dictionary dict1, dict2;
vm::DictIterator it1, it2;
public:
DictDiffCont(Ref<FiftCont> _func, Ref<FiftCont> _after, int _n, Ref<vm::Cell> dict1_root, Ref<vm::Cell> dict2_root)
: LoopCont(std::move(_func), std::move(_after))
, n(_n)
, dict1(std::move(dict1_root), n)
, dict2(std::move(dict2_root), n) {
}
DictDiffCont(const DictDiffCont&) = default;
DictDiffCont* make_copy() const override {
return new DictDiffCont(*this);
}
bool init(IntCtx& ctx) override {
it1 = dict1.begin();
it2 = dict2.begin();
return true;
}
bool pre_exec(IntCtx& ctx) override;
bool post_exec(IntCtx& ctx) override;
bool finalize(IntCtx& ctx) override;
};
bool DictDiffCont::pre_exec(IntCtx& ctx) {
while (!it1.eof() || !it2.eof()) {
int c = it1.eof() ? 1 : (it2.eof() ? -1 : it1.cur_pos().compare(it2.cur_pos(), n));
bool run = true;
if (c < 0) {
ctx.stack.push_int(dict1.key_as_integer(it1.cur_pos()));
ctx.stack.push_cellslice(it1.cur_value());
@ -1691,20 +1823,33 @@ void interpret_dict_diff(IntCtx& ctx) {
ctx.stack.push_cellslice(it1.cur_value());
ctx.stack.push_cellslice(it2.cur_value());
} else {
run = false;
++it1;
++it2;
continue;
}
++it1;
++it2;
}
if (run) {
func->run(ctx);
if (!ctx.stack.pop_bool()) {
ctx.stack.push_bool(false);
return;
}
}
return true;
}
ctx.stack.push_bool(true);
return false;
}
bool DictDiffCont::post_exec(IntCtx& ctx) {
return (ok = ctx.stack.pop_bool());
}
bool DictDiffCont::finalize(IntCtx& ctx) {
ctx.stack.push_bool(ok);
return true;
}
Ref<FiftCont> interpret_dict_diff(IntCtx& ctx) {
auto func = pop_exec_token(ctx);
int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits);
auto dict2_root = ctx.stack.pop_maybe_cell();
return td::make_ref<DictDiffCont>(std::move(func), std::move(ctx.next), n, ctx.stack.pop_maybe_cell(),
std::move(dict2_root));
}
void interpret_pfx_dict_add(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add_builder) {
@ -1791,7 +1936,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<WordDef>{wordlist_ref}});
stack.push({vm::from_object, Ref<FiftCont>{wordlist_ref}});
}
void interpret_wordlist_end(IntCtx& ctx) {
@ -1846,7 +1991,7 @@ void interpret_create(IntCtx& ctx) {
interpret_create_aux(ctx, 0);
}
Ref<WordDef> create_aux_wd{Ref<CtxWord>{true, std::bind(interpret_create_aux, std::placeholders::_1, -1)}};
Ref<FiftCont> create_aux_wd{Ref<CtxWord>{true, std::bind(interpret_create_aux, std::placeholders::_1, -1)}};
// { bl word <mode> 2 ' (create) } :: :
void interpret_colon(IntCtx& ctx, int mode) {
@ -2027,39 +2172,43 @@ void interpret_parse_hex_number(vm::Stack& stack) {
}
void interpret_quit(IntCtx& ctx) {
throw Quit{0};
// TODO: change to correct behavior
ctx.set_exit_code(0);
ctx.next.clear();
}
void interpret_bye(IntCtx& ctx) {
throw Quit{-1};
ctx.set_exit_code(-1);
ctx.next.clear();
}
void interpret_halt(vm::Stack& stack) {
int code = stack.pop_smallint_range(255);
throw Quit{~code};
void interpret_halt(IntCtx& ctx) {
ctx.set_exit_code(~ctx.stack.pop_smallint_range(255));
ctx.next.clear();
}
void interpret_abort(IntCtx& ctx) {
throw IntError{ctx.stack.pop_string()};
}
Ref<WordDef> interpret_execute(IntCtx& ctx) {
Ref<FiftCont> interpret_execute(IntCtx& ctx) {
return pop_exec_token(ctx);
}
Ref<WordDef> interpret_execute_times(IntCtx& ctx) {
Ref<FiftCont> interpret_execute_times(IntCtx& ctx) {
int count = ctx.stack.pop_smallint_range(1000000000);
auto wd_ref = pop_exec_token(ctx);
if (!count) {
auto body = pop_exec_token(ctx);
if (count <= 0) {
return {};
}
while (--count > 0) {
wd_ref->run(ctx);
if (count == 1) {
return body;
}
return wd_ref;
ctx.next = td::make_ref<TimesCont>(body, std::move(ctx.next), count - 1);
return body;
}
Ref<WordDef> interpret_if(IntCtx& ctx) {
Ref<FiftCont> interpret_if(IntCtx& ctx) {
auto true_ref = pop_exec_token(ctx);
if (ctx.stack.pop_bool()) {
return true_ref;
@ -2068,7 +2217,7 @@ Ref<WordDef> interpret_if(IntCtx& ctx) {
}
}
Ref<WordDef> interpret_ifnot(IntCtx& ctx) {
Ref<FiftCont> interpret_ifnot(IntCtx& ctx) {
auto false_ref = pop_exec_token(ctx);
if (ctx.stack.pop_bool()) {
return {};
@ -2077,7 +2226,7 @@ Ref<WordDef> interpret_ifnot(IntCtx& ctx) {
}
}
Ref<WordDef> interpret_cond(IntCtx& ctx) {
Ref<FiftCont> interpret_cond(IntCtx& ctx) {
auto false_ref = pop_exec_token(ctx);
auto true_ref = pop_exec_token(ctx);
if (ctx.stack.pop_bool()) {
@ -2087,23 +2236,17 @@ Ref<WordDef> interpret_cond(IntCtx& ctx) {
}
}
void interpret_while(IntCtx& ctx) {
auto body_ref = pop_exec_token(ctx);
auto cond_ref = pop_exec_token(ctx);
while (true) {
cond_ref->run(ctx);
if (!ctx.stack.pop_bool()) {
break;
}
body_ref->run(ctx);
}
Ref<FiftCont> interpret_while(IntCtx& ctx) {
auto body = pop_exec_token(ctx);
auto cond = pop_exec_token(ctx);
ctx.next = td::make_ref<WhileCont>(cond, std::move(body), std::move(ctx.next), true);
return cond;
}
void interpret_until(IntCtx& ctx) {
auto body_ref = pop_exec_token(ctx);
do {
body_ref->run(ctx);
} while (!ctx.stack.pop_bool());
Ref<FiftCont> interpret_until(IntCtx& ctx) {
auto body = pop_exec_token(ctx);
ctx.next = td::make_ref<UntilCont>(body, std::move(ctx.next));
return body;
}
void interpret_tick(IntCtx& ctx) {
@ -2133,25 +2276,39 @@ void interpret_find(IntCtx& ctx) {
}
}
void interpret_tick_nop(vm::Stack& stack) {
stack.push({vm::from_object, Dictionary::nop_word_def});
void interpret_leave_source(IntCtx& ctx) {
if (!ctx.leave_ctx()) {
throw IntError{"cannot leave included file interpretation context"};
}
}
void interpret_include(IntCtx& ctx) {
Ref<FiftCont> interpret_include(IntCtx& ctx) {
auto fname = ctx.stack.pop_string();
auto r_file = ctx.source_lookup->lookup_source(fname, ctx.currentd_dir);
if (r_file.is_error()) {
throw IntError{"cannot locate file `" + fname + "`"};
}
auto file = r_file.move_as_ok();
std::stringstream ss(std::move(file.data));
IntCtx::Savepoint save{ctx, td::PathView(file.path).file_name().str(), td::PathView(file.path).parent_dir().str(),
&ss};
funny_interpret_loop(ctx);
auto ss = std::make_unique<std::stringstream>(std::move(file.data));
if (!ctx.enter_ctx(td::PathView(file.path).file_name().str(), td::PathView(file.path).parent_dir().str(),
std::move(ss))) {
throw IntError{"cannot enter included file interpretation context"};
}
ctx.next = SeqCont::seq(td::make_ref<CtxWord>(interpret_leave_source), std::move(ctx.next));
return td::make_ref<InterpretCont>();
}
void interpret_skip_source(vm::Stack& stack) {
throw SkipToEof();
td::Ref<vm::Box> exit_interpret{true};
Ref<FiftCont> interpret_skip_source(IntCtx& ctx) {
auto cont = exit_interpret->get().as_object<FiftCont>();
ctx.next.clear();
/*
if (cont.is_null()) {
throw IntError{"no interpreter exit point set"};
}
*/
return cont;
}
void interpret_words(IntCtx& ctx) {
@ -2161,6 +2318,14 @@ void interpret_words(IntCtx& ctx) {
*ctx.output_stream << std::endl;
}
void interpret_print_backtrace(IntCtx& ctx) {
ctx.print_backtrace(*ctx.output_stream, ctx.next);
}
void interpret_print_continuation(IntCtx& ctx) {
ctx.print_backtrace(*ctx.output_stream, pop_exec_token(ctx));
}
void interpret_pack_std_smc_addr(vm::Stack& stack) {
block::StdAddress a;
stack.check_underflow(3);
@ -2450,17 +2615,17 @@ void interpret_get_fixed_cmdline_arg(vm::Stack& stack, int n) {
}
// n -- executes $n
void interpret_get_cmdline_arg(IntCtx& ctx) {
Ref<FiftCont> interpret_get_cmdline_arg(IntCtx& ctx) {
int n = ctx.stack.pop_smallint_range(999999);
if (n) {
interpret_get_fixed_cmdline_arg(ctx.stack, n);
return;
return {};
}
auto entry = ctx.dictionary->lookup("$0 ");
if (!entry) {
throw IntError{"-?"};
} else {
(*entry)(ctx);
return entry->get_def();
}
}
@ -2493,16 +2658,16 @@ void interpret_getenv_exists(vm::Stack& stack) {
}
// x1 .. xn n 'w -->
void interpret_execute_internal(IntCtx& ctx) {
Ref<WordDef> word_def = pop_exec_token(ctx);
Ref<FiftCont> interpret_execute_internal(IntCtx& ctx) {
Ref<FiftCont> word_def = pop_exec_token(ctx);
int count = ctx.stack.pop_smallint_range(255);
ctx.stack.check_underflow(count);
word_def->run(ctx);
return word_def;
}
// wl x1 .. xn n 'w --> wl'
void interpret_compile_internal(vm::Stack& stack) {
Ref<WordDef> word_def = pop_exec_token(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) {
@ -2510,12 +2675,14 @@ void interpret_compile_internal(vm::Stack& stack) {
}
}
void do_compile(vm::Stack& stack, Ref<WordDef> 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 ((td::uint64)word_def->list_size() <= 1) {
auto list_size = word_def->list_size();
if ((td::uint64)list_size <= 1) {
// inline short definitions
wl_ref.write().append(*(word_def->get_list()));
auto list = word_def->get_list();
wl_ref.write().append(list, list + list_size);
} else {
wl_ref.write().push_back(word_def);
}
@ -2524,19 +2691,7 @@ void do_compile(vm::Stack& stack, Ref<WordDef> word_def) {
}
void compile_one_literal(WordList& wlist, vm::StackEntry val) {
using namespace std::placeholders;
if (val.type() == vm::StackEntry::t_int) {
auto x = std::move(val).as_int();
if (!x->signed_fits_bits(257)) {
throw IntError{"invalid numeric literal"};
} else if (x->signed_fits_bits(td::BigIntInfo::word_shift)) {
wlist.push_back(Ref<StackWord>{true, std::bind(interpret_const, _1, x->to_long())});
} else {
wlist.push_back(Ref<StackWord>{true, std::bind(interpret_big_const, _1, std::move(x))});
}
} else {
wlist.push_back(Ref<StackWord>{true, std::bind(interpret_literal, _1, std::move(val))});
}
wlist.push_back(LitCont::literal(std::move(val)));
}
void do_compile_literals(vm::Stack& stack, int count) {
@ -2548,8 +2703,16 @@ void do_compile_literals(vm::Stack& stack, int count) {
if (wl_ref.is_null()) {
throw IntError{"list of words expected"};
}
for (int i = count - 1; i >= 0; i--) {
compile_one_literal(wl_ref.write(), std::move(stack[i]));
if (count >= 2) {
std::vector<vm::StackEntry> literals;
for (int i = count - 1; i >= 0; i--) {
literals.push_back(std::move(stack[i]));
}
wl_ref.write().push_back(td::make_ref<MultiLitCont>(std::move(literals)));
} else {
for (int i = count - 1; i >= 0; i--) {
compile_one_literal(wl_ref.write(), std::move(stack[i]));
}
}
stack.pop_many(count + 1);
stack.push({vm::from_object, wl_ref});
@ -2654,13 +2817,13 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("or ", interpret_or);
d.def_stack_word("xor ", interpret_xor);
// integer constants
d.def_stack_word("false ", std::bind(interpret_const, _1, 0));
d.def_stack_word("true ", std::bind(interpret_const, _1, -1));
d.def_stack_word("0 ", std::bind(interpret_const, _1, 0));
d.def_stack_word("1 ", std::bind(interpret_const, _1, 1));
d.def_stack_word("2 ", std::bind(interpret_const, _1, 2));
d.def_stack_word("-1 ", std::bind(interpret_const, _1, -1));
d.def_stack_word("bl ", std::bind(interpret_const, _1, 32));
d.def_word("false ", IntLitCont::literal(0));
d.def_word("true ", IntLitCont::literal(-1));
d.def_word("0 ", IntLitCont::literal(0));
d.def_word("1 ", IntLitCont::literal(1));
d.def_word("2 ", IntLitCont::literal(2));
d.def_word("-1 ", IntLitCont::literal(-1));
d.def_word("bl ", IntLitCont::literal(32));
// integer comparison
d.def_stack_word("cmp ", std::bind(interpret_cmp, _1, "\xff\x00\x01"));
d.def_stack_word("= ", std::bind(interpret_cmp, _1, "\x00\xff\x00"));
@ -2835,16 +2998,16 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("pfxdict!+ ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Add, false));
d.def_stack_word("pfxdict! ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Set, false));
d.def_stack_word("pfxdict@ ", interpret_pfx_dict_get);
d.def_ctx_word("dictmap ", std::bind(interpret_dict_map, _1, false, false));
d.def_ctx_word("dictmapext ", std::bind(interpret_dict_map, _1, true, false));
d.def_ctx_word("idictmapext ", std::bind(interpret_dict_map, _1, true, true));
d.def_ctx_word("dictforeach ", std::bind(interpret_dict_foreach, _1, false, false));
d.def_ctx_word("idictforeach ", std::bind(interpret_dict_foreach, _1, false, true));
d.def_ctx_word("dictforeachrev ", std::bind(interpret_dict_foreach, _1, true, false));
d.def_ctx_word("idictforeachrev ", std::bind(interpret_dict_foreach, _1, true, true));
d.def_ctx_word("dictforeachfromx ", std::bind(interpret_dict_foreach_from, _1, -1));
d.def_ctx_word("dictmerge ", interpret_dict_merge);
d.def_ctx_word("dictdiff ", interpret_dict_diff);
d.def_ctx_tail_word("dictmap ", std::bind(interpret_dict_map, _1, false, false));
d.def_ctx_tail_word("dictmapext ", std::bind(interpret_dict_map, _1, true, false));
d.def_ctx_tail_word("idictmapext ", std::bind(interpret_dict_map, _1, true, true));
d.def_ctx_tail_word("dictforeach ", std::bind(interpret_dict_foreach, _1, false, false));
d.def_ctx_tail_word("idictforeach ", std::bind(interpret_dict_foreach, _1, false, true));
d.def_ctx_tail_word("dictforeachrev ", std::bind(interpret_dict_foreach, _1, true, false));
d.def_ctx_tail_word("idictforeachrev ", std::bind(interpret_dict_foreach, _1, true, true));
d.def_ctx_tail_word("dictforeachfromx ", std::bind(interpret_dict_foreach_from, _1, -1));
d.def_ctx_tail_word("dictmerge ", interpret_dict_merge);
d.def_ctx_tail_word("dictdiff ", interpret_dict_diff);
// slice/bitstring constants
d.def_active_word("x{", interpret_bitstring_hex_literal);
d.def_active_word("b{", interpret_bitstring_binary_literal);
@ -2880,8 +3043,8 @@ void init_words_common(Dictionary& d) {
d.def_ctx_tail_word("if ", interpret_if);
d.def_ctx_tail_word("ifnot ", interpret_ifnot);
d.def_ctx_tail_word("cond ", interpret_cond);
d.def_ctx_word("while ", interpret_while);
d.def_ctx_word("until ", interpret_until);
d.def_ctx_tail_word("while ", interpret_while);
d.def_ctx_tail_word("until ", interpret_until);
// compiler control
d.def_active_word("[ ", interpret_internal_interpret_begin);
d.def_active_word("] ", interpret_internal_interpret_end);
@ -2890,9 +3053,9 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("({) ", interpret_wordlist_begin_aux);
d.def_stack_word("(}) ", interpret_wordlist_end_aux);
d.def_stack_word("(compile) ", interpret_compile_internal);
d.def_ctx_word("(execute) ", interpret_execute_internal);
d.def_ctx_tail_word("(execute) ", interpret_execute_internal);
d.def_active_word("' ", interpret_tick);
d.def_stack_word("'nop ", interpret_tick_nop);
d.def_word("'nop ", LitCont::literal({vm::from_object, Dictionary::nop_word_def}));
// dictionary manipulation
d.def_ctx_word("find ", interpret_find);
d.def_ctx_word("create ", interpret_create);
@ -2904,20 +3067,23 @@ 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_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("include ", interpret_include);
d.def_stack_word("skip-to-eof ", interpret_skip_source);
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));
d.def_ctx_word("abort ", interpret_abort);
d.def_ctx_word("quit ", interpret_quit);
d.def_ctx_word("bye ", interpret_bye);
d.def_stack_word("halt ", interpret_halt);
d.def_ctx_word("halt ", interpret_halt);
// cmdline args
d.def_stack_word("$* ", std::bind(interpret_literal, _1, vm::StackEntry{cmdline_args}));
d.def_word("$* ", LitCont::literal(cmdline_args));
d.def_stack_word("$# ", interpret_get_cmdline_arg_count);
d.def_ctx_word("$() ", interpret_get_cmdline_arg);
d.def_ctx_tail_word("$() ", interpret_get_cmdline_arg);
}
void init_words_ton(Dictionary& d) {
@ -2934,7 +3100,7 @@ void init_words_vm(Dictionary& d, bool enable_debug) {
using namespace std::placeholders;
vm::init_op_cp0(enable_debug);
// vm run
d.def_stack_word("vmlibs ", std::bind(interpret_literal, _1, vm::StackEntry{vm_libraries}));
d.def_word("vmlibs ", LitCont::literal(vm_libraries));
// d.def_ctx_word("runvmcode ", std::bind(interpret_run_vm, _1, 0x40));
// d.def_ctx_word("runvm ", std::bind(interpret_run_vm, _1, 0x45));
d.def_ctx_word("runvmx ", std::bind(interpret_run_vm, _1, -1));
@ -2947,7 +3113,7 @@ void init_words_vm(Dictionary& d, bool enable_debug) {
void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]) {
using namespace std::placeholders;
LOG(DEBUG) << "import_cmdlist_args(" << arg0 << "," << n << ")";
d.def_stack_word("$0 ", std::bind(interpret_literal, _1, vm::StackEntry{arg0}));
d.def_word("$0 ", LitCont::literal(arg0));
vm::StackEntry list;
for (int i = n - 1; i >= 0; i--) {
list = vm::StackEntry::cons(vm::StackEntry{argv[i]}, list);
@ -2978,104 +3144,81 @@ td::RefInt256 numeric_value(std::string s) {
return num;
}
int funny_interpret_loop(IntCtx& ctx) {
while (ctx.load_next_line()) {
if (ctx.is_sb()) {
continue;
}
std::ostringstream errs;
bool ok = true;
while (ok) {
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();
const char* ptr = ctx.get_input();
if (!*ptr) {
if (*ctx.get_input()) {
break;
}
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);
}
try {
if (entry) {
if (entry->is_active()) {
(*entry)(ctx);
} else {
ctx.stack.push_smallint(0);
ctx.stack.push({vm::from_object, entry->get_def()});
}
} else {
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);
}
}
if (ctx.state > 0) {
interpret_compile_internal(ctx.stack);
} else {
interpret_execute_internal(ctx);
}
} catch (IntError& ab) {
errs << ctx << Word << ": " << ab.msg;
ok = false;
} catch (vm::VmError& ab) {
errs << ctx << Word << ": " << ab.get_msg();
ok = false;
} catch (vm::CellBuilder::CellWriteError) {
errs << ctx << Word << ": Cell builder write error";
ok = false;
} catch (vm::VmFatal) {
errs << ctx << Word << ": fatal vm error";
ok = false;
} catch (Quit& q) {
if (ctx.include_depth) {
throw;
}
if (!q.res) {
ok = false;
} else {
return q.res;
}
} catch (SkipToEof) {
return 0;
}
};
if (!ok) {
auto err_msg = errs.str();
if (!err_msg.empty()) {
LOG(ERROR) << err_msg;
}
ctx.clear();
if (ctx.include_depth) {
throw IntError{"error interpreting included file `" + ctx.filename + "` : " + err_msg};
}
} else if (!ctx.state && !ctx.include_depth) {
}
if (!ctx.state && !ctx.include_depth) {
*ctx.output_stream << " ok" << std::endl;
}
if (!ctx.load_next_line()) {
return {};
}
}
return 0;
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

@ -21,23 +21,10 @@
namespace fift {
// thrown by 'quit', 'bye' and 'halt' for exiting to top level
struct Quit {
int res;
Quit() : res(0) {
}
Quit(int _res) : res(_res) {
}
};
struct SkipToEof {};
void init_words_common(Dictionary& dictionary);
void init_words_vm(Dictionary& dictionary, bool debug_enabled = false);
void init_words_ton(Dictionary& dictionary);
void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]);
int funny_interpret_loop(IntCtx& ctx);
} // namespace fift