diff --git a/Makefile b/Makefile index 1f3a1a1..5df5e8c 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ LIBCS_OBJ = \ cubescript.o \ cs_gen.o \ cs_vm.o \ + cs_util.o \ lib_str.o \ lib_math.o \ lib_list.o @@ -29,9 +30,9 @@ $(LIBCS_LIB): $(LIBCS_OBJ) clean: rm -f $(LIBCS_LIB) $(LIBCS_OBJ) -cubescript.o: cubescript.hh cubescript_conf.hh cs_vm.hh -cs_gen.o: cubescript.hh cubescript_conf.hh cs_vm.hh -cs_vm.o: cubescript.hh cubescript_conf.hh cs_vm.hh +cubescript.o: cubescript.hh cubescript_conf.hh cs_vm.hh cs_util.hh +cs_gen.o: cubescript.hh cubescript_conf.hh cs_vm.hh cs_util.hh +cs_vm.o: cubescript.hh cubescript_conf.hh cs_vm.hh cs_util.hh lib_str.o: cubescript.hh cubescript_conf.hh lib_math.o: cubescript.hh cubescript_conf.hh -lib_list.o: cubescript.hh cubescript_conf.hh +lib_list.o: cubescript.hh cubescript_conf.hh cs_util.hh diff --git a/cs_gen.cc b/cs_gen.cc index 869ad3d..e35e4fe 100644 --- a/cs_gen.cc +++ b/cs_gen.cc @@ -1,5 +1,6 @@ #include "cubescript.hh" #include "cs_vm.hh" +#include "cs_util.hh" #include #include @@ -10,16 +11,6 @@ namespace cscript { char *cs_dup_ostr(ostd::ConstCharRange s); -CsInt cs_parse_int(ostd::ConstCharRange s) { - if (s.empty()) return 0; - return parseint(s.data()); -} - -CsFloat cs_parse_float(ostd::ConstCharRange s) { - if (s.empty()) return 0.0f; - return parsefloat(s.data()); -} - char const *parsestring(char const *p) { for (; *p; p++) switch (*p) { case '\r': @@ -117,11 +108,11 @@ static void compilestatements(GenState &gs, int rettype, int brak = '\0', int pr static inline char const *compileblock(GenState &gs, char const *p, int rettype = RET_NULL, int brak = '\0'); void GenState::gen_int(ostd::ConstCharRange word) { - gen_int(cs_parse_int(word)); + gen_int(parser::parse_int(word)); } void GenState::gen_float(ostd::ConstCharRange word) { - gen_float(cs_parse_float(word)); + gen_float(parser::parse_float(word)); } void GenState::gen_value(int wordtype, ostd::ConstCharRange word) { @@ -801,10 +792,9 @@ noid: switch (rettype) { case VAL_ANY: case VAL_CANY: { - char *end = idname.get(); - ostd::Size idlen = strlen(idname.get()); - CsInt val = CsInt(strtoul(idname.get(), &end, 0)); - if (end < &idname[idlen]) gs.gen_str(idname.get(), rettype == VAL_CANY); + ostd::ConstCharRange end = idname.get(); + CsInt val = parser::parse_int(end, &end); + if (!end.empty()) gs.gen_str(idname.get(), rettype == VAL_CANY); else gs.gen_int(val); break; } diff --git a/cs_util.cc b/cs_util.cc new file mode 100644 index 0000000..281c8a0 --- /dev/null +++ b/cs_util.cc @@ -0,0 +1,255 @@ +#include "cubescript_conf.hh" +#include "cs_util.hh" + +#include +#include + +namespace cscript { +namespace parser { + +static inline void p_skip_white(ostd::ConstCharRange &v) { + while (!v.empty() && isspace(v.front())) { + v.pop_front(); + } +} + +static inline void p_set_end( + const ostd::ConstCharRange &v, ostd::ConstCharRange *end +) { + if (!end) { + return; + } + *end = v; +} + +static inline CsInt p_hexd_to_int(char c) { + if ((c >= 48) && (c <= 57)) { /* 0-9 */ + return c - '0'; + } + if ((c >= 65) && (c <= 70)) { /* A-F */ + return c - 'A'; + } + if ((c >= 97) && (c <= 102)) { /* a-f */ + return c - 'a'; + } + return 0; +} + +static inline CsInt p_decd_to_int(char c) { + return c - '0'; +} + +static inline CsInt p_bind_to_int(char c) { + return c - '0'; +} + +static inline bool p_is_hexdigit(char c) { + return isxdigit(c); +} + +static inline bool p_is_decdigit(char c) { + return isdigit(c); +} + +static inline bool p_is_bindigit(char c) { + return (c == '0') || (c == '1'); +} + +template +static inline ostd::ConstCharRange parse_gen_int( + ostd::ConstCharRange input, T &retv +) { + T ret = 0, mul = 1; + ostd::ConstCharRange oi = input; + while (!input.empty()) { + if (Base == 16) { /* hexadecimal */ + if (!p_is_hexdigit(input.front())) { + break; + } + } else if (Base == 2) { /* binary */ + if (!p_is_bindigit(input.front())) { + break; + } + } else { /* decimal */ + if (!p_is_decdigit(input.front())) { + break; + } + } + mul *= Base; + input.pop_front(); + } + while (!oi.equals_front(input)) { + mul /= Base; + if (Base == 16) { + ret += p_hexd_to_int(oi.front()) * mul; + } else if (Base == 2) { + ret += p_bind_to_int(oi.front()) * mul; + } else { + ret += p_decd_to_int(oi.front()) * mul; + } + oi.pop_front(); + } + retv = ret; + return oi; +} + +CsInt parse_int(ostd::ConstCharRange input, ostd::ConstCharRange *end) { + ostd::ConstCharRange orig = input; + p_skip_white(input); + if (input.empty()) { + p_set_end(orig, end); + return CsInt(0); + } + /* 1 for false, -1 for true */ + int neg = -(int(input.front() == '-') * 2 - 1); + if (neg < 0 || (input.front() == '+')) { + input.pop_front(); + } + CsInt ret = 0; + ostd::ConstCharRange past = input; + if (input.size() >= 2) { + ostd::ConstCharRange pfx = input.slice(0, 2); + if ((pfx == "0x") || (pfx == "0X")) { + input.pop_front_n(2); + past = parse_gen_int(input, ret); + goto done; + } else if ((pfx == "0b") || (pfx == "0B")) { + input.pop_front_n(2); + past = parse_gen_int(input, ret); + goto done; + } + } + past = parse_gen_int(input, ret); +done: + if (past.equals_front(input)) { + p_set_end(orig, end); + } else { + p_set_end(past, end); + } + return ret * neg; +} + +template +static inline bool p_read_exp(ostd::ConstCharRange &input, CsInt &fn) { + if (input.empty()) { + return true; + } + if ((input.front() != e1) && (input.front() != e2)) { + return true; + } + input.pop_front(); + if (input.empty()) { + return false; + } + bool neg = (input.front() == '-'); + if (neg || input.front() == '+') { + input.pop_front(); + } + if (input.empty() || !p_is_decdigit(input.front())) { + return false; + } + CsInt exp = 0; + while (!input.empty() && p_is_decdigit(input.front())) { + exp = exp * 10 + p_decd_to_int(input.front()); + input.pop_front(); + } + if (neg) { + exp = -exp; + } + fn += exp; + return true; +} + +static inline bool parse_hex_float( + ostd::ConstCharRange input, ostd::ConstCharRange *end, CsFloat &ret +) { + auto read_hd = [&input](double r, CsInt &n) { + while (!input.empty() && p_is_hexdigit(input.front())) { + r = r * 16.0 + double(p_hexd_to_int(input.front())); + ++n; + input.pop_front(); + } + return r; + }; + CsInt wn = 0, fn = 0; + double r = read_hd(0.0, wn); + if (!input.empty() && (input.front() == '.')) { + input.pop_front(); + r = read_hd(r, fn); + } + if (!wn && !fn) { + return false; + } + fn *= -4; + p_set_end(input, end); /* we have a valid number until here */ + if (p_read_exp<'p', 'P'>(input, fn)) { + p_set_end(input, end); + } + ret = CsFloat(ldexp(r, fn)); + return true; +} + +static inline bool parse_dec_float( + ostd::ConstCharRange input, ostd::ConstCharRange *end, CsFloat &ret +) { + auto read_hd = [&input](double r, CsInt &n) { + while (!input.empty() && p_is_decdigit(input.front())) { + r = r * 10.0 + double(p_decd_to_int(input.front())); + ++n; + input.pop_front(); + } + return r; + }; + CsInt wn = 0, fn = 0; + double r = read_hd(0.0, wn); + if (!input.empty() && (input.front() == '.')) { + input.pop_front(); + r = read_hd(r, fn); + } + if (!wn && !fn) { + return false; + } + fn = -fn; + p_set_end(input, end); + if (p_read_exp<'e', 'E'>(input, fn)) { + p_set_end(input, end); + } + ret = CsFloat(r * pow(10, fn)); + return true; +} + +CsFloat parse_float(ostd::ConstCharRange input, ostd::ConstCharRange *end) { + ostd::ConstCharRange orig = input; + p_skip_white(input); + if (input.empty()) { + p_set_end(orig, end); + return CsFloat(0); + } + bool neg = (input.front() == '-'); + if (neg || (input.front() == '+')) { + input.pop_front(); + } + bool hex = false; + CsFloat ret = CsFloat(0); + if (input.size() >= 2) { + ostd::ConstCharRange pfx = input.slice(0, 2); + if ((hex = ((pfx == "0x") || (pfx == "0X")))) { + input.pop_front_n(2); + if (!parse_hex_float(input, end, ret)) { + p_set_end(orig, end); + return ret; + } + } + } + if (!hex && !parse_dec_float(input, end, ret)) { + p_set_end(orig, end); + return ret; + } + if (neg) { + return -ret; + } + return ret; +} + +} /* namespace parser */ +} /* namespace cscript */ diff --git a/cs_util.hh b/cs_util.hh new file mode 100644 index 0000000..68d489d --- /dev/null +++ b/cs_util.hh @@ -0,0 +1,20 @@ +#ifndef LIBCUBESCRIPT_CS_UTIL_HH +#define LIBCUBESCRIPT_CS_UTIL_HH + +#include + +namespace cscript { +namespace parser { + +CsInt parse_int( + ostd::ConstCharRange input, ostd::ConstCharRange *end = nullptr +); + +CsFloat parse_float( + ostd::ConstCharRange input, ostd::ConstCharRange *end = nullptr +); + +} /* namespace parser */ +} /* namespace cscript */ + +#endif /* LIBCUBESCRIPT_CS_UTIL_HH */ diff --git a/cs_vm.cc b/cs_vm.cc index 6b7567a..dc4c470 100644 --- a/cs_vm.cc +++ b/cs_vm.cc @@ -1,5 +1,6 @@ #include "cubescript.hh" #include "cs_vm.hh" +#include "cs_util.hh" #include @@ -719,7 +720,7 @@ static ostd::Uint32 const *runcode(CsState &cs, ostd::Uint32 const *code, Tagged LOOKUPARG(args[numargs++].set_str(ostd::move(id->get_str())), args[numargs++].set_str("")); case CODE_LOOKUPU|RET_INT: LOOKUPU(arg.set_int(id->get_int()), - arg.set_int(parseint(*id->storage.sp)), + arg.set_int(parser::parse_int(*id->storage.sp)), arg.set_int(*id->storage.ip), arg.set_int(CsInt(*id->storage.fp)), arg.set_int(0)); @@ -729,7 +730,7 @@ static ostd::Uint32 const *runcode(CsState &cs, ostd::Uint32 const *code, Tagged LOOKUPARG(args[numargs++].set_int(id->get_int()), args[numargs++].set_int(0)); case CODE_LOOKUPU|RET_FLOAT: LOOKUPU(arg.set_float(id->get_float()), - arg.set_float(parsefloat(*id->storage.sp)), + arg.set_float(parser::parse_float(*id->storage.sp)), arg.set_float(CsFloat(*id->storage.ip)), arg.set_float(*id->storage.fp), arg.set_float(0.0f)); @@ -774,10 +775,10 @@ static ostd::Uint32 const *runcode(CsState &cs, ostd::Uint32 const *code, Tagged args[numargs++].set_str(*cs.identmap[op >> 8]->storage.sp); continue; case CODE_SVAR|RET_INT: - args[numargs++].set_int(parseint(*cs.identmap[op >> 8]->storage.sp)); + args[numargs++].set_int(parser::parse_int(*cs.identmap[op >> 8]->storage.sp)); continue; case CODE_SVAR|RET_FLOAT: - args[numargs++].set_float(parsefloat(*cs.identmap[op >> 8]->storage.sp)); + args[numargs++].set_float(parser::parse_float(*cs.identmap[op >> 8]->storage.sp)); continue; case CODE_SVARM: args[numargs++].set_cstr(*cs.identmap[op >> 8]->storage.sp); diff --git a/cs_vm.hh b/cs_vm.hh index 786dca7..f5b5b2a 100644 --- a/cs_vm.hh +++ b/cs_vm.hh @@ -203,9 +203,6 @@ struct GenState { } }; -CsInt parseint(char const *s); -CsFloat parsefloat(char const *s); - ostd::String intstr(int v); ostd::String floatstr(CsFloat v); diff --git a/cubescript.cc b/cubescript.cc index e32bd2b..fb98b7c 100644 --- a/cubescript.cc +++ b/cubescript.cc @@ -1,19 +1,9 @@ #include "cubescript.hh" #include "cs_vm.hh" +#include "cs_util.hh" namespace cscript { -CsInt parseint(char const *s) { - return CsInt(strtoul(s, nullptr, 0)); -} - -CsFloat parsefloat(char const *s) { - /* not all platforms (windows) can parse hexadecimal integers via strtod */ - char *end; - double val = strtod(s, &end); - return val || end==s || (*end!='x' && *end!='X') ? CsFloat(val) : CsFloat(parseint(s)); -} - CsFloat cs_parse_float(ostd::ConstCharRange s); ostd::String intstr(CsInt v) { @@ -335,7 +325,7 @@ CsFloat TaggedValue::force_float() { case VAL_STR: case VAL_MACRO: case VAL_CSTR: - rf = parsefloat(s); + rf = parser::parse_float(s); break; case VAL_FLOAT: return f; @@ -354,7 +344,7 @@ CsInt TaggedValue::force_int() { case VAL_STR: case VAL_MACRO: case VAL_CSTR: - ri = parseint(s); + ri = parser::parse_int(s); break; case VAL_INT: return i; @@ -394,7 +384,7 @@ static inline CsInt cs_get_int(IdentValue const &v, int type) { case VAL_STR: case VAL_MACRO: case VAL_CSTR: - return parseint(v.s); + return parser::parse_int(v.s); } return 0; } @@ -416,7 +406,7 @@ static inline CsFloat cs_get_float(IdentValue const &v, int type) { case VAL_STR: case VAL_MACRO: case VAL_CSTR: - return parsefloat(v.s); + return parser::parse_float(v.s); } return 0.0f; } @@ -528,36 +518,18 @@ bool TaggedValue::code_is_empty() const { } static inline bool cs_get_bool(ostd::ConstCharRange s) { - if (s.empty()) + if (s.empty()) { return false; - switch (s.front()) { - case '+': - case '-': - switch (s[1]) { - case '0': - break; - case '.': - return !isdigit(s[2]) || (cs_parse_float(s) != 0); - default: - return true; - } - /* fallthrough */ - case '0': { - char *end; - int val = int(strtoul(s.data(), &end, 0)); - if (val) return true; - switch (*end) { - case 'e': - case '.': - return (cs_parse_float(s) != 0); - default: - return false; - } } - case '.': - return !isdigit(s[1]) || (cs_parse_float(s) != 0); - case '\0': - return false; + ostd::ConstCharRange end = s; + CsInt ival = parser::parse_int(end, &end); + if (end.empty()) { + return !!ival; + } + end = s; + CsFloat fval = parser::parse_float(end, &end); + if (end.empty()) { + return !!fval; } return true; } diff --git a/lib_list.cc b/lib_list.cc index 5760df5..3a71b87 100644 --- a/lib_list.cc +++ b/lib_list.cc @@ -1,10 +1,9 @@ #include "cubescript.hh" +#include "cs_util.hh" namespace cscript { char *cs_dup_ostr(ostd::ConstCharRange s); -CsInt cs_parse_int(ostd::ConstCharRange s); -CsFloat cs_parse_float(ostd::ConstCharRange s); ostd::ConstCharRange cs_parse_str(ostd::ConstCharRange str); char const *parseword(char const *p); @@ -328,8 +327,8 @@ found: res.set_int(-1); \ }); - CS_CMD_LIST_FIND("listfind=", "i", get_int, cs_parse_int(p.item) == val); - CS_CMD_LIST_FIND("listfind=f", "f", get_float, cs_parse_float(p.item) == val); + CS_CMD_LIST_FIND("listfind=", "i", get_int, parser::parse_int(p.item) == val); + CS_CMD_LIST_FIND("listfind=f", "f", get_float, parser::parse_float(p.item) == val); CS_CMD_LIST_FIND("listfind=s", "s", get_strr, p.item == val); #undef CS_CMD_LIST_FIND @@ -352,8 +351,8 @@ found: } \ }); - CS_CMD_LIST_ASSOC("listassoc=", "i", get_int, cs_parse_int(p.item) == val); - CS_CMD_LIST_ASSOC("listassoc=f", "f", get_float, cs_parse_float(p.item) == val); + CS_CMD_LIST_ASSOC("listassoc=", "i", get_int, parser::parse_int(p.item) == val); + CS_CMD_LIST_ASSOC("listassoc=f", "f", get_float, parser::parse_float(p.item) == val); CS_CMD_LIST_ASSOC("listassoc=s", "s", get_strr, p.item == val); #undef CS_CMD_LIST_ASSOC