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.)
* C++14 lambdas can be used as commands (including captures and type inference)
* Error handling including recovery (protected call system similar to Lua)
* Loop control statements (`break` and `continue`)
* No manual memory mangement, values manage themselves
* Clean codebase that is easy to read and contribute to
* 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)
* Custom allocator support (control over how heap memory is allocated)
* Coroutines
* Loop control statements (`break` and `continue`)
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

View File

@ -351,6 +351,10 @@ private:
struct CsErrorException;
struct CsSharedState;
enum class CsLoopState {
Normal = 0, Break, Continue
};
struct OSTD_EXPORT CsState {
friend struct CsErrorException;
@ -505,6 +509,13 @@ struct OSTD_EXPORT CsState {
void run(ostd::ConstCharRange code);
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<CsInt> run_file_int(ostd::ConstCharRange fname);
ostd::Maybe<CsFloat> run_file_float(ostd::ConstCharRange fname);
@ -551,10 +562,15 @@ struct OSTD_EXPORT CsState {
private:
CsIdent *add_ident(CsIdent *id);
int p_inloop = 0;
CsAllocCb p_allocf;
void *p_aptr;
char p_errbuf[512];
CsHookCb p_callhook;
CsStream *p_out, *p_err;
};

View File

@ -1750,6 +1750,28 @@ void CsState::run(CsIdent *id, CsValueRange args) {
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(
CsState &cs, ostd::ConstCharRange fname, CsValue &ret
) {

View File

@ -94,6 +94,12 @@ struct CsSharedState {
CsVector<CsIdent *> identmap;
};
struct CsBreakException {
};
struct CsContinueException {
};
template<typename T>
constexpr ostd::Size CsTypeStorageSize =
(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)) {
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(
@ -1056,13 +1063,21 @@ static inline void cs_loop_conc(
idv.set_int(offset + i * step);
idv.push();
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());
if (space && i) {
s.push(' ');
}
s.push_n(vstr.data(), vstr.size());
}
end:
s.push('\0');
ostd::Size len = s.size() - 1;
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) {
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", [](
@ -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) {
if (args[0].get_bool()) {
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();
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", [](

View File

@ -86,10 +86,18 @@ static void cs_loop_list_conc(
r.push(' ');
}
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());
r.push_n(vstr.data(), vstr.size());
}
end:
r.push('\0');
ostd::Size len = r.size();
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) {
idv.set_mstr(p.element().disown());
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", [](
@ -329,8 +344,15 @@ void cs_init_lib_list(CsState &gcs) {
idv2.set_mstr(p.parse() ? p.element().disown() : cs_dup_ostr(""));
idv1.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", [](
@ -351,8 +373,15 @@ void cs_init_lib_list(CsState &gcs) {
idv1.push();
idv2.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", [](