|
From: Dave D. <dde...@es...> - 2005-06-25 13:50:54
|
Ethan A Merritt <merritt@u.washington.edu> writes:
> On Friday 24 June 2005 12:11 pm, you wrote:
>> >> How about changing these arrays to hold dynamically
>> >> allocated pointers instead. [[...]]
>>
>> - how are string literals in expressions represented?
>
> A string literal that is part of a user-defined function is held in
> a dynamically-allocated udv in the pre-built evaluation stack for
> that function.
Hmm - not sure what you mean by the pre-built eval stack - I know
gnuplot fairly intimately back in 3.6 but that was a while ago now.
I was thinking of expressions mostly for user-defined functions, but
there are also the expressions which are anonymous, but which are
evaluated many times (eg plot ... using )
> In any case, when the string is referenced during the course of evaluating
> an expression, a copy of the string is made by allocating dynamic storage.
> This temporary copy is pushed/popped/dereferenced using the stack during
> the expression evaluation, and freed again at the end of evaluation.
>
>> When I was
>> deliberating over this area, I was tending towards having the notion
>> of a string pool associated with an exression. The pool would
>> survive for as long as the expression did, and if that meant the
>> expression was stored in a user-defined fn, then the string pool
>> would persist.
>
> If I understand correctly, that is what it currently does.
> But I don't quite follow the use of the word "pool", except insofar
> as the strings exist in the heap managed by malloc() via gp_alloc().
>
Okay... I had invented a scheme using a pool simply to avoid the pain
of having to clean up when the expression itself was freed. Rather
than having an arbitrary number of pointers to strings stored
elsewhere, I had in mind one lump of memory associated with the
expression, and then string literals were stored within that
pool, and the "action" to access the string literal stored an index into
that pool, rather than a direct pointer (so that as the pool grows, we
don't need to fixup pointer). Then since all expressions (or action
tables) have one constant-pool pointer, there is only one lump of
memory to free when the expression gets deleted. But this is just a
detail.
>> If that approach was used, then the names of the dummy variables
>> could be stored in that string pool.
>
> There you lost me. The dummy variable names are independent of
> any particular function, and must be available to the parser before
> any user-defined functions have been defined. Unless, as before,
> by "stored in that pool" you mean "dynamically allocated by gp_alloc()"?
>
Ah - sorry - perhaps I'm being stupid. I vaguelly recalled that the
dummy variable names were preserved in order to be able to echo them
back to the user when listing / saving udf's. But it probably just
saves the entire expression as a string. So it doesn't need to keep
the names of the variables.
Perhaps MAX_NUM_VAR is being misused - it is the size of an array to
store the current "global" dummy names, but also gets used as the size
of array for parameters passed to fns.
>> - the other aspect of MAX_NUM_VAR is to actually have space for the
>> values during (recursive) expression parsing. My recollection is
>> that gnuplot used to have a fixed area for the parameter values, and
>> when recursing into a udf, would copy the old values into temporary
>> space, and put the new values into the globals. I don't know if this
yeah...
/* user-defined function table entry */
typedef struct udft_entry {
struct udft_entry *next_udf; /* pointer to next udf in linked list */
char *udf_name; /* name of this function entry */
struct at_type *at; /* pointer to action table to execute */
char *definition; /* definition of function as typed */
t_value dummy_values[MAX_NUM_VAR]; /* current value of dummy variables */
} udft_entry;
Okay, not globals, but a dummy_values[] array stored with each udf.
>> is still how it works. But a much more elegant solution would be to
>> use an evaluation stack. (I work on java vm's these days, so it's
>> obvious where this idea comes from...)
>
> It does use an evaluation stack. Currently the stack is of fixed size
> eval.c: static struct value stack[STACK_DEPTH];
> but I think it would be straightforward to make it dynamically evaluated
> instead. The push() routine would have to check whether the stack needs
> to be expanded via realloc().
>
yes, it uses an eval stack. But it's a "pure" estack. When a udf
function is invoked, the parameters are copied off the estack into
some other storage. What I'm suggesting a small change to leave them
on the estack, with a frame being pushed. This gets round the problem
of having an upper limit number on the number of dummy variables,
without having to set aside storage to copy parameters.
Looking at the 4.0 sources, f_calln() in internal.c has to
for (i = 0; i < MAX_NUM_VAR; i++)
save_dummy[i] = udf->dummy_values[i];
copy the old parameters out into some spare space (since this might be
a recursive call, presumably), then copy the parameters off the stack
into the dummy_values for the udf, and then resume resume. And then on
return, it has to copy back the saved ones into the current
dummy_values[]
I'm proposing that we (er... someone !) changes this so that,
instead, f_calln() lays down some notion of frame linkage in the
estack, so that it can find dummy parameters.
Then f_pushd() [push dummy variable onto estack] is changed to not
fetch it from the (fixed size) array in the udf defn, but instead,
fetch from lower down in the estack.
And returning from a udf just removes the linkage, actually pops the
fn parameters off the caller's estack, and pushes the result.
dd
--
Dave Denholm <dde...@es...> http://www.esmertec.com
|