Fun with Forth Recognizers
⍽⍽⍽⍽⍽⍽⍽⍽⍽⍽⍽⍽⍽⍽⍽
April 27, 2024
Forth Recognizers
┄───────────────┄
• Matthias Trute RfD in 2018
• Allow extension of the Forth interpreter
◦ Change what happens while parsing + interpreting
◦ Compose set of "recognizers" of different types
◦ Extend literals, add strings, and more
• Homologous to changing colorForth color dispatch
• Caveat: I'm ignorant of the standards process
Terminology
┄─────────┄
Recognizer = Word given the chance to handle parsing
Recognizer Sequence / Stack = Ordered set of recognizers
Recognizer Type = Dispatch table returned by a recognizer
Recognizer Types
┄──────────────┄
RECTYPE: ( xt-interpret
xt-compile
xt-postpone
"name" -- )
: RECTYPE: ( xt1 xt2 xt3 "name" -- ) CREATE , , , ;
: do-notfound ( a n -- ) -1 'notfound @ execute ;
' do-notfound ' do-notfound ' do-notfound RECTYPE: RECTYPE-NONE
' execute ' , ' postpone, RECTYPE: RECTYPE-WORD
' execute ' execute ' , RECTYPE: RECTYPE-IMM
' drop ' execute ' execute RECTYPE: RECTYPE-NUM
: RECOGNIZE ( c-addr len addr1 -- i*x addr2 )
dup @ for aft
cell+ 3dup >r >r >r @ execute
dup RECTYPE-NONE <> if rdrop rdrop rdrop rdrop exit then
drop r> r> r>
then next
drop RECTYPE-NONE
;
Recognizer Stacks/Sequences
┄─────────────────────────┄
RECSTACK ( -- a )
+RECOGNIZER ( xt -- )
-RECOGNIZER ( -- )
GET-RECOGNIZERS ( -- xtn..xt1 n )
SET-RECOGNIZERS ( xtn..xt1 n -- )
create RECSTACK 0 , 10 cells allot
: +RECOGNIZER ( xt -- )
1 RECSTACK +! RECSTACK dup @ cells + ! ;
: -RECOGNIZER ( -- ) -1 RECSTACK +! ;
: GET-RECOGNIZERS ( -- xtn..xt1 n )
RECSTACK @ for RECSTACK r@ cells + @ next ;
: SET-RECOGNIZERS ( xtn..xt1 n -- )
0 RECSTACK ! for aft +RECOGNIZER then next ;
: postpone ( "name" -- )
bl parse RECSTACK RECOGNIZE @ execute ; immediate
: +evaluate1
bl parse dup 0= if 2drop exit then
RECSTACK RECOGNIZE state @ 1+ 1+ cells + @ execute
;
: REC-FIND ( c-addr len -- xt addr1 | addr2 )
find dup if
dup immediate? if RECTYPE-IMM else RECTYPE-WORD then
else
drop RECTYPE-NONE
then
;
' REC-FIND +RECOGNIZER
: REC-NUM ( c-addr len -- n addr1 | addr2 )
s>number? if
['] aliteral RECTYPE-NUM
else
RECTYPE-NONE
then
;
' REC-NUM +RECOGNIZER
also recognizers definitions
: REC-FNUM ( c-addr len -- f addr1 | addr2 )
s>float? if
['] afliteral RECTYPE-NUM
else
RECTYPE-NONE
then
;
' REC-FNUM +RECOGNIZER
previous definitions
: interpret0 begin +evaluate1 again ; interpret0
Effect on uEforth
┄───────────────┄
• PROs:
◦ Allows removing int/float parsing from core
- In theory, can be done in Forth
◦ Allows removing int/float parsing from core
• CONs:
◦ Requires opcodes for 0, 1, -1, 10, 41
◦ Fairly complex
PARSE
FIND
CREATE
EVALUATE1
┄───────────────┄
S>FLOAT?
S>NUMBER?
Applications
┄──────────┄
• New kinds of literals:
◦ Complex Numbers - 12-43i
◦ Rational Numbers - 22/7
◦ Logarithmic Number Systems - ~123
◦ Strings - "No need for a space"
◦ Atoms - 'myname
◦ New base prefixes - b$11010010
• General prefix/suffix:
◦ Store to Values - 123 >myvar
◦ Defining Syntax - square: dup * ;
Logarithmic Number Systems
┄────────────────────────┄
• Compute on fixed-point logarithms
• Use Gaussian Logarithms for add/subtract
log(x) + log(y) = log(x * y)
log(x) - log(y) = log(x / y)
log(x) * n = log(x ^ n)
Gaussian Logarithms
┄─────────────────┄
log2(x) + S(log2(y) - log2(x)) = log2(x + y)
log2(x) + D(log2(y) - log2(x)) = log2(|x - y|)
S(x) = log2(1 + 2^x)
D(x) = log2(|1 - 2^x|)
@summer.png
@differ.png
@both.png
log2(x) + S(log2(y) - log2(x))
= log2(x) + S(log2(y / x))
= log2(x) + log2(1 + 2^log2(y / x))
= log2(x) + log2(1 + y / x)
= log2(x + y)
LNS vs Floating Point vs Fixed
┄────────────────────────────┄
• LNS uniformly log scale vs tiered
• If you know your range, use fixed-point!
• LNS add/subtract is expensive
◦ Table vs CORDIC
- 89 (4-bit), 2440 (8-bit)
• Most problems have an actual scale
◦ But Audio, Light, Ritcher scale,
are logarithmic
LNS Syntax
┄────────┄
~3.14159
~22 ~7 ~/ CONSTANT PI
~100 ~20 ~3 ~+ ~+ ~.
: ~* ( ~ ~ -- ~ ) + ;
: ~/ ( ~ ~ -- ~ ) - ;
: ~/1 ( ~ -- ~ ) negate ;
: ~>f ( ~ -- f ) 2e s>f ~precision s>f f/ f** ;
: f>~ ( f -- ~ ) fln 2e fln f/ ~precision s>f f* 0.5e f+ floor f>s ;
: ~. ( ~ -- ) ~>f f. ;
: ~summer ( ~ -- ~ ) ~>f 1e f+ f>~ ;
: ~differ ( ~ -- ~ ) 1e ~>f f- fabs f>~ ;
: ~order ( ~ ~ -- ~ ~ ) 2dup max >r min r> ;
0 value entries
: tabulate
begin
entries ~summer ,
entries ~differ ,
1 +to entries
entries ~summer entries =
entries ~differ entries = and if exit then
again
;
create table tabulate
: ~summer1 ( ~ -- ~ ) dup entries < if 2* cells table + @ then ;
: ~differ1 ( ~ -- ~ ) dup entries < if 2* 1+ cells table + @ then ;
: ~+ ( ~ ~ -- ~ ) ~order over - ~summer1 + ;
: ~- ( ~ ~ -- ~ ) ~order over - ~differ1 + ;
0 value result
0 value places
0 value fract
: !digit ( ch -- ) dup [char] 0 < over [char] 9 > or if -1 throw then ;
: =digit ( ch -- )
dup [char] . = if drop -1 to fract exit then
!digit
[char] 0 - to result ;
: +digit ( ch -- )
dup [char] . = if drop -1 to fract exit then
!digit
fract if 1 +to places then
[char] 0 - result 10 * + to result ;
: ~conv ( a n -- )
>r 1+ dup c@ =digit r>
2 - for aft 1+ dup c@ +digit then next
drop
result s>~ 10 places dig ~/
['] aliteral rectype-num
;
: rec-~ ( a n -- )
dup 2 < if 2drop rectype-none exit then
over c@ [char] ~ <> if 2drop rectype-none exit then
0 to fract
0 to result
0 to places
['] ~conv catch if 2drop rectype-none exit then
;
' rec-~ +recognizer
DEMO
QUESTIONS❓
🙏
Thank you!
┄────────────────────────┄
http://eforth.appspot.com/