Krishna Myneni
2022-09-29 00:39:05 UTC
Forth 2012 standardizes the words FVALUE and 2VALUE which, like VALUE,
work with TO. Thus, the typed value contains in addition to the datum
another data field indicating the execution semantics for TO. Recently I
revised the implementation of TO and VALUE in kForth-64, using
standardized Forth 200x source, to support FVALUE and 2VALUE. It turned
out to be beneficial to use the name token (nt) instead of using the
(xt) for informing TO of how to compile the execution semantics in
kForth. This is because, for primitive words (those built-in to the
dictionary), the sequence
( xt addr -- ) POSTPONE LITERAL POSTPONE LITERAL POSTPONE EXECUTE
generates less efficient code for primitive words than the sequence
( nt addr -- ) POSTPONE LITERAL NAME>COMPILE EXECUTE
when TO executes in compilation state. In the stack diagram, addr is the
address of the VALUE's data, xt is the execution semantics for TO, and
nt is the name token for a word which provides execution semantics for
TO. The improved efficiency is realized because NAME>COMPILE can
recognize a primitive word and generate byte code for a direct call to
the primitive word rather than indirect execution via EXECUTE --
basically NAME>COMPILE permits a compiler-specific optimization in
kForth, which would otherwise not be possible if one were dealing with xt's.
The following implementation might serve as a reference implementation
for Forth 200x systems -- I say Forth 200x rather than 2012 because
FIND-NAME is required. FIND-NAME was only standardized in 2018 and does
not appear in the Forth 2012 standard.
Note the two kForth-specific definitions A@ and NONDEFERRED have simple
standard definitions:
------
: NONDEFERRED ;
SYNONYM
: TO ( i*x "name" -- ) \ or ( F: i*r -- ) ( "name" -- )
' >body
dup a@ swap cell+
state @ IF
postpone literal
name>compile execute
ELSE
swap name>interpret execute
THEN ; immediate
: xVALUE ( i*x nt-put usize -- ) ( F: j*r -- )
create 1 cells + allot? \ -- i*x nt-put a
2dup ! cell+ swap name>interpret execute immediate nondeferred
;
: VALUE ( n "name" -- )
[ s" !" find-name ] literal 1 cells xVALUE
does> cell+ state @ if postpone literal postpone @
else @ then ;
: 2VALUE ( x1 x2 "name" -- )
[ s" 2!" find-name ] literal 2 cells xVALUE
does> cell+ state @ if postpone literal postpone 2@
else 2@ then ;
: FVALUE ( F: r -- ) ( "name" -- )
[ s" F!" find-name ] literal 1 floats xVALUE
does> cell+ state @ if postpone literal postpone f@
else f@ then ;
------
Note that xVALUE is used as a common factor in defining the various
VALUE types. It may be used to write a user-defined value type such as
structure values. It takes as arguments the data associated with the
value (could be a combination of cell size, double cell size, and
floating point data), a name token which provides the execution
semantics for TO and the size in cells for the value. Besides
associating the nt with the value, xVALUE also uses the sequence
NAME>INTERPRET EXECUTE to initialize the named value.
The primitive words associated with execution semantics of TO for VALUE,
2VALUE, and FVALUE are "!" , "2!", and "F!", respectively. The nt's for
these words are stored in the created values of those types. Such an
approach won't be practical on embedded systems with tightly limited
memory, but it should be fine otherwise. I have done some tests on this
code under Gforth to ensure it works as expected, but it is still
experimental. The revised TO and VALUE and FVALUE and 2VALUE are now
included in the kForth-64 master branch, in ans-words.4th.
A few tests:
------------
5 value n1 ok
n1 . 5 ok
7 to n1 ok
n1 . 7 ok
: test n1 n1 + to n1 ; ok
test ok
n1 . 14 ok
3.14e fvalue almostPI ok
almostPI f. 3.14 ok
3.14159e to almostPI ok
almostPI f. 3.14159 ok
-1 0 2value d1 ok
d1 d. 18446744073709551615 ok
: test2
cr ['] almostPI execute f.
cr ['] n1 execute .
cr ['] d1 execute d.
cr ;
ok
test2
3.14159
14
18446744073709551615
ok
: test3 postpone almostPI postpone n1 postpone d1 ; immediate ok
: test4 test3 cr d. cr . cr f. cr ; ok
test4
18446744073709551615
14
3.14159
ok
-----
-- Krishna Myneni
work with TO. Thus, the typed value contains in addition to the datum
another data field indicating the execution semantics for TO. Recently I
revised the implementation of TO and VALUE in kForth-64, using
standardized Forth 200x source, to support FVALUE and 2VALUE. It turned
out to be beneficial to use the name token (nt) instead of using the
(xt) for informing TO of how to compile the execution semantics in
kForth. This is because, for primitive words (those built-in to the
dictionary), the sequence
( xt addr -- ) POSTPONE LITERAL POSTPONE LITERAL POSTPONE EXECUTE
generates less efficient code for primitive words than the sequence
( nt addr -- ) POSTPONE LITERAL NAME>COMPILE EXECUTE
when TO executes in compilation state. In the stack diagram, addr is the
address of the VALUE's data, xt is the execution semantics for TO, and
nt is the name token for a word which provides execution semantics for
TO. The improved efficiency is realized because NAME>COMPILE can
recognize a primitive word and generate byte code for a direct call to
the primitive word rather than indirect execution via EXECUTE --
basically NAME>COMPILE permits a compiler-specific optimization in
kForth, which would otherwise not be possible if one were dealing with xt's.
The following implementation might serve as a reference implementation
for Forth 200x systems -- I say Forth 200x rather than 2012 because
FIND-NAME is required. FIND-NAME was only standardized in 2018 and does
not appear in the Forth 2012 standard.
Note the two kForth-specific definitions A@ and NONDEFERRED have simple
standard definitions:
------
: NONDEFERRED ;
SYNONYM
: TO ( i*x "name" -- ) \ or ( F: i*r -- ) ( "name" -- )
' >body
dup a@ swap cell+
state @ IF
postpone literal
name>compile execute
ELSE
swap name>interpret execute
THEN ; immediate
: xVALUE ( i*x nt-put usize -- ) ( F: j*r -- )
create 1 cells + allot? \ -- i*x nt-put a
2dup ! cell+ swap name>interpret execute immediate nondeferred
;
: VALUE ( n "name" -- )
[ s" !" find-name ] literal 1 cells xVALUE
does> cell+ state @ if postpone literal postpone @
else @ then ;
: 2VALUE ( x1 x2 "name" -- )
[ s" 2!" find-name ] literal 2 cells xVALUE
does> cell+ state @ if postpone literal postpone 2@
else 2@ then ;
: FVALUE ( F: r -- ) ( "name" -- )
[ s" F!" find-name ] literal 1 floats xVALUE
does> cell+ state @ if postpone literal postpone f@
else f@ then ;
------
Note that xVALUE is used as a common factor in defining the various
VALUE types. It may be used to write a user-defined value type such as
structure values. It takes as arguments the data associated with the
value (could be a combination of cell size, double cell size, and
floating point data), a name token which provides the execution
semantics for TO and the size in cells for the value. Besides
associating the nt with the value, xVALUE also uses the sequence
NAME>INTERPRET EXECUTE to initialize the named value.
The primitive words associated with execution semantics of TO for VALUE,
2VALUE, and FVALUE are "!" , "2!", and "F!", respectively. The nt's for
these words are stored in the created values of those types. Such an
approach won't be practical on embedded systems with tightly limited
memory, but it should be fine otherwise. I have done some tests on this
code under Gforth to ensure it works as expected, but it is still
experimental. The revised TO and VALUE and FVALUE and 2VALUE are now
included in the kForth-64 master branch, in ans-words.4th.
A few tests:
------------
5 value n1 ok
n1 . 5 ok
7 to n1 ok
n1 . 7 ok
: test n1 n1 + to n1 ; ok
test ok
n1 . 14 ok
3.14e fvalue almostPI ok
almostPI f. 3.14 ok
3.14159e to almostPI ok
almostPI f. 3.14159 ok
-1 0 2value d1 ok
d1 d. 18446744073709551615 ok
: test2
cr ['] almostPI execute f.
cr ['] n1 execute .
cr ['] d1 execute d.
cr ;
ok
test2
3.14159
14
18446744073709551615
ok
: test3 postpone almostPI postpone n1 postpone d1 ; immediate ok
: test4 test3 cr d. cr . cr f. cr ; ok
test4
18446744073709551615
14
3.14159
ok
-----
-- Krishna Myneni