Add support for switch/case/default statements.
This commit is contained in:
parent
af1e02b0bb
commit
4e14a57016
@ -23,7 +23,7 @@ namespace chaiscript
|
||||
Comparison, Addition, Subtraction, Multiplication, Division, Modulus, Array_Call, Dot_Access, Quoted_String, Single_Quoted_String,
|
||||
Lambda, Block, Def, While, If, For, Inline_Array, Inline_Map, Return, File, Prefix, Break, Map_Pair, Value_Range,
|
||||
Inline_Range, Annotation, Try, Catch, Finally, Method, Attr_Decl, Shift, Equality, Bitwise_And, Bitwise_Xor, Bitwise_Or,
|
||||
Logical_And, Logical_Or
|
||||
Logical_And, Logical_Or, Switch, Case, Default
|
||||
};
|
||||
};
|
||||
|
||||
@ -37,7 +37,7 @@ namespace chaiscript
|
||||
"Comparison", "Addition", "Subtraction", "Multiplication", "Division", "Modulus", "Array_Call", "Dot_Access", "Quoted_String", "Single_Quoted_String",
|
||||
"Lambda", "Block", "Def", "While", "If", "For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Map_Pair", "Value_Range",
|
||||
"Inline_Range", "Annotation", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Shift", "Equality", "Bitwise_And", "Bitwise_Xor", "Bitwise_Or",
|
||||
"Logical_And", "Logical_Or"};
|
||||
"Logical_And", "Logical_Or", "Switch", "Case", "Default"};
|
||||
|
||||
return ast_node_types[ast_node_type];
|
||||
}
|
||||
|
@ -780,6 +780,79 @@ namespace chaiscript
|
||||
|
||||
};
|
||||
|
||||
struct Switch_AST_Node : public AST_Node {
|
||||
public:
|
||||
Switch_AST_Node(const std::string &t_ast_node_text = "", int t_id = AST_Node_Type::Switch, const boost::shared_ptr<std::string> &t_fname=boost::shared_ptr<std::string>(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) :
|
||||
AST_Node(t_ast_node_text, t_id, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { }
|
||||
virtual ~Switch_AST_Node() {}
|
||||
virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) {
|
||||
Boxed_Value match_value;
|
||||
bool breaking = false;
|
||||
int currentCase = 1;
|
||||
bool hasMatched = false;
|
||||
|
||||
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
||||
|
||||
match_value = this->children[0]->eval(t_ss);
|
||||
|
||||
while (!breaking) {
|
||||
try {
|
||||
if (this->children[currentCase]->identifier == AST_Node_Type::Case) {
|
||||
//This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here.
|
||||
try {
|
||||
if (hasMatched || boxed_cast<bool>(t_ss.call_function("==", match_value, this->children[currentCase]->children[0]->eval(t_ss)))) {
|
||||
this->children[currentCase]->eval(t_ss);
|
||||
hasMatched = true;
|
||||
}
|
||||
}
|
||||
catch (const exception::bad_boxed_cast &) {
|
||||
throw exception::eval_error("Internal error: case guard evaluation not boolean");
|
||||
}
|
||||
}
|
||||
else if (this->children[currentCase]->identifier == AST_Node_Type::Default) {
|
||||
this->children[currentCase]->eval(t_ss);
|
||||
breaking = true;
|
||||
}
|
||||
}
|
||||
catch (detail::Break_Loop &) {
|
||||
breaking = true;
|
||||
}
|
||||
++currentCase;
|
||||
if (currentCase == this->children.size())
|
||||
breaking = true;
|
||||
}
|
||||
return Boxed_Value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Case_AST_Node : public AST_Node {
|
||||
public:
|
||||
Case_AST_Node(const std::string &t_ast_node_text = "", int t_id = AST_Node_Type::Case, const boost::shared_ptr<std::string> &t_fname=boost::shared_ptr<std::string>(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) :
|
||||
AST_Node(t_ast_node_text, t_id, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { }
|
||||
virtual ~Case_AST_Node() {}
|
||||
virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) {
|
||||
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
||||
|
||||
this->children[1]->eval(t_ss);
|
||||
|
||||
return Boxed_Value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Default_AST_Node : public AST_Node {
|
||||
public:
|
||||
Default_AST_Node(const std::string &t_ast_node_text = "", int t_id = AST_Node_Type::Default, const boost::shared_ptr<std::string> &t_fname=boost::shared_ptr<std::string>(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) :
|
||||
AST_Node(t_ast_node_text, t_id, t_fname, t_start_line, t_start_col, t_end_line, t_end_col) { }
|
||||
virtual ~Default_AST_Node() {}
|
||||
virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) {
|
||||
chaiscript::eval::detail::Scope_Push_Pop spp(t_ss);
|
||||
|
||||
this->children[0]->eval(t_ss);
|
||||
|
||||
return Boxed_Value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Inline_Array_AST_Node : public AST_Node {
|
||||
public:
|
||||
Inline_Array_AST_Node(const std::string &t_ast_node_text = "", int t_id = AST_Node_Type::Inline_Array, const boost::shared_ptr<std::string> &t_fname=boost::shared_ptr<std::string>(), int t_start_line = 0, int t_start_col = 0, int t_end_line = 0, int t_end_col = 0) :
|
||||
|
@ -1415,6 +1415,92 @@ namespace chaiscript
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a case block from input
|
||||
*/
|
||||
bool Case() {
|
||||
bool retval = false;
|
||||
|
||||
size_t prev_stack_top = m_match_stack.size();
|
||||
|
||||
if (Keyword("case")) {
|
||||
retval = true;
|
||||
|
||||
if (!Char('(')) {
|
||||
throw exception::eval_error("Incomplete 'case' expression", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
if (!(Operator() && Char(')'))) {
|
||||
throw exception::eval_error("Incomplete 'case' expression", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
while (Eol()) {}
|
||||
|
||||
if (!Block()) {
|
||||
throw exception::eval_error("Incomplete 'case' block", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
build_match(AST_NodePtr(new eval::Case_AST_Node()), prev_stack_top);
|
||||
}
|
||||
else if (Keyword("default")) {
|
||||
while (Eol()) {}
|
||||
|
||||
if (!Block()) {
|
||||
throw exception::eval_error("Incomplete 'default' block", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
build_match(AST_NodePtr(new eval::Default_AST_Node()), prev_stack_top);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a switch statement from input
|
||||
*/
|
||||
bool Switch() {
|
||||
bool retval = false;
|
||||
|
||||
size_t prev_stack_top = m_match_stack.size();
|
||||
|
||||
if (Keyword("switch")) {
|
||||
retval = true;
|
||||
|
||||
if (!Char('(')) {
|
||||
throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
if (!(Operator() && Char(')'))) {
|
||||
throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
while (Eol()) {}
|
||||
|
||||
if (Char('{')) {
|
||||
retval = true;
|
||||
|
||||
while (Eol()) {}
|
||||
|
||||
while (Case()) {
|
||||
while (Eol());
|
||||
}
|
||||
|
||||
while (Eol());
|
||||
|
||||
if (!Char('}')) {
|
||||
throw exception::eval_error("Incomplete block", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw exception::eval_error("Incomplete block", File_Position(m_line, m_col), *m_filename);
|
||||
}
|
||||
|
||||
build_match(AST_NodePtr(new eval::Switch_AST_Node()), prev_stack_top);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a curly-brace C-style block from input
|
||||
*/
|
||||
@ -1928,6 +2014,14 @@ namespace chaiscript
|
||||
retval = true;
|
||||
saw_eol = true;
|
||||
}
|
||||
else if (Switch()) {
|
||||
if (!saw_eol) {
|
||||
throw exception::eval_error("Two function definitions missing line separator", File_Position(prev_line, prev_col), *m_filename);
|
||||
}
|
||||
has_more = true;
|
||||
retval = true;
|
||||
saw_eol = true;
|
||||
}
|
||||
else if (Return()) {
|
||||
if (!saw_eol) {
|
||||
throw exception::eval_error("Two expressions missing line separator", File_Position(prev_line, prev_col), *m_filename);
|
||||
|
22
unittests/switch_break.chai
Normal file
22
unittests/switch_break.chai
Normal file
@ -0,0 +1,22 @@
|
||||
var total = 0;
|
||||
|
||||
switch(2) {
|
||||
case (1) {
|
||||
total += 1;
|
||||
break;
|
||||
}
|
||||
case (2) {
|
||||
total += 2;
|
||||
break;
|
||||
}
|
||||
case (3) {
|
||||
total += 4;
|
||||
break;
|
||||
}
|
||||
case (4) {
|
||||
total += 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal(total, 2)
|
18
unittests/switch_default.chai
Normal file
18
unittests/switch_default.chai
Normal file
@ -0,0 +1,18 @@
|
||||
var total = 0;
|
||||
|
||||
switch(2) {
|
||||
case (1) {
|
||||
total += 1;
|
||||
}
|
||||
case (3) {
|
||||
total += 4;
|
||||
}
|
||||
case (4) {
|
||||
total += 8;
|
||||
}
|
||||
default {
|
||||
total += 16;
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal(total, 16)
|
18
unittests/switch_fallthru.chai
Normal file
18
unittests/switch_fallthru.chai
Normal file
@ -0,0 +1,18 @@
|
||||
var total = 0;
|
||||
|
||||
switch(2) {
|
||||
case (1) {
|
||||
total += 1;
|
||||
}
|
||||
case (2) {
|
||||
total += 2;
|
||||
}
|
||||
case (3) {
|
||||
total += 4;
|
||||
}
|
||||
case (4) {
|
||||
total += 8;
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal(total, 14);
|
19
unittests/switch_fallthru_and_break.chai
Normal file
19
unittests/switch_fallthru_and_break.chai
Normal file
@ -0,0 +1,19 @@
|
||||
var total = 0;
|
||||
|
||||
switch(2) {
|
||||
case (1) {
|
||||
total += 1;
|
||||
}
|
||||
case (2) {
|
||||
total += 2;
|
||||
}
|
||||
case (3) {
|
||||
total += 4;
|
||||
break;
|
||||
}
|
||||
case (4) {
|
||||
total += 8;
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal(total, 6)
|
Loading…
x
Reference in New Issue
Block a user