I am a Forth newbie, trying to develop some (pseudo useful) toys to learn the language. I want to make the following operations condensed:
[ifundef] vehicles 2variable vehicles [then]
[ifundef] cars< 2variable cars< [then]
vehicles 2@ s" cars " s+ vehicles 2!
cars< 2@ s" vehicles" s+ cars< 2!
by the following (much more compact) instruction
-> vehicles cars
Or, in other words:
I made an hack to obtain just this behavior using strings manipulation and evaluating...
: space+ ( str -- ) s" " s+ ;
\ use like: cars add alfa-romeo (first is a 2variable name, second a parsed name)
: add ( a "name" -- ) dup >r 2@ parse-name space+ s+ r> 2! ;
create _x 256 chars allot align
: _x@ ( -- ) _x count ;
: _x! ( -- ) _x place ;
create _y 256 chars allot align
: _y@ ( -- ) _y count ;
: _y! ( -- ) _y place ;
: init_x ( str -- ) 2dup s" [ifundef] " 2swap s+ s" 2variable " s+ 2swap s+ s" [then]" s+ evaluate ;
: init_y ( str -- ) 2dup s" [ifundef] " 2swap s+ s" < 2variable " s+ 2swap s" <" s+ s+ s" [then]" s+ evaluate ;
: make-dictionary-entries ( -- ) _x@ init_x _y@ init_y ;
: add-strings-to-entries ( -- ) _x@ s" add " s+ _y@ s+ evaluate
_y@ s" < add " s+ _x@ s+ evaluate ;
: -> parse-name _x! parse-name _y!
make-dictionary-entries
add-strings-to-entries ;
\ CUSTOM TESTING to improve readability of the examples
: test( POSTPONE assert( ; immediate
: true! 0= throw ;
: false! throw ;
: same-string! str= true! ;
-> vehicles cars
test( vehicles 2@ s" cars " same-string! )
test( cars< 2@ s" vehicles " same-string! )
-> vehicles trucks
-> vehicles dreams
test( vehicles 2@ s" cars trucks dreams " same-string! )
test( trucks< 2@ s" vehicles " same-string! )
-> cars ferrari
-> cars lamborghini
-> dreams lamborghini
test( cars 2@ s" ferrari lamborghini " same-string! )
test( lamborghini< 2@ s" cars dreams " same-string! )
I think another more direct, more elegant way exists, but this is the best I can do at this time. Any suggestions?
NB: It is not the general rules, but some weak points from the code in the question.
One common rule is following. In the plumbing part the postfix syntax should be only used. The prefix syntax (parsing words) may take place as sugar on the top level only. I.e. any parsing word should have a postfix variant.
A general postfix solution for the given problem is: s" content" s" name" update-var
\ reference implementations of some underlying words for testing purpose only
: s, ( sd.data -- ) here swap dup allot move ;
: s+ ( sd1 sd2 -- sd3 ) here >r 2swap s, s, r> here over - 0 c, ;
: s+! ( sd addr -- ) dup >r 2@ 2swap s+ r> 2! ; \ '+!' naming convention
: gs+ ( sd1 -- sd2 ) s" " s+ ; \ add gap string ('space+' is too long)
\ some Forth-systems have these words as factors:
: created ( sd.name -- ) s" create " 2swap s+ evaluate ;
: obey ( i*x sd.name wid -- j*x true | i*x sd.name false )
>r 2dup r> search-wordlist if nip nip execute true exit then false
;
\ the solution itself
wordlist constant v \ for special auto-created variables
: make-var ( sd.name -- addr )
get-current >r v set-current
created here 0 , 0 ,
r> set-current
;
: obtain-var ( sd.name -- addr )
v obey if exit then make-var
;
: update-var ( sd.content sd.name -- )
obtain-var s+!
;
: -> \ "vehicles" "cars"
parse-name parse-name
2over gs+ 2over s" <" s+ update-var
gs+ 2swap update-var
;
The data type symbol sd
stands for ( c-addr u )
cell-pair that represents a character string.
s,
( sd -- ) stores given string into the data space as is (see also ,
and c,
); NB: in Gforth a word with the same name stores a string in counted string format (see 3.1.3.4 Counted strings).
created
is a postfix variant of create
(in a well designed system the latter should be defined via the former); for etymology see also the standard words included
(a postfix form) and include
(a prefix form, parsing word).
It is also better to use a separate wordlist for these auto-created variables to avoid possible issues with clashing names (e.g. what if you need -> here str
). See the variant without a separate wordlist in the revision 8.
To write testcases the well-known ttester.fs library can be also used (in Gforth it is located in the test/
directory). In this library, the ->
word is defined for its own purpose, so the synonyms can be used to overcome undesired shadowing.