forked from OctaForge/libcubescript
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.)
|
* 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
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
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);
|
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
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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", [](
|
||||||
|
|
|
@ -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", [](
|
||||||
|
|
Loading…
Reference in New Issue