Skip to content

Commit

Permalink
statement_sequence,air_node: Generalize for-each loops
Browse files Browse the repository at this point in the history
Closes #135.
  • Loading branch information
lhmouse committed Oct 23, 2023
1 parent 74eac5b commit f670491
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 38 deletions.
31 changes: 16 additions & 15 deletions asteria/compiler/statement_sequence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -927,25 +927,27 @@ do_accept_for_complement_range_opt(Token_Stream& tstrm, const Source_Location& o
Scope_Flags scfl)
{
// for-complement-range ::=
// "each" identifier "," identifier "->" expression ")" statement
// "each" identifier ( ( "," | ":" | "=" ) identifier ) ? "->" expression ")" statement
auto qkwrd = do_accept_keyword_opt(tstrm, { keyword_each });
if(!qkwrd)
return nullopt;

auto qkname = do_accept_identifier_opt(tstrm, true);
if(!qkname)
cow_string key;
auto qmapped = do_accept_identifier_opt(tstrm, true);
if(!qmapped)
throw Compiler_Error(Compiler_Error::M_status(),
compiler_status_identifier_expected, tstrm.next_sloc());

auto kpunct = do_accept_punctuator_opt(tstrm, { punctuator_comma });
if(!kpunct)
throw Compiler_Error(Compiler_Error::M_status(),
compiler_status_comma_expected, tstrm.next_sloc());

auto qvname = do_accept_identifier_opt(tstrm, true);
if(!qvname)
throw Compiler_Error(Compiler_Error::M_status(),
compiler_status_identifier_expected, tstrm.next_sloc());
auto kpunct = do_accept_punctuator_opt(tstrm, { punctuator_comma, punctuator_colon, punctuator_assign });
if(kpunct) {
// Move the first identifier into the key and expect the name for mapped
// references.
key = ::std::move(*qmapped);
qmapped = do_accept_identifier_opt(tstrm, true);
if(!qmapped)
throw Compiler_Error(Compiler_Error::M_status(),
compiler_status_identifier_expected, tstrm.next_sloc());
}

kpunct = do_accept_punctuator_opt(tstrm, { punctuator_arrow });
if(!kpunct)
Expand All @@ -969,9 +971,8 @@ do_accept_for_complement_range_opt(Token_Stream& tstrm, const Source_Location& o
throw Compiler_Error(Compiler_Error::M_status(),
compiler_status_statement_expected, tstrm.next_sloc());

Statement::S_for_each xstmt = { ::std::move(*qkname), ::std::move(*qvname),
::std::move(sloc_init), ::std::move(*qinit),
::std::move(*qblock) };
Statement::S_for_each xstmt = { ::std::move(key), ::std::move(*qmapped), ::std::move(sloc_init),
::std::move(*qinit), ::std::move(*qblock) };
return ::std::move(xstmt);
}

Expand Down
45 changes: 23 additions & 22 deletions asteria/runtime/air_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,8 @@ rebind_opt(Abstract_Context& ctx) const
S_for_each_statement bound = altr;

Analytic_Context ctx_for(Analytic_Context::M_plain(), ctx);
ctx_for.insert_named_reference(altr.name_key);
if(!altr.name_key.empty())
ctx_for.insert_named_reference(altr.name_key);
ctx_for.insert_named_reference(altr.name_mapped);
do_rebind_nodes(dirty, bound.code_init, ctx_for);

Expand Down Expand Up @@ -1123,9 +1124,8 @@ solidify(AVMC_Queue& queue) const
Executive_Context ctx_for(Executive_Context::M_plain(), ctx);

// Create key and mapped references.
auto& key = ctx_for.insert_named_reference(sp.name_key);
auto& mapped = ctx_for.insert_named_reference(sp.name_mapped);
refcnt_ptr<Variable> kvar;
auto& mapped = ctx_for.insert_named_reference(sp.name_mapped);

// Evaluate the range initializer and set the range up, which isn't
// going to change for all loops.
Expand All @@ -1140,19 +1140,21 @@ solidify(AVMC_Queue& queue) const
}
else if(range.is_array()) {
const auto& arr = range.as_array();
mapped.push_modifier(Reference_Modifier::S_array_head()); // placeholder
for(int64_t i = 0; i < arr.ssize(); ++i) {
// Set the key variable which is the subscript of the mapped
// element in the array.
if(!kvar) {
kvar = ctx.global().garbage_collector()->create_variable();
key.set_variable(kvar);
if(!sp.name_key.empty()) {
if(!kvar) {
kvar = ctx.global().garbage_collector()->create_variable();
ctx_for.insert_named_reference(sp.name_key).set_variable(kvar);
}
kvar->initialize(i);
kvar->set_immutable();
}
else
mapped.pop_modifier();

kvar->initialize(i);
kvar->set_immutable();

// Set the mapped reference.
mapped.pop_modifier();
Reference_Modifier::S_array_index xmod = { i };
mapped.push_modifier(::std::move(xmod));
mapped.dereference_readonly();
Expand All @@ -1171,19 +1173,21 @@ solidify(AVMC_Queue& queue) const
}
else if(range.is_object()) {
const auto& obj = range.as_object();
mapped.push_modifier(Reference_Modifier::S_array_head()); // placeholder
for(auto it = obj.begin(); it != obj.end(); ++it) {
// Set the key variable which is the name of the mapped element
// in the object.
if(!kvar) {
kvar = ctx.global().garbage_collector()->create_variable();
key.set_variable(kvar);
if(!sp.name_key.empty()) {
if(!kvar) {
kvar = ctx.global().garbage_collector()->create_variable();
ctx_for.insert_named_reference(sp.name_key).set_variable(kvar);
}
kvar->initialize(it->first.rdstr());
kvar->set_immutable();
}
else
mapped.pop_modifier();

kvar->initialize(it->first.rdstr());
kvar->set_immutable();

// Set the mapped reference.
mapped.pop_modifier();
Reference_Modifier::S_object_key xmod = { it->first };
mapped.push_modifier(::std::move(xmod));
mapped.dereference_readonly();
Expand All @@ -1197,9 +1201,6 @@ solidify(AVMC_Queue& queue) const
else if(::rocket::is_none_of(status, { air_status_next, air_status_continue_unspec,
air_status_continue_for }))
break;

// Restore the mapped reference.
mapped.pop_modifier();
}
return status;
}
Expand Down
2 changes: 1 addition & 1 deletion doc/syntax.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ for-complement ::=
for-complement-range | for-complement-triplet

for-complement-range ::=
"each" identifier "," identifier "->" expression ")" statement
"each" identifier ( ( "," | ":" | "=" ) identifier ) ? "->" expression ")" statement

for-complement-triplet ::=
for-initializer expression ? ";" expression ? ")" statement
Expand Down
1 change: 1 addition & 0 deletions test/Makefile.inc.am
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ check_PROGRAMS += \
%reldir%/ptc_hooks_throw.test \
%reldir%/ptc_hooks_return.test \
%reldir%/switch_defer.test \
%reldir%/for_each.test \
${END}

EXTRA_DIST += \
Expand Down
40 changes: 40 additions & 0 deletions test/for_each.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// This file is part of Asteria.
// Copyleft 2018 - 2023, LH_Mouse. All wrongs reserved.

#include "utils.hpp"
#include "../asteria/simple_script.hpp"
using namespace ::asteria;

int main()
{
Simple_Script code;
code.reload_string(
sref(__FILE__), __LINE__, sref(R"__(
///////////////////////////////////////////////////////////////////////////////
var output;
output = "";
for(each k, v -> ["a","b","c"])
output += std.string.format("$1=$2;", k, v);
assert output == "0=a;1=b;2=c;";
output = "";
for(each k: v -> ["a","b","c"])
output += std.string.format("$1=$2;", k, v);
assert output == "0=a;1=b;2=c;";
output = "";
for(each k = v -> ["a","b","c"])
output += std.string.format("$1=$2;", k, v);
assert output == "0=a;1=b;2=c;";
output = "";
for(each v -> ["a","b","c"])
output += std.string.format("$1;", v);
assert output == "a;b;c;";
///////////////////////////////////////////////////////////////////////////////
)__"));
code.execute();
}

0 comments on commit f670491

Please sign in to comment.