Commit 6ae5bdcf authored by Jonathan Wilkes's avatar Jonathan Wilkes
Browse files

flesh out handling of overflows, improve messages to explain these type issues

parent 263341a3
......@@ -45,31 +45,106 @@ static t_symbol *class_extern_dir = &s_;
int symbol_can_float(t_symbol *s, t_float *f);
char *type_hint(t_symbol *s, int argc, t_atom *argv)
/* try to give the user some help with the uglier cases of unexpected
atom types in messages.
dostof flag controls whether we want to try to convert a symbol
to a float.
for [float]'s symbol conversion we even check if we received an
out of range error, but here for the general case we don't do that. */
char *type_hint(t_symbol *s, int argc, t_atom *argv, int dostof)
{
static char hint[MAXPDSTRING];
t_float f;
t_float f = 0;
/* Null selectors-- the user typically shouldn't encounter these,
especially one that triggers an unknown method error. But we
check for them anyway. */
if (!s)
sprintf(hint, " (null selector detected)");
else if (s == &s_)
sprintf(hint, " (empty symbol selector)");
else if (s && s == &s_symbol && argc && argv->a_type == A_SYMBOL
{
sprintf(hint, " (Note: null selector detected)");
return hint;
}
/* Empty symbol selector is also rare. But a user can easily generate
one with [symbol( or [symbol] then send to [list trim]. */
if (s == &s_)
{
sprintf(hint, " (Note: empty symbol selector)");
return hint;
}
/* More commonly, the user may have a "symbol" message where the
payload is an empty symbol. This can cause confusion for debugging
since the empty symbol doesn't print out anything. */
if (s && s == &s_symbol && argc && argv->a_type == A_SYMBOL
&& argv->a_w.w_symbol == &s_)
sprintf(hint, " (empty symbol message detected)");
else if (symbol_can_float(s, &f))
sprintf(hint, " (%s is actually a symbol atom here)", s->s_name);
else if (symbol_can_float(atom_getsymbolarg(0, argc, argv), &f))
sprintf(hint, " (symbol message with floatlike payload detected."
" Did you mean 'float %s'?)", argv->a_w.w_symbol->s_name);
else
hint[0] = '\0';
{
sprintf(hint, " (Note: empty symbol message payload detected)");
return hint;
}
/* Somewhat common edge case-- [makefilename] and other objects can
output a symbol message with a payload that looks numeric but is
indeed a symbol atom. In fact, Pd's text parser would interpret
such a string as a float if you sent it through the parser.
This can cause problems if the user tries to serialize the data
and read it back-- say, by saving the symbol message "symbol 123"
in a file. The next time they load it, "123" will be parsed as
a float atom, and any symbol methods will read from the wrong
union field and (probably) interpret it as an empty symbol. */
if (dostof)
{
if (symbol_can_float(atom_getsymbolarg(0, argc, argv), &f))
{
sprintf(hint, " (Note: this symbol message has a floatlike payload "
"which cannot be saved properly. Did you mean 'float %s'?)",
argv->a_w.w_symbol->s_name);
return hint;
}
else if (f == -1 || f == 1)
{
/* For values which would overflow, give a hint but don't
suggest float type */
sprintf(hint, " (Note: this symbol message has an %s floatlike "
"payload which cannot be saved properly.",
f == 1 ? "overflowing" : "underflowing");
return hint;
}
}
/* Rather uncommon case where the selector itself is a symbol atom
that would normally have been parsed as a float. */
if (dostof)
{
if (symbol_can_float(s, &f))
{
sprintf(hint, " (Note: %s looks like a float but is actually a "
"symbol which cannot be saved properly)", s->s_name);
return hint;
}
else if (f == -1 || f == 1)
{
/* For values which would overflow, give a hint but don't
suggest float type */
sprintf(hint, " (Note: this symbol message has an %s floatlike "
"payload which cannot be saved properly.",
f == 1 ? "overflowing" : "underflowing");
return hint;
}
}
hint[0] = '\0';
return hint;
}
static void pd_defaultanything(t_pd *x, t_symbol *s, int argc, t_atom *argv)
{
pd_error(x, "%s: no method for '%s'%s",
(*x)->c_name->s_name, s->s_name, type_hint(s, argc, argv));
(*x)->c_name->s_name, s->s_name, type_hint(s, argc, argv,
*(*x)->c_floatmethod != pd_defaultfloat ? 1 : 0));
}
static void pd_defaultbang(t_pd *x)
......
......@@ -72,12 +72,12 @@ t_inlet *signalinlet_new(t_object *owner, t_float f)
return (x);
}
char *type_hint(t_symbol *s, int argc, t_atom *argv);
char *type_hint(t_symbol *s, int argc, t_atom *argv, int dostof);
static void inlet_wrong(t_inlet *x, t_symbol *s, int argc, t_atom *argv)
{
pd_error(x->i_owner, "inlet: expected '%s' but got '%s'%s",
x->i_symfrom->s_name, s->s_name, type_hint(s, argc, argv));
x->i_symfrom->s_name, s->s_name, type_hint(s, argc, argv, 1));
}
static void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv);
......
......@@ -8,6 +8,8 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
extern t_pd *newest;
......@@ -86,33 +88,56 @@ static void pdfloat_float(t_pdfloat *x, t_float f)
outlet_float(x->x_obj.ob_outlet, x->x_f = f);
}
/* check if a symbol payload can be interpreted as a floating point number.
We get these cases sometimes from [makefilename], [keyname], and some
externals that shoot out data that would be parsed as a float if loaded
from a file.
return values are: 0 not a number
1 successfully parsed as float (stored in f)
if the number would underflow f is set to -1
if the number would overflow f is set to greater than zero
*/
int symbol_can_float(t_symbol *s, t_float *f)
{
char c;
if (!s || s == &s_) return 0;
int ret;
char c, *str_end;
c = s->s_name[0];
if (c != '-' && c != '+' && c < 48 && c > 57) return 0;
char *str_end = NULL;
if (c != '-' && c != '+' && (c < 48 || c > 57)) return 0;
errno = 0;
*f = strtod(s->s_name, &str_end);
/* Add error checking here like in cxc/hex2dec */
if (*f == 0 && s->s_name == str_end)
return 0;
return 1;
if (errno == ERANGE)
{
ret = 0;
if (*f == 0) *f = -1; /* underflow */
else *f = 1; /* assume overflow otherwise */
}
else if (*f == 0 && s->s_name == str_end)
{
ret = 0;
}
else
ret = 1;
return ret;
}
char *type_hint(t_symbol *s, int argc, t_atom *argv);
char *type_hint(t_symbol *s, int argc, t_atom *argv, int check_symforfloat);
static void pdfloat_symbol(t_pdfloat *x, t_symbol *s)
{
t_float f;
t_float f = 0;
if (symbol_can_float(s, &f))
outlet_float(x->x_obj.ob_outlet, x->x_f = f);
else
{
t_atom at;
SETSYMBOL(&at, s);
pd_error(x, "couldn't convert %s to float%s",
s->s_name, type_hint(&s_symbol, 1, &at));
pd_error(x, "couldn't convert 'symbol %s' to float%s",
s->s_name,
f == -1 ? " (number too small)" :
f == 1 ? " (number too large)" :
type_hint(&s_symbol, 1, &at, 0));
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment