Skip to content

Commit

Permalink
Merge pull request #32 from mjp41/0-strech-for
Browse files Browse the repository at this point in the history
Add `while` loops and add linked list example with while loops
  • Loading branch information
xFrednet authored Oct 24, 2024
2 parents d91489a + 2fb5b1d commit fd7ec03
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 24 deletions.
20 changes: 12 additions & 8 deletions src/lang/lang.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ using namespace trieste;
inline const TokenDef Ident{"ident", trieste::flag::print};
inline const TokenDef Assign{"assign"};
inline const TokenDef For{"for"};
inline const TokenDef While{"while"};
inline const TokenDef If{"if"};
inline const TokenDef Else{"else"};
inline const TokenDef Block{"block"};
Expand All @@ -29,20 +30,23 @@ inline const TokenDef Compile{"compile"};

namespace verona::wf
{
inline const auto cond = Eq | Neq;
inline const auto lv = Ident | Lookup;
inline const auto rv = lv | Empty | Null | String | Call | Method | Take;
inline const auto cmp_values = Ident | Lookup | Null;
inline const auto rv =
lv | Empty | Null | String | Call | Method | Take | cond;
inline const auto cmp_values = Ident | Lookup | Null | Call | Method;
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) |
(Block <<=
(Assign | If | For | Func | Return | ReturnValue | Call | Method)++) |
(Assign | If | For | While | Func | Return | ReturnValue | Call |
Method)++) |
(Assign <<= (Lhs >>= lv) * (Rhs >>= rv)) | (Take <<= (Lhs >>= lv)) |
(Lookup <<= (Op >>= operand) * (Rhs >>= key)) |
(If <<= (Op >>= Cond) * Block * Block) |
(If <<= (Op >>= cond) * Block * Block) | (While <<= (Op >>= cond) * Block) |
(While <<= (Op >>= cond) * Block) |
(For <<= (Key >>= Ident) * (Value >>= Ident) * (Op >>= lv) * Block) |
(Eq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) |
(Neq <<= (Lhs >>= cmp_values) * (Rhs >>= cmp_values)) |
Expand All @@ -60,13 +64,13 @@ namespace verona::wf
(Func <<= Body) | (Label <<= Ident)[Ident];
}

inline const auto COND = T(Eq, Neq);
inline const auto LV = T(Ident, Lookup);
inline const auto RV =
T(Empty, Ident, Lookup, Null, String, Call, Method, Take);
inline const auto CMP_V = T(Ident, Lookup, Null);
T(Empty, Ident, Lookup, Null, String, Call, Method, Take, Eq, Neq);
inline const auto CMP_V = T(Ident, Lookup, Null, Call, Method);
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
2 changes: 1 addition & 1 deletion src/lang/passes/call_stmts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace verona::wf

inline const auto call_stmts = grouping |
(Block <<=
(Assign | If | For | Func | Return | ReturnValue | Call | Method |
(Assign | If | For | While | Func | Return | ReturnValue | Call | Method |
ClearStack | Print)++);
}

Expand Down
23 changes: 22 additions & 1 deletion src/lang/passes/flatten.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,27 @@ PassDef flatten()
// Join
<< (Label << (Ident ^ join_label));
},

T(While)[While] << (COND[Op] * (T(Block) << Any++[Block])) >>
[](auto& _) {
auto start_label = new_jump_label();
auto break_label = new_jump_label();

auto while_head = expr_header(_(While));

return Seq
// Loop header
<< (Label << (Ident ^ start_label)) << _(Op)
<< (JumpFalse ^ break_label)
<< create_print(_(While), while_head + " (Next)")
// Loop body
<< _[Block]
<< (Jump ^ start_label)
// Loop end
<< (Label << (Ident ^ break_label))
<< create_print(_(While), while_head + " (Break)");
},

T(For)[For]
<< (T(Ident)[Key] * T(Ident)[Value] * LV[Op] *
(T(Block) << Any++[Block]) * End) >>
Expand Down Expand Up @@ -102,8 +123,8 @@ PassDef flatten()
<< create_print(_(For), for_head + " (Next)")
// Block
<< _[Block]
// Final cleanup
<< (Jump ^ start_label)
// Final cleanup
<< ((Label ^ "break:") << (Ident ^ break_label))
<< create_print(_(For), for_head + " (Break)")
<< ((Assign ^ ("drop " + std::string(_(Value)->location().view())))
Expand Down
16 changes: 13 additions & 3 deletions src/lang/passes/grouping.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,17 @@ PassDef grouping()
<< ((T(Group) << LV[Lhs] * End) *
((T(Group) << (RV[Rhs] * End)) / (RV[Rhs] * End)) * End) >>
[](auto& _) { return Assign << _[Lhs] << _[Rhs]; },
COND[Op]
<< ((T(Group) << CMP_V[Lhs] * End) * (T(Group) << CMP_V[Rhs] * End) *
End) >>

// Normalize `if x:` -> `if x == True`
In(If, While) * ((T(Group) << End) * (!COND)[Op]) >>
[](auto& _) {
return Seq << Group << (Eq << _(Op) << (Ident ^ "True"));
},
COND[Op] << (Any[Lhs] * (T(Group) << CMP_V[Rhs] * End) * End) >>
[](auto& _) {
return create_from(_(Op)->type(), _(Op)) << _[Lhs] << _[Rhs];
},
COND[Op] << ((T(Group) << CMP_V[Lhs] * End) * Any[Rhs] * End) >>
[](auto& _) {
return create_from(_(Op)->type(), _(Op)) << _[Lhs] << _[Rhs];
},
Expand Down Expand Up @@ -94,6 +102,8 @@ PassDef grouping()
return create_from(For, _(For))
<< _(Key) << _(Value) << _(Op) << _(Block);
},
(T(While) << (T(Group) * COND[Op] * (T(Group) << T(Block)[Block]))) >>
[](auto& _) { return While << _(Op) << _(Block); },

