On Monday, May 29, 2017 at 12:17:04 PM UTC+9, Joel Rees
in "Re: Forth Implementations advantages and disadvantages."
wrote:
> > [...]
> Oh, never mind. The business with no static initializers supposed to be
> allowed with a flexible array in a struct is making the interpreter unstable.
Here is my current header:
-------------------------
typedef struct definition_header_s
{
cell_u nameLink; /* Bits above length are mode flags. Name is not far from header. C init with pointer. */
cell_u interpMode; /* Making the mode bits explicit to help the C source-level initializations. */
cell_u allocLink; /* Want to separate the allocation fields. */
cell_u allocMode; /* Decoupling the allocation, making the C initializations easier. */
cell_u vocabLink; /* Root of this definition's vocabulary. */
cell_u leftLink; /* Binary tree, left child in order. */
cell_u rightLink; /* Right child in order. */
cell_u codeLink; /* Interpretation code for this definition. */
cell_u parameterLink[]; /* Parameters for the code for this definition. */
} definition_header_s;
-------------------------
from
https://sourceforge.net/p/bif-c/code/HEAD/tree/trunk/bifu_i.h
An example of how I'm disobeying the rules by using it in a static
header can be found in
https://sourceforge.net/p/bif-c/code/HEAD/tree/trunk/bif2b_a.c
towards the bottome, where I defined IF, ELSE, and ENDIF as lists
of Forth i-codes. I'll show IF:
---------------------------
static character_t sIF[] = "\x2" "IF";
definition_header_s hIF =
{
{ (natural_t) sIF },
{ MIMM | MCOMP },
{ (natural_t) &hQCST },
{ MFORE },
{ (natural_t) &hBIF },
{ (natural_t) &hDO },
{ (natural_t) &hQUERY },
{ (natural_t) XCOL },
{
{ (natural_t) &hCOMP },
{ (natural_t) &hZBR },
{ (natural_t) &hHERE }, /* adr */
{ (natural_t) &hZERO },
{ (natural_t) &hCOMMA },
{ (natural_t) &hLIT },
{ IF_FLAG },
{ (natural_t) &hSEMIS }
}
};
---------------------------
Over at comp.lang.c, they have been kind enough to point out to me
that the standard specifically does not support this kind of static
initialization of flexible arrays. If we want to use flexible arrays,
we must set them up at run time in our own code.
Now, gcc compiled that for me nicely until fairly recently. Actually,
I'm not sure which of the stricter points of the standard are tripping
me up. But, if the code is to remain useful, I'll have to do this a
different way.
One way is to convert all those lists of i-codes to their calls in
function bodies, something roughly like this:
---------------------------
static character_t sIF[] = "\x2" "IF";
definition_header_s hIF =
{
{ (natural_t) sIF },
{ MIMM | MCOMP },
{ (natural_t) &hQCST },
{ MFORE },
{ (natural_t) &hBIF },
{ (natural_t) &hDO },
{ (natural_t) &hQUERY },
{ (natural_t) IF }
};
void IF( void )
{
( * --SP ).icode = ZBR;
COMPILEFROMSTACK();
HERE(); /* adr */
ZERO();
COMMA();
( * --SP ).integer = IF_FLAG;
}
---------------------------
This has the disadvantage that the code no longer tests itself.
However, having it test itself is no longer necessary because I know
the flexible array should work. And if I still want it to test itself
this way, I can add a simulated RAM disk that contains some of the
optional words, like doubles, and have it load those in the startup
code.
The running interpreter would keep the parameters adjacent to the word
headers in the dictionary.
Another option is to split the parameter list from the header, in
the same way that I've split low-level code from the header:
-------------------------
typedef struct definition_header_s
{
cell_u nameLink; /* Bits above length are mode flags. Name is not far from header. C init with pointer. */
cell_u interpMode; /* Making the mode bits explicit to help the C source-level initializations. */
cell_u allocLink; /* Want to separate the allocation fields. */
cell_u allocMode; /* Decoupling the allocation, making the C initializations easier. */
cell_u vocabLink; /* Root of this definition's vocabulary. */
cell_u leftLink; /* Binary tree, left child in order. */
cell_u rightLink; /* Right child in order. */
cell_u codeLink; /* Interpretation code for this definition. */
cell_u parameterLink[ 2 ]; /* Parameters for the code for this definition. */
} definition_header_s;
-------------------------
with the result looking something roughly like this:
---------------------------
static character_t sIF[] = "\x2" "IF";
definition_header_s hIF =
{
{ (natural_t) sIF },
{ MIMM | MCOMP },
{ (natural_t) &hQCST },
{ MFORE },
{ (natural_t) &hBIF },
{ (natural_t) &hDO },
{ (natural_t) &hQUERY },
{ (natural_t) XCOL },
{ { (natural_t) IIF }, 0 }
};
icode_f IIF[] =
{ { (natural_t) &hCOMP },
{ (natural_t) &hZBR },
{ (natural_t) &hHERE }, /* adr */
{ (natural_t) &hZERO },
{ (natural_t) &hCOMMA },
{ (natural_t) &hLIT },
{ IF_FLAG },
{ (natural_t) &hSEMIS }
};
---------------------------
Now, my word headers are a bit bulky to start with, containing binary
tree links and vocabulary parent links in addition to the usual
allocation links. (Yes, FORGETting is tricky, and I never have gotten
it to work in this bif-c. I had it working in bif-6809.)
So, adding a parameter list link is just adding to the bulk. And if
I handle DOES> as I've indicated above, that's two extra links.
But it allows me to keep most of the self-testing aspect intact.
On the other hand, it induces a logical separation of the parameters
from the header. We can keep the parameters next to the headers, still,
but we can also separate them.
(Which, in and of itself, might mean that DOES> links can be handled
similarly to ordinary DOCOLON links, not actually needing the second
link after all. I haven't worked that out, yet.)
There is actually a potential advantage in this separation, but I am
under the impression that it would tend to push my interpreter even
further from actual Forth.
I'm mostly thinking out loud here, but if anyone cares to comment,
I'd be interested.