add loop control (break and continue)

master
Daniel Kolesa 2016-09-14 23:24:13 +02:00
parent b20eb94a9e
commit c5772f0720
6 changed files with 120 additions and 9 deletions

View File

@ -30,6 +30,7 @@ advantages, including:
* Modern C++14 API (no macros, strongly typed enums, lambdas, ranges etc.) * Modern C++14 API (no macros, strongly typed enums, lambdas, ranges etc.)
* C++14 lambdas can be used as commands (including captures and type inference) * C++14 lambdas can be used as commands (including captures and type inference)
* Error handling including recovery (protected call system similar to Lua) * Error handling including recovery (protected call system similar to Lua)
* Loop control statements (`break` and `continue`)
* No manual memory mangement, values manage themselves * No manual memory mangement, values manage themselves
* Clean codebase that is easy to read and contribute to * Clean codebase that is easy to read and contribute to
* Support for arbitrary size integers and floats (can be set at compile time) * Support for arbitrary size integers and floats (can be set at compile time)
@ -40,7 +41,6 @@ There are some features that are a work in progress and will come later:
* A degree of thread safety (see below) * A degree of thread safety (see below)
* Custom allocator support (control over how heap memory is allocated) * Custom allocator support (control over how heap memory is allocated)
* Coroutines * Coroutines
* Loop control statements (`break` and `continue`)
The API is currently very unstable, as is the actual codebase. Therefore you The API is currently very unstable, as is the actual codebase. Therefore you
should not use the project in production environments just yet, but you're should not use the project in production environments just yet, but you're

View File

