[defined] end-unit 0= [IF]
: unit (
-- unit-sys ) Create ;
: internal ( unit-sys1 -- unit-sys2 ) ;
: external ( unit-sys1 -- unit-sys2 ) ;
: end-unit ( unit-sys -- ) ;
[THEN]
unit -map-
\ How to use maps:
\ In order to create a map you have two choices:
\
\ 1. Create a named map (similar to a Forth VARIABLE):
\ `Map: `
\ `` gets the stack effect `( c-addr u -- addr ).
\ Executing `` later will take a key string and will return
\ a cell address where a value can be stored and retrieved with `!` and `@`.
\ Each time you invoke `` on the same string, you get the same address.
\ (*same string* in the sense of a string with the same characters,
\ `c-addr` might be different).
\
\ 2. Create an unnamed map (similar to OPEN-FILE)
\ `map`
\ This puts a map identifier on the stack which you can store in a
\ variable or use it to define a constant:
\ `VARIABLE my-map map my-map !`
\
\ In order to access items in the map you put the map identifier on the
\ stack and the perfom the desired operation on the map:
\
\ - store a value (e.g. 99) for a given key (e.g. )
\ `99 S" " my-map @ >value !`
\ - fetch a value for a given key (e.g. )
\ `S" " my-map @ >value @`
\
\ - iterate through the keys of a map
\ ```:noname ( i*x c-addr u -- j*x flag )
\ type space true ;
\ my-map @ iterate-map
\ ```
\
\ `iterate-map ( i*x xt map -- j*x )`
\ will take a map identifier `map` and an execution token `xt` from the stack
\ and invokes `xt` on every key in the map until all keys are processed
\ or until `xt` returns a `false` value. The stack `i*x` below the `xt` is
\ guaranteed to be accessible by `xt` so it can be used to manage context
\ information required for `xt` (such as the map identifier or a count).
\ ************ Implementation *************
\ Maps aka key/value structures are implemented
\ here with wordlists.
\ So internally we call them word lists `wid` but externally
\ we call them maps `map`.
1 cells VALUE map.space
\ The size in bytes of the storage space for each value
\ default is 1 cell
\ Check if there is an item in the given word list `wid`
\ with key `c-addr` `u`.
\ If the item exists return its body address and `true`.
\ If the item does not exist, return 0 and `false`.
: item? ( c-addr u wid -- addr true | 0 false )
search-wordlist IF >body true EXIT THEN 0 false ;
\ Create a variable like item with name `c-addr` `u`
\ in the current word list.
: "variable ( c-addr u -- )
s" CREATE " >r pad r@ move \ CREATE
pad r@ + swap dup >r move \ caddr u
pad r> r> + evaluate
map.space ALLOT ;
\ Create a new item with name `c-addr` `u` in the word list `wid`.
\ Return its body address.
: new-item ( c-addr u wordlist -- addr )
get-current >r set-current
['] "variable catch
r> set-current throw
here map.space - ;
external
\ Return the body address of an item with key `c-addr` `u`
\ in the key/value map `map`
: >value ( c-addr u map -- addr )
>r 2dup r@ item? IF r> drop nip nip EXIT THEN drop
r> new-item ;
\ Create a new map with the identifier `map` that can be
\ passed on the stack and stored in variables etc.
\ Items in the map are accessed via `>value` in the
\ ( c-addr u map -- addr )
: map ( -- map ) wordlist ;
\ Create a new named map ``. Items in `` are
\ accessed in the form: ( c-addr u -- addr ).
: Map: ( -- )
map Create ,
Does> ( c-addr u -- addr ) @ >value ;
internal
: invoke-xt ( i*x xt nt -- j*x xt flag )
swap >r name>string r@ execute r> swap ;
external
: iterate-map ( i*x xt map -- j*x )
['] invoke-xt swap traverse-wordlist drop ;
end-unit
cr .( Key/Value maps loaded )
cr .( Usage: map Constant