diff --git a/src/lang/bytecode.h b/src/lang/bytecode.h index c40b897..957048b 100644 --- a/src/lang/bytecode.h +++ b/src/lang/bytecode.h @@ -18,6 +18,13 @@ inline const trieste::TokenDef Body{"body", trieste::flag::symtab}; inline const trieste::TokenDef Return{"return"}; inline const trieste::TokenDef ReturnValue{"return_value"}; +/// Duplicates an item on the stack. The location of the +/// note is an index from the end of the stack, that indicates +/// which value should be duplicate. +/// +/// Stack: `[]::<arg_0>` -> `[]::<arg_0>::<arg_0>` +inline const trieste::TokenDef Copy{"copy", trieste::flag::print}; + inline const trieste::TokenDef CreateRegion{"create_region"}; inline const trieste::TokenDef FreezeObject{"freeze_object"}; inline const trieste::TokenDef Null{"null"}; diff --git a/src/lang/interpreter.cc b/src/lang/interpreter.cc index 7391383..a98257d 100644 --- a/src/lang/interpreter.cc +++ b/src/lang/interpreter.cc @@ -346,6 +346,22 @@ namespace verona::interpreter return action; } + if (node == Copy) + { + // This breaks the normal idea of a stack machine, but every other + // solution would require more effort and would be messier + auto copy_idx = std::stoul(std::string(node->location().view())); + auto stack_size = stack().size(); + assert(copy_idx < stack_size && "the stack is too small for this copy"); + + auto var = stack()[stack_size - copy_idx - 1]; + stack().push_back(var); + std::cout << "push " << var << std::endl; + rt::add_reference(frame(), var); + + return ExecNext{}; + } + if (node == Return) { return ExecReturn{}; diff --git a/src/lang/lang.h b/src/lang/lang.h index d33ad1d..149cb38 100644 --- a/src/lang/lang.h +++ b/src/lang/lang.h @@ -20,6 +20,7 @@ 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"}; inline const TokenDef Op{"op"}; inline const TokenDef Rhs{"rhs"}; @@ -31,9 +32,10 @@ inline const TokenDef Compile{"compile"}; namespace verona::wf { inline const auto lv = Ident | Lookup; - inline const auto rv = lv | Empty | Null | String | Create | Call; + inline const auto rv = lv | Empty | Null | String | Create | Call | Method; inline const auto cmp_values = Ident | Lookup | Null; inline const auto key = Ident | Lookup | String; + inline const auto operand = Lookup | Call | Method | Ident; inline const auto grouping = (Top <<= File) | (File <<= Body) | (Body <<= Block) | @@ -45,22 +47,26 @@ namespace verona::wf (Freeze <<= Ident) | (Create <<= Ident) | (If <<= Eq * Block * Block) | (For <<= (Key >>= Ident) * (Value >>= Ident) * (Op >>= lv) * Block) | (Eq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) | - (Func <<= Ident * Params * Body) | (Call <<= (Op >>= key) * List) | - (ReturnValue <<= rv) | (List <<= rv++) | (Params <<= Ident++); + (Func <<= Ident * Params * Body) | (Call <<= Ident * List) | + (Method <<= Lookup * List) | (ReturnValue <<= rv) | (List <<= rv++) | + (Params <<= Ident++); inline const trieste::wf::Wellformed bytecode = (Top <<= Body) | (Body <<= (LoadFrame | StoreFrame | LoadField | StoreField | Drop | Null | CreateObject | CreateRegion | FreezeObject | IterNext | Print | Eq | Neq | - Jump | JumpFalse | Label | Call | Return | ReturnValue | ClearStack)++) | + Jump | JumpFalse | Label | Call | Return | ReturnValue | ClearStack | + Copy)++) | (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); +inline const auto RV = + T(Empty, Ident, Lookup, Null, String, Create, Call, Method); 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); // Parsing && AST construction Parse parser(); diff --git a/src/lang/passes/bytecode.cc b/src/lang/passes/bytecode.cc index 42f95f2..3767372 100644 --- a/src/lang/passes/bytecode.cc +++ b/src/lang/passes/bytecode.cc @@ -85,6 +85,24 @@ PassDef bytecode() << create_print(_(Op)); }, + T(Compile) + << (T(Method)[Method] + << ((T(Lookup)[Lookup] << (OPERAND[Op] * T(String)[Key])) * + (T(List) << Any++[List]) * End)) >> + [](auto& _) { + auto arg_ctn = _[List].size(); + return Seq + // `self` lookup and first argument + << (Compile << _(Op)) + << (Compile << _[List]) + // Copy self + << (Copy ^ std::to_string(arg_ctn)) + // Fetch the function + << (Compile << _(Key)) + << create_from(LoadField, _(Lookup)) + // function call, +1 for the self argument + << (Call ^ std::to_string(arg_ctn + 1)); + }, T(Compile) << (T(Call)[Call] << (KEY[Op] * (T(List) << Any++[List]) * End)) >> [](auto& _) { diff --git a/src/lang/passes/flatten.cc b/src/lang/passes/flatten.cc index 3beb5c2..44e2cf9 100644 --- a/src/lang/passes/flatten.cc +++ b/src/lang/passes/flatten.cc @@ -8,13 +8,13 @@ namespace verona::wf (Body <<= (Freeze | Region | Assign | Eq | Neq | Label | Jump | JumpFalse | Print | StoreFrame | LoadFrame | CreateObject | Ident | IterNext | Create | - StoreField | Lookup | String | Call | Return | ReturnValue | + StoreField | Lookup | String | Call | Method | Return | ReturnValue | ClearStack)++) | (CreateObject <<= (KeyIter | String | Dictionary | Func)) | (Func <<= Compile) | (Compile <<= Body) | (Create <<= Ident) | (Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | (Lookup <<= (Lhs >>= lv) * (Rhs >>= key)) | (Region <<= Ident) | - (Freeze <<= Ident) | (Call <<= (Op >>= key) * List) | (List <<= rv++) | + (Freeze <<= Ident) | (Call <<= Ident * List) | (Method <<= Lookup * List) | (List <<= rv++) | (Params <<= Ident++) | (Eq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) | (Neq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) | diff --git a/src/lang/passes/grouping.cc b/src/lang/passes/grouping.cc index 38bb55d..f8e5cb6 100644 --- a/src/lang/passes/grouping.cc +++ b/src/lang/passes/grouping.cc @@ -42,6 +42,19 @@ PassDef grouping() return create_from(Call, _(Group)) << _(Ident) << list; }, + --In(Method) * + (T(Group)[Group] + << ((T(Lookup)[Lookup]) * (T(Parens)[Parens] << (~T(List)[List])) * + End)) >> + [](auto& _) { + auto list = _(List); + if (!list) + { + list = create_from(List, _(Parens)); + } + + return create_from(Method, _(Group)) << _(Lookup) << list; + }, T(Group) << ((T(Create)[Create] << End) * T(Ident)[Ident] * End) >> [](auto& _) { @@ -89,7 +102,9 @@ PassDef grouping() << ((T(Group) << End) * (T(Group) << ((T(Ident)[Ident]) * - (T(Parens)[Parens] << (~(T(List) << T(Ident)++[List]))) * + (T(Parens)[Parens] << ( + // (T(Group) << T(Ident)[List]) / + ~(T(List) << T(Ident)++[List]))) * End)) * (T(Group) << T(Block)[Block]) * End) >> [](auto& _) { @@ -97,6 +112,12 @@ PassDef grouping() << _(Ident) << (create_from(Params, _(Parens)) << _[List]) << (Body << _(Block)); }, + // Normalize functions with a single ident to also have a list token + T(Parens)[Parens] << (T(Group) << (T(Ident)[Ident] * End)) >> + [](auto& _) { + return create_from(Parens, _(Parens)) + << (create_from(List, _(Parens)) << _(Ident)); + }, T(Return)[Return] << ((T(Group) << End) * End) >> [](auto& _) { return create_from(Return, _(Return)); }, diff --git a/tests/self.vpy b/tests/self.vpy new file mode 100644 index 0000000..844d7a5 --- /dev/null +++ b/tests/self.vpy @@ -0,0 +1,11 @@ + +def str_name(self): + return "String" + +value = "Music :3" +value.name = str_name +drop str_name + +name = value.name() + +