oh, oh wow
-
oh, oh wow
we found a bug in our Forth
if a line ends with the word
~, which introduces a comment that lasts until the end of the line, the next line is commentedit is not clear what the best way to fix that is; it happens because the lexer consumes the whitespace that terminates the word (there's no "peek" feature)
-
it is not clear what the best way to fix that is; it happens because the lexer consumes the whitespace that terminates the word (there's no "peek" feature)
we should probably implement "peek", really. the only reason not to is that.... the semantics of what characters get consumed, when, are part of the user-facing API, because that's how Forth does things
-
we should probably implement "peek", really. the only reason not to is that.... the semantics of what characters get consumed, when, are part of the user-facing API, because that's how Forth does things
like it needs to interact well with user code that reads from the same input stream the internals do
-
like it needs to interact well with user code that reads from the same input stream the internals do
ha, hm, should we call it "peek". hm. early microcomputers were very big on using the words "peek" and "poke" for an entirely different purpose, we don't know for sure but we assume that may have been true on minicomputers as well.
-
ha, hm, should we call it "peek". hm. early microcomputers were very big on using the words "peek" and "poke" for an entirely different purpose, we don't know for sure but we assume that may have been true on minicomputers as well.
we've been calling the output routines variations on "emit", which is the historical Forth style, rather than "print" which is what every mainstream language created in our body's lifetime has used
-
we've been calling the output routines variations on "emit", which is the historical Forth style, rather than "print" which is what every mainstream language created in our body's lifetime has used
yeah we definitely don't need the names peek/poke for anything else, because those operations (arbitrary memory access) are
@and!in Evocation, and more generally most Forths call them either that or fetch/store.also in the event we ever have users, it would only be confusing to people our exact age who had those childhood experiences, and most trans people our age are dead
-
yeah we definitely don't need the names peek/poke for anything else, because those operations (arbitrary memory access) are
@and!in Evocation, and more generally most Forths call them either that or fetch/store.also in the event we ever have users, it would only be confusing to people our exact age who had those childhood experiences, and most trans people our age are dead
so we're gonna call it "peek"
(the word that reads a character and properly consumes it is "key", by the way, also following historical Forth practice)
-
so we're gonna call it "peek"
(the word that reads a character and properly consumes it is "key", by the way, also following historical Forth practice)
C avoids having a
peek()operation by instead havingungetc()but that's terrible. we've always disliked that, it exposes too much implementation detail.
-
C avoids having a
peek()operation by instead havingungetc()but that's terrible. we've always disliked that, it exposes too much implementation detail.
okay, thinking it through... moving to using
peekas part ofwordwill only affect words that read strings, which is justs"and.". they'll both need to consume that character explicitly. that seems fine to us, though it does defy conventional Forth practice. we're gonna get way more blasphemous eventually, so that's fine. -
okay, thinking it through... moving to using
peekas part ofwordwill only affect words that read strings, which is justs"and.". they'll both need to consume that character explicitly. that seems fine to us, though it does defy conventional Forth practice. we're gonna get way more blasphemous eventually, so that's fine.it was quite frustrating debugging this, btw. we kind of got nowhere with bisecting how we introduced the problem, which in hindsight makes sense because we were doing that by commenting out lines, and doing so was having effects we didn't expect
-
it was quite frustrating debugging this, btw. we kind of got nowhere with bisecting how we introduced the problem, which in hindsight makes sense because we were doing that by commenting out lines, and doing so was having effects we didn't expect
we tried dropping back to
gdbwhich is how we debugged everything until Evocation's own debugging abilities got better, but that didn't tell us anything useful. we had to fall back to modding the interpreter to print every word before executing it, which did get us on the right track. -
we tried dropping back to
gdbwhich is how we debugged everything until Evocation's own debugging abilities got better, but that didn't tell us anything useful. we had to fall back to modding the interpreter to print every word before executing it, which did get us on the right track.the hardest bugs to find are the ones that are direct consequences of intended behavior

-
the hardest bugs to find are the ones that are direct consequences of intended behavior

okay so the current hierarchy of things is that the top-level word is
quit, which loops and callsinterpret, which callsword, which callskeyin its own loop, which callsmain-input-bufferto get the metadata pointer and passes that tokey-from. -
okay so the current hierarchy of things is that the top-level word is
quit, which loops and callsinterpret, which callsword, which callskeyin its own loop, which callsmain-input-bufferto get the metadata pointer and passes that tokey-from.so we want to split the behavior of
key-fromintopeek-fromandconsume-fromand have `key-from simplified to just call thoseand then we probably, as backfill, want to also implement
peekandconsumewhich callmain-input-bufferand invoke the*-fromvariants on it. we won't use those variants inwordbut user code might want to -
so we want to split the behavior of
key-fromintopeek-fromandconsume-fromand have `key-from simplified to just call thoseand then we probably, as backfill, want to also implement
peekandconsumewhich callmain-input-bufferand invoke the*-fromvariants on it. we won't use those variants inwordbut user code might want tothe responsibilities that
keyhave now are to check what input is present; to invoke a system call to read more if needed; to put the next byte of it on the value stack as a return value; and to advance the input buffer so that the same input won't be returned next time.so the simple way to do this would be to delegate those first three things to
peekand the last one toconsume -
the responsibilities that
keyhave now are to check what input is present; to invoke a system call to read more if needed; to put the next byte of it on the value stack as a return value; and to advance the input buffer so that the same input won't be returned next time.so the simple way to do this would be to delegate those first three things to
peekand the last one toconsumehowever we're realizing that this also bears on one of the big compatibility questions we've had in mind from the start, which is how to make sure it's not too annoying to deal with situations where the temporal behavior of input matters
-
however we're realizing that this also bears on one of the big compatibility questions we've had in mind from the start, which is how to make sure it's not too annoying to deal with situations where the temporal behavior of input matters
that's been a big frustration of ours in Rust (though it's been better since the Rust library
smolcame along, it was quite frustrating with several popular async libraries prior to that point)a stream isn't just a sequence of bytes, it's a sequence of bytes that arrive at some point in time and possibly in response to things happening in the world outside of the program
-
that's been a big frustration of ours in Rust (though it's been better since the Rust library
smolcame along, it was quite frustrating with several popular async libraries prior to that point)a stream isn't just a sequence of bytes, it's a sequence of bytes that arrive at some point in time and possibly in response to things happening in the world outside of the program
sometimes you care about the difference between "the next available byte" and "the next byte available right now"
-
sometimes you care about the difference between "the next available byte" and "the next byte available right now"
and we're just trying to make sure we don't paint ourselves into a corner with how we organize this functionality
-
and we're just trying to make sure we don't paint ourselves into a corner with how we organize this functionality
we don't need to add anything to deal with temporal behavior right this moment, but we want to make sure it's clear how we would