drop libostd requirement entirely

master
Daniel Kolesa 2021-03-20 08:22:15 +01:00
parent cb926a5750
commit 82d366366e
11 changed files with 95 additions and 96 deletions

View File

@ -27,8 +27,8 @@ advantages, including:
* Independent implementation (can be embedded in any project)
* No global state (multiple CubeScripts in a single program)
* Modern C++17 API (no macros, use of strongly typed enums, lambdas, ranges etc.)
* C++17 lambdas can be used as commands (including captures and type inference)
* Modern C++20 API
* C++ lambdas can be used as commands (including captures and type inference)
* Error handling including recovery (protected call system similar to Lua)
* Stricter parsing (strings cannot be left unfinished etc.)
* Loop control statements (`break` and `continue`)
@ -70,13 +70,8 @@ utilized in the outside native code.
## Building and usage
The only dependency is libostd:
https://git.octaforge.org/OctaForge/libostd
https://github.com/OctaForge/libostd
If libostd can work on your system, so can libcubescript.
There are no dependencies (other than a suitable compiler and the standard
library).
Libostd is built using Meson. Therefore, you need to install Meson and then
you can compile it as usual. Typically, this will be something like
@ -84,16 +79,17 @@ you can compile it as usual. Typically, this will be something like
~~~
mkdir build && cd build
meson ..
meson compile
ninja all
~~~
Link the libcubescript library together with your application and everything should just work.
It also builds the REPL.
Link the libcubescript library together with your application and everything
should just work. It also builds the REPL.
The project also bundles the linenoise line editing library which has been modified
to compile cleanly as C++ (with the same flags as libcubescript). It's used strictly
for the REPL only (you don't need it to build libcubescript itself). The version
in the repository tracks Git revision https://github.com/antirez/linenoise/commit/c894b9e59f02203dbe4e2be657572cf88c4230c3.
The project also bundles the linenoise line editing library which has been
modified to compile cleanly as C++ (with the same flags as libcubescript).
It's used strictly for the REPL only (you don't need it to build libcubescript
itself). The version in the repository tracks Git revision
https://github.com/antirez/linenoise/commit/c894b9e59f02203dbe4e2be657572cf88c4230c3.
## Licensing

View File

