retroforth/interfaces/rre.forth
crc b8d1f4c722 start merge of the sqrt encoding words into rre, add f:fetch and f:store
FossilOrigin-Name: 43b3e7614a996222d820ffdac8f8288e2f2bd7e4dc92e05959bff9af12df8f67
2018-11-18 21:27:40 +00:00

478 lines
10 KiB
Forth

# RETRO
This is a set of extensions for RRE.
# Console Input
~~~
:c:get (-c) `1001 ;
~~~
---------------------------------------------------------------
# Floating Point
~~~
:n:to-float (n-_f:-n) #0 `-6000 ;
:s:to-float (s-_f:-n) #1 `-6000 ;
:f:to-string (f:n-__-s) s:empty dup #2 `-6000 ;
:f:+ (f:ab-c) #3 `-6000 ;
:f:- (f:ab-c) #4 `-6000 ;
:f:* (f:ab-c) #5 `-6000 ;
:f:/ (f:ab-c) #6 `-6000 ;
:f:floor (f:ab-c) #7 `-6000 ;
:f:eq? (f:ab-c) #8 `-6000 ;
:f:-eq? (f:ab-c) #9 `-6000 ;
:f:lt? (f:ab-c) #10 `-6000 ;
:f:gt? (f:ab-c) #11 `-6000 ;
:f:depth (-n) #12 `-6000 ;
:f:dup (f:a-aa) #13 `-6000 ;
:f:drop (f:a-) #14 `-6000 ;
:f:swap (f:ab-ba) #15 `-6000 ;
:f:log (f:ab-c) #16 `-6000 ;
:f:power (f:ab-c) #17 `-6000 ;
:f:to-number (f:a-__-n) #18 `-6000 ;
:f:sin (f:f-f) #19 `-6000 ;
:f:cos (f:f-f) #20 `-6000 ;
:f:tan (f:f-f) #21 `-6000 ;
:f:asin (f:f-f) #22 `-6000 ;
:f:acos (f:f-f) #23 `-6000 ;
:f:atan (f:f-f) #24 `-6000 ;
:f:ceiling (f:f-f) #25 `-6000 ;
:f:sqrt (f:f-f) #26 `-6000 ;
:f:square (f:n-m) f:dup f:* ;
:f:over (f:ab-aba) f:to-string f:dup s:to-float f:swap ;
:f:tuck (f:ab-bab) f:swap f:over ;
:f:positive? (-f__f:a-) #0 n:to-float f:gt? ;
:f:negative? (-f__f:a-) #0 n:to-float f:lt? ;
:f:negate (f:a-b) #-1 n:to-float f:* ;
:f:abs (f:a-b) f:dup f:negative? [ f:negate ] if ;
:prefix:. (s-__f:-a)
compiling? [ s:keep ] [ s:temp ] choose &s:to-float class:word ; immediate
:f:put (f:a-) f:to-string s:put ;
:f:PI (f:-F) .3.141592 ;
:f:E (f:-F) .2.718281 ;
:f:NAN (f:-n) .0 .0 f:/ ;
:f:INF (f:-n) .1.0 .0 f:/ ;
:f:-INF (f:-n) .-1.0 .0 f:/ ;
:f:nan? (f:n-,-f) f:dup f:-eq? ;
:f:inf? (f:n-,-f) f:INF f:eq? ;
:f:-inf? (f:n-,-f) f:-INF f:eq? ;
:f:round (-|f:a-b)
f:dup f:negative?
[ .0.5 f:- f:ceiling ]
[ .0.5 f:+ f:floor ] choose ;
~~~
---------------------------------------------------------------
# Float
## Description
This implements a means of encoding floating point values into signed integer cells. The technique is described in the paper titled "Encoding floating point numbers to shorter integers" by Kiyoshi Yoneda and Charles Childers.
This will extend the `f:` vocabulary and adds a new `u:` vocabulary.
## Code & Commentary
Define some constants. The range is slightly reduced from the standard integer range as the smallest value is used for NaN.
~~~
n:MAX n:dec 'u:MAX const
n:MAX n:dec n:negate 'u:MIN const
n:MIN 'u:NAN const
n:MAX 'u:INF const
n:MAX n:negate 'u:-INF const
~~~
~~~
:u:n? (u-f)
u:MIN n:inc u:MAX n:dec n:between? ;
:u:max? (u-f) u:MAX eq? ;
:u:min? (u-f) u:MIN eq? ;
:u:zero? (u-f) n:zero? ;
:u:nan? (u-f) u:NAN eq? ;
:u:inf? (u-f) u:INF eq? ;
:u:-inf? (u-f) u:-INF eq? ;
:u:clip (u-u) u:MIN u:MAX n:limit ;
~~~
Define the scaling factors. Adjust these as needed for your application.
~~~
:f:U1 (-|f:-b) .1.e9 ;
:f:BALANCE (-|f:-b) .1. ;
~~~
~~~
{{
:f:scale (-|f:a-b) f:U1 f:* ;
:f:descale (-|f:a-b) f:U1 f:/ ;
:f:encode (-|f:a-b)__n/(s_+_n_)
f:BALANCE f:over f:+ f:/ ;
:f:decode (-|f:a-b)_su/(1_-_u_)
.1. f:over f:- f:/ f:BALANCE f:* ;
---reveal---
:f:to-u (-u|f:a-)
f:dup f:encode f:scale f:round f:to-number u:clip
f:dup f:nan? [ drop u:NAN ] if
f:dup f:inf? [ drop u:INF ] if
f:dup f:-inf? [ drop u:-INF ] if
f:drop ;
:u:to-f (u-|f:-b)
dup n:to-float f:descale f:decode
dup u:nan? [ f:drop f:NAN ] if
dup u:inf? [ f:drop f:INF ] if
dup u:-inf? [ f:drop f:-INF ] if
drop ;
}}
~~~
~~~
:f:store (a-|f:n-) [ f:to-u ] dip store ;
:f:fetch (a-|f:-n) fetch u:to-f ;
~~~
---------------------------------------------------------------
# Gopher
RETRO has Gopher support via `gopher:get`.
Takes:
destination
server name
port
selector
Returns:
number of characters read
~~~
:gopher:get `-6200 ;
~~~
---------------------------------------------------------------
# Scripting: Command Line Arguments
~~~
:sys:argc (-n) `-6100 ;
:sys:argv (n-s) s:empty swap `-6101 ;
~~~
# System Interaction
The `unix:` namespace contains words for interacting with the
host operating system.
`unix:system` runs another application using the system shell
and returns after execution is completed.
~~~
:unix:system (s-) #-8000 `-6300 ;
~~~
`unix:fork` forks the current process and returns a process
identifier.
~~~
:unix:fork (-n) #-8001 `-6300 ;
~~~
`unix:exit` takes a return code and exits RRE, returning the
specified code.
~~~
:unix:exit (n-) #-8002 `-6300 ;
~~~
`unix:getpid` returns the current process identifier.
~~~
:unix:getpid (-n) #-8003 `-6300 ;
~~~
This group is used to execute a new process in place of the
current one. These take a program and optionally 1-3 arguments.
They map to the execl() system call.
Example:
'/usr/bin/cal '2 '2019 unix:exec2
~~~
:unix:exec0 (s-) #-8004 `-6300 ;
:unix:exec1 (ss-) #-8005 `-6300 ;
:unix:exec2 (sss-) #-8006 `-6300 ;
:unix:exec3 (ssss-) #-8007 `-6300 ;
~~~
`unix:wait` waits for a child process to complete. This maps to
the wait() system call.
~~~
:unix:wait (-n) #-8008 `-6300 ;
~~~
`unix:kill` terminates a process. Takes a process and a signal
to send.
~~~
:unix:kill (nn-) #-8009 `-6300 ;
~~~
The next two words allow opening and closing pipes. The first,
`unix:popen` takes the name of a program and a file mode and
returns a file handle usable with words in the `file:` namespace.
The second, `unix:pclose` closes the pipe.
~~~
:unix:popen (sn-n) #-8010 `-6300 ;
:unix:pclose (n-) #-8011 `-6300 ;
~~~
~~~
:unix:write (sh-) [ dup s:length ] dip #-8012 `-6300 ;
~~~
`unix:chdir` changes the current working directory to the
specified one.
~~~
:unix:chdir (s-) #-8013 `-6300 ;
~~~
~~~
:unix:getenv (sa-) #-8014 `-6300 ;
:unix:putenv (s-) #-8015 `-6300 ;
~~~
`unix:sleep` pauses execution for the specified number of
seconds.
~~~
:unix:sleep (n-) #-8016 `-6300 ;
~~~
---------------------------------------------------------------
# File I/O
This implements words for interfacing with the POSIX file I/O words if
you are using an interface supporting them. All of these are in the
`file:` namespace.
These are pretty much direct wrappers for fopen(), fclose(), etc.
First up, constants for the file modes.
| # | Used For |
| - | ------------------ |
| R | Mode for READING |
| W | Mode for WRITING |
| A | Mode for APPENDING |
~~~
#0 'file:R const
#1 'file:W const
#2 'file:A const
#3 'file:R+ const
~~~
For opening a file, provide the file name and mode. This will return a
number identifying the file handle.
~~~
:file:open (sm-h) `118 ;
~~~
Given a file handle, close the file.
~~~
:file:close (h-) `119 ;
~~~
Given a file handle, read a character.
~~~
:file:read (h-c) `120 ;
~~~
Write a character to an open file.
~~~
:file:write (ch-) `121 ;
~~~
Return the current pointer within a file.
~~~
:file:tell (h-n) `122 ;
~~~
Move the file pointer to the specified location.
~~~
:file:seek (nh-) `123 ;
~~~
Return the size of the opened file.
~~~
:file:size (h-n) `124 ;
~~~
Given a file name, delete the file.
~~~
:file:delete (s-) `125 ;
~~~
Flush pending writes to disk.
~~~
:file:flush (f-) `126 ;
~~~
Given a file name, return `TRUE` if it exists or `FALSE` otherwise.
~~~
:file:exists? (s-f)
file:R file:open dup n:-zero?
[ file:close TRUE ]
[ drop FALSE ] choose ;
~~~
~~~
:file:open<for-reading> (s-nn)
file:R file:open dup file:size swap ;
:file:open<for-append> (s-nn)
file:A file:open dup file:size swap ;
:file:open<for-writing> (s-n)
file:W file:open ;
~~~
With that out of the way, we can begin building higher level functionality.
The first of these reads a line from the file. This is read to `here`; move
it somewhere safe if you need to keep it around.
The second goes with it. The `for-each-line` word will invoke a combinator
once for each line in a file. This makes some things trivial. E.g., a simple
'cat' implementation could be as simple as:
'filename [ s:put nl ] file:for-each-line
~~~
{{
'FID var
'Size var
'Action var
'Buffer var
:-eof? (-f) @FID file:tell @Size lt? ;
:preserve (q-) &FID [ &Size [ call ] v:preserve ] v:preserve ;
---reveal---
:file:read-line (f-s)
!FID
[ here dup !Buffer buffer:set
[ @FID file:read dup buffer:add
[ ASCII:CR eq? ] [ ASCII:LF eq? ] [ ASCII:NUL eq? ] tri or or ] until
buffer:get drop ] buffer:preserve
@Buffer ;
:file:for-each-line (sq-)
[ !Action
file:open<for-reading> !FID !Size
[ @FID file:read-line @Action call -eof? ] while
@FID file:close
] preserve ;
}}
~~~
`file:slurp` reads a file into a buffer.
~~~
{{
'FID var
'Size var
---reveal---
:file:slurp (as-)
[ file:open<for-reading> !FID !Size
buffer:set
@Size [ @FID file:read buffer:add ] times
@FID file:close
] buffer:preserve ;
}}
~~~
~~~
{{
'FID var
---reveal---
:file:spew (ss-)
file:open<for-writing> !FID
[ @FID file:write ] s:for-each
@FID file:close ;
}}
~~~
~~~
:unix:io:n:put (n-) #-8100 `-6300 ;
:unix:io:s:put (s-) #-8101 `-6300 ;
~~~
# Interactive Listener
~~~
'NoEcho var
{{
:version (-) @Version #100 /mod n:put $. c:put n:put ;
:eol? (c-f) [ ASCII:CR eq? ] [ ASCII:LF eq? ] [ ASCII:SPACE eq? ] tri or or ;
:valid? (s-sf) dup s:length n:-zero? ;
:ok (-) @NoEcho not 0; drop compiling? [ nl 'Ok_ s:put ] -if ;
:check-eof (c-c) dup [ #-1 eq? ] [ #4 eq? ] bi or [ 'bye d:lookup d:xt fetch call ] if ;
:check-bs (c-c) dup [ #8 eq? ] [ #127 eq? ] bi or [ buffer:get buffer:get drop-pair ] if ;
:s:get (-s) [ #1025 buffer:set
[ c:get dup buffer:add check-eof check-bs eol? ] until
buffer:start s:chop ] buffer:preserve ;
---reveal---
:banner (-) @NoEcho not 0; drop
'RETRO_12_(rx- s:put version $) c:put nl
EOM n:put '_MAX,_TIB_@_1025,_Heap_@_ s:put here n:put nl ;
:bye (-) #0 unix:exit ;
:listen (-)
ok repeat s:get valid? [ interpret ok ] [ drop ] choose again ;
}}
~~~
~~~
:include (s-) `-9999 ;
~~~
~~~
{{
:gather (c-)
dup [ #8 eq? ] [ #127 eq? ] bi or [ drop ] [ buffer:add ] choose ;
:cycle (q-qc) repeat c:get dup-pair swap call not 0; drop gather again ;
---reveal---
:parse-until (q-s)
[ s:empty buffer:set cycle drop-pair buffer:start ] buffer:preserve ;
}}
:s:get (-s) [ [ ASCII:LF eq? ] [ ASCII:CR eq? ] bi or ] parse-until ;
~~~