parent
c31ebb5665
commit
09bdec4882
@ -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
|
||||
|
||||
```
|
||||
|
@ -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 {
|
||||
|
@ -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"};
|
||||
|
||||
|
@ -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) :
|
||||
|
@ -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
21
unittests/ranged_for.chai
Normal 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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user