diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2023-07-16 02:03:33 +0200 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2023-07-16 02:03:33 +0200 |
commit | 5a40295c4cf0af5ea8da9ced04a4ce7d3621a080 (patch) | |
tree | cb21506e7b04d10b45d6066a0ee1655563d5d52b /src/compiler/coerce.c |
Squashed 'flatcc/' content from commit 473da2a
git-subtree-dir: flatcc
git-subtree-split: 473da2afa5ca435363f8c5e6569167aee6bc31c5
Diffstat (limited to 'src/compiler/coerce.c')
-rw-r--r-- | src/compiler/coerce.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/compiler/coerce.c b/src/compiler/coerce.c new file mode 100644 index 0000000..6ab12a6 --- /dev/null +++ b/src/compiler/coerce.c @@ -0,0 +1,266 @@ +#include "coerce.h" + +/* + * Be aware that some value variants represents actual values (e.g. + * vt_int), and others represent a type (e.g. vt_scalar) which holds a + * type identifier token. Here we implicitly expect a vt_scalar type as + * first argument, but only receive the token. The second argument is a + * value literal. Our job is to decide if the value fits within the + * given type. Our internal representation already ensures that value + * fits within a 64bit signed or unsigned integer, or double; otherwise + * the parser would have set vt_invalid type on the value. + * + * If the value is invalid, success is returned because the + * error is presumably already generated. If the value is some other + * type than expect, an error is generated. + * + * Symbolic names are not allowed as values here. + * + * Converts positive integers to signed type and unsigned integers to + * signed type, integers to floats and floats to integers. + * + * Optionally allows 1 to be assigned as true and 0 as false, and vice + * versa when allow_boolean_conversion is enabled. + * + * Returns 0 on success, -1 on error. + */ +int fb_coerce_scalar_type(fb_parser_t *P, fb_symbol_t *sym, fb_scalar_type_t st, fb_value_t *value) +{ + double d; + float f; + + if (!value->type) { + return 0; + } + /* + * The parser only produces negative vt_int values, which simplifies + * the logic, but to make this operation robust against multiple + * coercion steps, we first convert back to uint if the assumption turns + * out false. + */ + if (value->type == vt_int && value->i >= 0) { + value->type = vt_uint; + value->u = (uint64_t)value->i; + } + if (value->type == vt_invalid) { + /* Silently ignore past errors. */ + return 0; + } + if (value->type == vt_bool && st != fb_bool && P->opts.allow_boolean_conversion) { + value->type = vt_uint; + value->u = (uint64_t)value->b; + assert(value->u == 1 || value->u == 0); + } + switch (st) { + case fb_ulong: + if (value->type != vt_uint) { + error_sym(P, sym, "64-bit uint32_t type only accepts unsigned integers"); + value->type = vt_invalid; + return -1; + } + return 0; + case fb_uint: + if (value->type != vt_uint) { + error_sym(P, sym, "32-bit unsigned int type only accepts unsigned integers"); + value->type = vt_invalid; + return -1; + } + if (value->u > UINT32_MAX) { + error_sym(P, sym, "32-bit unsigned int overflow"); + value->type = vt_invalid; + return -1; + } + return 0; + case fb_ushort: + if (value->type != vt_uint) { + error_sym(P, sym, "16-bit unsigned short type only accepts unsigned integers"); + value->type = vt_invalid; + return -1; + } + if (value->u > UINT16_MAX) { + error_sym(P, sym, "16-bit unsigned short overflow"); + value->type = vt_invalid; + return -1; + } + return 0; + case fb_char: + /* Although C treats char as signed by default, flatcc treats it as unsigned. */ + case fb_ubyte: + if (value->type != vt_uint) { + error_sym(P, sym, "8-bit unsigned byte type only accepts unsigned integers"); + value->type = vt_invalid; + return -1; + } + if (value->u > UINT8_MAX) { + error_sym(P, sym, "8-bit unsigned byte overflow"); + value->type = vt_invalid; + return -1; + } + return 0; + case fb_long: + if (value->type == vt_int) { + /* Native format is always ok, or parser would have failed. */ + return 0; + } + if (value->type == vt_uint) { + if (value->u >= (1ULL << 63)) { + error_sym(P, sym, "64-bit signed int32_t overflow"); + value->type = vt_invalid; + return -1; + } + value->i = (int64_t)value->u; + value->type = vt_int; + return 0; + } + error_sym(P, sym, "64-bit int32_t type only accepts integers"); + value->type = vt_invalid; + return -1; + case fb_int: + if (value->type == vt_int) { + if (value->i < INT32_MIN) { + error_sym(P, sym, "32-bit signed int underflow"); + value->type = vt_invalid; + return -1; + } + return 0; + } + if (value->type == vt_uint) { + if (value->i > INT32_MAX) { + error_sym(P, sym, "32-bit signed int overflow"); + value->type = vt_invalid; + return -1; + } + value->i = (int64_t)value->u; + value->type = vt_int; + return 0; + } + error_sym(P, sym, "32-bit signed int type only accepts integers"); + value->type = vt_invalid; + return -1; + case fb_short: + if (value->type == vt_int) { + if (value->i < INT16_MIN) { + error_sym(P, sym, "16-bit signed short underflow"); + value->type = vt_invalid; + return -1; + } + return 0; + } + if (value->type == vt_uint) { + if (value->i > INT16_MAX) { + error_sym(P, sym, "16-bit signed short overflow"); + value->type = vt_invalid; + return -1; + } + value->i = (int64_t)value->u; + value->type = vt_int; + return 0; + } + error_sym(P, sym, "16-bit signed short type only accepts integers"); + value->type = vt_invalid; + return -1; + case fb_byte: + if (value->type == vt_int) { + if (value->i < INT8_MIN) { + error_sym(P, sym, "8-bit signed byte underflow"); + value->type = vt_invalid; + return -1; + } + return 0; + } + if (value->type == vt_uint) { + if (value->i > INT8_MAX) { + error_sym(P, sym, "8-bit signed byte overflow"); + value->type = vt_invalid; + return -1; + } + value->i = (int64_t)value->u; + value->type = vt_int; + return 0; + } + error_sym(P, sym, "8-bit signed byte type only accepts integers"); + value->type = vt_invalid; + return -1; + case fb_bool: + if (value->type == vt_uint && P->opts.allow_boolean_conversion) { + if (value->u > 1) { + error_sym(P, sym, "boolean integer conversion only accepts 0 (false) or 1 (true)"); + value->type = vt_invalid; + return -1; + } + } else if (value->type != vt_bool) { + error_sym(P, sym, "boolean type only accepts 'true' or 'false' as values"); + value->type = vt_invalid; + return -1; + } + return 0; + case fb_double: + switch (value->type) { + case vt_int: + d = (double)value->i; + if ((int64_t)d != value->i) { + /* We could make this a warning. */ + error_sym(P, sym, "precision loss in 64-bit double type assignment"); + value->type = vt_invalid; + return -1; + } + value->f = d; + value->type = vt_float; + return 0; + case vt_uint: + d = (double)value->u; + if ((uint64_t)d != value->u) { + /* We could make this a warning. */ + error_sym(P, sym, "precision loss in 64-bit double type assignment"); + value->type = vt_invalid; + return -1; + } + value->f = d; + value->type = vt_float; + return 0; + case vt_float: + /* Double is our internal repr., so not loss at this point. */ + return 0; + default: + error_sym(P, sym, "64-bit double type only accepts integer and float values"); + value->type = vt_invalid; + return -1; + } + case fb_float: + switch (value->type) { + case vt_int: + f = (float)value->i; + if ((int64_t)f != value->i) { + /* We could make this a warning. */ + error_sym(P, sym, "precision loss in 32-bit float type assignment"); + value->type = vt_invalid; + return -1; + } + value->f = f; + value->type = vt_float; + return 0; + case vt_uint: + f = (float)value->u; + if ((uint64_t)f != value->u) { + /* We could make this a warning. */ + error_sym(P, sym, "precision loss in 32-bit float type assignment"); + value->type = vt_invalid; + return -1; + } + value->f = f; + value->type = vt_float; + return 0; + case vt_float: + return 0; + default: + error_sym(P, sym, "32-bit float type only accepts integer and float values"); + value->type = vt_invalid; + return -1; + } + default: + error_sym(P, sym, "scalar type expected"); + value->type = vt_invalid; + return -1; + } +} + |