diff --git a/doc/teapot.lyx b/doc/teapot.lyx index daa22c1..f7dce0a 100644 --- a/doc/teapot.lyx +++ b/doc/teapot.lyx @@ -4960,7 +4960,7 @@ and \series default \emph default -y evaluates to the logical conjunction of boolean values +y evaluates to the logical conjunction of values \emph on x \emph default @@ -4979,6 +4979,32 @@ y \emph default is never evaluated, and so does not affect the value even if it is an error, for example. + In determining the value, the boolean conversions of +\emph on +x +\emph default + and +\emph on +y +\emph default + are used (see the description of the bool() function below), but the actual + value returned is either that of +\emph on +x +\emph default + or +\emph on +y +\emph default +, namely, +\emph on +x +\emph default + if it corresponds to a boolean false value or +\emph on +y +\emph default + otherwise. \end_layout \begin_layout Description @@ -5024,6 +5050,15 @@ and x \emph default is true. + Also in similar fashion, the value is +\emph on +x +\emph default + if it corresponds to a boolean true value and +\emph on +y +\emph default + otherwise. \end_layout \begin_layout Description @@ -6442,24 +6477,41 @@ or \end_layout \begin_layout Description -bool currently only acts as a keyword to + +\series medium +bool +\begin_inset space ~ +\end_inset + + +\series default +bool +\series medium +[([ +\emph on +x +\emph default +])] +\series default + Converts +\emph on +x +\emph default +to a boolean value. + This is very permissive; error values are unaffected; empty, integer 0, + float 0.0, and boolean false values are false, and everything else is true. + If +\emph on +x +\emph default + is omitted, defaults to the value of the current cell. + If the parentheses are omitted as well, acts as a keyword, for example + for testing types with \family sans \series bold is \series default -(); -\family default -there are not currently any conversions to boolean type. - Use e.g. - -\family sans - -\shape italic -expr -\shape default - != 0 -\family default -to obtain a boolean value from a numerical expression. +(). \end_layout \begin_layout Description @@ -6755,6 +6807,69 @@ function identifier, There is probably little practical call for this type. \end_layout +\begin_layout Description +find +\series medium +( +\emph on +expr +\emph default +, +\emph on + +\begin_inset space ~ +\end_inset + + +\emph default +location +\emph on + +\begin_inset space ~ +\end_inset + +stride +\emph default +[,location +\emph on + +\begin_inset space ~ +\end_inset + +start +\emph default +]) examines cells in turn, returning the location of the first one at which + +\series default +\emph on +expr +\emph default + evaluates to true, or boolean false if the boundary of the sheet is encountered. + The second argument +\emph on +stride +\emph default + must not have all zero components, and is added to the location being examined + at each iteration. + Often it is useful to use a direction constant like +\family sans +\series bold +up +\family default +\series default + for the stride. + The search begins at location +\emph on +start +\emph default +, which defaults to the current location plus the +\emph on +stride +\emph default + if it is not specified. + +\end_layout + \begin_layout Description \series medium diff --git a/src/common/eval.c b/src/common/eval.c index e9d0288..448654c 100644 --- a/src/common/eval.c +++ b/src/common/eval.c @@ -946,33 +946,42 @@ Token tne(Token l, Token r) } /*}}}*/ +/* tbool -- token considered as a boolean value */ /*{{{*/ +Token tbool(Token x) +{ + Token result; + result.type = BOOL; + result.u.bl = true; + switch (x.type) { + case EEK: return x; + case EMPTY: result.u.bl = false; return result; + case BOOL: return x; + case INT: result.u.bl = (x.u.integer != 0); return result; + case STRING: result.u.bl = (strlen(x.u.string) > 0); return result; + case FLOAT: result.u.bl = x.u.flt != 0.0; return result; + default: break; /* everything else is true */ + } + return result; +} +/*}}}*/ + /* tor -- logical or of two tokens */ /*{{{*/ Token tor(Token l, Token r) { - switch (l.type) { - case EEK: return tcopy(l); - case EMPTY : return tcopy(r); - case BOOL: - if (r.type == EMPTY || l.u.bl) return l; - return r; - default: break; - } - return operand_type_error("or: ", l.type, r.type); + if (l.type == EEK) return tcopy(l); + Token lbool = tbool(l); + if (lbool.u.bl) return tcopy(l); + return tcopy(r); } /*}}}*/ /* tand -- logical and of two tokens */ /*{{{*/ Token tand(Token l, Token r) { - switch (l.type) { - case EEK: return tcopy(l); - case EMPTY : return l; - case BOOL: - if (!l.u.bl) return l; - return r; - default: break; - } - return operand_type_error("and: ", l.type, r.type); + if (l.type == EEK) return tcopy(l); + Token lbool = tbool(l); + if (!lbool.u.bl) return tcopy(l); + return tcopy(r); } /*}}}*/ diff --git a/src/common/eval.h b/src/common/eval.h index e2660af..b32f134 100644 --- a/src/common/eval.h +++ b/src/common/eval.h @@ -25,6 +25,7 @@ Token tgt(Token l, Token r); Token teq(Token l, Token r); Token tabouteq(Token l, Token r); Token tne(Token l, Token r); +Token tbool(Token x); Token tor(Token l, Token r); Token tand(Token l, Token r); #endif diff --git a/src/common/func.c b/src/common/func.c index dae1ff9..52bd696 100644 --- a/src/common/func.c +++ b/src/common/func.c @@ -512,6 +512,54 @@ static Token dim_func(FunctionIdentifier self, int argc, const Token argv[]) } /*}}}/ + /* find_macro -- search for a cell satisfying some expression */ /*{{{*/ +static Token find_macro(FunctionIdentifier self, int argc, const Token argv[]) +{ + const char *usage = _("Usage: find(expr, loc_stride[, loc_start])"); + Token result; + if (argc < 2 || argc > 3) return duperror(&result, usage); + Token tstride = evaltoken(argv[1], FULL); + if (tstride.type == EEK) return tstride; + if (tstride.type != LOCATION) { + tfree(&tstride); + return duperror(&result, usage); + } + Location stride; + LOCATION_GETS(stride, tstride.u.location); + if (stride[X] == 0 && stride[Y] == 0 && stride[Z] == 0) + return duperror(&result, _("Stride in find() must be nonzero.")); + Location current; + if (argc == 3) { + Token tstart = evaltoken(argv[2], FULL); + if (tstart.type == EEK) return tstart; + if (tstart.type != LOCATION) { + tfree(&tstart); + return duperror(&result, usage); + } + LOCATION_GETS(current, tstart.u.location); + } else { + LOCATION_GETS(current, upd_l); + LOCATION_ADD(current, stride); + } + result.type = BOOL; + result.u.bl = false; + while (IN_OCTANT(current) && LOC_WITHIN(upd_sheet, current)) { + Token tprobe = evaluate_at(argv[0], upd_sheet, current); + Token probe = tbool(tprobe); + tfree_protected(&tprobe, probe); + if (probe.type == EEK) return probe; + assert(probe.type == BOOL); + if (probe.u.bl) { + result.type = LOCATION; + LOCATION_GETS(result.u.location, current); + return result; + } + LOCATION_ADD(current, stride); + } + return result; +} +/*}}}*/ + /* bit_func -- common implementation of the bitwise functions */ /*{{{*/ static Token bit_func(FunctionIdentifier self, int argc, const Token argv[]) { @@ -637,6 +685,21 @@ static Token self_func(FunctionIdentifier self, int argc, const Token argv[]) } /*}}}*/ +static Token bool_func(FunctionIdentifier self, int argc, const Token argv[]) +{ + assert(self == FUNC_BOOL); + Token arg; + switch (argc) { + case -1: return self_func(self, argc, argv); + case 0: arg = recompvalue(upd_sheet, upd_l); break; + case 1: arg = tcopy(argv[0]); break; + default: return duperror(&arg, _("Usage:bool[([val])]")); + } + Token result = tbool(arg); + tfree_protected(&arg, result); + return result; +} + /* empty -- ignores arguments and returns empty value */ /*{{{*/ static Token empty_func(FunctionIdentifier self, int argc, const Token argv[]) { @@ -688,48 +751,6 @@ static Token float_func(FunctionIdentifier self, int argc, const Token argv[]) } /*}}}*/ -/* number -- convert to most appropriate number type */ /*{{{*/ -static Token number_func(FunctionIdentifier self, int argc, const Token argv[]) -{ - assert(self == FUNC_NUMBER); - const char *usage = _("Usage: number[([bool|empty|int|float|string x])]"); - Token result, arg; - result.type = INT; - result.u.integer = 0; - - switch (argc) { - case -1: return self_func(self, argc, argv); - case 0: arg = recompvalue(upd_sheet, upd_l); break; - case 1: arg = tcopy(argv[0]); break; - default: return duperror(&result, usage); - } - switch (arg.type) { - case INT: return arg; - case FLOAT: return arg; - case BOOL: if (arg.u.bl) result.u.integer = 1; /* FALL THROUGH */ - case EMPTY: return result; - case STRING: { - char *p = arg.u.string; - Token *st = scan_integer(&p); - if (st != NULLTOKEN && *p == '\0') result = *st; - else { - p = arg.u.string; - st = scan_flt(&p); - if (st == NULLTOKEN || *p != '\0') - duperror(&result, _("string does not represent a number")); - else result = *st; - } - free(st); - tfree(&arg); - return result; - } - default: break; - } - tfree(&arg); - return duperror(&result, usage); -} -/*}}}*/ - /* int */ /*{{{*/ static Token int_func(FunctionIdentifier self, int argc, const Token argv[]) { @@ -786,6 +807,47 @@ static Token int_func(FunctionIdentifier self, int argc, const Token argv[]) } /*}}}*/ +/* number -- convert to most appropriate number type */ /*{{{*/ +static Token number_func(FunctionIdentifier self, int argc, const Token argv[]) +{ + assert(self == FUNC_NUMBER); + const char *usage = _("Usage: number[([bool|empty|int|float|string x])]"); + Token result, arg; + result.type = INT; + result.u.integer = 0; + + switch (argc) { + case -1: return self_func(self, argc, argv); + case 0: arg = recompvalue(upd_sheet, upd_l); break; + case 1: arg = tcopy(argv[0]); break; + default: return duperror(&result, usage); + } + switch (arg.type) { + case INT: return arg; + case FLOAT: return arg; + case BOOL: if (arg.u.bl) result.u.integer = 1; /* FALL THROUGH */ + case EMPTY: return result; + case STRING: { + char *p = arg.u.string; + Token *st = scan_integer(&p); + if (st != NULLTOKEN && *p == '\0') result = *st; + else { + p = arg.u.string; + st = scan_flt(&p); + if (st == NULLTOKEN || *p != '\0') + duperror(&result, _("string does not represent a number")); + else result = *st; + } + free(st); + tfree(&arg); + return result; + } + default: break; + } + tfree(&arg); + return duperror(&result, usage); +} +/*}}}*/ /* string */ /*{{{*/ static Token string_func(FunctionIdentifier self, int argc, const Token argv[]) @@ -972,7 +1034,16 @@ static Token region_func(RegFuncInit init, RegFuncUpdt updt, RegFuncFinl finl, Token tmp = recompvalue(upd_sheet, w); updt(id, &l, &t, &w, &tmp); tfree_protected(&tmp, t); - if (t.type == EEK) return t; + if (t.type == EEK) { + const char *templ = _("While computing %s() at &(%d,%d,%d): %s"); + Token report; + report.type = EEK; + report.u.err = + malloc(strlen(templ) + MAX_FUNC_NAME_LENGTH + 3*20 + strlen(t.u.err)); + sprintf(report.u.err, templ, tfunc[id].name, w[X], w[Y], w[Z], t.u.err); + tfree(&t); + return report; + } } if (finl != (RegFuncFinl)0) finl(id, &l, &t); return t; @@ -1539,21 +1610,22 @@ Tfunc tfunc[]= [FUNC_CARET] = { "^", accum_func, INFIX_POW, FUNCT, 0 }, /* Addressing/cell fetching/cell-position functions */ - [FUNC_AMPERSAND] = { "&", adr_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_D] = { "D", adr_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_X_AMPERSAND] = { "X&", adr_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_AT_SYMBOL] = { "@", at_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_R] = { "R", at_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_CAP_X] = { "X", at_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_RIGHT] = { "right", disp_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_LEFT] = { "left", disp_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_DOWN] = { "down", disp_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_UP] = { "up", disp_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_BELOW] = { "below", disp_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_ABOVE] = { "above", disp_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_X] = { "x", dim_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_Y] = { "y", dim_func, PREFIX_FUNC, FUNCT, 0 }, - [FUNC_Z] = { "z", dim_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_AMPERSAND] = { "&", adr_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_D] = { "D", adr_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_X_AMPERSAND] = { "X&", adr_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_AT_SYMBOL] = { "@", at_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_R] = { "R", at_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_CAP_X] = { "X", at_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_RIGHT] = { "right", disp_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_LEFT] = { "left", disp_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_DOWN] = { "down", disp_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_UP] = { "up", disp_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_BELOW] = { "below", disp_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_ABOVE] = { "above", disp_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_X] = { "x", dim_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_Y] = { "y", dim_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_Z] = { "z", dim_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_FIND] = { "find", find_macro, PREFIX_FUNC, MACRO, 0 }, /* Evaluation control functions */ [FUNC_CLOCK] = { "clock", clock_func, PREFIX_FUNC, FUNCT, 0 }, @@ -1566,7 +1638,7 @@ Tfunc tfunc[]= [FUNC_TRUE] = { "true", blcnst_func, PREFIX_FUNC, FUNCT, 0 }, /* Type conversion/testing functions and keywords */ - [FUNC_BOOL] = { "bool", self_func, PREFIX_FUNC, FUNCT, 0 }, + [FUNC_BOOL] = { "bool", bool_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_EMPTY] = { "empty", empty_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_ERROR] = { "error", error_func, PREFIX_FUNC, FUNCT, 0 }, [FUNC_FIDENT] = { "fident", self_func, PREFIX_FUNC, FUNCT, 0 }, diff --git a/src/common/func.h b/src/common/func.h index 4166580..ee4128e 100644 --- a/src/common/func.h +++ b/src/common/func.h @@ -37,7 +37,7 @@ typedef enum FUNC_BOOL, FUNC_EMPTY, FUNC_FIDENT, FUNC_FUNCALL, FUNC_LIDENT, FUNC_LOCATION, FUNC_NUMBER, FUNC_OPERATOR, - FUNC_IS, + FUNC_IS, FUNC_FIND, N_FUNCTION_IDS } FunctionIdentifier; diff --git a/src/common/scanner.c b/src/common/scanner.c index bfd2c33..7a44d4c 100644 --- a/src/common/scanner.c +++ b/src/common/scanner.c @@ -133,15 +133,19 @@ static Token *charstring(char **s) Token *scan_integer(char **s) { char *r = *s; + bool ishex = (*s)[0] == '0' && (*s)[1] == 'x'; IntT i = STRTOINT(r, s, 0); - if (*s != r && **s != '.' && **s != 'e') - { - Token *n = malloc(sizeof(Token)); - n->type = INT; - n->u.integer = i; - return n; + if (*s == r + || **s == '.' || **s == 'e' || **s == 'E' || (ishex && **s == 'p')) + { /* either doesn't look like a number, or looks like a float, not an int */ + *s = r; + return NULLTOKEN; } - else { *s = r; return NULLTOKEN; } + /* looks like an int */ + Token *n = malloc(sizeof(Token)); + n->type = INT; + n->u.integer = i; + return n; } /*}}}*/ diff --git a/src/common/sheet.c b/src/common/sheet.c index 5ca7f6b..84ac653 100644 --- a/src/common/sheet.c +++ b/src/common/sheet.c @@ -611,6 +611,22 @@ Token recompvalue(Sheet *sheet, const Location at) } /*}}}*/ +/* evaluate_at -- evaluate a token in a sheet at a location */ /*{{{*/ +Token evaluate_at(Token t, Sheet *sheet, const Location at) +{ + assert(sheet != (Sheet *)0); + Sheet *oldsheet = upd_sheet; + upd_sheet = sheet; + Location old_l; + LOCATION_GETS(old_l, upd_l); + LOCATION_GETS(upd_l, at); + Token result = evaltoken(t, FULL); + LOCATION_GETS(upd_l, old_l); + upd_sheet = oldsheet; + return result; +} +/*}}}*/ + /* update -- update all cells that need it */ /*{{{*/ void update(Sheet *sheet) { diff --git a/src/common/sheet.h b/src/common/sheet.h index 35ca5c5..3c622a4 100644 --- a/src/common/sheet.h +++ b/src/common/sheet.h @@ -91,6 +91,7 @@ bool setwidth(Sheet *sheet, int x, int z, int width); int cellwidth(Sheet *sheet, const Location at); void puttok(Sheet *sheet, const Location at, Token t, TokVariety v); Token recompvalue(Sheet *sheet, const Location at); +Token evaluate_at(Token t, Sheet *sheet, const Location at); void update(Sheet *sheet); char *geterror(Sheet *sheet, const Location at); void printvalue(char *s, size_t size, size_t chars, StringFormat sf,