@ -598,12 +598,12 @@ struct cs_error {
}
template<typename ...A>
cs_error(cs_state &cs, std::string_view msg, A &&...args):
cs_error(cs_state &cs, std::string_view msg, A const &...args):
p_errmsg(), p_stack(cs)
{
char fbuf[512];
int written = std::snprintf(
fbuf, sizeof(fbuf), "%.*s", int(msg.size()), msg.data()
fbuf, sizeof(fbuf), msg.data(), args...
);
if (written >= int(sizeof(fbuf))) {
written = std::strlen(fbuf);

View File

@ -110,8 +110,8 @@ cs_stack_state cs_error::save_stack(cs_state &cs) {
std::string_view cs_error::save_msg(
cs_state &cs, std::string_view msg
) {
if (msg.size() > sizeof(cs.p_errbuf)) {
msg = msg.substr(0, sizeof(cs.p_errbuf));
if (msg.size() >= sizeof(cs.p_errbuf)) {
msg = msg.substr(0, sizeof(cs.p_errbuf) - 1);
}
cs_gen_state *gs = cs.p_pstate;
if (gs) {
@ -137,6 +137,7 @@ std::string_view cs_error::save_msg(
return std::string_view{cs.p_errbuf, std::size_t(sz)};
}
memcpy(cs.p_errbuf, msg.data(), msg.size());
cs.p_errbuf[msg.size()] = '\0';
return std::string_view{cs.p_errbuf, msg.size()};
}
@ -527,7 +528,7 @@ struct RunDepthRef {
static inline cs_alias *cs_get_lookup_id(cs_state &cs, uint32_t op) {
cs_ident *id = cs.p_state->identmap[op >> 8];
if (id->get_flags() & CS_IDF_UNKNOWN) {
throw cs_error(cs, "unknown alias lookup: %s", id->get_name());
throw cs_error(cs, "unknown alias lookup: %s", id->get_name().data());
}
return static_cast<cs_alias *>(id);
}
@ -574,7 +575,7 @@ static inline int cs_get_lookupu_type(
return CsIdUnknown;
}
}
throw cs_error(cs, "unknown alias lookup: %s", arg.get_str());
throw cs_error(cs, "unknown alias lookup: %s", arg.get_str().data());
}
static uint32_t *runcode(cs_state &cs, uint32_t *code, cs_value &result) {
@ -1421,7 +1422,7 @@ static uint32_t *runcode(cs_state &cs, uint32_t *code, cs_value &result) {
if (id->get_flags() & CS_IDF_UNKNOWN) {
force_arg(result, op & CS_CODE_RET_MASK);
throw cs_error(
cs, "unknown command: %s", id->get_name()
cs, "unknown command: %s", id->get_name().data()
);
}
cs_call_alias(
@ -1471,8 +1472,9 @@ noid:
}
result.force_none();
force_arg(result, op & CS_CODE_RET_MASK);
std::string_view ids{idn};
throw cs_error(
cs, "unknown command: %s", std::string_view{idn}
cs, "unknown command: %s", ids.data()
);
}
result.force_none();

View File

@ -474,7 +474,7 @@ LIBCUBESCRIPT_EXPORT cs_ident *cs_state::new_ident(std::string_view name, int fl
if (!id) {
if (cs_check_num(name)) {
throw cs_error(
*this, "number %s is not a valid identifier name", name
*this, "number %s is not a valid identifier name", name.data()
);
}
id = add_ident(p_state->create<cs_alias>(
@ -560,10 +560,10 @@ LIBCUBESCRIPT_EXPORT cs_svar *cs_state::new_svar(
LIBCUBESCRIPT_EXPORT void cs_state::reset_var(std::string_view name) {
cs_ident *id = get_ident(name);
if (!id) {
throw cs_error(*this, "variable %s does not exist", name);
throw cs_error(*this, "variable %s does not exist", name.data());
}
if (id->get_flags() & CS_IDF_READONLY) {
throw cs_error(*this, "variable %s is read only", name);
throw cs_error(*this, "variable %s is read only", name.data());
}
clear_override(*id);
}
@ -600,11 +600,11 @@ LIBCUBESCRIPT_EXPORT void cs_state::set_alias(std::string_view name, cs_value v)
default:
throw cs_error(
*this, "cannot redefine builtin %s with an alias",
id->get_name()
id->get_name().data()
);
}
} else if (cs_check_num(name)) {
throw cs_error(*this, "cannot alias number %s", name);
throw cs_error(*this, "cannot alias number %s", name.data());
} else {
add_ident(p_state->create<cs_alias>(
*this, cs_strref{*p_state, name}, std::move(v), identflags
@ -657,7 +657,8 @@ static inline void cs_override_var(cs_state &cs, cs_var *v, int &vflags, SF sf)
if ((cs.identflags & CS_IDF_OVERRIDDEN) || (vflags & CS_IDF_OVERRIDE)) {
if (vflags & CS_IDF_PERSIST) {
throw cs_error(
cs, "cannot override persistent variable '%s'", v->get_name()
cs, "cannot override persistent variable '%s'",
v->get_name().data()
);
}
if (!(vflags & CS_IDF_OVERRIDDEN)) {
@ -825,14 +826,14 @@ cs_int cs_clamp_var(cs_state &cs, cs_ivar *iv, cs_int v) {
: "valid range for '%s' is 0x%X..0x%X"
)
: "valid range for '%s' is %d..%d",
iv->get_name(), iv->get_val_min(), iv->get_val_max()
iv->get_name().data(), iv->get_val_min(), iv->get_val_max()
);
}
LIBCUBESCRIPT_EXPORT void cs_state::set_var_int_checked(cs_ivar *iv, cs_int v) {
if (iv->get_flags() & CS_IDF_READONLY) {
throw cs_error(
*this, "variable '%s' is read only", iv->get_name()
*this, "variable '%s' is read only", iv->get_name().data()
);
}
cs_override_var(
@ -871,7 +872,7 @@ cs_float cs_clamp_fvar(cs_state &cs, cs_fvar *fv, cs_float v) {
vmin.set_float(fv->get_val_min());
vmax.set_float(fv->get_val_max());
throw cs_error(
cs, "valid range for '%s' is %s..%s", fv->get_name(),
cs, "valid range for '%s' is %s..%s", fv->get_name().data(),
vmin.force_str(), vmax.force_str()
);
return v;
@ -880,7 +881,7 @@ cs_float cs_clamp_fvar(cs_state &cs, cs_fvar *fv, cs_float v) {
LIBCUBESCRIPT_EXPORT void cs_state::set_var_float_checked(cs_fvar *fv, cs_float v) {
if (fv->get_flags() & CS_IDF_READONLY) {
throw cs_error(
*this, "variable '%s' is read only", fv->get_name()
*this, "variable '%s' is read only", fv->get_name().data()
);
}
cs_override_var(
@ -899,7 +900,7 @@ LIBCUBESCRIPT_EXPORT void cs_state::set_var_str_checked(
) {
if (sv->get_flags() & CS_IDF_READONLY) {
throw cs_error(
*this, "variable '%s' is read only", sv->get_name()
*this, "variable '%s' is read only", sv->get_name().data()
);
}
cs_override_var(

View File

@ -14,11 +14,8 @@ libcubescript_src = [
'lib_str.cc'
]
libostd_dep = dependency('libostd', fallback: ['libostd', 'libostd_static'])
libcubescript_lib = both_libraries('cubescript',
libcubescript_src,
dependencies: libostd_dep,
include_directories: libcubescript_includes + [include_directories('.')],
cpp_args: extra_cxxflags,
install: true,

View File

@ -1,4 +0,0 @@
[wrap-git]
directory = libostd
url = https://git.octaforge.org/OctaForge/libostd.git
revision = head

View File

@ -2,15 +2,23 @@
/* use nothing (no line editing support) */
#include <optional>
#include <ostd/string.hh>
#include <string>
inline void init_lineedit(cs_state &, std::string_view) {
}
inline std::optional<std::string> read_line(cs_state &, cs_svar *pr) {
ostd::write(pr->get_value());
return ostd::cin.get_line(ostd::appender<std::string>()).get();
std::string lbuf;
char buf[512];
printf("%s", pr->get_value().data());
std::fflush(stdout);
while (fgets(buf, sizeof(buf), stdin)) {
lbuf += static_cast<char const *>(buf);
if (strchr(buf, '\n')) {
break;
}
}
return std::move(lbuf);
}
inline void add_history(cs_state &, std::string_view) {

View File

@ -1,5 +1,5 @@
#ifdef CS_REPL_USE_LINENOISE
#ifdef OSTD_PLATFORM_POSIX
#ifndef _WIN32
#ifndef CS_REPL_HAS_EDIT
#define CS_REPL_HAS_EDIT
/* use the bundled linenoise library, default */
@ -10,8 +10,6 @@
#include <optional>
#include <ostd/string.hh>
#include "linenoise.hh"
static cs_state *ln_cs = nullptr;

View File

@ -3,12 +3,9 @@
#define CS_REPL_HAS_EDIT
/* use the GNU readline library */
#include <string.h>
#include <cstring>
#include <optional>
#include <ostd/string.hh>
#include <readline/readline.h>
#include <readline/history.h>
@ -16,15 +13,14 @@ static cs_state *rd_cs = nullptr;
inline char *ln_complete_list(char const *buf, int state) {
static std::string_view cmd;
static ostd::iterator_range<cs_ident **> itr;
static std::span<cs_ident *> itr;
if (!state) {
cmd = get_complete_cmd(buf);
itr = rd_cs->get_idents();
}
for (; !itr.empty(); itr.pop_front()) {
cs_ident *id = itr.front();
for (cs_ident *id: itr) {
if (!id->is_command()) {
continue;
}
@ -33,7 +29,7 @@ inline char *ln_complete_list(char const *buf, int state) {
continue;
}
if (idname.substr(0, cmd.size()) == cmd) {
itr.pop_front();
++itr;
return strdup(idname.data());
}
}

View File

@ -2,7 +2,7 @@ repl_src = [
'repl.cc'
]
repl_deps = [libostd_dep, libcubescript]
repl_deps = [libcubescript]
repl_flags = []
if get_option('readline')

View File

@ -1,13 +1,12 @@
#include <signal.h>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <optional>
#include <memory>
#include <iterator>
#include <ostd/platform.hh>
#include <ostd/io.hh>
#include <ostd/string.hh>
#include <cubescript/cubescript.hh>
using namespace cscript;
@ -16,7 +15,7 @@ std::string_view version = "CubeScript 0.0.1";
/* util */
#ifdef OSTD_PLATFORM_WIN32
#if defined(_WIN32)
#include <io.h>
static bool stdin_is_tty() {
return _isatty(_fileno(stdin));
@ -165,9 +164,9 @@ inline cs_command *get_hint_cmd(cs_state &cs, std::string_view buf) {
/* usage */
void print_usage(std::string_view progname, bool err) {
auto &s = err ? ostd::cerr : ostd::cout;
s.writeln(
"Usage: ", progname, " [options] [file]\n"
std::fprintf(
err ? stderr : stdout,
"Usage: %s [options] [file]\n"
"Options:\n"
" -e str run string \"str\"\n"
" -i enter interactive mode after the above\n"
@ -175,12 +174,13 @@ void print_usage(std::string_view progname, bool err) {
" -h show this message\n"
" -- stop handling options\n"
" - execute stdin and stop handling options"
"\n",
progname.data()
);
s.flush();
}
void print_version() {
ostd::writeln(version);
printf("%s\n", version.data());
}
static cs_state *scs = nullptr;
@ -200,33 +200,35 @@ static void repl_print_var(cs_state const &cs, cs_var const &var) {
auto &iv = static_cast<cs_ivar const &>(var);
auto val = iv.get_value();
if (!(iv.get_flags() & CS_IDF_HEX) || (val < 0)) {
ostd::writefln("%s = %d", iv.get_name(), val);
std::printf("%s = %d\n", iv.get_name().data(), val);
} else if (iv.get_val_max() == 0xFFFFFF) {
ostd::writefln(
"%s = 0x%.6X (%d, %d, %d)", iv.get_name(),
std::printf(
"%s = 0x%.6X (%d, %d, %d)\n",
iv.get_name().data(),
val, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF
);
} else {
ostd::writefln("%s = 0x%X", iv.get_name(), val);
std::printf("%s = 0x%X\n", iv.get_name().data(), val);
}
break;
}
case cs_ident_type::FVAR: {
auto &fv = static_cast<cs_fvar const &>(var);
auto val = fv.get_value();
ostd::writefln(
(floor(val) == val) ? "%s = %.1f" : "%s = %.7g",
fv.get_name(), val
);
if (std::floor(val) == val) {
std::printf("%s = %.1f", fv.get_name().data(), val);
} else {
std::printf("%s = %.7g", fv.get_name().data(), val);
}
break;
}
case cs_ident_type::SVAR: {
auto &sv = static_cast<cs_svar const &>(var);
auto val = std::string_view{sv.get_value()};
if (val.find('"') == val.npos) {
ostd::writefln("%s = \"%s\"", sv.get_name(), val);
std::printf("%s = \"%s\"", sv.get_name().data(), val.data());
} else {
ostd::writefln("%s = [%s]", sv.get_name(), val);
std::printf("%s = [%s]", sv.get_name().data(), val.data());
}
break;
}
@ -236,28 +238,29 @@ static void repl_print_var(cs_state const &cs, cs_var const &var) {
}
static bool do_run_file(cs_state &cs, std::string_view fname, cs_value &ret) {
std::unique_ptr<char[]> buf;
std::size_t len;
ostd::file_stream f{fname, ostd::stream_mode::READ};
if (!f.is_open()) {
FILE *f = std::fopen(fname.data(), "rb");
if (!f) {
return false;
}
len = f.size();
buf = std::make_unique<char[]>(len + 1);
std::fseek(f, 0, SEEK_END);
auto len = std::ftell(f);
std::fseek(f, 0, SEEK_SET);
auto buf = std::make_unique<char[]>(len + 1);
if (!buf) {
std::fclose(f);
return false;
}
try {
f.get(buf.get(), len);
} catch (...) {
if (std::fread(buf.get(), 1, len, f) != std::size_t(len)) {
std::fclose(f);
return false;
}
buf[len] = '\0';
cs.run(std::string_view{buf.get(), len}, ret, fname);
cs.run(std::string_view{buf.get(), std::size_t(len)}, ret, fname);
return true;
}
@ -268,7 +271,7 @@ static bool do_call(cs_state &cs, std::string_view line, bool file = false) {
try {
if (file) {
if (!do_run_file(cs, line, ret)) {
ostd::cerr.writeln("cannot read file: ", line);
std::fprintf(stderr, "cannot read file: %s\n", line.data());
}
} else {
cs.run(line, ret);
@ -291,18 +294,20 @@ static bool do_call(cs_state &cs, std::string_view line, bool file = false) {
if (!file && ((terr == "missing \"]\"") || (terr == "missing \")\""))) {
return true;
}
ostd::writeln(!is_lnum ? "stdin: " : "stdin:", e.what());
std::printf(
"%s%s\n", !is_lnum ? "stdin: " : "stdin:", e.what().data()
);
if (e.get_stack().get()) {
std::string str;
cscript::util::print_stack(std::back_inserter(str), e.get_stack());
ostd::writeln(str);
std::printf("%s\n", str.data());
}
return false;
}
signal(SIGINT, SIG_DFL);
scs = nullptr;
if (ret.get_type() != cs_value_type::NONE) {
ostd::writeln(std::string_view{ret.get_str()});
std::printf("%s\n", std::string_view{ret.get_str()}.data());
}
return false;
}
@ -316,7 +321,7 @@ static void do_tty(cs_state &cs) {
do_exit = true;
});
ostd::writeln(version, " (REPL mode)");
std::printf("%s (REPL mode)\n", version.data());
for (;;) {
auto line = read_line(cs, prompt);
if (!line) {
@ -364,7 +369,7 @@ int main(int argc, char **argv) {
});
gcs.new_command("echo", "C", [](auto &, auto args, auto &) {
ostd::writeln(std::string_view{args[0].get_str()});
std::printf("%s\n", std::string_view{args[0].get_str()}.data());
});
int firstarg = 0;
@ -450,7 +455,7 @@ endargs:
return 0;
} else {
std::string str;
for (signed char c = '\0'; (c = ostd::cin.get_char()) != EOF;) {
for (signed char c = '\0'; (c = std::fgetc(stdin)) != EOF;) {
str += c;
}
do_call(gcs, str);