add loop control (break and continue)
parent
b20eb94a9e
commit
c5772f0720
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
22
src/cs_vm.cc
22
src/cs_vm.cc
|
@ -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
|
||||
) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1039,9 +1039,16 @@ 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(
|
||||
CsState &cs, CsValue &res, CsIdent &id, CsInt offset, CsInt n,
|
||||
|
@ -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", [](
|
||||
|
|
|
@ -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", [](
|
||||
|
|
Loading…
Reference in New Issue