@@ -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.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
|
chai.set_global(chaiscript::var(somevar), "somevar"); // global non-const, overwrites existing object
|
||||||
```
|
```
|
||||||
|
|
||||||
# Using STL
|
# Using STL
|
||||||
ChaiScript recognize many types from STL, but you have to add specific instantiation yourself.
|
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
|
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
|
## Built in Types
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@@ -462,6 +462,26 @@ namespace chaiscript
|
|||||||
add_object(name, std::move(obj));
|
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
|
/// Adds a named object to the current scope
|
||||||
/// \warning This version does not check the validity of the name
|
/// \warning This version does not check the validity of the name
|
||||||
/// it is meant for internal use only
|
/// it is meant for internal use only
|
||||||
@@ -477,7 +497,7 @@ namespace chaiscript
|
|||||||
throw chaiscript::exception::name_conflict_error(t_name);
|
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();
|
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 {
|
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 {
|
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
|
/// 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,
|
enum class AST_Node_Type { Id, Fun_Call, Unused_Return_Fun_Call, Arg_List, Equation, Var_Decl,
|
||||||
Array_Call, Dot_Access,
|
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,
|
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
|
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) {
|
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",
|
static const char * const ast_node_types[] = { "Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl",
|
||||||
"Array_Call", "Dot_Access",
|
"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",
|
"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"};
|
"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>
|
template<typename T>
|
||||||
struct For_AST_Node final : AST_Node_Impl<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) :
|
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;
|
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() {
|
bool For_Guards() {
|
||||||
if (!(Equation() && Eol()))
|
if (!(Equation() && Eol()))
|
||||||
{
|
{
|
||||||
if (!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 {
|
} else {
|
||||||
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
|
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())
|
if (!Eol())
|
||||||
{
|
{
|
||||||
throw exception::eval_error("'for' loop condition missing", File_Position(m_position.line, m_position.col), *m_filename);
|
return false;
|
||||||
} else {
|
} else {
|
||||||
m_match_stack.push_back(chaiscript::make_shared<eval::AST_Node_Impl<Tracer>, eval::Noop_AST_Node<Tracer>>());
|
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);
|
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);
|
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);
|
throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (classic_for) {
|
||||||
build_match<eval::For_AST_Node<Tracer>>(prev_stack_top);
|
build_match<eval::For_AST_Node<Tracer>>(prev_stack_top);
|
||||||
|
} else {
|
||||||
|
build_match<eval::Ranged_For_AST_Node<Tracer>>(prev_stack_top);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
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);
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user