<KipIngram>
The run macro actually assembles the offset of the indicated label from the beginning of the definition region. That's why the literal values have +do on them - the address of do gets subtracted from everything when it's assembled. So I have to "pre-add" do to litera values and jump offsets.
<KipIngram>
But that's better than having to subtract somethinng manually from every label, which is the way my last one worked. These macros are much nicer.
lonjil has quit [Ping timeout: 276 seconds]
lonjil has joined #forth
LispSporks has joined #forth
boru` has joined #forth
boru has quit [Disconnected by services]
boru` is now known as boru
proteus-guy has quit [Ping timeout: 240 seconds]
lonjil has quit [Ping timeout: 276 seconds]
proteus-guy has joined #forth
lonjil has joined #forth
<KipIngram>
I guess I should do CREATE now.
<KipIngram>
That should actually be pretty easy. bl word, fill in link, cfa, pfa, bump dp forward.
<KipIngram>
Oh, and update vocabulary pointer.
proteus-guy has quit [Ping timeout: 265 seconds]
sts-q has quit [Ping timeout: 246 seconds]
sts-q has joined #forth
LispSporks has quit [Quit: My MacBook has gone to sleep. ZZZzzz…]
proteus-guy has joined #forth
Zarutian_HTC has quit [Ping timeout: 252 seconds]
LispSporks has joined #forth
gravicappa has joined #forth
LispSporks has quit [Quit: My MacBook has gone to sleep. ZZZzzz…]
<KipIngram>
Got a draft of create now. There are quite a few moving parts in that one, but it "chunked out" into reasonable factors fairly well.
<veltas>
So the output of B on a PDP-11 is pretty close to a Forth program
<siraben>
remexre: pausing the Forth formalization until after my real analysis and differential geometry finals, but chapters 8 and 10 of https://github.com/achlipala/frap look pretty relevant
<siraben>
so I need to give a simulation argument that two programs are equivalent iff they have the same "trace"
<siraben>
or that they pass through the same states
<siraben>
so this properly accounts for nonterminating programs, otherwise all nonterminating programs would be equivalent!
<cmtptr>
ah that wouldn't work would it. it would underflow the stack
<dave0>
i thought execute had to be a primitive but then i was able to write it in forth !
<KipIngram>
I think that's getting down to the details of specific implementations. My own execute is a primitive. I suppose you could slap an xt into a blank spot of a definition and run it in any Forth, most likely.
<KipIngram>
I would say, though, that that definition isn't really "execute."
<KipIngram>
It's just a word that runs a word, and you can change what that word is.
<KipIngram>
It functions as execute, but...
<KipIngram>
It's not that all by itself.
Zarutian_HTC has quit [Ping timeout: 260 seconds]
<cmtptr>
it wouldn't work for words that read from the execution stream
<cmtptr>
i would guess execute does?
<KipIngram>
Also, consider the null word.
<KipIngram>
If execute is a primitive, and if execute is called from the top level of the interpret loop, then this works for null:
<KipIngram>
: "" >r drop ;
<KipIngram>
But if your execute is itself a definition, that won't work.
<KipIngram>
You have to go deeper into the return stack, because execute has put something on it.
<KipIngram>
You'd want to NIP the return stack instead of drop it.
<KipIngram>
You have to leave the return from execute alone.
<cmtptr>
did you mean rdrop
<KipIngram>
Sure - if you have that it would be the same thing.
<KipIngram>
: rdrop >r drop ;
<KipIngram>
I'm sorry.
<KipIngram>
: rdrop r> drop ;
<cmtptr>
yeah i was confused about why you were pushing something to the rstack
<KipIngram>
Sorry. No caffeine yet.
<KipIngram>
The point is that the null word's implemenntation depends on how your execute works and also on the "structure" of your interpreter loop.
<KipIngram>
You've got an infinite loop going, popping words out of the input stream, and somehow null has to exit you from that loop.
<KipIngram>
By returning up out of it.
<KipIngram>
Of course you don't have to do it that way.
<KipIngram>
You could explicitly code a null check in the interpret loop - looking for null as DATA.
<KipIngram>
But FIG Forth did it as I descrite, where NULL is an immediate word that diddles the return stack to break the loop.
<cmtptr>
what is this purpose of this
<KipIngram>
In my system execute is a primitive, and it is on the top level of the interpret loop, so my definition is thus:
<KipIngram>
: "" ;; ;
<KipIngram>
Just a double return.
<KipIngram>
The purpose is to return you to another QUERY after you've exhausted all the words in an input line.
<KipIngram>
: INTERPRET FIND <execute or compile> INTERPRET ;
<KipIngram>
So there's another infinite loop, and null sends you from that one back to the QUIT loop.
<KipIngram>
So you can QUERY again.
<cmtptr>
wtf is query??
<KipIngram>
Oh, sorry. It's a FIG word; it EXPECTS a line of input from the keyboard into the terminal input buffer.
<KipIngram>
It's how traditional Forth gets a line from the keyboard.
<cmtptr>
oh, you read a line at a time
<KipIngram>
Right.
<cmtptr>
so for each line (for each word (...))
<KipIngram>
Right, exactly.
<KipIngram>
And if that (for each word) is just an infinite, nonterminating loop, then the only way out of it is to do a "return" to the next level up.
<KipIngram>
That's how mine is written.
<cmtptr>
cool
<KipIngram>
The (for each word) loop doesn't actually CHECK for end of line - end of line is actually an executable word.
<KipIngram>
But exactly how it works depends on these details we're discussing.
<cmtptr>
yeah i guess you have to have a line loop or else you'd prompt with OKAY between every word on a multi-word line from the user
<KipIngram>
That's right.
<cmtptr>
admittedly i've never written a forth repl, all of my incarnations have just read from a file
<KipIngram>
I have mine written so that if STATE = 0, the prompt is " ok" and if STATE = 1 (compiling) the prompt changes to " ..."
<cmtptr>
somebody should make a forth website called the ok corral
<KipIngram>
hahahahah
<KipIngram>
For sure.
Zarutian_HTC has joined #forth
<KipIngram>
This is basically how I do most all of my looping - more often than not my loops are infinite, and I "return out of them."
<KipIngram>
If I want to do that return from a lower factored level, then the return has to be further.
<KipIngram>
So I have a whole family of conditional returns, and also a whole family of conditional double-returns, just for that purpose.
<KipIngram>
I try to be disciplined about it to avoid spaghetti code. If push that return down a level by factoring, I try to name the word that does it in a way that implies it's a "return word." Usually incorporate ; into its name.
<KipIngram>
So a one level one would look like this:
<KipIngram>
: foo ... 0=; ... foo ;
<KipIngram>
Tail optimization turns that into a loop.
<KipIngram>
But if I pushed the 0=; down, it might be this:
<KipIngram>
: foo ... ?bar; ... foo ;
<KipIngram>
: bar ... 0=;; ... ;
<KipIngram>
You see the two-level return in bar there.
<KipIngram>
And I fouled up the bar name in its definition - sorry.
<KipIngram>
Should have been
<KipIngram>
: ?bar; ... 0=;; ... ;
<KipIngram>
Anyway, I haven't taken it any deeper than that - I have no three-level return words.
<KipIngram>
Hasn't been required for the amount of factoring I've wanted to do.
<KipIngram>
Anyway, my code has definitely gotten shorter and tighter since I started using these words.
tech_exorcist has joined #forth
<KipIngram>
siraben: How hard does it look like it would be to give the TI-84 Forth a right and proper interpreter loop? From what I could tell it looks like it just executes word by word.
<KipIngram>
So, I drafted CREATE last night, and supposedly the compie actions of the interpret loop are working. That gets me pretty close to doing : definitions. Another big missing piece is numeric output. I've been hobbling by EMITting single characters so far.
<KipIngram>
And then it seems like disk I/O would be in order, and at that point I think it's ready to start doing native development.
<siraben>
KipIngram: it is a proper interpreter loop
<siraben>
see the screesnhots
<siraben>
screenshots
<KipIngram>
Ok - I just saw some statement in there about getting ok after each executed word.
<siraben>
KipIngram: ah, that was the old behavior, and even then it was an interpreter loop
<KipIngram>
Anyone have any thoughts to share on handing strings?
<KipIngram>
I have it in mind I'll have a word, say str, that you'd use like this:
<KipIngram>
str <delimiter><string><delimiter>
<KipIngram>
So, first non-blank char encountered is adopted as the delimiter.
<veltas>
COUNT can be used to pop a character from the front of a string, that is my hot tip for strings stolen from mark4
<KipIngram>
But any thoughts about what to do with the string?
<KipIngram>
Yes - I do that quite a bit.
<KipIngram>
Maybe I pass a buffer address.
<KipIngram>
Similar to EXPECT, except it will come from the input stream instead of the keyboard.
<KipIngram>
<addr> <max> str <string>
<KipIngram>
That seems consistent with other things.
<KipIngram>
Then if I felt like implementing a string stack or something, I could. But at this level the word is more generic.
<KipIngram>
I guess I'll go with that unless someone has something better.
<KipIngram>
The way I did output formatting last time was kind of c-like. That string I input with the word just discussed could contain format fields.
<KipIngram>
Then I could do
<KipIngram>
<val> <val> <val> <addr> <max> str "string with three format fields"
<KipIngram>
and it would output the <val> items, formatted.
<KipIngram>
That worked pretty well last time, and was fun to implement.
<KipIngram>
I created a little "baby stack machine" and actually "interpreted" the format field chars with a little virtual machine.
<KipIngram>
Treated the format fields as little byte code sequences.
<KipIngram>
The "stack" lived in a regular stack element - each byte was an item of that stack.
<KipIngram>
So I could use it to parse numeric values from the format fields and so on.
<KipIngram>
So each % that I came to would instantiate an empty stack for the following chars to work on; eventually the last char of the format field commanded the output itself, using the parameters that had accumulated on the format stack.
<KipIngram>
The format field could specify the base to use for rendering the value.
<KipIngram>
As well as the usual width controlling stuff.
<KipIngram>
I guess I asked the question because I was contemplating a string stack, but I think I'll postpone that decision until later. Just specifying a buffer the same way EXPECT does seems like the right way to go for now.
<KipIngram>
Actually another option is just to say <delimiter> WORD - that would use pre-existing code to capture the string, and it would put it in the same place words usually go, just beyond the end of the dictionary. Then I could use it there or move it, as I saw fit.
<KipIngram>
My WORD takes a delimiter byte as a parameter.
<KipIngram>
I could modify it to take a buffer address as well, if I wanted to.
<KipIngram>
Nice thing about putting there in unallocated space is I don't have to worry about the length. Tons of unused RAM there.
<KipIngram>
I definitely want to use WORD. No point in duplicating that work.
Zarutian_HTC has quit [Ping timeout: 240 seconds]
<mark4>
i did not invent count being used to fetch each byte of a string in sequence and purists dont like it :)
<KipIngram>
Oh, I didn't realize it was suggested that you invented it. That's been around a long time.
<KipIngram>
I actually name it .c@++ in my system, for naming consistency with other fetch words.
dave0 has quit [Ping timeout: 245 seconds]
<KipIngram>
The . as usual means "keep a parameter," which in this case is the address.
<KipIngram>
I have a family of those, .w@++ .h@++ .@++
<KipIngram>
I think that alternate naming should address the concerns of purists.
<KipIngram>
It makes it so the word is doing exactly what the name says it is.
<KipIngram>
.c@++ is 1) c@ is "fetch a char," 2) . is "keep the addr," 3) ++ is "suitably increment the addr."
<KipIngram>
Purists don't like using COUNT for that because it's exploiting a non-required side effect of the string implementation, right?
<KipIngram>
But .c@++ has no such issue.
<KipIngram>
I also have pre-increment and pre- and post- decrement versions.
<KipIngram>
You start to see what I mean when I say my implementation is "primitive heavy." Lots and lots of primitives. :-)
andrei-n has joined #forth
<siraben>
KipIngram: you can also plug in headphones/speakers in the I/O port and play sounds with my Forth
<KipIngram>
Ok, so I'm going to go with just using <delimiter> WORD to pull the string and place it there just after the dictionary. Then I'll process it char by char, handling format fields as I find them, and will make an output string a little FURTHER beyond the dictionary. Then do whatever I want with that.
<KipIngram>
The only down side I see to that is that I wouldn't be able to have multiple threads processing strings at the same time, but I'll resolve that later if it becomes an issue.
<KipIngram>
Then when the dust settles I'll be able to do . something like this:
<KipIngram>
: . str "%d " type ;
<KipIngram>
Or maybe some other word - I might prefer type be "dumb."
<KipIngram>
: . str "%d " process type ;
<KipIngram>
"process" subject to renaming.
proteus-guy has quit [Ping timeout: 240 seconds]
Zarutian_HTC has joined #forth
proteus-guy has joined #forth
<KipIngram>
So I think the way this will go is I'll be copying characters from the input string to the output string one at a time, looking for a format char. When I find one I'll advance the pointer forward based on the field length and then build the field up in place. The information to do that properly should be available. Then when I come to the end of the format field I'll pick up from just after that new stuff
<KipIngram>
and resume copying.
<KipIngram>
I think I will "imaginen" a transient string stack there beyond the end of the dictionary. I have a word wbuf now that returns here+2 (the +2 is where the link will go, so that's the start of the name string region). So maybe I'll think of that as wbuf[0], and wbuf[1] will be 256 bytes further along, wbuf[2] 512, etc.
<KipIngram>
Then I'll have a well-defined home for the output string in this string-to-string processing.
<KipIngram>
One "feature" of my WORD is that it always ensures there's a null byte after the word string. It's a counted string, but it also has that byte just AFTER the string data. So I can choose whether to use the count to process it, or just start at str[1] and regard it as a null-terminated string.
<KipIngram>
I did that just by storing each char in it using w! instead of c!, so it's not an endian portable implementation.
<KipIngram>
What endian is ARM?
<MrMobius>
on some of them you can change endianess
<KipIngram>
How flexible of them. :-)
<KipIngram>
Promoting diversity like that. ;-)
<KipIngram>
Anyway, I regarded that w! thing as a "trick" and made sure I commented on it in my source code.
<KipIngram>
The only thing I use those parsed words for so far is dictionary search, which treates them as counted. But the option of the null is there if I want it for anything.