aboutsummaryrefslogtreecommitdiff
path: root/src/compiler/coerce.c
diff options
context:
space:
mode:
authorToni Uhlig <matzeton@googlemail.com>2023-07-16 02:03:33 +0200
committerToni Uhlig <matzeton@googlemail.com>2023-07-16 02:03:33 +0200
commit5a40295c4cf0af5ea8da9ced04a4ce7d3621a080 (patch)
treecb21506e7b04d10b45d6066a0ee1655563d5d52b /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.c266
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;
+ }
+}
+