commit 4450bf3d272dd931dcab64ad0364251261e7624c
parent 09e5373e839e928fdad262c792e665c4d330bc70
Author: Virgil Dupras <hsoft@hardcoded.net>
Date: Thu, 8 Sep 2022 07:59:43 -0400
lib/io: introduce StdIn and StdOut
See doc/io. This makes I/O redirection a bit more straightforward because we
can set handles directly, removing the need for the [f<] hack in sys/file.
Diffstat:
6 files changed, 52 insertions(+), 36 deletions(-)
diff --git a/fs/app/cos/tools/blkpack.c b/fs/app/cos/tools/blkpack.c
@@ -3,6 +3,7 @@
// TODO: string scanning
// TODO: add NULL const
// TODO: add strlen() (alias to c@)
+// TODO: add \n in string literals
// TODO: do multiple "return" paths work?
// TODO: can we "return" early in a void function?
// TODO: signed arithmetics
diff --git a/fs/doc/io.txt b/fs/doc/io.txt
@@ -7,7 +7,7 @@ needed, all states needed to fulfill the API's specifications.
I/O operations are byte based.
-When "EOF" is mentioned, it means "end of file". In same case, it's a misnomer,
+When "EOF" is mentioned, it means "end of file". In some cases, it's a misnomer,
but the idea is the same: if all possible data from the I/O source have been
consumed, we're in EOF condition.
@@ -17,15 +17,15 @@ The structure of these handle will vary depending on the driver or filesystem,
but they all start with the same structure, which is a list of 4b pointers to
words, in this order:
-readbuf
-writebuf
-flush
+:readbuf
+:writebuf
+:flush
These words have the following API. In all words, "hdl" is a pointer to a IO
handle. If the handle has no concept of "flush", it can point to "drop" (as a
noop).
-readbuf ( n hdl -- a? read-n )
+:readbuf ( n hdl -- a? read-n )
Try to read "n" bytes from file from current position. Unless EOF is reached,
at least 1 byte must be read, but otherwise "read-n" can be lower than n, even
if that doesn't take the handle to EOF. The idea is to take the handle, at
@@ -34,7 +34,7 @@ readbuf ( n hdl -- a? read-n )
After the read operation, advance current position by "read-n" bytes.
-writebuf ( a n hdl -- written-n )
+:writebuf ( a n hdl -- written-n )
Try to write "n" bytes from buffer "a" to file from current position, growing
the file if needed. The idea is the same as with freadbuf: the filesystem can
proceed as is best for its implementation, as long as it writes at least 1
@@ -42,21 +42,38 @@ writebuf ( a n hdl -- written-n )
After the write operation, advance current position by "written-n" bytes.
-flush ( hdl -- )
+:flush ( hdl -- )
If the handle's buffer is "dirty" (has been changed compared to the permanent
storage it represents), save that data to permanent storage and flag the
handle as clean.
+In addition to those device-specific words, all IO structures share these
+convenience words:
+
+: write ( a n hdl -- )
+ Repeatedly call :writebuf until all requested bytes have been written.
+
## Generic API
From the base API of IO handlers, lib/io has these generic words:
-getc ( hdl -- b )
+getc ( hdl -- c )
Read 1 byte from hdl an return it. Advance position by 1 byte. Return -1 on
EOF.
-putc ( b hdl -- )
+putc ( c hdl -- )
Write 1 byte to hdl. Advance position by 1 byte. Aborts if unable to write.
It also has, for each IO handler word, a helper to directly call it. For
example, "hdl flush" simply does "hdl 8 + @ execute".
+
+## StdIn and StdOut
+
+lib/io defines StdIn and StdOut values. These point to IO structures and, at
+initialization, point to ConsoleIn (an IO structure that reads from in<) and
+ConsoleOut (an IO structure that writes to emit).
+
+You can change those values to redirect I/Os for code that uses these values.
+
+There is also stdin, which is a shortcut for "StdIn getc" and stdout, for
+"StdOut putc".
diff --git a/fs/lib/io.fs b/fs/lib/io.fs
@@ -1,19 +1,31 @@
\ Input/Output. See doc/io
-: getc ( hdl -- c ) 1 swap IO :readbuf if c@ else -1 ( EOF ) then ;
-
-alias in< stdin ( -- c )
-
create _buf( $100 allot
here value _)buf
+: getc ( hdl -- c ) 1 swap IO :readbuf if c@ else -1 ( EOF ) then ;
+: putc ( c hdl -- ) swap _buf( c! _buf( 1 rot IO :writebuf ;
+
+\ StdIO
+: _err abort" Invalid I/O" ;
+: _readbuf ( a hdl -- a? read-n )
+ drop in< dup 0< if drop 0 else _buf( c! _buf( 1 then ;
+create ConsoleIn ' _readbuf , ' _err , ' _err ,
+ConsoleIn value StdIn
+: stdin StdIn getc ;
+: _writebuf ( a n hdl -- written-n ) 2drop c@ emit 1 ;
+create ConsoleOut ' _err , ' _writebuf , ' drop ,
+ConsoleOut value StdOut
+: stdout StdOut putc ;
+: stdio$ ConsoleIn to StdIn ConsoleOut to StdOut ;
+
\ read stdin for a maximum of STR_MAXSZ-1 characters until LF is encountered,
\ then return a string representing that read line. The LF character is not
\ included. Aborts on LNSZ overflow.
: readline ( -- str )
_buf( 1+ >r begin ( ) \ V1=buf
V1 _)buf = if abort" readline overflow" then
- in< dup LF = not while 8b to!+ V1 repeat drop
+ stdin dup LF = not while 8b to!+ V1 repeat drop
r> _buf( - 1- ( len ) _buf( c! _buf( ( str ) ;
: _iowrite ( a n self -- ) >r begin ( a n ) ?dup while
@@ -21,4 +33,3 @@ here value _)buf
( a n written-n ) tuck - ( a written-n new-n ) rot> + swap repeat ( a )
drop rdrop ;
current to IO :write
-
diff --git a/fs/sys/file.fs b/fs/sys/file.fs
@@ -112,21 +112,9 @@ Path _curpath structbind Path curpath
: f<< word curpath :find# Path :fload ;
: ?f<< word curpath :find# dup Path :floaded? if drop else Path :fload then ;
-?f<< /lib/scratch.fs
-\ We need a private scratchpad here because some cursors can be quite
-\ long-lived. If we use the system scratchpad, short-lived data will overwrite
-\ our cursors.
-create _filespad $200 scratchpad$
-_filespad structbind Scratchpad filespad
-
-\ This creates a "f<" reader with the file descriptor embedded in it. This
-\ allows for a straightforward override of input/output words.
-: [f<] ( curfd -- word )
- filespad :[ litn compile getc exit, filespad :] ;
-
: require word dup curpath :find# Path :floaded? not if
stype abort" required" else drop then ;
: with-stdin-file ( w str -- )
- to@ stdin >r curpath :find# Path :open dup >r ( w hdl )
- [f<] to stdin execute
- r> File :close r> to stdin ;
+ StdIn >r curpath :find# Path :open dup >r ( w hdl )
+ to StdIn execute
+ r> File :close r> to StdIn ;
diff --git a/fs/tests/cc/ast.fs b/fs/tests/cc/ast.fs
@@ -2,8 +2,7 @@
?f<< cc/ast.fs
testbegin
\ Tests for the C compiler AST
-S" tests/cc/test.c" curpath :find# Path :open dup [f<] to stdin parseast
-File :close
+' parseast S" tests/cc/test.c" with-stdin-file
curunit Node firstchild dup Node id AST_FUNCTION #eq ( fnode )
dup Function name S" retconst" s= #
diff --git a/fs/xcomp/bootlo.fs b/fs/xcomp/bootlo.fs
@@ -265,11 +265,11 @@ struct[ Drive
\ I/O API
\ Anticipating lib/io
struct[ IO
- smethod :readbuf
- smethod :writebuf
- smethod :flush
+ smethod :readbuf ( n hdl -- a? read-n )
+ smethod :writebuf ( a n hdl -- written-n )
+ smethod :flush ( hdl -- )
\ forward declarations
- alias abort :write
+ alias abort :write ( a n hdl -- )
]struct
\ File API