fix float formats; fix sime defval edge cases
[m6w6/ext-psi] / src / types / impl_def_val.c
index eec90e6783ea123dac39de26f5eb058a25b92909..e8d7b4697a0d1b38bde3f3402fccda9f1ea58d4e 100644 (file)
@@ -26,6 +26,8 @@
 #include "php_psi_stdinc.h"
 #include "data.h"
 
+#include "zend_smart_str.h"
+
 #include <assert.h>
 #include <math.h>
 
@@ -37,7 +39,11 @@ struct psi_impl_def_val *psi_impl_def_val_init(token_t t, void *data)
 
        switch ((def->type = t)) {
        case PSI_T_TRUE:
+               def->ival.zend.bval = 1;
+               /* no break */
        case PSI_T_FALSE:
+               def->ityp = PSI_T_UINT8;
+               /* no break */
        case PSI_T_NULL:
                break;
        case PSI_T_QUOTED_STRING:
@@ -85,192 +91,262 @@ void psi_impl_def_val_free(struct psi_impl_def_val **def_ptr)
        }
 }
 
-bool psi_impl_def_val_validate(struct psi_data *data,
-               struct psi_impl_def_val *val, struct psi_impl_type *type,
-               struct psi_validate_scope *scope)
+void psi_impl_def_val_get_zval(struct psi_impl_def_val *val, token_t typ, zval *zv)
 {
-       if (val->type == PSI_T_NULL) {
-               return true;
-       } else if (val->type == PSI_T_NUMBER) {
-               if (!psi_num_exp_validate(data, val->data.num, scope)) {
-                       return false;
-               }
-       }
+       impl_val tmp = val->ival;
 
-       switch (type ? type->type : PSI_T_MIXED) {
+       /* c->val has already been forced to the type of the containing constant or impl_arg */
+       switch (typ) {
        case PSI_T_BOOL:
-               val->ival.zend.bval = val->type == PSI_T_TRUE ? 1 : 0;
-               return true;
-               break;
-
-       /* macros */
-       case PSI_T_NUMBER:
-               if (val->type == PSI_T_NUMBER) {
-                       token_t typ = psi_num_exp_exec(val->data.num, &val->ival, NULL, scope->cpp);
-
-                       switch (typ) {
-                       case PSI_T_FLOAT:
-                               val->ival.dval = val->ival.fval;
-                               /* no break */
-                       case PSI_T_DOUBLE:
-                               val->type = PSI_T_FLOAT;
-                               type->type = PSI_T_FLOAT;
-                               zend_string_release(type->name);
-                               type->name = zend_string_init_interned(ZEND_STRL("float"), 1);
-                               break;
-                       case PSI_T_UINT64:
-                               if (val->ival.u64 > ZEND_LONG_MAX) {
-                                       data->error(data, val->token, PSI_WARNING,
-                                                       "Integer too big for signed representation: '%" PRIu64 "'",
-                                                       val->ival.u64);
-                               }
-                       default:
-                               /* FIXME big integers */
-                               val->type = PSI_T_INT;
-                               type->type = PSI_T_INT;
-                               zend_string_release(type->name);
-                               type->name = zend_string_init_interned(ZEND_STRL("int"), 1);
-                               break;
-                       }
-                       psi_num_exp_free(&val->data.num);
-                       return true;
-               }
+               ZVAL_BOOL(zv, tmp.zend.bval);
                break;
-
        case PSI_T_INT:
-               if (val->type == PSI_T_NUMBER) {
-                       val->type = PSI_T_INT;
-                       val->ival.zend.lval = psi_num_exp_get_long(val->data.num, NULL, scope->cpp);
-#if PSI_IMPL_DEF_VAL_DEBUG
-                       PSI_DEBUG_PRINT(data, "PSI: NUMBER (long) %" PRIi64 " from ", val->ival.zend.lval);
-                       PSI_DEBUG_DUMP(data, psi_num_exp_dump, val->data.num);
-                       PSI_DEBUG_PRINT(data, "\n");
-#endif
-                       psi_num_exp_free(&val->data.num);
-               }
-               if (val->type == PSI_T_INT) {
-                       return true;
-               }
+               ZVAL_LONG(zv, tmp.zend.lval);
                break;
-
        case PSI_T_FLOAT:
        case PSI_T_DOUBLE:
-               if (val->type == PSI_T_NUMBER) {
-                       val->type = PSI_T_DOUBLE;
-                       val->ival.dval = psi_num_exp_get_double(val->data.num, NULL, scope->cpp);
-#if PSI_IMPL_DEF_VAL_DEBUG
-                       PSI_DEBUG_PRINT(data, "PSI: NUMBER (double) %" PRIdval " from ", val->ival.dval);
-                       PSI_DEBUG_DUMP(data, psi_num_exp_dump, val->data.num);
-                       PSI_DEBUG_PRINT(data, "\n");
-#endif
-                       psi_num_exp_free(&val->data.num);
-               }
-               if (val->type == PSI_T_DOUBLE) {
-                       return true;
-               }
+       is_double: ;
+               ZVAL_DOUBLE(zv, tmp.dval);
                break;
-
        case PSI_T_STRING:
-               if (val->type == PSI_T_STRING) {
-                       return true;
+       is_string: ;
+               ZVAL_NEW_STR(zv, zend_string_copy(tmp.zend.str));
+               if (ZSTR_IS_INTERNED(Z_STR_P(zv))) {
+                       Z_TYPE_FLAGS_P(zv) = 0;
                }
                break;
-
        case PSI_T_MIXED:
                switch (val->type) {
+               case PSI_T_NULL:
+                       ZVAL_NULL(zv);
+                       break;
                case PSI_T_TRUE:
+                       ZVAL_TRUE(zv);
+                       break;
                case PSI_T_FALSE:
-               case PSI_T_NULL:
+                       ZVAL_FALSE(zv);
+                       break;
                case PSI_T_STRING:
-               case PSI_T_FLOAT:
-               case PSI_T_DOUBLE:
-               case PSI_T_INT:
-                       return true;
-               default:
+                       goto is_string;
                        break;
-               }
-               if (val->type == PSI_T_NUMBER) {
-                       token_t typ = psi_num_exp_exec(val->data.num, &val->ival, NULL, scope->cpp);
-
-                       switch (typ) {
+               case PSI_T_NUMBER:
+                       switch (val->ityp) {
+                       case PSI_T_INT8:
+                       case PSI_T_UINT8:
+                       case PSI_T_INT16:
+                       case PSI_T_UINT16:
+                       case PSI_T_INT32:
+                       case PSI_T_UINT32:
+                               psi_calc_cast(val->ityp, &tmp, PSI_T_INT64, &tmp);
+                               /* no break */
+                       case PSI_T_INT64:
+                               ZVAL_LONG(zv, tmp.i64);
+                               break;
+                       case PSI_T_UINT64:
+                               ZVAL_LONG_DOUBLE_STR(zv, tmp.u64, is_signed=false;persistent=true);
+                               break;
+#if HAVE_INT128
+                       case PSI_T_INT128:
+                               ZVAL_LONG_DOUBLE_STR(zv, tmp.i128, is_signed=true;persistent=true);
+                               break;
+                       case PSI_T_UINT128:
+                               ZVAL_LONG_DOUBLE_STR(zv, tmp.u128, is_signed=false;persistent=true);
+#endif
+                               break;
                        case PSI_T_FLOAT:
+                               ZVAL_DOUBLE(zv, tmp.fval);
+                               break;
+                       case PSI_T_DOUBLE:
+                               goto is_double;
+                               break;
 #if HAVE_LONG_DOUBLE
                        case PSI_T_LONG_DOUBLE:
+                               ZVAL_DOUBLE(zv, tmp.ldval);
+                               break;
 #endif
-                       promote_double: ;
-                               psi_calc_cast(typ, &val->ival, PSI_T_DOUBLE, &val->ival);
-                               /* no break */
-                       case PSI_T_DOUBLE:
-                               val->type = PSI_T_DOUBLE;
-                               return true;
-                       case PSI_T_UINT64:
-                               if (val->ival.u64 > (uint64_t) ZEND_LONG_MAX) {
-                                       if (val->ival.u64 > (1LU<<53)) {
-                                               char buf[0x20];
+                       default:
+                               assert(0);
+                       }
+                       break;
+               default:
+                       assert(0);
+                       break;
+               }
+               break;
+       default:
+               assert(0);
+               break;
+       }
 
-                                               /* convert to string */
-                                               char *res = zend_print_ulong_to_buf(buf + sizeof(buf) - 1, val->ival.u64);
-                                               val->type = PSI_T_STRING;
-                                               val->ival.zend.str = zend_string_init(res, buf + sizeof(buf) - 1 - res, 1);
+}
 
-                                               return true;
-                                       }
-                                       goto promote_double;
-                               }
+static inline bool psi_impl_def_val_validate_impl_type(struct psi_data *data,
+               struct psi_impl_def_val *val, struct psi_impl_type *type,
+               struct psi_validate_scope *scope)
+{
+       switch (type->type) {
+       case PSI_T_BOOL:
+               switch (val->type) {
+               case PSI_T_TRUE:
+               case PSI_T_FALSE:
+                       return true;
+               case PSI_T_STRING:
+                       if (val->ival.zend.str) {
+                               zend_string *tmp = val->ival.zend.str;
+
+                               val->ival.zend.bval = (*tmp->val && *tmp->val != '0');
+                               zend_string_release(tmp);
+                       }
+                       val->ityp = PSI_T_UINT8;
+                       val->type = PSI_T_BOOL;
+                       return true;
+               default:
+                       assert(0);
+                       break;
+               }
+               break;
+       case PSI_T_INT:
+               if (val->type == PSI_T_STRING) {
+                       zend_string *str = val->ival.zend.str;
+                       switch (is_numeric_str_function(str, &val->ival.zend.lval, &val->ival.dval)) {
+                       case IS_DOUBLE:
+                               val->ival.zend.lval = zend_dval_to_lval_cap(val->ival.dval);
                                /* no break */
+                       case IS_LONG:
+                               break;
                        default:
-                               psi_calc_cast(typ, &val->ival, PSI_T_INT64, &val->ival);
+                               val->ival.zend.lval = 0;
+                       }
+                       zend_string_release(str);
+                       val->ityp = PSI_T_INT64;
+                       return true;
+               }
+               psi_calc_cast(val->ityp, &val->ival, PSI_T_INT64, &val->ival);
+               val->type = PSI_T_INT;
+               val->ityp = PSI_T_INT64;
+               return true;
+       case PSI_T_FLOAT:
+       case PSI_T_DOUBLE:
+               if (val->type == PSI_T_STRING) {
+                       zend_string *str = val->ival.zend.str;
+                       switch (is_numeric_str_function(str, &val->ival.zend.lval, &val->ival.dval)) {
+                       case IS_LONG:
+                               val->ival.dval = val->ival.zend.lval;
                                /* no break */
-                       case PSI_T_INT64:
-                               val->type = PSI_T_INT;
-                               return true;
+                       case IS_DOUBLE:
+                               break;
+                       default:
+                               val->ival.dval = 0;
                        }
+                       zend_string_release(str);
+                       val->type = val->ityp = PSI_T_DOUBLE;
+                       return true;
                }
-               /* no break */
+               psi_calc_cast(val->ityp, &val->ival, PSI_T_DOUBLE, &val->ival);
+               val->ityp = PSI_T_DOUBLE;
+               return true;
+       case PSI_T_STRING:
+               if (val->type == PSI_T_STRING) {
+                       return true;
+               } else {
+                       smart_str str = {0};
+                       struct psi_dump dump = {{.hn = &str},
+                                       .fun = (psi_dump_cb) smart_str_append_printf};
+
+                       switch (val->ityp) {
+                       CASE_IMPLVAL_NUM_DUMP(&dump, val->ival, false);
+                       default:
+                               assert(0);
+                       }
+                       val->ival.zend.str = smart_str_extract(&str);
+                       val->type = PSI_T_STRING;
+                       return true;
+               }
+               break;
        default:
                data->error(data, val->token, PSI_WARNING,
                                "Invalid default value type '%s', "
-                               "expected one of bool, int, float, string.",
-                               type ? type->name->val : "mixed");
+                               "expected one of bool, int, float/double or string.",
+                               type->name->val);
+
+       }
+       return false;
+}
+
+bool psi_impl_def_val_validate(struct psi_data *data,
+               struct psi_impl_def_val *val, struct psi_impl_type *type,
+               struct psi_validate_scope *scope)
+{
+       /* NULL can be anything */
+       if (val->type == PSI_T_NULL) {
+               return true;
+       }
+
+       /* a number can be anything */
+       if (val->type == PSI_T_NUMBER) {
+               if (!psi_num_exp_validate(data, val->data.num, scope)) {
+                       return false;
+               }
+               val->ityp = psi_num_exp_exec(val->data.num, &val->ival, NULL, scope->cpp);
+       }
+
+       /* forced type, like `const <type> foo`, or function param `<type> $xyz` */
+       if (type) {
+               return psi_impl_def_val_validate_impl_type(data, val, type, scope);
+       }
+
+       switch (val->type) {
+       case PSI_T_NUMBER:
+       case PSI_T_NULL:
+       case PSI_T_TRUE:
+       case PSI_T_FALSE:
+       case PSI_T_STRING:
+               return true;
+       default:
+               assert(0);
+               break;
        }
 
        return false;
 }
 
-void psi_impl_def_val_dump(int fd, struct psi_impl_def_val *val) {
+void psi_impl_def_val_dump(struct psi_dump *dump, struct psi_impl_def_val *val) {
        switch (val->type) {
        case PSI_T_NULL:
-               dprintf(fd, "NULL");
+               PSI_DUMP(dump, "NULL");
                break;
        case PSI_T_TRUE:
-               dprintf(fd, "true");
+               PSI_DUMP(dump, "true");
                break;
        case PSI_T_FALSE:
-               dprintf(fd, "false");
+               PSI_DUMP(dump, "false");
                break;
        case PSI_T_BOOL:
-               dprintf(fd, "%s", val->ival.zend.bval ? "true" : "false");
+               PSI_DUMP(dump, "%s", val->ival.zend.bval ? "true" : "false");
                break;
        case PSI_T_INT:
-               dprintf(fd, ZEND_LONG_FMT, val->ival.zend.lval);
+               PSI_DUMP(dump, ZEND_LONG_FMT, val->ival.zend.lval);
                break;
        case PSI_T_FLOAT:
        case PSI_T_DOUBLE:
                if (isinf(val->ival.dval)) {
-                       dprintf(fd, "\\INF");
+                       PSI_DUMP(dump, "\\INF");
                } else if (isnan(val->ival.dval)) {
-                       dprintf(fd, "\\NAN");
+                       PSI_DUMP(dump, "\\NAN");
                } else {
-                       dprintf(fd, "%" PRIdval, val->ival.dval);
+                       PSI_DUMP(dump, "%" PRIdval, val->ival.dval);
                }
                break;
        case PSI_T_STRING:
-               dprintf(fd, "\"%s\"", val->ival.zend.str->val);
+               PSI_DUMP(dump, "\"%s\"", val->ival.zend.str->val);
                break;
        case PSI_T_NUMBER:
-               psi_num_exp_dump(fd, val->data.num);
+               psi_num_exp_dump(dump, val->data.num);
                break;
        default:
                assert(0);
        }
+#if 0
+       PSI_DUMP(dump, "\t/* impl_def_val.type=%d */ ", val->type);
+#endif
 }