Digest #11 2016-09-03
Some time ago I floated on the c.l.f. mailing list the desire for future Forth specs to include some "natural" forward reference resolution mechanism rather than rely on defer (cf gforth section 5.9.10 on Deferred Words).
Unsurprisingly, I did not get an enthusiastic reception. The Forth veterans were not impressed with defer being inefficient and with defer reducing readability. So I decided to see how difficult it would be to provide such mechanism in my pet Forth project.
Since I use a Python script (amforth-shell.py) to upload Forth code to the SUT (system under test) this turned out to be a trivial task:
Add an immediate Forth word … (Unicode ellipsis, e.g., easy to introduce via Emacs \ldots TeX input) to display the current DP (dictionary pointer) value.
The ellipsis is used to mark in compilations the beginning of the two common forward reference instances, for example:
… ['] my-last-word execute.
Capture the above two source line instances via a simple Python regular expression:
re.compile(ur"…\s+(\['\]\s+)?(\S+)", re.UNICODE). Replace group(2) with an erased Flash placeholder code (
ffffin my case) and collect from the SUT into a dictionary the name to IP (instruction pointer) value mappings.
A source line directive (aka a pragma), #resolve, sends to the SUT the appropriate
' name ip !icommands to patch the Flash memory appropriately.
For your consideration, Respectfully, Enoch.
P/S Why \ldots (ellipsis) for a name: It is not something that you type in by accident. It takes little space on your source line. It hints that there is more to it...
The reference implementation should be clearer than pointing to amForth's assembler listings. The actual definition of WLSCOPE itself is trivial:
DEFER WLSCOPE ' GET-CURRENT IS WLSCOPE \ default
Integrating WLSCOPE into a system is system-specific, the GET-CURRENT in a word like HEADER needs to be replaced with WLSCOPE at a place where it can still modify the name. A simple dictionary might work like this:
Variable last : string, ( addr u -- ) dup c, here over allot swap move ; : header ( "name" -- ) \ create header with link field and name parse-name align here last ! wlscope 1 or , string, align ; : reveal ( -- ) \ add last definition to linked list last @ dup @ dup 1 and IF -2 and 2dup @ swap ! ! ELSE 2drop THEN ;
It seems that such typical use is applicable only in special case when you don't need to use the words from the target wordlist in the definition (e.g. when
: gui_init-gl ... ; doesn't use the words from
gui in its definition). Moreover it makes sense when definitions from several wordlists are interleaved.
By my experience such use case is very rare.
Chaining via deferred word is awkward — usually we need to revert back any special behavior to limit its scope.
Indeed, a reference implementation of wlscope in Gforth would be better than AmForth* cryptic VM ASM.
In defense of wlscope rule chaining via static defer-s I say, give me please a simpler idea. KISS.
Another wlscope example rule which I did not bring for its weirdness turns Forth variable step=100 into C language static int step = 100; equivalent. P/S In AmForth* value is EEPROM based...
A simpler interface (but slightly more complex implementation) is
add-wlscope ( xt -- ) \ where xt has the stack effect ( c-addr1 u1 -- c-addr2 u2 wid|c-addr1 u1 0 )
where 0 is returned if the xt does not match. The chaining is done by the system, until a match is found. The final wlscope is GET-CURRENT.
With this interface, if we later find the need to do it, we can blow it up to a full stack (like the search order), without needing to rewrite all the words written for this interface.
Another, more pedestrian way (but not requiring extra system work) to deal with deactivation of wlscopes is to have a flag variable for each wlscope, and the wlscope is only active if the flag is true. This would have to be hand-coded in every wlscope word.
Your two wordlist examples could be generalized to e generic "dot-parser" wlscope. I.e., if the input string is "FOO.BAR", it looks if FOO exists, and if so, there's a match, FOO is executed (should return a wid), and returns "BAR" for c-addr2 u2 and the wid. If we have such a generic wlscope, it may be ok to always keep it active.
Minor clarification: I meant that chaining via deferred word is awkward not by itself, but as API when you need to revert the chaining back. Although it can be confidently used under the hood.
Even a generic wlscope cannot be always kept active. What if you need some library that defines and uses both:
FOO vocabulary and
FOO.BAR word (and this library does not know anything about wlscope mechanism)?
Yes, active-flag variable for each wlscope is possible solution. But I think that we should prefer easy API usage to easy API implementation.
push-wlscope ( xt -- ) drop-wlscope ( -- )
can be clear implemented using linked list in the dictionary with leaking the last element in
On the other hand the proposed wlscope mechanism is just partial solution. What is really needed is straight-forward postfix API to create vocabulary entries. And having such API there is no need for wlscope mechanism. You can just define your own alternative for colon-word
: in your limited scope.
I also prefer easy usage over easy implementation. In particular, the active-variable convention has a social problem: If someone does not follow the convention and fails to make his wlscope deactivatable, it's often not him, but the users of his wlscope (or even further downstream) who suffer the consequences. So I also prefer a proper API; I just wanted to point out this other option.
- Perhaps an obvious comment: In a multi developer project each contributor can have his own wlscope rule chain while still using the simple defer based chaining. Thus, I strongly recommend to Keep It Sweet and Simple :-)
- Semi related: Once there is a convenient wordlist switching method the committee may consider recommending the use of a dedicated wordlist for each library/task. For example, _local to collect words which have no global interest or those that are just a result of factorization. OpenGL to collect words related to...