From 700c0ef664c6bec3e0315dcc5efe82ea45762a81 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 23 Oct 2024 11:54:02 +0200 Subject: [PATCH 1/5] Test: Cleanup `three_regions.vpy` --- tests/three_regions.vpy | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tests/three_regions.vpy b/tests/three_regions.vpy index cbb7426..6b2874e 100644 --- a/tests/three_regions.vpy +++ b/tests/three_regions.vpy @@ -5,25 +5,24 @@ b = {} a["b"] = b region a + c = {} c["self"] = c -d = {} -c["d"] = d +c["d"] = {} region c e = {} e["self"] = e -f = {} -e["f"] = f -region e +e["f"] = {} +region e # connect first region to second in two ways -a["c"] = c -b["d"] = d +a.c = c +a.b.d = c.d # Connect first region to third with single entry point -b["e"] = f +a.b.e = e.f # Now freeze part of the first region, and the reachable parts of the # second and third regions @@ -32,8 +31,6 @@ freeze b # Drop all the references drop a -drop d -drop f -drop b +drop b drop c drop e From 25c94f25d087743b094720740bd34ef7aa654f0f Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 23 Oct 2024 13:21:29 +0200 Subject: [PATCH 2/5] Cown: Add cown objects --- src/rt/core.h | 53 +++++++++++++++++++++++++++++ src/rt/core/builtin.cc | 15 +++++++++ src/rt/objects/dyn_object.h | 66 +++++++++++++++++++++++++++++++------ src/rt/rt.cc | 7 +++- src/rt/rt.h | 1 + 5 files changed, 131 insertions(+), 11 deletions(-) diff --git a/src/rt/core.h b/src/rt/core.h index 0f801ac..b3cc575 100644 --- a/src/rt/core.h +++ b/src/rt/core.h @@ -197,6 +197,58 @@ namespace rt::core } }; + // The prototype object for cown + inline PrototypeObject* cownPrototypeObject() + { + static PrototypeObject* proto = new PrototypeObject("Cown"); + return proto; + } + + class CownObject : public objects::DynObject + { + public: + CownObject(objects::DynObject* region) + : objects::DynObject(cownPrototypeObject()) + { + // FIXME: Add once regions are reified + // assert( + // region->get_prototype() == regionPrototype() && + // "Cowns can only store regions"); + // + // FIXME: Also check that the region has a LRC == 1, with 1 + // being the reference passed into this constructor + + assert( + region->change_rc(0) == 1 && + "regions used for cown creation need to have an rc of 1"); + + // this->set would fail, since this is a cown + this->fields["region"] = region; + } + + std::string get_name() + { + return ""; + } + + objects::DynObject* is_primitive() + { + return this; + } + + bool is_opaque() override + { + // For now there is no mechanism to aquire the cown, it'll therefore + // always be opaque. + return true; + } + + bool is_cown() override + { + return true; + } + }; + inline std::set* globals() { static std::set* globals = @@ -207,6 +259,7 @@ namespace rt::core builtinFuncPrototypeObject(), stringPrototypeObject(), keyIterPrototypeObject(), + cownPrototypeObject(), trueObject(), falseObject(), }; diff --git a/src/rt/core/builtin.cc b/src/rt/core/builtin.cc index 12caa44..8718922 100644 --- a/src/rt/core/builtin.cc +++ b/src/rt/core/builtin.cc @@ -50,8 +50,23 @@ namespace rt::core }); } + void ctor_builtins() + { + add_builtin("cown", [](auto frame, auto stack, auto args) { + assert(args == 1); + + auto region = stack->back(); + auto cown = make_cown(region); + move_reference(frame, cown, region); + stack->pop_back(); + + return std::nullopt; + }); + } + void init_builtins(ui::UI* ui) { mermaid_builtins(ui); + ctor_builtins(); } } diff --git a/src/rt/objects/dyn_object.h b/src/rt/objects/dyn_object.h index 45e5009..306efe8 100644 --- a/src/rt/objects/dyn_object.h +++ b/src/rt/objects/dyn_object.h @@ -16,6 +16,11 @@ #include #include +namespace rt::core +{ + class CownObject; +} + namespace rt::objects { constexpr uintptr_t ImmutableTag{1}; @@ -29,6 +34,7 @@ namespace rt::objects friend objects::DynObject* rt::make_iter(objects::DynObject* obj); friend class ui::MermaidUI; friend class ui::MermaidDiagram; + friend class core::CownObject; friend void destruct(DynObject* obj); friend void dealloc(DynObject* obj); template @@ -77,8 +83,11 @@ namespace rt::objects return; } - assert(target->parent == src); - Region::dec_prc(target); + if (src) + { + assert(target->parent == src); + Region::dec_prc(target); + } return; } @@ -110,7 +119,7 @@ namespace rt::objects { std::cout << "Change RC: " << get_name() << " " << rc << " + " << delta << std::endl; - if (!is_immutable()) + if (!(is_immutable() || is_cown())) { assert(delta == 0 || rc != 0); rc += delta; @@ -230,12 +239,22 @@ namespace rt::objects return nullptr; } + virtual bool is_opaque() + { + return false; + } + + virtual bool is_cown() + { + return false; + } + void freeze() { // TODO SCC algorithm visit(this, [](Edge e) { auto obj = e.target; - if (obj->is_immutable()) + if (!obj || obj->is_immutable()) return false; auto r = get_region(obj); @@ -244,7 +263,8 @@ namespace rt::objects get_region(obj)->objects.erase(obj); } obj->region.set_tag(ImmutableTag); - return true; + + return !obj->is_cown(); }); } @@ -255,6 +275,18 @@ namespace rt::objects [[nodiscard]] DynObject* get(std::string name) { + if (is_opaque()) + { + if (is_cown()) + { + ui::error("Cannot access data on a cown that is not aquired"); + } + else + { + ui::error("Cannot access data on an opaque type"); + } + } + auto result = fields.find(name); if (result != fields.end()) return result->second; @@ -271,12 +303,29 @@ namespace rt::objects return nullptr; } - [[nodiscard]] DynObject* set(std::string name, DynObject* value) + void assert_modifiable() { if (is_immutable()) { ui::error("Cannot mutate immutable object"); } + + if (is_opaque()) + { + if (is_cown()) + { + ui::error("Cannot mutate a cown that is not aquired"); + } + else + { + ui::error("Cannot mutate opaque object"); + } + } + } + + [[nodiscard]] DynObject* set(std::string name, DynObject* value) + { + assert_modifiable(); DynObject* old = fields[name]; fields[name] = value; return old; @@ -285,10 +334,7 @@ namespace rt::objects // The caller must provide an rc for value. [[nodiscard]] DynObject* set_prototype(DynObject* value) { - if (is_immutable()) - { - ui::error("Cannot mutate immutable object"); - } + assert_modifiable(); DynObject* old = prototype; prototype = value; return old; diff --git a/src/rt/rt.cc b/src/rt/rt.cc index 6125ffb..80f2c5d 100644 --- a/src/rt/rt.cc +++ b/src/rt/rt.cc @@ -45,17 +45,22 @@ namespace rt { return new objects::DynObject(); } - objects::DynObject* make_frame(objects::DynObject* parent) { return new core::FrameObject(parent); } + objects::DynObject* make_cown(objects::DynObject* region) + { + return new core::CownObject(region); + } thread_local objects::RegionPointer objects::DynObject::local_region = new Region(); void freeze(objects::DynObject* obj) { + // Cown specific handling of the freeze operation is handled by the + // `freeze()` implementation of the object obj->freeze(); } diff --git a/src/rt/rt.h b/src/rt/rt.h index f9b4634..bb5a993 100644 --- a/src/rt/rt.h +++ b/src/rt/rt.h @@ -22,6 +22,7 @@ namespace rt objects::DynObject* make_str(std::string str_value); objects::DynObject* make_object(); objects::DynObject* make_frame(objects::DynObject* parent); + objects::DynObject* make_cown(objects::DynObject* region); void freeze(objects::DynObject* obj); void create_region(objects::DynObject* objects); From f14bf034d887f0c2bc4a19f4ca053bd9028ccf06 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 23 Oct 2024 14:01:12 +0200 Subject: [PATCH 3/5] Cown: Add test files --- CMakeLists.txt | 5 ++++- tests/cowns/invalid_read.vpy | 7 +++++++ tests/cowns/invalid_shared_region.vpy | 9 +++++++++ tests/cowns/invalid_write.vpy | 7 +++++++ tests/cowns/valid_01.vpy | 16 ++++++++++++++++ 5 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 tests/cowns/invalid_read.vpy create mode 100644 tests/cowns/invalid_shared_region.vpy create mode 100644 tests/cowns/invalid_write.vpy create mode 100644 tests/cowns/valid_01.vpy diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e2db2c..867a68b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ clangformat_targets() message(STATUS "Adding tests") -FILE(GLOB ALL_FILES +FILE(GLOB_RECURSE ALL_FILES CONFIGURE_DEPENDS tests/* ) @@ -67,3 +67,6 @@ endforeach() set_property(TEST three_regions.vpy PROPERTY WILL_FAIL true) set_property(TEST leak_with_global.vpy PROPERTY WILL_FAIL true) +set_property(TEST invalid_read.vpy PROPERTY WILL_FAIL true) +set_property(TEST invalid_shared_region.vpy PROPERTY WILL_FAIL true) +set_property(TEST invalid_write.vpy PROPERTY WILL_FAIL true) diff --git a/tests/cowns/invalid_read.vpy b/tests/cowns/invalid_read.vpy new file mode 100644 index 0000000..68ccf91 --- /dev/null +++ b/tests/cowns/invalid_read.vpy @@ -0,0 +1,7 @@ +# A simple cown +a = {} +region a +co = cown(a) + +# Reading the cown is forbidden +dummy = co.region diff --git a/tests/cowns/invalid_shared_region.vpy b/tests/cowns/invalid_shared_region.vpy new file mode 100644 index 0000000..f2ae610 --- /dev/null +++ b/tests/cowns/invalid_shared_region.vpy @@ -0,0 +1,9 @@ +# Creating a region +a = {} +region a + +# LRC = 2 +r01 = a + +# Error due to invalid RC +co = cown(a) diff --git a/tests/cowns/invalid_write.vpy b/tests/cowns/invalid_write.vpy new file mode 100644 index 0000000..a0a6a27 --- /dev/null +++ b/tests/cowns/invalid_write.vpy @@ -0,0 +1,7 @@ +# A simple cown +a = {} +region a +co = cown(a) + +# Modifying the cown is forbidden +co.other = {} diff --git a/tests/cowns/valid_01.vpy b/tests/cowns/valid_01.vpy new file mode 100644 index 0000000..699bb36 --- /dev/null +++ b/tests/cowns/valid_01.vpy @@ -0,0 +1,16 @@ +global = {} + +# A simple cown +a = {} +a.b = {} +region a +c01 = cown(a) + +# Store the cown in a global +global.cown = c01 + +# Freeze global with a cown +freeze global + +drop c01 +drop global From 5dead009c5584ff1299b819de8cb7adc1a70991a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 23 Oct 2024 15:48:54 +0200 Subject: [PATCH 4/5] Lang: Add `take` and fix minor bugs --- src/lang/bytecode.h | 3 +++ src/lang/interpreter.cc | 32 +++++++++++++++++++++++++++++++- src/lang/lang.h | 16 +++++++++------- src/lang/passes/bytecode.cc | 9 +++++++++ src/lang/passes/flatten.cc | 10 +++++----- src/lang/passes/grouping.cc | 7 +++++-- src/lang/passes/parse.cc | 3 ++- src/rt/core/builtin.cc | 2 +- src/rt/objects/dyn_object.h | 12 ------------ src/rt/rt.cc | 11 +++++++++++ tests/cowns/valid_01.vpy | 6 ++++-- tests/exprs/take.vpy | 7 +++++++ 12 files changed, 87 insertions(+), 31 deletions(-) create mode 100644 tests/exprs/take.vpy diff --git a/src/lang/bytecode.h b/src/lang/bytecode.h index 14c4a63..11a5f9b 100644 --- a/src/lang/bytecode.h +++ b/src/lang/bytecode.h @@ -7,6 +7,7 @@ /// The value name is stored in the location of the node inline const trieste::TokenDef LoadFrame{"load_frame", trieste::flag::print}; inline const trieste::TokenDef StoreFrame{"store_frame", trieste::flag::print}; +inline const trieste::TokenDef SwapFrame{"swap_frame", trieste::flag::print}; /// Loads a value with a given name from the current scope or the global /// namespace. This should be used for function resolution. /// @@ -14,6 +15,8 @@ inline const trieste::TokenDef StoreFrame{"store_frame", trieste::flag::print}; inline const trieste::TokenDef LoadGlobal{"load_global", trieste::flag::print}; inline const trieste::TokenDef LoadField{"load_field"}; inline const trieste::TokenDef StoreField{"store_field"}; +/// Stack: `[]::::::` -> `[]::` +inline const trieste::TokenDef SwapField{"swap_field"}; inline const trieste::TokenDef CreateObject{"create_object"}; inline const trieste::TokenDef Proto{"prototype"}; inline const trieste::TokenDef Dictionary{"dictionary"}; diff --git a/src/lang/interpreter.cc b/src/lang/interpreter.cc index 037ae7a..21b245e 100644 --- a/src/lang/interpreter.cc +++ b/src/lang/interpreter.cc @@ -244,6 +244,7 @@ namespace verona::interpreter if (node == StoreFrame) { + assert(stack().size() >= 1 && "the stack is too small"); auto v = pop("value to store"); std::string field{node->location().view()}; auto v2 = rt::set(frame(), field, v); @@ -251,6 +252,18 @@ namespace verona::interpreter return ExecNext{}; } + if (node == SwapFrame) + { + assert(stack().size() >= 1 && "the stack is too small"); + auto new_var = pop("swap value"); + std::string field{node->location().view()}; + + auto old_var = rt::set(frame(), field, new_var); + stack().push_back(old_var); + + return ExecNext{}; + } + if (node == LoadField) { assert(stack().size() >= 2 && "the stack is too small"); @@ -275,6 +288,7 @@ namespace verona::interpreter if (node == StoreField) { + assert(stack().size() >= 3 && "the stack is too small"); auto v = pop("value to store"); auto k = pop("lookup-key"); auto v2 = pop("lookup-value"); @@ -286,6 +300,23 @@ namespace verona::interpreter return ExecNext{}; } + if (node == SwapField) + { + assert(stack().size() >= 3 && "the stack is too small"); + auto new_var = pop("swap value"); + auto key = pop("lookup-key"); + auto obj = pop("lookup-value"); + auto old_var = rt::set(obj, key, new_var); + stack().push_back(old_var); + + rt::move_reference(frame(), obj, new_var); + rt::move_reference(obj, frame(), old_var); + rt::remove_reference(frame(), obj); + rt::remove_reference(frame(), key); + + return ExecNext{}; + } + if (node == CreateRegion) { auto v = pop("region source"); @@ -401,7 +432,6 @@ namespace verona::interpreter if (result) { stack().push_back(result.value()); - rt::add_reference(frame(), result.value()); } rt::remove_reference(frame(), func); return ExecNext{}; diff --git a/src/lang/lang.h b/src/lang/lang.h index a685c5c..545823c 100644 --- a/src/lang/lang.h +++ b/src/lang/lang.h @@ -16,6 +16,7 @@ inline const TokenDef Else{"else"}; inline const TokenDef Block{"block"}; inline const TokenDef Empty{"empty"}; inline const TokenDef Drop{"drop"}; +inline const TokenDef Take{"take"}; inline const TokenDef Freeze{"freeze"}; inline const TokenDef Region{"region"}; inline const TokenDef Lookup{"lookup"}; @@ -32,7 +33,8 @@ inline const TokenDef Compile{"compile"}; namespace verona::wf { inline const auto lv = Ident | Lookup; - inline const auto rv = lv | Empty | Null | String | Create | Call | Method; + inline const auto rv = + lv | Empty | Null | String | Create | Call | Method | Take; inline const auto cmp_values = Ident | Lookup | Null; inline const auto key = Ident | Lookup | String; inline const auto operand = Lookup | Call | Method | Ident; @@ -43,7 +45,7 @@ namespace verona::wf (Block <<= (Freeze | Region | Assign | If | For | Func | Return | ReturnValue | Call | Method)++) | - (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | + (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | (Take <<= (Lhs >>= lv)) | (Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (Region <<= Ident) | (Freeze <<= Ident) | (Create <<= Ident) | (If <<= (Op >>= Cond) * Block * Block) | @@ -56,17 +58,17 @@ namespace verona::wf inline const trieste::wf::Wellformed bytecode = (Top <<= Body) | (Body <<= - (LoadFrame | LoadGlobal | StoreFrame | LoadField | StoreField | Drop | - Null | CreateObject | CreateRegion | FreezeObject | IterNext | Print | - Eq | Neq | Jump | JumpFalse | Label | Call | Return | ReturnValue | - ClearStack | Dup)++) | + (LoadFrame | LoadGlobal | StoreFrame | SwapFrame | LoadField | StoreField | + SwapField | Drop | Null | CreateObject | CreateRegion | FreezeObject | + IterNext | Print | Eq | Neq | Jump | JumpFalse | Label | Call | Return | + ReturnValue | ClearStack | Dup)++) | (CreateObject <<= (Dictionary | String | KeyIter | Proto | Func)) | (Func <<= Body) | (Label <<= Ident)[Ident]; } inline const auto LV = T(Ident, Lookup); inline const auto RV = - T(Empty, Ident, Lookup, Null, String, Create, Call, Method); + T(Empty, Ident, Lookup, Null, String, Create, Call, Method, Take); inline const auto CMP_V = T(Ident, Lookup, Null); inline const auto KEY = T(Ident, Lookup, String); inline const auto OPERAND = T(Lookup, Call, Method, Ident); diff --git a/src/lang/passes/bytecode.cc b/src/lang/passes/bytecode.cc index ba54fee..0654af4 100644 --- a/src/lang/passes/bytecode.cc +++ b/src/lang/passes/bytecode.cc @@ -51,6 +51,15 @@ PassDef bytecode() << create_from(LoadField, _(Lookup)); }, + T(Compile) << (T(Take) << T(Ident)[Ident]) >> + [](auto& _) { return Seq << Null << create_from(SwapFrame, _(Ident)); }, + T(Compile) + << (T(Take) << (T(Lookup)[Lookup] << (Any[Op] * Any[Key] * End))) >> + [](auto& _) { + return Seq << (Compile << _[Op]) << (Compile << _[Key]) << Null + << SwapField; + }, + T(Compile) << (T(Assign)[Op] << (T(Ident)[Ident] * Any[Rhs])) >> [](auto& _) { return Seq << (Compile << _[Rhs]) << create_from(StoreFrame, _(Ident)) diff --git a/src/lang/passes/flatten.cc b/src/lang/passes/flatten.cc index 50210e1..4e025da 100644 --- a/src/lang/passes/flatten.cc +++ b/src/lang/passes/flatten.cc @@ -6,13 +6,13 @@ namespace verona::wf inline const trieste::wf::Wellformed flatten = (Top <<= File) | (File <<= Body) | (Body <<= - (Freeze | Region | Assign | Eq | Neq | Label | Jump | JumpFalse | Print | - StoreFrame | LoadFrame | CreateObject | Ident | IterNext | Create | - StoreField | Lookup | String | Call | Method | Return | ReturnValue | - ClearStack)++) | + (Freeze | Region | Assign | Take | Eq | Neq | Label | Jump | JumpFalse | + Print | StoreFrame | LoadFrame | CreateObject | Ident | IterNext | + Create | StoreField | Lookup | String | Call | Method | Return | + ReturnValue | ClearStack)++) | (CreateObject <<= (KeyIter | String | Dictionary | Func)) | (Func <<= Compile) | (Compile <<= Body) | (Create <<= Ident) | - (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | + (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | (Take <<= lv) | (Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (Region <<= Ident) | (Freeze <<= Ident) | (Call <<= Ident * List) | (Method <<= Lookup * List) | (List <<= rv++) | (Params <<= Ident++) | diff --git a/src/lang/passes/grouping.cc b/src/lang/passes/grouping.cc index 4226669..db42511 100644 --- a/src/lang/passes/grouping.cc +++ b/src/lang/passes/grouping.cc @@ -30,6 +30,9 @@ PassDef grouping() T(Group) << ((T(Drop)[Drop] << End) * LV[Lhs] * End) >> [](auto& _) { return Assign << _(Lhs) << Null; }, + T(Group) << ((T(Take)[Take] << End) * LV[Lhs] * End) >> + [](auto& _) { return create_from(Take, _(Take)) << _(Lhs); }, + // function(arg, arg) --In(Func) * (T(Group)[Group] << (T(Ident)[Ident]) * @@ -123,10 +126,10 @@ PassDef grouping() << (Body << _(Block)); }, // Normalize parenthesis with a single node to also have a list token - T(Parens)[Parens] << (T(Group) << (Any[Ident] * End)) >> + T(Parens)[Parens] << ((T(Group) << (RV[Rhs] * End)) / (RV[Rhs] * End)) >> [](auto& _) { return create_from(Parens, _(Parens)) - << (create_from(List, _(Parens)) << _(Ident)); + << (create_from(List, _(Parens)) << _(Rhs)); }, T(Return)[Return] << ((T(Group) << End) * End) >> diff --git a/src/lang/passes/parse.cc b/src/lang/passes/parse.cc index df3cb1f..cc68859 100644 --- a/src/lang/passes/parse.cc +++ b/src/lang/passes/parse.cc @@ -5,7 +5,7 @@ namespace verona::wf using namespace trieste::wf; inline const auto parse_tokens = Region | Ident | Lookup | Empty | Freeze | - Drop | Null | String | Create | Parens; + Drop | Take | Null | String | Create | Parens; inline const auto parse_groups = Group | Assign | If | Else | Block | For | Func | List | Return; @@ -157,6 +157,7 @@ trieste::Parse parser() m.push(Block); }, "drop\\b" >> [](auto& m) { m.add(Drop); }, + "take\\b" >> [](auto& m) { m.add(Take); }, "create\\b" >> [](auto& m) { m.add(Create); }, "freeze\\b" >> [](auto& m) { m.add(Freeze); }, "region\\b" >> [](auto& m) { m.add(Region); }, diff --git a/src/rt/core/builtin.cc b/src/rt/core/builtin.cc index 8718922..6c9ee46 100644 --- a/src/rt/core/builtin.cc +++ b/src/rt/core/builtin.cc @@ -60,7 +60,7 @@ namespace rt::core move_reference(frame, cown, region); stack->pop_back(); - return std::nullopt; + return cown; }); } diff --git a/src/rt/objects/dyn_object.h b/src/rt/objects/dyn_object.h index 306efe8..0a6cde8 100644 --- a/src/rt/objects/dyn_object.h +++ b/src/rt/objects/dyn_object.h @@ -275,18 +275,6 @@ namespace rt::objects [[nodiscard]] DynObject* get(std::string name) { - if (is_opaque()) - { - if (is_cown()) - { - ui::error("Cannot access data on a cown that is not aquired"); - } - else - { - ui::error("Cannot access data on an opaque type"); - } - } - auto result = fields.find(name); if (result != fields.end()) return result->second; diff --git a/src/rt/rt.cc b/src/rt/rt.cc index 80f2c5d..1a73f0b 100644 --- a/src/rt/rt.cc +++ b/src/rt/rt.cc @@ -71,6 +71,17 @@ namespace rt objects::DynObject* get(objects::DynObject* obj, std::string key) { + if (obj->is_opaque()) + { + if (obj->is_cown()) + { + ui::error("Cannot access data on a cown that is not aquired"); + } + else + { + ui::error("Cannot access data on an opaque type"); + } + } return obj->get(key); } diff --git a/tests/cowns/valid_01.vpy b/tests/cowns/valid_01.vpy index 699bb36..1314e06 100644 --- a/tests/cowns/valid_01.vpy +++ b/tests/cowns/valid_01.vpy @@ -4,10 +4,12 @@ global = {} a = {} a.b = {} region a -c01 = cown(a) + + +c01 = cown(take a) # Store the cown in a global -global.cown = c01 +global.cown = take c01 # Freeze global with a cown freeze global diff --git a/tests/exprs/take.vpy b/tests/exprs/take.vpy new file mode 100644 index 0000000..289af4e --- /dev/null +++ b/tests/exprs/take.vpy @@ -0,0 +1,7 @@ +a = {} +a.var = "hey" + +temp = take a.var +a.var = take temp + +a.var = take a.var From a6e04295ba56d98300a61fde26d32a9c68531063 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 23 Oct 2024 17:08:29 +0200 Subject: [PATCH 5/5] Lang: Convert `create`, `freeze`, `region` into functions --- src/lang/bytecode.h | 4 --- src/lang/interpreter.cc | 22 ------------- src/lang/lang.h | 22 +++++-------- src/lang/passes/bytecode.cc | 17 ---------- src/lang/passes/call_stmts.cc | 4 +-- src/lang/passes/flatten.cc | 14 ++++---- src/lang/passes/grouping.cc | 18 ----------- src/lang/passes/parse.cc | 7 ++-- src/rt/core.h | 7 ++-- src/rt/core/builtin.cc | 46 +++++++++++++++++++++++++-- tests/cowns/invalid_read.vpy | 4 +-- tests/cowns/invalid_shared_region.vpy | 4 +-- tests/cowns/invalid_write.vpy | 4 +-- tests/cowns/valid_01.vpy | 5 ++- tests/prototype.vpy | 8 ++--- tests/recursive_list.vpy | 4 +-- tests/three_regions.vpy | 8 ++--- tests/tobias3.vpy | 2 +- 18 files changed, 84 insertions(+), 116 deletions(-) diff --git a/src/lang/bytecode.h b/src/lang/bytecode.h index 11a5f9b..7f0425c 100644 --- a/src/lang/bytecode.h +++ b/src/lang/bytecode.h @@ -15,10 +15,8 @@ inline const trieste::TokenDef SwapFrame{"swap_frame", trieste::flag::print}; inline const trieste::TokenDef LoadGlobal{"load_global", trieste::flag::print}; inline const trieste::TokenDef LoadField{"load_field"}; inline const trieste::TokenDef StoreField{"store_field"}; -/// Stack: `[]::::::` -> `[]::` inline const trieste::TokenDef SwapField{"swap_field"}; inline const trieste::TokenDef CreateObject{"create_object"}; -inline const trieste::TokenDef Proto{"prototype"}; inline const trieste::TokenDef Dictionary{"dictionary"}; inline const trieste::TokenDef String{"string", trieste::flag::print}; inline const trieste::TokenDef KeyIter{"key_iter"}; @@ -36,8 +34,6 @@ inline const trieste::TokenDef ReturnValue{"return_value"}; /// Stack: `[]::` -> `[]::::` inline const trieste::TokenDef Dup{"dup", trieste::flag::print}; -inline const trieste::TokenDef CreateRegion{"create_region"}; -inline const trieste::TokenDef FreezeObject{"freeze_object"}; inline const trieste::TokenDef Null{"null"}; inline const trieste::TokenDef Label{"label"}; inline const trieste::TokenDef Eq{"=="}; diff --git a/src/lang/interpreter.cc b/src/lang/interpreter.cc index 21b245e..d721f05 100644 --- a/src/lang/interpreter.cc +++ b/src/lang/interpreter.cc @@ -165,12 +165,6 @@ namespace verona::interpreter obj = rt::make_iter(v); rt::remove_reference(frame(), v); } - else if (payload == Proto) - { - obj = rt::make_object(); - // RC transferred - rt::set_prototype(obj, pop("prototype source")); - } else if (payload == Func) { assert( @@ -317,22 +311,6 @@ namespace verona::interpreter return ExecNext{}; } - if (node == CreateRegion) - { - auto v = pop("region source"); - rt::create_region(v); - rt::remove_reference(frame(), v); - return ExecNext{}; - } - - if (node == FreezeObject) - { - auto v = pop("object to freeze"); - rt::freeze(v); - rt::remove_reference(frame(), v); - return ExecNext{}; - } - if (node == Eq || node == Neq) { auto b = pop("Rhs"); diff --git a/src/lang/lang.h b/src/lang/lang.h index 545823c..d6ad4d8 100644 --- a/src/lang/lang.h +++ b/src/lang/lang.h @@ -9,7 +9,6 @@ using namespace trieste; inline const TokenDef Ident{"ident", trieste::flag::print}; inline const TokenDef Assign{"assign"}; -inline const TokenDef Create{"create"}; inline const TokenDef For{"for"}; inline const TokenDef If{"if"}; inline const TokenDef Else{"else"}; @@ -17,8 +16,6 @@ inline const TokenDef Block{"block"}; inline const TokenDef Empty{"empty"}; inline const TokenDef Drop{"drop"}; inline const TokenDef Take{"take"}; -inline const TokenDef Freeze{"freeze"}; -inline const TokenDef Region{"region"}; inline const TokenDef Lookup{"lookup"}; inline const TokenDef Parens{"parens"}; inline const TokenDef Method{"method"}; @@ -33,8 +30,7 @@ inline const TokenDef Compile{"compile"}; namespace verona::wf { inline const auto lv = Ident | Lookup; - inline const auto rv = - lv | Empty | Null | String | Create | Call | Method | Take; + inline const auto rv = lv | Empty | Null | String | Call | Method | Take; inline const auto cmp_values = Ident | Lookup | Null; inline const auto key = Ident | Lookup | String; inline const auto operand = Lookup | Call | Method | Ident; @@ -43,11 +39,9 @@ namespace verona::wf inline const auto grouping = (Top <<= File) | (File <<= Body) | (Body <<= Block) | (Block <<= - (Freeze | Region | Assign | If | For | Func | Return | ReturnValue | Call | - Method)++) | + (Assign | If | For | Func | Return | ReturnValue | Call | Method)++) | (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | (Take <<= (Lhs >>= lv)) | - (Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (Region <<= Ident) | - (Freeze <<= Ident) | (Create <<= Ident) | + (Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (If <<= (Op >>= Cond) * Block * Block) | (For <<= (Key >>= Ident) * (Value >>= Ident) * (Op >>= lv) * Block) | (Eq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) | @@ -59,16 +53,16 @@ namespace verona::wf inline const trieste::wf::Wellformed bytecode = (Top <<= Body) | (Body <<= (LoadFrame | LoadGlobal | StoreFrame | SwapFrame | LoadField | StoreField | - SwapField | Drop | Null | CreateObject | CreateRegion | FreezeObject | - IterNext | Print | Eq | Neq | Jump | JumpFalse | Label | Call | Return | - ReturnValue | ClearStack | Dup)++) | - (CreateObject <<= (Dictionary | String | KeyIter | Proto | Func)) | + SwapField | Drop | Null | CreateObject | IterNext | Print | Eq | Neq | + Jump | JumpFalse | Label | Call | Return | ReturnValue | ClearStack | + Dup)++) | + (CreateObject <<= (Dictionary | String | KeyIter | Func)) | (Func <<= Body) | (Label <<= Ident)[Ident]; } inline const auto LV = T(Ident, Lookup); inline const auto RV = - T(Empty, Ident, Lookup, Null, String, Create, Call, Method, Take); + T(Empty, Ident, Lookup, Null, String, Call, Method, Take); inline const auto CMP_V = T(Ident, Lookup, Null); inline const auto KEY = T(Ident, Lookup, String); inline const auto OPERAND = T(Lookup, Call, Method, Ident); diff --git a/src/lang/passes/bytecode.cc b/src/lang/passes/bytecode.cc index 0654af4..b487e83 100644 --- a/src/lang/passes/bytecode.cc +++ b/src/lang/passes/bytecode.cc @@ -77,23 +77,6 @@ PassDef bytecode() << create_print(_(Assign)); }, - T(Compile) << (T(Freeze)[Op] << T(Ident)[Ident]) >> - [](auto& _) { - return Seq << (Compile << _[Ident]) << FreezeObject - << create_print(_(Op)); - }, - - T(Compile) << (T(Create)[Op] << T(Ident)[Ident]) >> - [](auto& _) { - return Seq << (Compile << _[Ident]) << (CreateObject << Proto); - }, - - T(Compile) << (T(Region)[Op] << T(Ident)[Ident]) >> - [](auto& _) { - return Seq << (Compile << _[Ident]) << CreateRegion - << create_print(_(Op)); - }, - T(Compile) << (T(Method)[Method] << ((T(Lookup)[Lookup] << (OPERAND[Op] * T(String)[Key])) * diff --git a/src/lang/passes/call_stmts.cc b/src/lang/passes/call_stmts.cc index 6eb6ce4..84bd886 100644 --- a/src/lang/passes/call_stmts.cc +++ b/src/lang/passes/call_stmts.cc @@ -6,8 +6,8 @@ namespace verona::wf inline const auto call_stmts = grouping | (Block <<= - (Freeze | Region | Assign | If | For | Func | Return | ReturnValue | Call | - Method | ClearStack | Print)++); + (Assign | If | For | Func | Return | ReturnValue | Call | Method | + ClearStack | Print)++); } PassDef call_stmts() diff --git a/src/lang/passes/flatten.cc b/src/lang/passes/flatten.cc index 4e025da..d0d9b20 100644 --- a/src/lang/passes/flatten.cc +++ b/src/lang/passes/flatten.cc @@ -6,16 +6,14 @@ namespace verona::wf inline const trieste::wf::Wellformed flatten = (Top <<= File) | (File <<= Body) | (Body <<= - (Freeze | Region | Assign | Take | Eq | Neq | Label | Jump | JumpFalse | - Print | StoreFrame | LoadFrame | CreateObject | Ident | IterNext | - Create | StoreField | Lookup | String | Call | Method | Return | - ReturnValue | ClearStack)++) | + (Assign | Take | Eq | Neq | Label | Jump | JumpFalse | Print | StoreFrame | + LoadFrame | CreateObject | Ident | IterNext | StoreField | Lookup | + String | Call | Method | Return | ReturnValue | ClearStack)++) | (CreateObject <<= (KeyIter | String | Dictionary | Func)) | - (Func <<= Compile) | (Compile <<= Body) | (Create <<= Ident) | + (Func <<= Compile) | (Compile <<= Body) | (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | (Take <<= lv) | - (Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (Region <<= Ident) | - (Freeze <<= Ident) | (Call <<= Ident * List) | (Method <<= Lookup * List) | - (List <<= rv++) | (Params <<= Ident++) | + (Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (Call <<= Ident * List) | + (Method <<= Lookup * List) | (List <<= rv++) | (Params <<= Ident++) | (Eq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) | (Neq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) | (Label <<= Ident)[Ident]; diff --git a/src/lang/passes/grouping.cc b/src/lang/passes/grouping.cc index db42511..e827d8e 100644 --- a/src/lang/passes/grouping.cc +++ b/src/lang/passes/grouping.cc @@ -16,18 +16,6 @@ PassDef grouping() In(Group) * OPERAND[Op] * (T(Lookup)[Lookup] << (T(Group) << KEY[Rhs])) >> [](auto& _) { return Lookup << _(Op) << _(Rhs); }, - T(Group) << ((T(Region)[Region] << End) * T(Ident)[Ident] * End) >> - [](auto& _) { - _(Region)->extend(_(Ident)->location()); - return _(Region) << _(Ident); - }, - - T(Group) << ((T(Freeze)[Freeze] << End) * T(Ident)[Ident] * End) >> - [](auto& _) { - _(Freeze)->extend(_(Ident)->location()); - return _(Freeze) << _(Ident); - }, - T(Group) << ((T(Drop)[Drop] << End) * LV[Lhs] * End) >> [](auto& _) { return Assign << _(Lhs) << Null; }, T(Group) << ((T(Take)[Take] << End) * LV[Lhs] * End) >> @@ -69,12 +57,6 @@ PassDef grouping() return create_from(Method, _(Group)) << _(Lookup) << list; }, - T(Group) << ((T(Create)[Create] << End) * T(Ident)[Ident] * End) >> - [](auto& _) { - _(Create)->extend(_(Ident)->location()); - return Group << (_(Create) << _(Ident)); - }, - T(Assign) << ((T(Group) << LV[Lhs] * End) * ((T(Group) << (RV[Rhs] * End)) / (RV[Rhs] * End)) * End) >> diff --git a/src/lang/passes/parse.cc b/src/lang/passes/parse.cc index cc68859..2930608 100644 --- a/src/lang/passes/parse.cc +++ b/src/lang/passes/parse.cc @@ -4,8 +4,8 @@ namespace verona::wf { using namespace trieste::wf; - inline const auto parse_tokens = Region | Ident | Lookup | Empty | Freeze | - Drop | Take | Null | String | Create | Parens; + inline const auto parse_tokens = + Ident | Lookup | Empty | Drop | Take | Null | String | Parens; inline const auto parse_groups = Group | Assign | If | Else | Block | For | Func | List | Return; @@ -158,9 +158,6 @@ trieste::Parse parser() }, "drop\\b" >> [](auto& m) { m.add(Drop); }, "take\\b" >> [](auto& m) { m.add(Take); }, - "create\\b" >> [](auto& m) { m.add(Create); }, - "freeze\\b" >> [](auto& m) { m.add(Freeze); }, - "region\\b" >> [](auto& m) { m.add(Region); }, "None\\b" >> [](auto& m) { m.add(Null); }, "[0-9A-Za-z_]+" >> [](auto& m) { m.add(Ident); }, "\\[" >> [](auto& m) { m.push(Lookup); }, diff --git a/src/rt/core.h b/src/rt/core.h index b3cc575..9fb0499 100644 --- a/src/rt/core.h +++ b/src/rt/core.h @@ -218,9 +218,10 @@ namespace rt::core // FIXME: Also check that the region has a LRC == 1, with 1 // being the reference passed into this constructor - assert( - region->change_rc(0) == 1 && - "regions used for cown creation need to have an rc of 1"); + if (region->change_rc(0) != 1) + { + ui::error("regions used for cown creation need to have an rc of 1"); + } // this->set would fail, since this is a cown this->fields["region"] = region; diff --git a/src/rt/core/builtin.cc b/src/rt/core/builtin.cc index 6c9ee46..4810b02 100644 --- a/src/rt/core/builtin.cc +++ b/src/rt/core/builtin.cc @@ -3,6 +3,15 @@ namespace rt::core { + rt::objects::DynObject* + pop(std::vector* stack, char const* data_info) + { + auto v = stack->back(); + stack->pop_back(); + std::cout << "pop " << v << " (" << data_info << ")" << std::endl; + return v; + } + void mermaid_builtins(ui::UI* ui) { if (!ui->is_mermaid()) @@ -55,18 +64,51 @@ namespace rt::core add_builtin("cown", [](auto frame, auto stack, auto args) { assert(args == 1); - auto region = stack->back(); + auto region = pop(stack, "region for cown creation"); auto cown = make_cown(region); move_reference(frame, cown, region); - stack->pop_back(); return cown; }); + + add_builtin("create", [](auto frame, auto stack, auto args) { + assert(args == 1); + + auto obj = make_object(); + // RC transferred + rt::set_prototype(obj, pop(stack, "prototype source")); + + return obj; + }); + } + + void action_builtins() + { + add_builtin("freeze", [](auto frame, auto stack, auto args) { + assert(args == 1); + + auto value = pop(stack, "object to freeze"); + freeze(value); + remove_reference(frame, value); + + return std::nullopt; + }); + + add_builtin("region", [](auto frame, auto stack, auto args) { + assert(args == 1); + + auto value = pop(stack, "region source"); + create_region(value); + remove_reference(frame, value); + + return std::nullopt; + }); } void init_builtins(ui::UI* ui) { mermaid_builtins(ui); ctor_builtins(); + action_builtins(); } } diff --git a/tests/cowns/invalid_read.vpy b/tests/cowns/invalid_read.vpy index 68ccf91..636b8bc 100644 --- a/tests/cowns/invalid_read.vpy +++ b/tests/cowns/invalid_read.vpy @@ -1,7 +1,7 @@ # A simple cown a = {} -region a -co = cown(a) +region(a) +co = cown(take a) # Reading the cown is forbidden dummy = co.region diff --git a/tests/cowns/invalid_shared_region.vpy b/tests/cowns/invalid_shared_region.vpy index f2ae610..50ac365 100644 --- a/tests/cowns/invalid_shared_region.vpy +++ b/tests/cowns/invalid_shared_region.vpy @@ -1,9 +1,9 @@ # Creating a region a = {} -region a +region(a) # LRC = 2 r01 = a # Error due to invalid RC -co = cown(a) +co = cown(take a) diff --git a/tests/cowns/invalid_write.vpy b/tests/cowns/invalid_write.vpy index a0a6a27..f7a73fb 100644 --- a/tests/cowns/invalid_write.vpy +++ b/tests/cowns/invalid_write.vpy @@ -1,7 +1,7 @@ # A simple cown a = {} -region a -co = cown(a) +region(a) +co = cown(take a) # Modifying the cown is forbidden co.other = {} diff --git a/tests/cowns/valid_01.vpy b/tests/cowns/valid_01.vpy index 1314e06..5ed36f9 100644 --- a/tests/cowns/valid_01.vpy +++ b/tests/cowns/valid_01.vpy @@ -3,7 +3,7 @@ global = {} # A simple cown a = {} a.b = {} -region a +region(a) c01 = cown(take a) @@ -12,7 +12,6 @@ c01 = cown(take a) global.cown = take c01 # Freeze global with a cown -freeze global +freeze(global) -drop c01 drop global diff --git a/tests/prototype.vpy b/tests/prototype.vpy index e6d374e..cb3f5e8 100644 --- a/tests/prototype.vpy +++ b/tests/prototype.vpy @@ -2,11 +2,9 @@ a = {} b = {} c = {} -b["f"] = a -drop a +b["f"] = take a -c = create b -drop b +c = create(take b) c["g"] = c["f"] -drop c \ No newline at end of file +drop c diff --git a/tests/recursive_list.vpy b/tests/recursive_list.vpy index becc02d..1179807 100644 --- a/tests/recursive_list.vpy +++ b/tests/recursive_list.vpy @@ -32,13 +32,13 @@ def new_list(): list = new_list() # Required to not leak memory -region list +region(list) value = "x" # Dyrona doesn't freeze shared objects automatically (yet) proto = value["__proto__"] -freeze proto +freeze(proto) drop proto insert(list.head, {}) diff --git a/tests/three_regions.vpy b/tests/three_regions.vpy index 6b2874e..d37fc86 100644 --- a/tests/three_regions.vpy +++ b/tests/three_regions.vpy @@ -3,19 +3,19 @@ a = {} a["self"] = a b = {} a["b"] = b -region a +region(a) c = {} c["self"] = c c["d"] = {} -region c +region(c) e = {} e["self"] = e e["f"] = {} -region e +region(e) # connect first region to second in two ways @@ -26,7 +26,7 @@ a.b.e = e.f # Now freeze part of the first region, and the reachable parts of the # second and third regions -freeze b +freeze(b) # Drop all the references diff --git a/tests/tobias3.vpy b/tests/tobias3.vpy index b0e06b4..4fd8d94 100644 --- a/tests/tobias3.vpy +++ b/tests/tobias3.vpy @@ -2,7 +2,7 @@ a = {} b = {} a["f"] = b c = a.f -region a +region(a) drop b drop c