From 82d366366e1c9dedf2d1401cc7d603546ed55a3a Mon Sep 17 00:00:00 2001 From: Daniel Kolesa Date: Sat, 20 Mar 2021 08:22:15 +0100 Subject: [PATCH] drop libostd requirement entirely --- README.md | 28 +++++------ include/cubescript/cubescript.hh | 4 +- src/cs_vm.cc | 14 +++--- src/cubescript.cc | 23 ++++----- src/meson.build | 3 -- subprojects/libostd.wrap | 4 -- tools/edit_fallback.hh | 16 +++++-- tools/edit_linenoise.hh | 4 +- tools/edit_readline.hh | 12 ++--- tools/meson.build | 2 +- tools/repl.cc | 81 +++++++++++++++++--------------- 11 files changed, 95 insertions(+), 96 deletions(-) delete mode 100644 subprojects/libostd.wrap diff --git a/README.md b/README.md index 1f498f8..323c0a4 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/include/cubescript/cubescript.hh b/include/cubescript/cubescript.hh index 1bea15f..052f622 100644 --- a/include/cubescript/cubescript.hh +++ b/include/cubescript/cubescript.hh @@ -598,12 +598,12 @@ struct cs_error { } template - 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); diff --git a/src/cs_vm.cc b/src/cs_vm.cc index 356b919..16178e3 100644 --- a/src/cs_vm.cc +++ b/src/cs_vm.cc @@ -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(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(); diff --git a/src/cubescript.cc b/src/cubescript.cc index 79845c7..56b2b1b 100644 --- a/src/cubescript.cc +++ b/src/cubescript.cc @@ -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( @@ -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( *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( diff --git a/src/meson.build b/src/meson.build index 6e8637e..cb48f07 100644 --- a/src/meson.build +++ b/src/meson.build @@ -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, diff --git a/subprojects/libostd.wrap b/subprojects/libostd.wrap deleted file mode 100644 index 7e071b6..0000000 --- a/subprojects/libostd.wrap +++ /dev/null @@ -1,4 +0,0 @@ -[wrap-git] -directory = libostd -url = https://git.octaforge.org/OctaForge/libostd.git -revision = head diff --git a/tools/edit_fallback.hh b/tools/edit_fallback.hh index 136012b..6852c67 100644 --- a/tools/edit_fallback.hh +++ b/tools/edit_fallback.hh @@ -2,15 +2,23 @@ /* use nothing (no line editing support) */ #include - -#include +#include inline void init_lineedit(cs_state &, std::string_view) { } inline std::optional read_line(cs_state &, cs_svar *pr) { - ostd::write(pr->get_value()); - return ostd::cin.get_line(ostd::appender()).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(buf); + if (strchr(buf, '\n')) { + break; + } + } + return std::move(lbuf); } inline void add_history(cs_state &, std::string_view) { diff --git a/tools/edit_linenoise.hh b/tools/edit_linenoise.hh index c6c483e..33acf71 100644 --- a/tools/edit_linenoise.hh +++ b/tools/edit_linenoise.hh @@ -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 -#include - #include "linenoise.hh" static cs_state *ln_cs = nullptr; diff --git a/tools/edit_readline.hh b/tools/edit_readline.hh index f12026a..a0a8bc1 100644 --- a/tools/edit_readline.hh +++ b/tools/edit_readline.hh @@ -3,12 +3,9 @@ #define CS_REPL_HAS_EDIT /* use the GNU readline library */ -#include - +#include #include -#include - #include #include @@ -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 itr; + static std::span 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()); } } diff --git a/tools/meson.build b/tools/meson.build index f4bb041..68df9a7 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -2,7 +2,7 @@ repl_src = [ 'repl.cc' ] -repl_deps = [libostd_dep, libcubescript] +repl_deps = [libcubescript] repl_flags = [] if get_option('readline') diff --git a/tools/repl.cc b/tools/repl.cc index d609c79..c181061 100644 --- a/tools/repl.cc +++ b/tools/repl.cc @@ -1,13 +1,12 @@ #include +#include +#include +#include #include #include #include -#include -#include -#include - #include using namespace cscript; @@ -16,7 +15,7 @@ std::string_view version = "CubeScript 0.0.1"; /* util */ -#ifdef OSTD_PLATFORM_WIN32 +#if defined(_WIN32) #include 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(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(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(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 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(len + 1); + std::fseek(f, 0, SEEK_END); + auto len = std::ftell(f); + std::fseek(f, 0, SEEK_SET); + + auto buf = std::make_unique(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);