evocation/files.e
Irene Knapp 3b565483d9
properly report and handle EOF when handling input
this should be a robust fix for the double-termination issue when compiling

Force-Push: yes
Change-Id: Ieaacdc232ea7e3036916d81139fc5c5a41087c64
2026-09-23 20:51:14 -07:00

161 lines
5.1 KiB
Text

~ ~~~~~~~~~~~~~~~~~~~~~~~
~ ~~ File manipulation ~~
~ ~~~~~~~~~~~~~~~~~~~~~~~
~
~ This file has high-level features for working with files and the
~ filesystem. There's a tension as to what belongs here vs. what belongs in
~ input.e or output.e, and it's likely there will be some refactoring as the
~ boundaries become clear.
~ (output point, filename -- output point)
: pack-file-contents
dup 0 0 sys-open
~ (output point, filename, open result code or file descriptor)
dup 0 > {
." Couldn't open " swap emitstring space
." because " space . ." ." newline
2 ndrop exit
} if
~ (output point, filename, file descriptor)
{
dup 3 pick 1024 sys-read
~ (output point, filename, file descriptor, read result code or length)
dup 0 > {
over sys-close drop
~ Ignore a failed close.
." Error while reading from " 3roll emitstring
." because " space . ." ." newline
drop exit
} if
dup 0 = {
over sys-close drop
~ Ignore a failed close.
3 ndrop exit
} if
4 roll + 3unroll
} forever ;
~ TODO probably make this more similar to pack-file-contents
~ (delimiter pointer, buffer size -- buffer address)
: read-to-buffer
dup allocate dup dup
~ (buffer size, buffer address, word start, output point)
{ key
~ Exit if it's a zero byte.
dup not {
~ Make sure to pack the zero to serve as a null terminator.
pack8
drop drop swap drop swap drop exit } if
~ If it's a space character, we need to check if we just consumed the
~ magic word, and do some handling to reset the word tracking.
dup is-space
{ ~ (buffer size, buffer address, word start, output point, key)
~ Tuck the key out of the way until we've done some stuff.
3unroll
~ Add a null terminator so we can use stringcmp
dup 0 swap 8!
~ Check for the magic word.
over 6 pick stringcmp 0 =
{ ~ It's magic, so exit.
~ Make sure to pack a zero to serve as a null terminator.
0 pack8
drop drop drop swap drop swap drop exit }
{ ~ It's not magic, so reset the word start. Of course whitespace is
~ not a word but this will help us keep track of things.
3roll pack8
swap drop dup } if-else }
{ ~ (buffer size, buffer address, word start, output point, key)
~ Tuck the key out of the way again.
3unroll
~ Check if the word just started and the previous character is space.
2dup = dup { drop dup @ is-space } if
{ ~ If so, this is the actual first character of the word.
drop swap pack8 dup }
{ ~ If not, leave the word start alone.
3roll pack8 } if-else } if-else } forever ;
~ In logical terms, this modifies an input buffer metadata structure
~ in-place to push a new, zeroed one into the start of the linked list formed
~ through the next-source field.
~
~ In physical terms, it works by allocating a new structure, copying the
~ fields of the existing one into it, and zeroing the existing one. That's
~ necessary because otherwise we'd need a mutable handle (a pointer to a
~ pointer) to update the start of the list, and there's no way to do that with
~ the main-input-buffer variable working the way it presently does.
~
~ (input buffer metadata pointer --)
: push-input-buffer
allocate-input-buffer-metadata
~ (original metadata pointer, new metadata pointer)
2dup 7 8 * memcopy
~ (original metadata pointer, new metadata pointer)
swap dup zero-input-buffer-metadata
input-buffer-next-source ! ;
~ This does the inverse of push-input-buffer. In the event that the
~ next-source field is null, it zeroes the buffer.
~
~ Note, however, that it doesn't deallocate the memory, because that's not
~ how memory allocation on the log works. If necessary, it can be deallocated
~ with "forget", though as usual that requires careful planning.
~
~ (input buffer metadata pointer --)
: pop-input-buffer
dup input-buffer-next-source @
~ (original metadata pointer, next source metadata pointer)
dup { swap 7 8 * memcopy }
{ drop zero-input-buffer-metadata } if-else ;
: default-buffer-size 1024 ;
~ (input buffer metadata pointer, file descriptor --)
: attach-input-buffer-to-file-descriptor
over buffer-file-descriptor !
~ (metadata pointer)
default-buffer-size allocate dup 2 pick buffer-physical-start !
~ (metadata pointer, buffer pointer)
over buffer-logical-start !
~ (metadata pointer)
default-buffer-size over buffer-physical-length !
0 over buffer-logical-length !
' refill-input-buffer-from-stream entry-to-execution-token
swap input-buffer-refill ! ;
~ Zero indicates success, nonzero indicates failure.
~
~ (filename -- *, success)
: include
dup 0 0 sys-open
dup 0 > {
." Couldn't open " swap emitstring space
." because " space . ." ." newline
drop 1 exit
} if
~ (file descriptor)
main-input-buffer dup push-input-buffer
swap attach-input-buffer-to-file-descriptor
{ peek -1 != } { interpret } while
main-input-buffer buffer-file-descriptor @ sys-close drop
~ Ignore a failed close.
main-input-buffer pop-input-buffer 0 ;