From 50be8bb8697b23ff469de142eaebe9666c2a9537 Mon Sep 17 00:00:00 2001 From: olpc user Date: Sun, 24 Nov 2019 10:50:02 -0800 Subject: wip fixing up, decided to refactor: see top of helpers.cpp --- starts/meaning-vm/concept.cpp | 18 +-- starts/meaning-vm/concept.hpp | 32 +++-- starts/meaning-vm/helpers.cpp | 284 ++++++++++++++++++++++++++++++------------ starts/meaning-vm/helpers.hpp | 39 +++--- starts/meaning-vm/main.cpp | 3 +- starts/meaning-vm/meaning.cpp | 6 +- 6 files changed, 257 insertions(+), 125 deletions(-) diff --git a/starts/meaning-vm/concept.cpp b/starts/meaning-vm/concept.cpp index e9fd8e5..993ac62 100644 --- a/starts/meaning-vm/concept.cpp +++ b/starts/meaning-vm/concept.cpp @@ -5,14 +5,14 @@ ref concept::id() return this; } -bool concept::linked(ref type) +bool concept::linked(ref const & type) const { return links.count(type.ptr) > 0; } -bool concept::linked(ref type, ref target) +bool concept::linked(ref const & type, ref const & target) const { - for (ref t : getAll(type)) { + for (ref const & t : getAll(type)) { if (t == target) { return true; } @@ -20,7 +20,7 @@ bool concept::linked(ref type, ref target) return false; } -ref concept::get(ref type, bool quick) +ref concept::get(ref const & type, bool quick) const { // this is called by name(), so it passes quick=true auto result = links.equal_range(type.ptr); @@ -34,7 +34,7 @@ ref concept::get(ref type, bool quick) return result.first->second; } -concept::array concept::getAll(ref type) +concept::array concept::getAll(ref const & type) const { array ret; for ( @@ -42,17 +42,17 @@ concept::array concept::getAll(ref type) range.first != range.second; ++ range.first ) { - ret.push_back(range.first->second); + ret.emplace_back(range.first->second); } return ret; } -void concept::link(ref type, ref target) +void concept::link(ref const & type, ref const & target) { links.insert({type.ptr, target.ptr}); } -void concept::unlink(ref type, ref target) +void concept::unlink(ref const & type, ref const & target) { auto ls = links.equal_range(type.ptr); for (auto l = ls.first; l != ls.second; ++ l) { @@ -64,7 +64,7 @@ void concept::unlink(ref type, ref target) throw std::out_of_range("no such concept link to erase"); } -void concept::unlink(ref type) +void concept::unlink(ref const & type) { auto ls = links.equal_range(type.ptr); if (ls.first == ls.second) { diff --git a/starts/meaning-vm/concept.hpp b/starts/meaning-vm/concept.hpp index eca2fa5..3308e10 100644 --- a/starts/meaning-vm/concept.hpp +++ b/starts/meaning-vm/concept.hpp @@ -8,13 +8,13 @@ template struct vref; struct concept; template struct value; -class statementevaluable; +struct statementcallref; struct ref { ref(concept *p) : ptr(p) { if (p == 0) throw std::logic_error("null reference"); } - ref(ref const &) = default; concept* operator->() { return ptr; } + concept const * operator->() const { return ptr; } bool operator==(ref const & that) const { return this->ptr == that.ptr; } bool operator!=(ref const & that) const { return this->ptr != that.ptr; } @@ -29,14 +29,22 @@ struct ref operator const char *() const; // helper linking syntax sugar - statementevaluable operator=(ref that); - ref operator<<(ref target); + statementcallref operator=(ref that); + //ref operator<<(ref target); ref operator[](ref links); bool isa(ref what) const; bool isan(ref what) const; concept * ptr; + + /* + // destructor handles evaluating refs as statements + ~ref() noexcept; + ref(ref & that); + ref(ref && that) noexcept; + void destatement(); + */ }; template @@ -64,15 +72,15 @@ struct concept using array = std::vector; ref id(); - bool linked(ref type); - bool linked(ref type, ref target); - ref get(ref type, bool quick = false); // returns first + bool linked(ref const & type) const; + bool linked(ref const & type, ref const & target) const; + ref get(ref const & type, bool quick = false) const; // returns first template - vref vget(ref type, bool quick = false) { return get(type, quick); } - array getAll(ref type); - void link(ref type, ref target); - void unlink(ref type, ref target); - void unlink(ref type); + vref vget(ref const & type, bool quick = false) const { return get(type, quick); } + array getAll(ref const & type) const; + void link(ref const & type, ref const & target); + void unlink(ref const & type, ref const & target); + void unlink(ref const & type); }; template diff --git a/starts/meaning-vm/helpers.cpp b/starts/meaning-vm/helpers.cpp index e29962b..da1a974 100644 --- a/starts/meaning-vm/helpers.cpp +++ b/starts/meaning-vm/helpers.cpp @@ -4,74 +4,111 @@ #include "memorystore.hpp" #include +#include + +// sometimes we use an assignment as an expression. +// is there any reason not to support this? +// well, we would evaluate the assignment when it is converted to a ref +// and currently that is used for something else +// what is it used for? +// I don't remember ... the assignment is destroyed and unevaluated, so it's likely +// throughout the internal implemntation +// it's used for the comma operator, which doesn't want evaluation. +// okay, hmm. +// well, if we wanted to support this, we could change the comma operator to not do the +// conversion. use some other trick. +// [ ] change comma operator to not do conversion: instead take statementcallref object +// [ ] change statementcallref::operator ref() to evaluate and return the statement +// since we're accumulating more possible bugs, let's think about design. +// really, ref should be different from syntax sugar. we should have a special syntax sugar +// object, and it should not use its own functions at all. that means not using meaning functions +// that use them eiher. +// work also brings to light how the product will be made in layers, with each layer using more interdependence and automatic meaning, etc, than the lower one. +// layer0 is raw references and allocations +// layer1 is very core meaning +// layer2 is very core syntax sugar +// each layer can only use layers below it. + +// concept names are for bootstrapping convenience, +// to make hardcoding structures easier. +// hence there is just one single list of them +std::unordered_map,std::equal_to> conceptsByName; + +struct name_t : public ref +{ + name_t(); +} name; + +static ref statement_function("statement-function"); +static ref statement_evaluated("statement-evaluated"); +static ref true_ref("true"); +static ref false_ref("false"); ref operator-(ref a, ref b) { return ref(a.name() + "-" + b.name()); } -statementevaluable ref::operator=(ref that) +statementcallref ref::operator=(ref that) { // for single-line evaluation, we'll need to return a special object declrefs(assign, expression); decllnks(value, to); - return { - a(assign-expression)[ - to = *this, - value = that - ], - [](ref expr) { - declrefs(link, target, unknown, source, type); - decllnks(value, to); - lnks(link-target, link-type, link-source); - decllnks(name, is); - ref lhs = expr.get(to); - ref rhs = expr.get(value); - if (lhs.isa(link) && lhs->get(link-target) == unknown) { - // we are a link missing a target: our assignment is making the link happen - lhs->unlink(link-target, unknown); - lhs->link(link-target, rhs); - ref src = lhs->get(link-source); - if (lhs->get(link-type) != unknown && src != unknown) { - src->link(lhs->get(link-type), rhs); - dealloc(lhs); - dealloc(expr); - } else { - throw std::logic_error("not sure what to do with incomplete link assignment"); - } - } else if (rhs->linked(anonymous, true) && !lhs->linked(anonymous, true)) { - // this is assignment of anonymous content to empty named concept - bool donealready = false; - for (auto & l : lhs->links) { - if (ref(l.first) == name) { continue; } - if (ref(l.first) == is && ref(l.second) == link-type) { continue; } - donealready = true; - } - if (donealready) { - // if we have links, and that has links we do not have, an error has been made - for (auto & link : rhs->links) { - if (ref(link.first) == anonymous) { continue; } - if (ref(link.first) == name) { continue; } - if (!lhs->linked(ref(link.first), ref(link.second))) { - throw std::logic_error(lhs.name() + " already defined otherwise from " + rhs->get(is).name()); - } + statementcallref ret(assign-expression, [](ref expr){ + declrefs(link, target, unknown, source, type); + decllnks(value, to); + lnks(link-target, link-type, link-source); + decllnks(name, is, anonymous); + ref lhs = expr->get(to); + ref rhs = expr->get(value); + if (lhs.isa(link) && lhs->get(link-target) == unknown) { + // we are a link missing a target: our assignment is making the link happen + lhs->unlink(link-target, unknown); + lhs->link(link-target, rhs); + ref src = lhs->get(link-source); + if (lhs->get(link-type) != unknown && src != unknown) { + src->link(lhs->get(link-type), rhs); + expr->unlink(to, lhs); + dealloc(lhs); + } else { + throw std::logic_error("not sure what to do with incomplete link assignment"); + } + } else if (rhs->linked(anonymous, true) && !lhs->linked(anonymous, true)) { + // this is assignment of anonymous content to empty named concept + bool donealready = false; + for (auto & l : lhs->links) { + if (ref(l.first) == name) { continue; } + if (ref(l.first) == is && ref(l.second) == link-type) { continue; } + donealready = true; + } + if (donealready) { + // if we have links, and that has links we do not have, an error has been made + for (auto & link : rhs->links) { + if (ref(link.first) == anonymous) { continue; } + if (ref(link.first) == name) { continue; } + if (!lhs->linked(ref(link.first), ref(link.second))) { + throw std::logic_error(lhs.name() + " already defined otherwise from " + rhs->get(is).name()); } - donealready = true; - } - rhs->unlink(anonymous, true); - auto nam = rhs->get(name); - rhs->unlink(name, nam); - if (!donealready) { - ptr->links.insert(rhs->links.begin(), rhs->links.end()); } - rhs->link(name, nam); - dealloc(rhs); - dealloc(nam); - } else { - throw std::logic_error("unexpected bare assignment"); + donealready = true; + } + rhs->unlink(anonymous, true); + auto nam = rhs->get(name); + rhs->unlink(name, nam); + if (!donealready) { + lhs->links.insert(rhs->links.begin(), rhs->links.end()); } + rhs->link(name, nam); + expr->unlink(value, rhs); + dealloc(rhs); + dealloc(nam); + } else { + throw std::logic_error("unexpected bare assignment"); } - } + }); + ret.r->link(to, *this); + ret.r->link(value, that); + return ret; /* decllnks(anonymous, is, name); declrefs(link, source, type, target, unknown); @@ -95,49 +132,127 @@ statementevaluable ref::operator=(ref that) */ } -ref ref::operator<<(ref target) +/*ref ref::operator<<(ref target) { // prep a link ref ret = alloc(); ret->link(*this, target); return ret; +}*/ + +void statementcallref::destatement() +{ + auto func = r->get(statement_function); + r->unlink(statement_function); + dealloc(func); } -ref ref::operator[](ref expr) { - declrefs(assign, expression); - decllnks(value, to); - declrefs(link, type, unknown); +statementcallref::statementcallref(ref type, std::function func) +: r(a(type).ptr) +{ + r->link(statement_function, valloc>(func)); +} - if (expr.isa(assign-expression)) { +statementcallref::statementcallref(ref const & that) +: r(that.ptr) +{ + if (!that->linked(statement_function)) { + throw std::logic_error(std::string(that) + " has no statement-function"); } - expr.take(); - ref lhs = expr.get(to); - ref rhs = expr.get(value); - if (that.isa(link-type)) { - return ::link(*this, that, unknown); - } else { - ptr->links.insert(that->links.begin(), that->links.end()); - dealloc(that); - return *this; +} + +statementcallref::operator ref() +{ + ref ret(r); + destatement(); + r.ptr = 0; + return ret; +} + +#include +statementcallref::~statementcallref() noexcept +{ + if (r.ptr == 0) { return; } + if (r->linked(statement_evaluated, true_ref)) { + std::cerr << "statement already evaluated: " << r << std::endl; + return; } + try { + auto func = r->vget>(statement_function, true); + func->data(r); + destatement(); + r->link(statement_evaluated, true_ref); + dealloc(r); + r.ptr = 0; + } catch (std::out_of_range &) { } } -ref operator,(ref a, ref b) +statementcallref::statementcallref(statementcallref && that) noexcept +: r(that.r.ptr) { - a->links.insert(b->links.begin(), b->links.end()); - dealloc(b); - return a; + that.r.ptr = 0; } -// concept names are for bootstrapping convenience, -// to make hardcoding structures easier. -// hence there is just one single list of them -std::unordered_map,std::equal_to> conceptsByName; +statementcallref::statementcallref(statementcallref & that) +: r(that.r.ptr) +{ + if (that.r.ptr == 0) { throw std::logic_error("empty ref"); } + if (that.r->linked(statement_function)) { + // statements are moved, for evaluation + that.r.ptr = 0; + } +} -struct name_t : public ref +ref ref::operator[](ref expr) { + declrefs(assign, comma, expression); + decllnks(value, to, what); + declrefs(link, type, unknown); + + if (expr.isa(assign-expression)) { + ref lhs = expr->get(to); + ref rhs = expr->get(value); + ptr->link(lhs, rhs); + dealloc(expr); + return *this; + } else if (expr.isa(comma-expression)) { + auto parts = expr->getAll(what); + dealloc(expr); + for (ref part : parts) { + if (!part.isa(assign-expression)) { + throw std::logic_error("[,] argument is not an assignment"); + } + operator[](part); + } + return *this; + } else if (expr.isa(link-type)) { + return ::link(*this, expr, unknown); + } else { + throw std::logic_error("[] argument is neither a link nor an assignment"); + } +} + +statementcallref operator,(statementcallref a, statementcallref b) { - name_t(); -} name; + declrefs(comma, expression); + decllnks(what); + if (a.r.isa(comma-expression)) { + if (b.r.isa(comma-expression)) { + a.r->links.insert(b.r->links.begin(), b.r->links.end()); + dealloc(b); b.r.ptr = 0; + } else { + a.r->link(what, b.r); b.r.ptr = 0; + } + return a; + } else if (b.r.isa(comma-expression)) { + b.r->link(what, a.r); a.r.ptr = 0; + return b; + } else { + statementcallref ret(comma-expression, [](ref) { throw std::logic_error("bare comma-expression"); }); + ret.r->link(what, a.r); a.r.ptr = 0; + ret.r->link(what, b.r); b.r.ptr = 0; + return ret; + } +} template <> vref::vref(std::string const & s) @@ -146,7 +261,7 @@ vref::vref(std::string const & s) ptr->link(::name, ptr); } name_t::name_t() -: ref(alloc()) +: ref(alloc().ptr) { vref nam(std::string("name")); ptr->link(::name, nam); @@ -171,7 +286,7 @@ vref ref::name() const { try { return ptr->vget(::name, true); - } catch (std::out_of_range) { + } catch (std::out_of_range &) { declrefs(UNNAMED); return UNNAMED.name(); } @@ -185,7 +300,10 @@ ref a(ref what) { static unsigned long long gid = 0; decllnks(is, anonymous); - return ref(what.name() + "-" + std::to_string(gid++))[is = what, anonymous = true]; + ref ret(what.name() + "-" + std::to_string(gid++)); + ret->link(is, what); + ret->link(anonymous, true); + return ret; } ref a(ref what, ref name) { diff --git a/starts/meaning-vm/helpers.hpp b/starts/meaning-vm/helpers.hpp index 12d1e9c..3caf10a 100644 --- a/starts/meaning-vm/helpers.hpp +++ b/starts/meaning-vm/helpers.hpp @@ -28,29 +28,36 @@ #include "concept.hpp" #include "memorystore.hpp" + #include +#include inline std::string operator+(vref a, char const * b) { return std::string(a) + b; } inline std::string operator+(vref a, std::string b) { return std::string(a) + b; } inline std::string operator+(char const * a, vref b) { return a + std::string(b); } inline std::string operator+(std::string a, vref b) { return a + std::string(b); } -// TODO TODO -// actually, get rid of this, and set the function as a value on a normal ref. -// then use ref destructor. -// have to delete ref copy constructor. use move constructor instead. -// could also alter ref copy constructor to tell copied ref is okay. -class statementevaluable : public ref +// reasons to use special struct: +// the reason not to use ref directly is because we want to pass a ref to the function handler, +// and it is easier to use if it is a bare copy. the bare copy triggers destruction and eval. +// an alternate would be to add an evaluation flag to ref, but this might make it big. +// reasons to not use special struct: +// the operator, takes a ref type and returns a ref type +struct statementcallref { -public: - statementevaluable(ref r, std::function evaluation) - : ref(r.ptr), - evaluation(evaluation) - { } - statementevaluable(statementevaluable const &) = delete; - ~statementevaluable() { if (evaluate) { evaluation(*this); } } -private: - std::function evaluation; + statementcallref(ref type, std::function func); + statementcallref(ref const & that); + + // handles evaluating refs as statements + ~statementcallref(); + statementcallref(statementcallref & that); + statementcallref(statementcallref && that) noexcept; + + operator ref(); + + void destatement(); + + ref r; }; template <> @@ -100,7 +107,7 @@ void lnks(T ... passedrefs) declrefs(__VA_ARGS__); \ lnks(__VA_ARGS__) -ref operator,(ref a, ref b); +statementcallref operator,(statementcallref a, statementcallref b); ref operator-(ref a, ref b); ref a(ref what); diff --git a/starts/meaning-vm/main.cpp b/starts/meaning-vm/main.cpp index 1d25975..513efd2 100644 --- a/starts/meaning-vm/main.cpp +++ b/starts/meaning-vm/main.cpp @@ -2,7 +2,6 @@ #include "helpers.hpp" #include "meaning.hpp" -using namespace std; #include @@ -14,7 +13,7 @@ void dumpconcept(ref r) if (ref(l.first) == name) { continue; } - cout << r << " " << ref(l.first) << " " << ref(l.second) << endl; + std::cout << r << " " << ref(l.first) << " " << ref(l.second) << std::endl; } if (!r->linked(dumped)) { r[dumped = true]; diff --git a/starts/meaning-vm/meaning.cpp b/starts/meaning-vm/meaning.cpp index a6156bf..a47a0ce 100644 --- a/starts/meaning-vm/meaning.cpp +++ b/starts/meaning-vm/meaning.cpp @@ -13,9 +13,9 @@ ref link(ref sourceref, ref typeref, ref targetref) declrefs(link, source, type, target); lnks(link-source, link-type, link-target); return a(link)[ - link-source << sourceref, - link-type << typeref, - link-target << targetref + link-source = sourceref, + link-type = typeref, + link-target = targetref ]; } -- cgit v1.2.3