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:
parent
040df63c98
commit
4e2624459b
153 changed files with 10760 additions and 1695 deletions
398
crypto/fift/Continuation.cpp
Normal file
398
crypto/fift/Continuation.cpp
Normal 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
272
crypto/fift/Continuation.h
Normal 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
|
|
@ -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"};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue