/* GNUPLOT - parse.c */
/*[
* Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted, but not the right to
* distribute the complete modified source code. Modifications are to
* be distributed as patches to the released version. Permission to
* distribute binaries produced by compiling modified sources is granted,
* provided you
* 1. distribute the corresponding source modifications from the
* released version in the form of a patch file along with the binaries,
* 2. add special version identification to distinguish your version
* in addition to the base release version number,
* 3. provide your name and address as the primary contact for the
* support of your modified version, and
* 4. retain our contact information in regard to use of the base
* software.
* Permission to distribute the released version of the source code along
* with corresponding source modifications in the form of a patch file is
* granted with same provisions 2 through 4 for binary distributions.
*
* This software is provided "as is" without express or implied warranty
* to the extent permitted by applicable law.
]*/
#include "parse.h"
#include "alloc.h"
#include "command.h"
#include "datablock.h"
#include "eval.h"
#include "help.h"
#include "util.h"
/* Protection mechanism for trying to parse a string followed by a + or - sign.
* Also suppresses an undefined variable message if an unrecognized token
* is encountered during try_to_get_string().
*/
TBOOLEAN string_result_only = FALSE;
static int parse_recursion_level;
/* Exported globals: the current 'dummy' variable names */
char c_dummy_var[MAX_NUM_VAR][MAX_ID_LEN+1];
char set_dummy_var[MAX_NUM_VAR][MAX_ID_LEN+1] = { "x", "y" };
int fit_dummy_var[MAX_NUM_VAR];
TBOOLEAN scanning_range_in_progress = FALSE;
/* This is used by plot_option_using() */
int at_highest_column_used = -1;
/* This is checked by df_readascii() */
TBOOLEAN parse_1st_row_as_headers = FALSE;
/* This is used by df_open() and df_readascii() */
udvt_entry *df_array = NULL;
/* Iteration structures used for bookkeeping */
t_iterator * plot_iterator = NULL;
t_iterator * set_iterator = NULL;
/* Internal prototypes: */
static void convert __PROTO((struct value *, int));
static void extend_at __PROTO((void));
static union argument *add_action __PROTO((enum operators sf_index));
static void parse_expression __PROTO((void));
static void accept_logical_OR_expression __PROTO((void));
static void accept_logical_AND_expression __PROTO((void));
static void accept_inclusive_OR_expression __PROTO((void));
static void accept_exclusive_OR_expression __PROTO((void));
static void accept_AND_expression __PROTO((void));
static void accept_equality_expression __PROTO((void));
static void accept_relational_expression __PROTO((void));
static void accept_bitshift_expression __PROTO((void));
static void accept_additive_expression __PROTO((void));
static void accept_multiplicative_expression __PROTO((void));
static void parse_primary_expression __PROTO((void));
static void parse_conditional_expression __PROTO((void));
static void parse_logical_OR_expression __PROTO((void));
static void parse_logical_AND_expression __PROTO((void));
static void parse_inclusive_OR_expression __PROTO((void));
static void parse_exclusive_OR_expression __PROTO((void));
static void parse_AND_expression __PROTO((void));
static void parse_equality_expression __PROTO((void));
static void parse_relational_expression __PROTO((void));
static void parse_bitshift_expression __PROTO((void));
static void parse_additive_expression __PROTO((void));
static void parse_multiplicative_expression __PROTO((void));
static void parse_unary_expression __PROTO((void));
static void parse_sum_expression __PROTO((void));
static int parse_assignment_expression __PROTO((void));
static int parse_array_assignment_expression __PROTO((void));
static int is_builtin_function __PROTO((int t_num));
static void set_up_columnheader_parsing __PROTO((struct at_entry *previous ));
static TBOOLEAN no_iteration __PROTO((t_iterator *));
static void reevaluate_iteration_limits __PROTO((t_iterator *iter));
static void reset_iteration __PROTO((t_iterator *iter));
/* Internal variables: */
static struct at_type *at = NULL;
static int at_size = 0;
static void
convert(struct value *val_ptr, int t_num)
{
*val_ptr = token[t_num].l_val;
}
int
int_expression()
{
return (int)real_expression();
}
double
real_expression()
{
double result;
struct value a;
result = real(const_express(&a));
gpfree_string(&a);
return result;
}
void
parse_reset_after_error()
{
string_result_only = FALSE;
parse_recursion_level = 0;
}
/* JW 20051126:
* Wrapper around const_express() called by try_to_get_string().
* Disallows top level + and - operators.
* This enables things like set xtics ('-\pi' -pi, '-\pi/2' -pi/2.)
*/
struct value *
const_string_express(struct value *valptr)
{
string_result_only = TRUE;
const_express(valptr);
string_result_only = FALSE;
return (valptr);
}
struct value *
const_express(struct value *valptr)
{
int tkn = c_token;
if (END_OF_COMMAND)
int_error(c_token, "constant expression required");
/* div - no dummy variables in a constant expression */
dummy_func = NULL;
evaluate_at(temp_at(), valptr); /* run it and send answer back */
if (undefined) {
int_error(tkn, "undefined value");
}
if (valptr->type == ARRAY) {
/* Make sure no one tries to free it later */
valptr->type = NOTDEFINED;
int_error(NO_CARET, "const_express: unsupported array operation");
}
return (valptr);
}
/* Used by plot2d/plot3d/stats/fit:
* Parse an expression that may return a filename string, a datablock name,
* a constant, or a dummy function using dummy variables x, y, ...
* If any dummy variables are present, set (*atptr) to point to an action table
* corresponding to the parsed expression, and return NULL.
* Otherwise evaluate the expression and return a string if there is one.
* The return value "str" and "*atptr" both point to locally-managed memory,
* which must not be freed by the caller!
*/
char*
string_or_express(struct at_type **atptr)
{
int i;
TBOOLEAN has_dummies;
static char *array_placeholder = "@@";
static char* str = NULL;
free(str);
str = NULL;
df_array = NULL;
if (atptr)
*atptr = NULL;
if (END_OF_COMMAND)
int_error(c_token, "expression expected");
/* parsing for datablocks */
if (equals(c_token,"$"))
return parse_datablock_name();
if (isstring(c_token) && (str = try_to_get_string()))
return str;
/* If this is a bare array name for an existing array, store a pointer */
/* for df_open() to use. "@@" is a magic pseudo-filename passed to */
/* df_open() that tells it to use the stored pointer. */
if (type_udv(c_token) == ARRAY && !equals(c_token+1, "[")) {
df_array = add_udv(c_token++);
return array_placeholder;
}
/* parse expression */
temp_at();
/* check if any dummy variables are used */
has_dummies = FALSE;
for (i = 0; i < at->a_count; i++) {
enum operators op_index = at->actions[i].index;
if ( op_index == PUSHD1 || op_index == PUSHD2 || op_index == PUSHD
|| op_index == SUM ) {
has_dummies = TRUE;
break;
}
}
if (!has_dummies) {
/* no dummy variables: evaluate expression */
struct value val;
evaluate_at(at, &val);
if (!undefined && val.type == STRING) {
/* prevent empty string variable from treated as special file '' or "" */
if (*val.v.string_val == '\0') {
free(val.v.string_val);
str = strdup(" ");
} else {
str = val.v.string_val;
}
}
}
/* prepare return */
if (atptr)
*atptr = at;
return str;
}
/* build an action table and return its pointer, but keep a pointer in at
* so that we can free it later if the caller hasn't taken over management
* of this table.
*/
struct at_type *
temp_at()
{
if (at != NULL)
free_at(at);
at = (struct at_type *) gp_alloc(sizeof(struct at_type), "action table");
memset(at, 0, sizeof(*at)); /* reset action table !!! */
at_size = MAX_AT_LEN;
parse_recursion_level = 0;
parse_expression();
return (at);
}
/* build an action table, put it in dynamic memory, and return its pointer */
struct at_type *
perm_at()
{
struct at_type *at_ptr;
size_t len;
(void) temp_at();
len = sizeof(struct at_type)
+ (at->a_count - MAX_AT_LEN) * sizeof(struct at_entry);
at_ptr = (struct at_type *) gp_realloc(at, len, "perm_at");
at = NULL; /* invalidate at pointer */
return (at_ptr);
}
/* Create an action table that describes a call to column("string"). */
/* This is used by plot_option_using() to handle 'plot ... using "string"' */
struct at_type *
create_call_column_at(char *string)
{
struct at_type *at = gp_alloc(sizeof(int) + 2*sizeof(struct at_entry),"");
at->a_count = 2;
at->actions[0].index = PUSHC;
at->actions[0].arg.j_arg = 3; /* FIXME - magic number! */
at->actions[0].arg.v_arg.type = STRING;
at->actions[0].arg.v_arg.v.string_val = string;
at->actions[1].index = COLUMN;
at->actions[1].arg.j_arg = 0;
return (at);
}
static void
extend_at()
{
size_t newsize = sizeof(struct at_type) + at_size * sizeof(struct at_entry);
at = gp_realloc(at, newsize, "extend_at");
at_size += MAX_AT_LEN;
FPRINTF((stderr, "Extending at size to %d\n", at_size));
}
/* Add function number <sf_index> to the current action table */
static union argument *
add_action(enum operators sf_index)
{
if (at->a_count >= at_size) {
extend_at();
}
at->actions[at->a_count].index = sf_index;
return (&(at->actions[at->a_count++].arg));
}
/* For external calls to parse_expressions()
* parse_recursion_level is expected to be 0 */
static void
parse_expression()
{ /* full expressions */
if (parse_assignment_expression())
return;
parse_recursion_level++;
accept_logical_OR_expression();
parse_conditional_expression();
parse_recursion_level--;
}
static void
accept_logical_OR_expression()
{ /* ? : expressions */
accept_logical_AND_expression();
parse_logical_OR_expression();
}
static void
accept_logical_AND_expression()
{
accept_inclusive_OR_expression();
parse_logical_AND_expression();
}
static void
accept_inclusive_OR_expression()
{
accept_exclusive_OR_expression();
parse_inclusive_OR_expression();
}
static void
accept_exclusive_OR_expression()
{
accept_AND_expression();
parse_exclusive_OR_expression();
}
static void
accept_AND_expression()
{
accept_equality_expression();
parse_AND_expression();
}
static void
accept_equality_expression()
{
accept_relational_expression();
parse_equality_expression();
}
static void
accept_relational_expression()
{
accept_bitshift_expression();
parse_relational_expression();
}
static void
accept_bitshift_expression()
{
accept_additive_expression();
parse_bitshift_expression();
}
static void
accept_additive_expression()
{
accept_multiplicative_expression();
parse_additive_expression();
}
static void
accept_multiplicative_expression()
{
parse_unary_expression(); /* - things */
parse_multiplicative_expression(); /* * / % */
}
static int
parse_assignment_expression()
{
/* Check for assignment operator Var = <expr> */
if (isletter(c_token) && equals(c_token + 1, "=")) {
/* push the variable name */
union argument *foo = add_action(PUSHC);
char *varname = NULL;
m_capture(&varname,c_token,c_token);
foo->v_arg.type = STRING;
foo->v_arg.v.string_val = varname;
/* push a dummy variable that would be the index if this were an array */
/* FIXME: It would be nice to hide this from "show at" */
foo = add_action(PUSHC);
foo->v_arg.type = NOTDEFINED;
/* push the expression whose value it will get */
c_token += 2;
parse_expression();
/* push the actual assignment operation */
(void) add_action(ASSIGN);
return 1;
}
/* Check for assignment to an array element Array[<expr>] = <expr> */
if (parse_array_assignment_expression())
return 1;
return 0;
}
/*
* If an array assignment is the first thing on a command line it is handled by
* the separate routine array_assignment().
* Here we catch assignments that are embedded in an expression.
* Examples:
* print A[2] = foo
* A[1] = A[2] = A[3] = 0
*/
static int
parse_array_assignment_expression()
{
/* Check for assignment to an array element Array[<expr>] = <expr> */
if (isletter(c_token) && equals(c_token+1, "[")) {
char *varname = NULL;
union argument *foo;
int save_action, save_token;
/* Quick check for the most common false positives */
/* i.e. other constructs that begin with "name[" */
/* FIXME: quicker than the full test below, but do we care? */
if (equals(c_token+3, "]") && !equals(c_token+4, "="))
return 0;
if (equals(c_token+3, ":")) /* substring s[foo:baz] */
return 0;
/* Is this really a known array name? */
if (type_udv(c_token) != ARRAY)
return 0;
/* Save state of the action table and the command line */
save_action = at->a_count;
save_token = c_token;
/* push the array name */
m_capture(&varname,c_token,c_token);
foo = add_action(PUSHC);
foo->v_arg.type = STRING;
foo->v_arg.v.string_val = varname;
/* push the index */
c_token += 2;
parse_expression();
/* If this wasn't really an array element assignment, back out. */
/* NB: Depending on what we just parsed, this may leak memory. */
if (!equals(c_token, "]") || !equals(c_token+1, "=")) {
c_token = save_token;
at->a_count = save_action;
free(varname);
return 0;
}
/* Now we evaluate the expression whose value it will get */
c_token += 2;
parse_expression();
/* push the actual assignment operation */
(void) add_action(ASSIGN);
return 1;
}
return 0;
}
/* add action table entries for primary expressions, i.e. either a
* parenthesized expression, a variable name, a numeric constant, a
* function evaluation, a power operator or postfix '!' (factorial)
* expression.
* Sep 2016 cardinality expression |Array| */
static void
parse_primary_expression()
{
if (equals(c_token, "(")) {
c_token++;
parse_expression();
/* Expressions may be separated by a comma */
while (equals(c_token,",")) {
c_token++;
(void) add_action(POP);
parse_expression();
}
if (!equals(c_token, ")"))
int_error(c_token, "')' expected");
c_token++;
} else if (equals(c_token, "$")) {
struct value a;
c_token++;
if (!isanumber(c_token)) {
if (equals(c_token+1, "[")) {
struct udvt_entry *datablock_udv;
c_token--;
datablock_udv = get_udv_by_name(parse_datablock_name());
if (!datablock_udv)
int_error(c_token-2,"No such datablock");
add_action(PUSH)->udv_arg = datablock_udv;
} else
int_error(c_token, "Column number or datablock line expected");
} else if (equals(c_token,"N")) {
/* $N == pseudocolumn -3 means "last column" */
c_token++;
Ginteger(&a, -3);
at_highest_column_used = -3;
add_action(DOLLARS)->v_arg = a;
} else {
convert(&a, c_token++);
if (a.type != INTGR || a.v.int_val < 0)
int_error(c_token, "Positive integer expected");
if (at_highest_column_used < a.v.int_val)
at_highest_column_used = a.v.int_val;
add_action(DOLLARS)->v_arg = a;
}
} else if (equals(c_token, "|")) {
struct udvt_entry *udv;
c_token++;
if (equals(c_token,"$")) {
udv = get_udv_by_name(parse_datablock_name());
if (!udv)
int_error(c_token-1, "no such datablock");
} else {
udv = add_udv(c_token++);
if (udv->udv_value.type != ARRAY)
int_error(c_token-1, "not an array");
}
add_action(PUSH)->udv_arg = udv;
if (!equals(c_token, "|"))
int_error(c_token, "'|' expected");
c_token++;
add_action(CARDINALITY);
} else if (isanumber(c_token)) {
union argument *foo = add_action(PUSHC);
convert(&(foo->v_arg), c_token);
c_token++;
} else if (isletter(c_token)) {
/* Found an identifier --- check whether its a function or a
* variable by looking for the parentheses of a function
* argument list */
if (equals(c_token + 1, "(")) {
enum operators whichfunc = is_builtin_function(c_token);
struct value num_params;
num_params.type = INTGR;
#if (1) /* DEPRECATED */
if (whichfunc && (strcmp(ft[whichfunc].f_name,"defined")==0)) {
/* Deprecated syntax: if (defined(foo)) ... */
/* New syntax: if (exists("foo")) ... */
struct udvt_entry *udv = add_udv(c_token+2);
union argument *foo = add_action(PUSHC);
foo->v_arg.type = INTGR;
if (udv->udv_value.type == NOTDEFINED)
foo->v_arg.v.int_val = 0;
else
foo->v_arg.v.int_val = 1;
c_token += 4; /* skip past "defined ( <foo> ) " */
return;
}
#endif
if (whichfunc) {
c_token += 2; /* skip fnc name and '(' */
parse_expression(); /* parse fnc argument */
num_params.v.int_val = 1;
while (equals(c_token, ",")) {
c_token++;
num_params.v.int_val++;
parse_expression();
}
if (!equals(c_token, ")"))
int_error(c_token, "')' expected");
c_token++;
/* So far sprintf is the only built-in function */
/* with a variable number of arguments. */
if (!strcmp(ft[whichfunc].f_name,"sprintf"))
add_action(PUSHC)->v_arg = num_params;
/* v4 timecolumn only had 1 param; v5 has 2. Accept either */
if (!strcmp(ft[whichfunc].f_name,"timecolumn"))
add_action(PUSHC)->v_arg = num_params;
/* The column() function has side effects requiring special handling */
if (!strcmp(ft[whichfunc].f_name,"column")) {
set_up_columnheader_parsing( &(at->actions[at->a_count-1]) );
}
(void) add_action(whichfunc);
} else {
/* it's a call to a user-defined function */
enum operators call_type = (int) CALL;
int tok = c_token;
c_token += 2; /* skip func name and '(' */
parse_expression();
if (equals(c_token, ",")) { /* more than 1 argument? */
num_params.v.int_val = 1;
while (equals(c_token, ",")) {
num_params.v.int_val += 1;
c_token += 1;
parse_expression();
}
add_action(PUSHC)->v_arg = num_params;
call_type = (int) CALLN;
}
if (!equals(c_token, ")"))
int_error(c_token, "')' expected");
c_token++;
add_action(call_type)->udf_arg = add_udf(tok);
}
} else if (equals(c_token, "sum") && equals(c_token+1, "[")) {
parse_sum_expression();
/* dummy_func==NULL is a flag to say no dummy variables active */
} else if (dummy_func) {
if (equals(c_token, c_dummy_var[0])) {
c_token++;
add_action(PUSHD1)->udf_arg = dummy_func;
fit_dummy_var[0]++;
} else if (equals(c_token, c_dummy_var[1])) {
c_token++;
add_action(PUSHD2)->udf_arg = dummy_func;
fit_dummy_var[1]++;
} else {
int i, param = 0;
for (i = 2; i < MAX_NUM_VAR; i++) {
if (equals(c_token, c_dummy_var[i])) {
struct value num_params;
num_params.type = INTGR;
num_params.v.int_val = i;
param = 1;
c_token++;
add_action(PUSHC)->v_arg = num_params;
add_action(PUSHD)->udf_arg = dummy_func;
fit_dummy_var[i]++;
break;
}
}
if (!param) { /* defined variable */
add_action(PUSH)->udv_arg = add_udv(c_token);
c_token++;
}
}
/* its a variable, with no dummies active - div */
} else {
add_action(PUSH)->udv_arg = add_udv(c_token);
c_token++;
}
}
/* end if letter */
/* Maybe it's a string constant */
else if (isstring(c_token)) {
union argument *foo = add_action(PUSHC);
foo->v_arg.type = STRING;
foo->v_arg.v.string_val = NULL;
/* this dynamically allocated string will be freed by free_at() */
m_quote_capture(&(foo->v_arg.v.string_val), c_token, c_token);
c_token++;
} else {
int_error(c_token, "invalid expression ");
}
/* The remaining operators are postfixes and can be stacked, e.g. */
/* Array[i]**2, so we may have to loop to catch all of them. */
while (TRUE) {
/* add action code for ! (factorial) operator */
if (equals(c_token, "!")) {
c_token++;
(void) add_action(FACTORIAL);
}
/* add action code for ** operator */
else if (equals(c_token, "**")) {
c_token++;
parse_unary_expression();
(void) add_action(POWER);
}
/* Parse and add actions for range specifier applying to previous entity.
* Currently the [beg:end] form is used to generate substrings, but could
* also be used to extract vector slices. The [i] form is used to index
* arrays, but could also be a shorthand for extracting a single-character
* substring.
*/
else if (equals(c_token, "[") && !isanumber(c_token-1)) {
/* handle '*' or empty start of range */
if (equals(++c_token,"*") || equals(c_token,":")) {
union argument *empty = add_action(PUSHC);
empty->v_arg.type = INTGR;
empty->v_arg.v.int_val = 1;
if (equals(c_token,"*"))
c_token++;
} else
parse_expression();
/* handle array indexing (single value in square brackets) */
if (equals(c_token, "]")) {
c_token++;
(void) add_action(INDEX);
continue;
}
if (!equals(c_token, ":"))
int_error(c_token, "':' expected");
/* handle '*' or empty end of range */
if (equals(++c_token,"*") || equals(c_token,"]")) {
union argument *empty = add_action(PUSHC);
empty->v_arg.type = INTGR;
empty->v_arg.v.int_val = 65535; /* should be INT_MAX */
if (equals(c_token,"*"))
c_token++;
} else
parse_expression();
if (!equals(c_token, "]"))
int_error(c_token, "']' expected");
c_token++;
(void) add_action(RANGE);
/* Whatever this is, it isn't another postfix operator */
} else {
break;
}
}
}
/* HBB 20010309: Here and below: can't store pointers into the middle
* of at->actions[]. That array may be realloc()ed by add_action() or
* express() calls!. Access via index savepc1/savepc2, instead. */
static void
parse_conditional_expression()
{
/* create action code for ? : expressions */
if (equals(c_token, "?")) {
int savepc1, savepc2;
/* Fake same recursion level for alternatives
* set xlabel a>b ? 'foo' : 'bar' -1, 1
* FIXME: This won't work:
* set xlabel a-b>c ? 'foo' : 'bar' offset -1, 1
*/
parse_recursion_level--;
c_token++;
savepc1 = at->a_count;
add_action(JTERN);
parse_expression();
if (!equals(c_token, ":"))
int_error(c_token, "expecting ':'");
c_token++;
savepc2 = at->a_count;
add_action(JUMP);
at->actions[savepc1].arg.j_arg = at->a_count - savepc1;
parse_expression();
at->actions[savepc2].arg.j_arg = at->a_count - savepc2;
parse_recursion_level++;
}
}
static void
parse_logical_OR_expression()
{
/* create action codes for || operator */
while (equals(c_token, "||")) {
int savepc;
c_token++;
savepc = at->a_count;
add_action(JUMPNZ); /* short-circuit if already TRUE */
accept_logical_AND_expression();
/* offset for jump */
at->actions[savepc].arg.j_arg = at->a_count - savepc;
(void) add_action(BOOLE);
}
}
static void
parse_logical_AND_expression()
{
/* create action code for && operator */
while (equals(c_token, "&&")) {
int savepc;
c_token++;
savepc = at->a_count;
add_action(JUMPZ); /* short-circuit if already FALSE */
accept_inclusive_OR_expression();
at->actions[savepc].arg.j_arg = at->a_count - savepc; /* offset for jump */
(void) add_action(BOOLE);
}
}
static void
parse_inclusive_OR_expression()
{
/* create action code for | operator */
while (equals(c_token, "|")) {
c_token++;
accept_exclusive_OR_expression();
(void) add_action(BOR);
}
}
static void
parse_exclusive_OR_expression()
{
/* create action code for ^ operator */
while (equals(c_token, "^")) {
c_token++;
accept_AND_expression();
(void) add_action(XOR);
}
}
static void
parse_AND_expression()
{
/* create action code for & operator */
while (equals(c_token, "&")) {
c_token++;
accept_equality_expression();
(void) add_action(BAND);
}
}
static void
parse_equality_expression()
{
/* create action codes for == and != numeric operators
* eq and ne string operators */
while (TRUE) {
if (equals(c_token, "==")) {
c_token++;
accept_relational_expression();
(void) add_action(EQ);
} else if (equals(c_token, "!=")) {
c_token++;
accept_relational_expression();
(void) add_action(NE);
} else if (equals(c_token, "eq")) {
c_token++;
accept_relational_expression();
(void) add_action(EQS);
} else if (equals(c_token, "ne")) {
c_token++;
accept_relational_expression();
(void) add_action(NES);
} else
break;
}
}
static void
parse_relational_expression()
{
/* create action code for < > >= or <=
* operators */
while (TRUE) {
if (equals(c_token, ">")) {
c_token++;
accept_bitshift_expression();
(void) add_action(GT);
} else if (equals(c_token, "<")) {
/* Workaround for * in syntax of range constraints */
if (scanning_range_in_progress && equals(c_token+1, "*") ) {
break;
}
c_token++;
accept_bitshift_expression();
(void) add_action(LT);
} else if (equals(c_token, ">=")) {
c_token++;
accept_bitshift_expression();
(void) add_action(GE);
} else if (equals(c_token, "<=")) {
c_token++;
accept_bitshift_expression();
(void) add_action(LE);
} else
break;
}
}
static void
parse_bitshift_expression()
{
/* create action codes for << and >> operators */
while (TRUE) {
if (equals(c_token, "<<")) {
c_token++;
accept_additive_expression();
(void) add_action(LEFTSHIFT);
} else if (equals(c_token, ">>")) {
c_token++;
accept_additive_expression();
(void) add_action(RIGHTSHIFT);
} else
break;
}
}
static void
parse_additive_expression()
{
/* create action codes for +, - and . operators */
while (TRUE) {
if (equals(c_token, ".")) {
c_token++;
accept_multiplicative_expression();
(void) add_action(CONCATENATE);
/* If only string results are wanted
* do not accept '-' or '+' at the top level. */
} else if (string_result_only && parse_recursion_level == 1) {
break;
} else if (equals(c_token, "+")) {
c_token++;
accept_multiplicative_expression();
(void) add_action(PLUS);
} else if (equals(c_token, "-")) {
c_token++;
accept_multiplicative_expression();
(void) add_action(MINUS);
} else
break;
}
}
static void
parse_multiplicative_expression()
{
/* add action code for * / and % operators */
while (TRUE) {
if (equals(c_token, "*")) {
c_token++;
parse_unary_expression();
(void) add_action(MULT);
} else if (equals(c_token, "/")) {
c_token++;
parse_unary_expression();
(void) add_action(DIV);
} else if (equals(c_token, "%")) {
c_token++;
parse_unary_expression();
(void) add_action(MOD);
} else
break;
}
}
static void
parse_unary_expression()
{
/* add code for unary operators */
if (equals(c_token, "!")) {
c_token++;
parse_unary_expression();
(void) add_action(LNOT);
} else if (equals(c_token, "~")) {
c_token++;
parse_unary_expression();
(void) add_action(BNOT);
} else if (equals(c_token, "-")) {
struct at_entry *previous;
c_token++;
parse_unary_expression();
/* Collapse two operations PUSHC <pos-const> + UMINUS
* into a single operation PUSHC <neg-const>
*/
previous = &(at->actions[at->a_count-1]);
if (previous->index == PUSHC && previous->arg.v_arg.type == INTGR) {
previous->arg.v_arg.v.int_val = -previous->arg.v_arg.v.int_val;
} else if (previous->index == PUSHC && previous->arg.v_arg.type == CMPLX) {
previous->arg.v_arg.v.cmplx_val.real = -previous->arg.v_arg.v.cmplx_val.real;
previous->arg.v_arg.v.cmplx_val.imag = -previous->arg.v_arg.v.cmplx_val.imag;
} else
(void) add_action(UMINUS);
} else if (equals(c_token, "+")) { /* unary + is no-op */
c_token++;
parse_unary_expression();
} else
parse_primary_expression();
}
/*
* Syntax: set link {x2|y2} {via <expression1> inverse <expression2>}
* Create action code tables for the functions linking primary and secondary axes.
* expression1 maps primary coordinates into the secondary coordinate space.
* expression2 maps secondary coordinates into the primary coordinate space.
*/
void
parse_link_via( struct udft_entry *udf )
{
int start_token;
/* Caller left us pointing at "via" or "inverse" */
c_token++;
start_token = c_token;
if (END_OF_COMMAND)
int_error(c_token,"Missing expression");
/* Save action table for the linkage mapping */
dummy_func = udf;
free_at(udf->at);
udf->at = perm_at();
dummy_func = NULL;
/* Save the mapping expression itself */
m_capture(&(udf->definition), start_token, c_token - 1);
}
/* create action code for 'sum' expressions */
static void
parse_sum_expression()
{
/* sum [<var>=<range>] <expr>
* - Pass a udf to f_sum (with action code (for <expr>) that is not added
* to the global action table).
* - f_sum uses a newly created udv (<var>) to pass the current value of
* <var> to <expr> (resp. its ac).
* - The original idea was to treat <expr> as function f(<var>), but there
* was the following problem: Consider 'g(x) = sum [k=1:4] f(k)'. There
* are two dummy variables 'x' and 'k' from different functions 'g' and
* 'f' which would require changing the parsing of dummy variables.
*/
char *errormsg = "Expecting 'sum [<var> = <start>:<end>] <expression>'\n";
char *varname = NULL;
union argument *arg;
struct udft_entry *udf;
struct at_type * save_at;
int save_at_size;
int i;
/* Caller already checked for string "sum [" so skip both tokens */
c_token += 2;
/* <var> */
if (!isletter(c_token))
int_error(c_token, errormsg);
/* create a user defined variable and pass it to f_sum via PUSHC, since the
* argument of f_sum is already used by the udf */
m_capture(&varname, c_token, c_token);
add_udv(c_token);
arg = add_action(PUSHC);
Gstring(&(arg->v_arg), varname);
c_token++;
if (!equals(c_token, "="))
int_error(c_token, errormsg);
c_token++;
/* <start> */
parse_expression();
if (!equals(c_token, ":"))
int_error(c_token, errormsg);
c_token++;
/* <end> */
parse_expression();
if (!equals(c_token, "]"))
int_error(c_token, errormsg);
c_token++;
/* parse <expr> and convert it to a new action table. */
/* modeled on code from temp_at(). */
/* 1. save environment to restart parsing */
save_at = at;
save_at_size = at_size;
at = NULL;
/* 2. save action table in a user defined function */
udf = (struct udft_entry *) gp_alloc(sizeof(struct udft_entry), "sum");
udf->next_udf = (struct udft_entry *) NULL;
udf->udf_name = NULL; /* TODO maybe add a name and definition */
udf->at = perm_at();
udf->definition = NULL;
udf->dummy_num = 0;
for (i = 0; i < MAX_NUM_VAR; i++)
(void) Ginteger(&(udf->dummy_values[i]), 0);
/* 3. restore environment */
at = save_at;
at_size = save_at_size;
/* pass the udf to f_sum using the argument */
add_action(SUM)->udf_arg = udf;
}
/* find or add value and return pointer */
struct udvt_entry *
add_udv(int t_num)
{
char varname[MAX_ID_LEN+1];
copy_str(varname, t_num, MAX_ID_LEN);
if (token[t_num].length > MAX_ID_LEN-1)
int_warn(t_num, "truncating variable name that is too long");
return add_udv_by_name(varname);
}
/* find or add function at index <t_num>, and return pointer */
struct udft_entry *
add_udf(int t_num)
{
struct udft_entry **udf_ptr = &first_udf;
int i;
while (*udf_ptr) {
if (equals(t_num, (*udf_ptr)->udf_name))
return (*udf_ptr);
udf_ptr = &((*udf_ptr)->next_udf);
}
/* get here => not found. udf_ptr points at first_udf or
* next_udf field of last udf
*/
if (is_builtin_function(t_num))
int_warn(t_num, "Warning : udf shadowed by built-in function of the same name");
/* create and return a new udf slot */
*udf_ptr = (struct udft_entry *)
gp_alloc(sizeof(struct udft_entry), "function");
(*udf_ptr)->next_udf = (struct udft_entry *) NULL;
(*udf_ptr)->definition = NULL;
(*udf_ptr)->at = NULL;
(*udf_ptr)->udf_name = gp_alloc (token_len(t_num)+1, "user func");
copy_str((*udf_ptr)->udf_name, t_num, token_len(t_num)+1);
for (i = 0; i < MAX_NUM_VAR; i++)
(void) Ginteger(&((*udf_ptr)->dummy_values[i]), 0);
return (*udf_ptr);
}
/* return standard function index or 0 */
static int
is_builtin_function(int t_num)
{
int i;
for (i = (int) SF_START; ft[i].f_name != NULL; i++) {
if (equals(t_num, ft[i].f_name))
return (i);
}
return (0);
}
/*
* Test for the existence of a function without triggering errors
* Return values:
* 0 no such function is defined
* -1 built-in function
* 1 user-defined function
*/
int
is_function(int t_num)
{
struct udft_entry **udf_ptr = &first_udf;
if (is_builtin_function(t_num))
return -1;
while (*udf_ptr) {
if (equals(t_num, (*udf_ptr)->udf_name))
return 1;
udf_ptr = &((*udf_ptr)->next_udf);
}
return 0;
}
/* Look for iterate-over-plot constructs, of the form
* for [<var> = <start> : <end> { : <increment>}] ...
* If one (or more) is found, an iterator structure is allocated and filled
* and a pointer to that structure is returned.
* The pointer is NULL if no "for" statements are found.
* If the iteration limits are constants, store them as is.
* If they are given as expressions, store an action table for the expression.
*/
t_iterator *
check_for_iteration()
{
char *errormsg = "Expecting iterator \tfor [<var> = <start> : <end> {: <incr>}]\n\t\t\tor\tfor [<var> in \"string of words\"]";
int nesting_depth = 0;
t_iterator *iter = NULL;
t_iterator *prev = NULL;
t_iterator *this_iter = NULL;
TBOOLEAN no_parent = FALSE;
/* Now checking for iteration parameters */
/* Nested "for" statements are supported, each one corresponds to a node of the linked list */
while (equals(c_token, "for")) {
struct udvt_entry *iteration_udv = NULL;
t_value original_udv_value;
char *iteration_string = NULL;
int iteration_start;
int iteration_end;
int iteration_increment = 1;
int iteration_current;
int iteration = 0;
struct at_type *iteration_start_at = NULL;
struct at_type *iteration_end_at = NULL;
c_token++;
if (!equals(c_token++, "[") || !isletter(c_token))
int_error(c_token-1, errormsg);
iteration_udv = add_udv(c_token++);
original_udv_value = iteration_udv->udv_value;
iteration_udv->udv_value.type = NOTDEFINED;
if (equals(c_token, "=")) {
c_token++;
if (isanumber(c_token) && equals(c_token+1,":")) {
/* Save the constant value only */
iteration_start = int_expression();
} else {
/* Save the expression as well as the value */
struct value v;
iteration_start_at = perm_at();
if (no_parent) {
iteration_start = 0;
} else {
evaluate_at(iteration_start_at, &v);
iteration_start = real(&v);
}
}
if (!equals(c_token++, ":"))
int_error(c_token-1, errormsg);
if (equals(c_token,"*")) {
iteration_end = INT_MAX;
c_token++;
} else if (isanumber(c_token) && (equals(c_token+1,":") || equals(c_token+1,"]"))) {
/* Save the constant value only */
iteration_end = int_expression();
} else {
/* Save the expression as well as the value */
struct value v;
iteration_end_at = perm_at();
if (no_parent) {
iteration_end = 0;
} else {
evaluate_at(iteration_end_at, &v);
iteration_end = real(&v);
}
}
if (equals(c_token,":")) {
c_token++;
iteration_increment = int_expression();
if (iteration_increment == 0)
int_error(c_token-1, errormsg);
}
if (!equals(c_token++, "]"))
int_error(c_token-1, errormsg);
gpfree_array(&(iteration_udv->udv_value));
gpfree_string(&(iteration_udv->udv_value));
Ginteger(&(iteration_udv->udv_value), iteration_start);
}
else if (equals(c_token++, "in")) {
/* Assume this is a string-valued expression. */
/* It might be worth treating a string constant as a special case */
struct value v;
iteration_start_at = perm_at();
evaluate_at(iteration_start_at, &v);
if (v.type != STRING)
int_error(c_token-1, errormsg);
if (!equals(c_token++, "]"))
int_error(c_token-1, errormsg);
iteration_string = v.v.string_val;
iteration_start = 1;
iteration_end = gp_words(iteration_string);
gpfree_array(&(iteration_udv->udv_value));
gpfree_string(&(iteration_udv->udv_value));
Gstring(&(iteration_udv->udv_value), gp_word(iteration_string, 1));
}
else /* Neither [i=B:E] or [s in "foo"] */
int_error(c_token-1, errormsg);
iteration_current = iteration_start;
this_iter = gp_alloc(sizeof(t_iterator), "iteration linked list");
this_iter->original_udv_value = original_udv_value;
this_iter->iteration_udv = iteration_udv;
this_iter->iteration_string = iteration_string;
this_iter->iteration_start = iteration_start;
this_iter->iteration_end = iteration_end;
this_iter->iteration_increment = iteration_increment;
this_iter->iteration_current = iteration_current;
this_iter->iteration = iteration;
this_iter->start_at = iteration_start_at;
this_iter->end_at = iteration_end_at;
this_iter->next = NULL;
if (nesting_depth == 0) {
/* first "for" statement: this will be the listhead */
iter = this_iter;
} else {
/* nested "for": attach newly created node to the end of the list */
prev->next = this_iter;
}
prev = this_iter;
/* If some depth of a nested iteration evaluates to an empty range, the
* evaluated limits of depths below it are moot (and possibly invalid).
* This flag tells us to skip their evaluation to avoid irrelevant errors.
*/
if (no_iteration(this_iter)) {
no_parent = TRUE;
FPRINTF((stderr,"iteration at level %d is moot\n", nesting_depth));
}
nesting_depth++;
}
return iter;
}
/*
* Reevaluate the iteration limits
* (in case they are functions whose parameters have taken
* on a new value)
*/
static void
reevaluate_iteration_limits(t_iterator *iter)
{
if (iter->start_at) {
struct value v;
evaluate_at(iter->start_at, &v);
if (iter->iteration_string) {
/* unnecessary if iteration string is a constant */
free(iter->iteration_string);
if (v.type != STRING)
int_error(NO_CARET, "corrupt iteration string");
iter->iteration_string = v.v.string_val;
iter->iteration_start = 1;
iter->iteration_end = gp_words(iter->iteration_string);
} else {
iter->iteration_start = real(&v);
}
}
if (iter->end_at) {
struct value v;
evaluate_at(iter->end_at, &v);
iter->iteration_end = real(&v);
}
}
/*
* Reset iteration at this level to start value.
* Any iteration levels underneath are reset also.
*/
static void
reset_iteration(t_iterator *iter)
{
if (!iter)
return;
reevaluate_iteration_limits(iter);
iter->iteration = -1;
iter->iteration_current = iter->iteration_start;
if (iter->iteration_string) {
gpfree_string(&(iter->iteration_udv->udv_value));
Gstring(&(iter->iteration_udv->udv_value),
gp_word(iter->iteration_string, iter->iteration_current));
} else {
/* This traps fatal user error of reassigning iteration variable to a string */
gpfree_string(&(iter->iteration_udv->udv_value));
Ginteger(&(iter->iteration_udv->udv_value), iter->iteration_current);
}
reset_iteration(iter->next);
}
/*
* Increment the iteration position recursively.
* returns TRUE if the iteration is still in range
* returns FALSE if the incement put it past the end limit
*/
TBOOLEAN
next_iteration(t_iterator *iter)
{
/* Once it goes out of range it will stay that way until reset */
if (!iter || no_iteration(iter))
return FALSE;
/* Give sub-iterations a chance to advance */
if (next_iteration(iter->next)) {
if (iter->iteration < 0)
iter->iteration = 0;
return TRUE;
}
/* Increment at this level */
if (iter->iteration < 0) {
/* Just reset, haven't used start value yet */
iter->iteration = 0;
if (!empty_iteration(iter))
return TRUE;
} else {
iter->iteration++;
iter->iteration_current += iter->iteration_increment;
}
if (iter->iteration_string) {
gpfree_string(&(iter->iteration_udv->udv_value));
Gstring(&(iter->iteration_udv->udv_value),
gp_word(iter->iteration_string, iter->iteration_current));
} else {
/* This traps fatal user error of reassigning iteration variable to a string */
gpfree_string(&(iter->iteration_udv->udv_value));
Ginteger(&(iter->iteration_udv->udv_value), iter->iteration_current);
}
/* If this runs off the end, leave the value out-of-range and return FALSE */
if (iter->iteration_increment > 0 && iter->iteration_end - iter->iteration_current < 0)
return FALSE;
if (iter->iteration_increment < 0 && iter->iteration_end - iter->iteration_current > 0)
return FALSE;
if (iter->next == NULL)
return TRUE;
/* Reset sub-iterations, if any */
reset_iteration(iter->next);
/* Go back to top or call self recursively */
return next_iteration(iter);
}
/*
* Returns TRUE if
* - this really is an iteration and
* - the top level iteration covers no usable range
*/
static TBOOLEAN
no_iteration(t_iterator *iter)
{
if (!iter)
return FALSE;
if ((iter->iteration_end > iter->iteration_start && iter->iteration_increment < 0)
|| (iter->iteration_end < iter->iteration_start && iter->iteration_increment > 0)) {
return TRUE;
}
return FALSE;
}
/*
* Recursive test that no empty iteration exists in a nested set of iterations
*/
TBOOLEAN
empty_iteration(t_iterator *iter)
{
if (!iter)
return FALSE;
else if (no_iteration(iter))
return TRUE;
else
return no_iteration(iter->next);
}
t_iterator *
cleanup_iteration(t_iterator *iter)
{
while (iter) {
t_iterator *next = iter->next;
gpfree_string(&(iter->iteration_udv->udv_value));
iter->iteration_udv->udv_value = iter->original_udv_value;
free(iter->iteration_string);
free_at(iter->start_at);
free_at(iter->end_at);
free(iter);
iter = next;
}
return NULL;
}
TBOOLEAN
forever_iteration(t_iterator *iter)
{
if (!iter)
return FALSE;
else
return (iter->iteration_end == INT_MAX);
}
/* The column() function requires special handling because
* - It has side effects if reference to a column entry
* requires matching it to the column header string.
* - These side effects must be handled at the time the
* expression is parsed rather than when it it evaluated.
*/
static void
set_up_columnheader_parsing( struct at_entry *previous )
{
/* column("string") means we expect the first row of */
/* a data file to contain headers rather than data. */
if (previous->index == PUSHC && previous->arg.v_arg.type == STRING)
parse_1st_row_as_headers = TRUE;
/* This allows plot ... using (column(<const>)) title columnhead */
if (previous->index == PUSHC && previous->arg.v_arg.type == INTGR) {
if (at_highest_column_used < previous->arg.v_arg.v.int_val)
at_highest_column_used = previous->arg.v_arg.v.int_val;
}
/* This attempts to catch plot ... using (column(<variable>)) */
if (previous->index == PUSH) {
udvt_entry *u = previous->arg.udv_arg;
if (u->udv_value.type == INTGR) {
if (at_highest_column_used < u->udv_value.v.int_val)
at_highest_column_used = u->udv_value.v.int_val;
}
}
/* NOTE: There is no way to handle ... using (column(<general expression>)) */
}