T(Func)[Func]
<< ((T(Group) << End) *
Expand Down
23 changes: 17 additions & 6 deletions src/lang/passes/parse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ namespace verona::wf
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;
Group | Assign | If | Else | Block | For | Func | List | Return | While;

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

struct Indent
Expand All @@ -33,7 +35,7 @@ trieste::Parse parser()
indent->push_back({0, File});

auto update_indent = [indent](detail::Make& m, size_t this_indent) {
m.term({Assign, Return});
m.term({Eq, Neq, Assign, Return});

if (this_indent > indent->back().indent)
{
Expand Down Expand Up @@ -100,6 +102,7 @@ trieste::Parse parser()
m.term({List, Parens});
m.extend(Parens);
},
"," >> [](auto& m) { m.seq(List); },
"return\\b" >> [](auto& m) { m.seq(Return); },

"for\\b" >> [](auto& m) { m.seq(For); },
Expand All @@ -108,7 +111,11 @@ trieste::Parse parser()
// In should always be in a list from the identifiers.
m.term({List});
},
"," >> [](auto& m) { m.seq(List); },
"while\\b" >>
[](auto& m) {
m.term();
m.seq(While);
},

"if\\b" >>
[](auto& m) {
Expand Down Expand Up @@ -138,6 +145,10 @@ trieste::Parse parser()
{
toc = Func;
}
else if (m.in(While))
{
toc = While;
}
else
{
m.error("unexpected colon");
Expand Down
5 changes: 5 additions & 0 deletions src/rt/core/builtin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ namespace rt::core

return std::nullopt;
});

add_builtin("unreachable", [](auto, auto, auto) {
ui::error("this method should never be called");
return std::nullopt;
});
}

void init_builtins(ui::UI* ui)
Expand Down
23 changes: 18 additions & 5 deletions tests/exprs/ifs.vpy
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@

# Build a region
a = {}
a["self"] = a

f = {}
f.a = a

# Check if with else (This should be true)
if a == f.a:
b = f.a
c = {}
# Check empty lines in blocks:

drop b
drop c
else:
# Check parsing nested if's
if f.b == None:
e = {}
drop e
unreachable()

else:
d = {}
drop d
unreachable()

# Check identifier as the condition
cond = f.a != a
if cond:
unreachable()

# Check function call as the condition
def check():
c = False
# Check return condition result
return c == True
if check():
unreachable()

drop a
drop f
38 changes: 38 additions & 0 deletions tests/exprs/while.vpy
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
lst = {}
lst.next = {}
lst.next.next = {}
lst.next.next.next = {}

obj = lst
while obj != null:
obj = obj.next

# Check identifiers in the condition
cond = False
while cond == True:
unreachable()
while cond:
unreachable()

# Check field access in the condition
x = {}
x.bool = False
while x.bool == True:
unreachable()
while x.bool:
unreachable()

# Check function calls in the condition
def bool(self):
return False
while bool({}) == True:
unreachable()
while bool({}):
unreachable()

# Check method calls in the condition
x.bool = bool
while x.bool() == True:
unreachable()
while x.bool():
unreachable()
File renamed without changes.
60 changes: 60 additions & 0 deletions tests/list_while.vpy
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
def new_list():
def insert(self, item):
node = self.head
while node.next != null:
node = node.next

node.next = {}
node.next.data = item
node.next.prev = node

def contains(self, item):
node = self.head
while node.next != null:
if node.data == item:
return True
node = node.next
return False

def remove(self, item):
node = self.head
while node.next != null:
if node.data == item:
node.prev.next = node.next
if node.next != null:
node.next.prev = node.prev
return True
node = node.next
return False

list = {}
list.head = {}
list.insert = take insert
list.contains = take contains
list.remove = take remove
return list

# Dyrona doesn't freeze shared objects automatically (yet)
def freeze_and_hide_proto(value):
proto = value["__proto__"]
freeze(proto)
mermaid_hide(proto)
freeze_and_hide_proto(freeze_and_hide_proto)
freeze_and_hide_proto("string")
drop freeze_and_hide_proto

list = new_list()

# Required to not leak memory
region(list)

value = "x"

list.insert({})
list.insert({})
list.insert(value)
list.insert({})
list.remove(value)

drop value
drop list

0 comments on commit fd7ec03

Please sign in to comment.