Skip to content

Commit

Permalink
Interpreter: Support recursive calls
Browse files Browse the repository at this point in the history
It turns out python scoping is a bit weird. Top level functions are
added to the `globals()` table. Function calls to self or a parent
function which is not on the top level are passed as local variables.
Here is an example:

```python
print("s00 " + str(globals()))
print("s00 " + str(locals()))

s00 = 0

def scope_01(con):
    s01 = 1
    print("s01" + str(globals()))
    print("s01 " + str(locals()))

    def scope_02(con):
        s02 = 2
        print("s02" + str(globals()))
        print("s02 " + str(locals()))

        def scope_03():
            s03 = 3
            print("s03" + str(globals()))
            print("s03 " + str(locals()))
            scope_01(False)
            scope_02(False)

        if con:
            scope_03()
    if con:
        scope_02(True)

scope_01(True)
```

This commit adds a hack that simulates the global namespace. This at
least allows simple recursion.
  • Loading branch information
xFrednet committed Oct 22, 2024
1 parent 0eb4d98 commit c256cc8
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 21 deletions.
8 changes: 8 additions & 0 deletions src/lang/bytecode.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@

#include "trieste/ast.h"

/// Loads a value with a given name from the current frame.
///
/// 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};
/// Loads a value with a given name from the current scope or the global
/// namespace. This should be used for function resolution.
///
/// The value name is stored in the location of the node
inline const trieste::TokenDef LoadGlobal{"load_global", trieste::flag::print};
inline const trieste::TokenDef LoadField{"load_field"};
inline const trieste::TokenDef StoreField{"store_field"};
inline const trieste::TokenDef CreateObject{"create_object"};
Expand Down
24 changes: 24 additions & 0 deletions src/lang/interpreter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ namespace verona::interpreter
{
return frame_stack.back()->frame;
}
rt::objects::DynObject* global_frame()
{
return frame_stack.front()->frame;
}

rt::objects::DynObject* pop(char const* data_info)
{
Expand Down Expand Up @@ -201,6 +205,26 @@ namespace verona::interpreter
return ExecNext{};
}

if (node == LoadGlobal)
{
std::string field{node->location().view()};

auto v = rt::get(frame(), field);
if (v)
{
rt::add_reference(frame(), v);
}
else
{
v = rt::get(global_frame(), field);
rt::add_reference(global_frame(), v);
}

stack().push_back(v);
std::cout << "push " << v << std::endl;
return ExecNext{};
}

if (node == StoreFrame)
{
auto v = pop("value to store");
Expand Down
14 changes: 9 additions & 5 deletions src/lang/lang.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ namespace verona::wf
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 Cond = Eq | Neq;

inline const auto grouping = (Top <<= File) | (File <<= Body) |
(Body <<= Block) |
Expand All @@ -44,19 +45,21 @@ namespace verona::wf
Method)++) |
(Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) |
(Lookup <<= (Op >>= operand) * (Rhs >>= key)) | (Region <<= Ident) |
(Freeze <<= Ident) | (Create <<= Ident) | (If <<= Eq * Block * Block) |
(Freeze <<= Ident) | (Create <<= Ident) |
(If <<= (Op >>= Cond) * Block * Block) |
(For <<= (Key >>= Ident) * (Value >>= Ident) * (Op >>= lv) * Block) |
(Eq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) |
(Neq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) |
(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 |
Dup)++) |
(LoadFrame | LoadGlobal | StoreFrame | LoadField | StoreField | 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];
}
Expand All @@ -67,6 +70,7 @@ inline const auto RV =
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);
inline const auto COND = T(Eq, Neq);

// Parsing && AST construction
Parse parser();
Expand Down
8 changes: 5 additions & 3 deletions src/lang/passes/bytecode.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ PassDef bytecode()
ClearStack)[Op] >>
[](auto& _) -> Node { return _(Op); },

T(Compile) << (T(Eq, Neq)[Op] << (Any[Lhs] * Any[Rhs])) >>
T(Compile) << (COND[Op] << (Any[Lhs] * Any[Rhs])) >>
[](auto& _) {
return Seq << (Compile << _(Lhs)) << (Compile << _(Rhs))
<< _(Op)->type();
Expand Down Expand Up @@ -104,10 +104,12 @@ PassDef bytecode()
<< (Call ^ std::to_string(arg_ctn + 1));
},
T(Compile)
<< (T(Call)[Call] << (KEY[Op] * (T(List) << Any++[List]) * End)) >>
<< (T(Call)[Call]
<< (T(Ident)[Ident] * (T(List) << Any++[List]) * End)) >>
[](auto& _) {
// The print is done by the called function
return Seq << (Compile << _[List]) << (Compile << _(Op))
return Seq << (Compile << _[List])
<< create_from(LoadGlobal, _(Ident))
<< (Call ^ std::to_string(_[List].size()));
},