@ -351,6 +351,10 @@ private:
struct CsErrorException; struct CsErrorException;
struct CsSharedState; struct CsSharedState;
enum class CsLoopState {
Normal = 0, Break, Continue
};
struct OSTD_EXPORT CsState { struct OSTD_EXPORT CsState {
friend struct CsErrorException; friend struct CsErrorException;
@ -505,6 +509,13 @@ struct OSTD_EXPORT CsState {
void run(ostd::ConstCharRange code); void run(ostd::ConstCharRange code);
void run(CsIdent *id, CsValueRange args); void run(CsIdent *id, CsValueRange args);
CsLoopState run_loop(CsBytecode *code, CsValue &ret);
CsLoopState run_loop(CsBytecode *code);
bool is_in_loop() const {
return p_inloop;
}
ostd::Maybe<CsString> run_file_str(ostd::ConstCharRange fname); ostd::Maybe<CsString> run_file_str(ostd::ConstCharRange fname);
ostd::Maybe<CsInt> run_file_int(ostd::ConstCharRange fname); ostd::Maybe<CsInt> run_file_int(ostd::ConstCharRange fname);
ostd::Maybe<CsFloat> run_file_float(ostd::ConstCharRange fname); ostd::Maybe<CsFloat> run_file_float(ostd::ConstCharRange fname);
@ -551,10 +562,15 @@ struct OSTD_EXPORT CsState {
private: private:
CsIdent *add_ident(CsIdent *id); CsIdent *add_ident(CsIdent *id);
int p_inloop = 0;
CsAllocCb p_allocf; CsAllocCb p_allocf;
void *p_aptr; void *p_aptr;
char p_errbuf[512]; char p_errbuf[512];
CsHookCb p_callhook; CsHookCb p_callhook;
CsStream *p_out, *p_err; CsStream *p_out, *p_err;
}; };

View File

@ -1750,6 +1750,28 @@ void CsState::run(CsIdent *id, CsValueRange args) {
run(id, args, ret); run(id, args, ret);
} }
CsLoopState CsState::run_loop(CsBytecode *code, CsValue &ret) {
++p_inloop;
try {
run(code, ret);
} catch (CsBreakException) {
--p_inloop;
return CsLoopState::Break;
} catch (CsContinueException) {
--p_inloop;
return CsLoopState::Continue;
} catch (...) {
--p_inloop;
throw;
}
return CsLoopState::Normal;
}
CsLoopState CsState::run_loop(CsBytecode *code) {
CsValue ret;
return run_loop(code, ret);
}
static bool cs_run_file( static bool cs_run_file(
CsState &cs, ostd::ConstCharRange fname, CsValue &ret CsState &cs, ostd::ConstCharRange fname, CsValue &ret
) { ) {

View File

@ -94,6 +94,12 @@ struct CsSharedState {
CsVector<CsIdent *> identmap; CsVector<CsIdent *> identmap;
}; };
struct CsBreakException {
};
struct CsContinueException {
};
template<typename T> template<typename T>
constexpr ostd::Size CsTypeStorageSize = constexpr ostd::Size CsTypeStorageSize =
(sizeof(T) - 1) / sizeof(ostd::Uint32) + 1; (sizeof(T) - 1) / sizeof(ostd::Uint32) + 1;

View File

@ -1039,8 +1039,15 @@ static inline void cs_do_loop(
if (cond && !cs.run_bool(cond)) { if (cond && !cs.run_bool(cond)) {
break; break;
} }
cs.run_int(body); switch (cs.run_loop(body)) {
case CsLoopState::Break:
goto end;
default: /* continue and normal */
break;
}
} }
end:
return;
} }
static inline void cs_loop_conc( static inline void cs_loop_conc(
@ -1056,13 +1063,21 @@ static inline void cs_loop_conc(
idv.set_int(offset + i * step); idv.set_int(offset + i * step);
idv.push(); idv.push();
CsValue v; CsValue v;
cs.run(body, v); switch (cs.run_loop(body, v)) {
case CsLoopState::Break:
goto end;
case CsLoopState::Continue:
continue;
default:
break;
}
CsString vstr = ostd::move(v.get_str()); CsString vstr = ostd::move(v.get_str());
if (space && i) { if (space && i) {
s.push(' '); s.push(' ');
} }
s.push_n(vstr.data(), vstr.size()); s.push_n(vstr.data(), vstr.size());
} }
end:
s.push('\0'); s.push('\0');
ostd::Size len = s.size() - 1; ostd::Size len = s.size() - 1;
res.set_mstr(ostd::CharRange(s.disown(), len)); res.set_mstr(ostd::CharRange(s.disown(), len));
@ -1070,7 +1085,7 @@ static inline void cs_loop_conc(
void cs_init_lib_base(CsState &gcs) { void cs_init_lib_base(CsState &gcs) {
gcs.new_command("error", "s", [](CsState &cs, CsValueRange args, CsValue &) { gcs.new_command("error", "s", [](CsState &cs, CsValueRange args, CsValue &) {
throw cscript::CsErrorException(cs, args[0].get_strr()); throw CsErrorException(cs, args[0].get_strr());
}); });
gcs.new_command("pcall", "err", []( gcs.new_command("pcall", "err", [](
@ -1102,6 +1117,22 @@ void cs_init_lib_base(CsState &gcs) {
} }
}); });
gcs.new_command("break", "", [](CsState &cs, auto, auto &) {
if (cs.is_in_loop()) {
throw CsBreakException();
} else {
throw CsErrorException(cs, "no loop to break");
}
});
gcs.new_command("continue", "", [](CsState &cs, auto, auto &) {
if (cs.is_in_loop()) {
throw CsContinueException();
} else {
throw CsErrorException(cs, "no loop to continue");
}
});
gcs.new_command("?", "tTT", [](CsState &, CsValueRange args, CsValue &res) { gcs.new_command("?", "tTT", [](CsState &, CsValueRange args, CsValue &res) {
if (args[0].get_bool()) { if (args[0].get_bool()) {
res = ostd::move(args[1]); res = ostd::move(args[1]);
@ -1262,8 +1293,15 @@ void cs_init_lib_base(CsState &gcs) {
) { ) {
CsBytecode *cond = args[0].get_code(), *body = args[1].get_code(); CsBytecode *cond = args[0].get_code(), *body = args[1].get_code();
while (cs.run_bool(cond)) { while (cs.run_bool(cond)) {
cs.run_int(body); switch (cs.run_loop(body)) {
case CsLoopState::Break:
goto end;
default: /* continue and normal */
break;
}
} }
end:
return;
}); });
gcs.new_command("loopconcat", "rie", []( gcs.new_command("loopconcat", "rie", [](

View File

@ -86,10 +86,18 @@ static void cs_loop_list_conc(
r.push(' '); r.push(' ');
} }
CsValue v; CsValue v;
cs.run(body, v); switch (cs.run_loop(body, v)) {
case CsLoopState::Break:
goto end;
case CsLoopState::Continue:
continue;
default:
break;
}
CsString vstr = ostd::move(v.get_str()); CsString vstr = ostd::move(v.get_str());
r.push_n(vstr.data(), vstr.size()); r.push_n(vstr.data(), vstr.size());
} }
end:
r.push('\0'); r.push('\0');
ostd::Size len = r.size(); ostd::Size len = r.size();
res.set_mstr(ostd::CharRange(r.disown(), len - 1)); res.set_mstr(ostd::CharRange(r.disown(), len - 1));
@ -311,8 +319,15 @@ void cs_init_lib_list(CsState &gcs) {
for (util::ListParser p(args[1].get_strr()); p.parse(); ++n) { for (util::ListParser p(args[1].get_strr()); p.parse(); ++n) {
idv.set_mstr(p.element().disown()); idv.set_mstr(p.element().disown());
idv.push(); idv.push();
cs.run_int(body); switch (cs.run_loop(body)) {
case CsLoopState::Break:
goto end;
default: /* continue and normal */
break;
}
} }
end:
return;
}); });
gcs.new_command("looplist2", "rrse", []( gcs.new_command("looplist2", "rrse", [](
@ -329,8 +344,15 @@ void cs_init_lib_list(CsState &gcs) {
idv2.set_mstr(p.parse() ? p.element().disown() : cs_dup_ostr("")); idv2.set_mstr(p.parse() ? p.element().disown() : cs_dup_ostr(""));
idv1.push(); idv1.push();
idv2.push(); idv2.push();
cs.run_int(body); switch (cs.run_loop(body)) {
case CsLoopState::Break:
goto end;
default: /* continue and normal */
break;
}
} }
end:
return;
}); });
gcs.new_command("looplist3", "rrrse", []( gcs.new_command("looplist3", "rrrse", [](
@ -351,8 +373,15 @@ void cs_init_lib_list(CsState &gcs) {
idv1.push(); idv1.push();
idv2.push(); idv2.push();
idv3.push(); idv3.push();
cs.run_int(body); switch (cs.run_loop(body)) {
case CsLoopState::Break:
goto end;
default: /* continue and normal */
break;
}
} }
end:
return;
}); });
gcs.new_command("looplistconcat", "rse", []( gcs.new_command("looplistconcat", "rse", [](