Add ranged-for loops

closes [#145]
This commit is contained in:
Jason Turner 2016-06-26 20:00:01 -06:00
parent c31ebb5665
commit 09bdec4882
6 changed files with 166 additions and 9 deletions

View File

@ -111,6 +111,7 @@ chai.add_global_const(chaiscript::const_var(somevar), "somevar"); // global cons
chai.add_global(chaiscript::var(somevar), "somevar"); // global non-const, throws if object exists
chai.set_global(chaiscript::var(somevar), "somevar"); // global non-const, overwrites existing object
```
# Using STL
ChaiScript recognize many types from STL, but you have to add specific instantiation yourself.
@ -260,6 +261,23 @@ if (g2.is_var_undef()) { g2 = 4; } // only initialize g2 once, if global decl hi
GLOBAL g3; // all upper case version also accepted
```
## Looping
```
// c-style for loops
for (var i = 0; i < 100; ++i) { print(i); }
```
```
// while
while (some_condition()) { /* do something */ }
```
```
// ranged for
for (x : [1,2,3]) { print(i); }
```
## Built in Types
```

View File

@ -462,6 +462,26 @@ namespace chaiscript
add_object(name, std::move(obj));
}
/// Adds a named object to the current scope
/// \warning This version does not check the validity of the name
/// it is meant for internal use only
Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj, Stack_Holder &t_holder)
{
auto &stack_elem = get_stack_data(t_holder).back();
if (std::any_of(stack_elem.begin(), stack_elem.end(),
[&](const std::pair<std::string, Boxed_Value> &o) {
return o.first == t_name;
}))
{
throw chaiscript::exception::name_conflict_error(t_name);
}
stack_elem.emplace_back(t_name, std::move(obj));
return stack_elem.back().second;
}
/// Adds a named object to the current scope
/// \warning This version does not check the validity of the name
/// it is meant for internal use only
@ -477,7 +497,7 @@ namespace chaiscript
throw chaiscript::exception::name_conflict_error(t_name);
}
get_stack_data(t_holder).back().emplace_back(t_name, std::move(obj));
stack_elem.emplace_back(t_name, std::move(obj));
}
@ -1478,8 +1498,12 @@ namespace chaiscript
return m_conversions.saves();
}
Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj) const {
return m_engine.get().add_get_object(t_name, std::move(obj), m_stack_holder.get());
}
void add_object(const std::string &t_name, Boxed_Value obj) const {
m_engine.get().add_object(t_name, std::move(obj), m_stack_holder.get());
return m_engine.get().add_object(t_name, std::move(obj), m_stack_holder.get());
}
Boxed_Value get_object(const std::string &t_name, std::atomic_uint_fast32_t &t_loc) const {

View File

@ -58,7 +58,7 @@ namespace chaiscript
/// Types of AST nodes available to the parser and eval
enum class AST_Node_Type { Id, Fun_Call, Unused_Return_Fun_Call, Arg_List, Equation, Var_Decl,
Array_Call, Dot_Access,
Lambda, Block, Scopeless_Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range,
Lambda, Block, Scopeless_Block, Def, While, If, For, Ranged_For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Continue, Map_Pair, Value_Range,
Inline_Range, Try, Catch, Finally, Method, Attr_Decl,
Logical_And, Logical_Or, Reference, Switch, Case, Default, Ternary_Cond, Noop, Class, Binary, Arg, Global_Decl, Constant, Compiled
};
@ -71,7 +71,7 @@ namespace chaiscript
const char *ast_node_type_to_string(AST_Node_Type ast_node_type) {
static const char * const ast_node_types[] = { "Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl",
"Array_Call", "Dot_Access",
"Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range",
"Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range",
"Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl",
"Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Ternary Condition", "Noop", "Class", "Binary", "Arg", "Global_Decl", "Constant", "Compiled"};

View File

@ -844,6 +844,89 @@ namespace chaiscript
}
};
template<typename T>
struct Ranged_For_AST_Node final : AST_Node_Impl<T> {
Ranged_For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :
AST_Node_Impl<T>(std::move(t_ast_node_text), AST_Node_Type::Ranged_For, std::move(t_loc), std::move(t_children)),
m_range_loc(0),
m_empty_loc(0),
m_front_loc(0),
m_pop_front_loc(0)
{ assert(this->children.size() == 3); }
Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{
const auto get_function = [&t_ss](const std::string &t_name, auto &t_hint){
uint_fast32_t hint = t_hint;
auto funs = t_ss->get_function(t_name, hint);
if (funs.first != hint) t_hint = uint_fast32_t(funs.first);
return std::move(funs.second);
};
const auto call_function = [&t_ss](const auto &t_funcs, const Boxed_Value &t_param) {
return dispatch::dispatch(*t_funcs, {t_param}, t_ss.conversions());
};
const std::string &loop_var_name = this->children[0]->text;
Boxed_Value range_expression_result = this->children[1]->eval(t_ss);
const auto do_loop = [&loop_var_name, &t_ss, this](const auto &ranged_thing){
try {
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
Boxed_Value &obj = t_ss.add_get_object(loop_var_name, void_var());
for (auto loop_var : ranged_thing) {
obj = Boxed_Value(std::move(loop_var));
try {
this->children[2]->eval(t_ss);
} catch (detail::Continue_Loop &) {
}
}
} catch (detail::Break_Loop &) {
// loop broken
}
return void_var();
};
if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::vector<Boxed_Value>))) {
return do_loop(boxed_cast<const std::vector<Boxed_Value> &>(range_expression_result));
} else if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::map<std::string, Boxed_Value>))) {
return do_loop(boxed_cast<const std::map<std::string, Boxed_Value> &>(range_expression_result));
} else {
const auto range_funcs = get_function("range", m_range_loc);
const auto empty_funcs = get_function("empty", m_empty_loc);
const auto front_funcs = get_function("front", m_front_loc);
const auto pop_front_funcs = get_function("pop_front", m_pop_front_loc);
try {
const auto range_obj = call_function(range_funcs, range_expression_result);
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
Boxed_Value &obj = t_ss.add_get_object(loop_var_name, void_var());
while (!boxed_cast<bool>(call_function(empty_funcs, range_obj))) {
obj = call_function(front_funcs, range_obj);
try {
this->children[2]->eval(t_ss);
} catch (detail::Continue_Loop &) {
}
call_function(pop_front_funcs, range_obj);
}
} catch (detail::Break_Loop &) {
// loop broken
}
return void_var();
}
}
private:
mutable std::atomic_uint_fast32_t m_range_loc;
mutable std::atomic_uint_fast32_t m_empty_loc;
mutable std::atomic_uint_fast32_t m_front_loc;
mutable std::atomic_uint_fast32_t m_pop_front_loc;
};
template<typename T>
struct For_AST_Node final : AST_Node_Impl<T> {
For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector<AST_Node_Impl_Ptr<T>> t_children) :

View File

@ -1647,14 +1647,20 @@ namespace chaiscript
return retval;
}
/// Reads the ranged `for` conditions from input
bool Range_Expression() {
// the first element will have already been captured by the For_Guards() call that preceeds it
return Char(':') && Equation();
}
/// Reads the C-style for conditions from input
/// Reads the C-style `for` conditions from input
bool For_Guards() {
if (!(Equation() && Eol()))
{
if (!Eol())
{
throw exception::eval_error("'for' loop initial statment missing", File_Position(m_position.line, m_position.col), *m_filename);
return false;
} else {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
@ -1664,7 +1670,7 @@ namespace chaiscript
{
if (!Eol())
{
throw exception::eval_error("'for' loop condition missing", File_Position(m_position.line, m_position.col), *m_filename);
return false;
} else {
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
}
@ -1692,7 +1698,8 @@ namespace chaiscript
throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename);
}
if (!(For_Guards() && Char(')'))) {
const bool classic_for = For_Guards() && Char(')');
if (!classic_for && !(Range_Expression() && Char(')'))) {
throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename);
}
@ -1702,7 +1709,11 @@ namespace chaiscript
throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename);
}
build_match<eval::For_AST_Node<Tracer>>(prev_stack_top);
if (classic_for) {
build_match<eval::For_AST_Node<Tracer>>(prev_stack_top);
} else {
build_match<eval::Ranged_For_AST_Node<Tracer>>(prev_stack_top);
}
}
return retval;

21
unittests/ranged_for.chai Normal file
View File

@ -0,0 +1,21 @@
var sum = 0;
for (x : [1,2,3,4]) {
sum += x;
}
assert_equal(sum, 10)
var result = 0.0;
for (x : retro(range([1,2,3,4]))) {
if (result == 0.0) {
result = x;
} else {
result /= x;
}
}
assert_true(result > .6 && result < .7);