Expand Down
2 changes: 1 addition & 1 deletion src/lang/passes/flatten.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ PassDef flatten()
In(Body) * (T(Block) << Any++[Block]) >>
[](auto& _) { return Seq << _[Block]; },
T(If)[If]
<< (T(Eq)[Op] * (T(Block) << Any++[Lhs]) *
<< (COND[Op] * (T(Block) << Any++[Lhs]) *
(T(Block) << Any++[Rhs])) >>
[](auto& _) {
auto else_label = new_jump_label();
Expand Down
16 changes: 10 additions & 6 deletions src/lang/passes/grouping.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,20 @@ PassDef grouping()
<< ((T(Group) << LV[Lhs] * End) *
((T(Group) << (RV[Rhs] * End)) / (RV[Rhs] * End)) * End) >>
[](auto& _) { return Assign << _[Lhs] << _[Rhs]; },
T(Eq)
COND[Op]
<< ((T(Group) << CMP_V[Lhs] * End) * (T(Group) << CMP_V[Rhs] * End) *
End) >>
[](auto& _) { return Eq << _[Lhs] << _[Rhs]; },
[](auto& _) {
return create_from(_(Op)->type(), _(Op)) << _[Lhs] << _[Rhs];
},

(T(If) << (T(Group) * T(Eq)[Eq] * (T(Group) << T(Block)[Block]))) >>
[](auto& _) { return If << _(Eq) << _(Block); },
(T(If) << (T(Group) * COND[Op] * (T(Group) << T(Block)[Block]))) >>
[](auto& _) { return If << _(Op) << _(Block); },
(T(Else) << (T(Group) * (T(Group) << T(Block)[Block]))) >>
[](auto& _) { return Else << _(Block); },
(T(If)[If] << (T(Eq) * T(Block) * End)) * (T(Else) << T(Block)[Block]) >>
(T(If)[If] << (COND * T(Block) * End)) * (T(Else) << T(Block)[Block]) >>
[](auto& _) { return _(If) << _(Block); },
(T(If)[If] << (T(Eq) * T(Block) * End)) * (--T(Else)) >>
(T(If)[If] << (COND * T(Block) * End)) * (--T(Else)) >>
[](auto& _) {
// This adds an empty else block, if no else was written
return _(If) << Block;
Expand Down Expand Up @@ -132,6 +134,8 @@ PassDef grouping()
T(Return)[Return]
<< ((T(Group) << End) * (T(Group) << (RV[Rhs] * End)) * End) >>
[](auto& _) { return create_from(ReturnValue, _(Return)) << _(Rhs); },
T(Return)[Return] << ((T(Group) << End) * (RV[Rhs] * End)) >>
[](auto& _) { return create_from(ReturnValue, _(Return)) << _(Rhs); },

}};

Expand Down
18 changes: 12 additions & 6 deletions src/lang/passes/parse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ namespace verona::wf
Group | Assign | If | Else | Block | For | Func | List | Return;

inline const auto parser = (Top <<= File) | (File <<= parse_groups++) |
(Assign <<= Group++) | (If <<= Group * Eq * Group) |
(Assign <<= Group++) | (If <<= Group * (Op >>= Cond) * Group) |
(Else <<= Group * Group) | (Group <<= (parse_tokens | Block | List)++) |
(Block <<= (parse_tokens | parse_groups)++) | (Eq <<= Group * Group) |
(Lookup <<= Group) | (For <<= Group * List * Group * Group) |
(List <<= Group++) | (Parens <<= (Group | List)++) |
(Func <<= Group * Group * Group) | (Return <<= Group++);
(Neq <<= Group * Group) | (Lookup <<= Group) |
(For <<= Group * List * Group * Group) | (List <<= Group++) |
(Parens <<= (Group | List)++) | (Func <<= Group * Group * Group) |
(Return <<= Group++);
}

struct Indent
Expand Down Expand Up @@ -109,12 +110,16 @@ trieste::Parse parser()
},
"," >> [](auto& m) { m.seq(List); },

"if\\b" >> [](auto& m) { m.seq(If); },
"if\\b" >>
[](auto& m) {
m.term();
m.seq(If);
},
"else\\b" >> [](auto& m) { m.seq(Else); },
":" >>
[indent](auto& m) {
// Exit conditionals expressions.
m.term({Eq});
m.term({Eq, Neq});

Token toc = Empty;
if (m.in(If))
Expand Down Expand Up @@ -167,6 +172,7 @@ trieste::Parse parser()
},
"\"([^\\n\"]+)\"" >> [](auto& m) { m.add(String, 1); },
"==" >> [](auto& m) { m.seq(Eq); },
"!=" >> [](auto& m) { m.seq(Neq); },
"=" >> [](auto& m) { m.seq(Assign); },
"{}" >> [](auto& m) { m.add(Empty); },
});
Expand Down
44 changes: 44 additions & 0 deletions tests/recursive_list.vpy
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# For now separate
def insert(node, item):
if node.next == None:
node.next = {}
node.next.data = item
node.next.prev = node
else:
insert(node.next, item)

def contains(node, item):
if node.data == item:
return True
if node.next == None:
return False
return find(node.next, item)

def remove(node, item):
if node.data == item:
node.prev.next = node.next
if node.next != None:
node.next.prev = node.prev
return True
if node.next == None:
return False
return remove(node.next, item)

def new_list():
list = {}
list.head = {}
return list

list = new_list()

# Required to not leak memory
region list

value = "x"
insert(list.head, {})
insert(list.head, {})
insert(list.head, value)
insert(list.head, {})
remove(list.head, value)

drop list

0 comments on commit c256cc8

Please sign in to comment.