2023-11-19 12:07:27 zelgomer: I've also made all my own control flow words. So I also have a tendency to forget exactly how the less common conventiaonal ones work. 2023-11-19 12:08:19 ollephone: loop is compile only because the way those looping constructs work is by changing where the instruction pointer points in a compiled definition. 2023-11-19 12:08:50 When you are interpreting from the console, you don't have such a compiled definition anywhere - the interpreter is just processing a text stream one word at a time. It doesn't build a "definition" anywhere. 2023-11-19 12:09:10 I once saw a Forth system that did; it would take the whole line and compile it, and then execute that, then throw it away. 2023-11-19 12:09:26 Such a system would let you use all those compile only things even when "interpreting." 2023-11-19 12:10:29 ollephone: It is possible to jump around in an interpreted command stream, by manipulating the variable >in. Doing so is just not "conventional." 2023-11-19 12:10:58 At any given time >in holds your current offset into the text input buffer. 2023-11-19 12:11:10 So if you change its value, you'll change what gets interpreted next. 2023-11-19 12:11:36 0 >in ! will send you back to the start of the line. So if you haven't written special words to do that carefully, it will catch you in an infinite loop. 2023-11-19 12:15:33 user51: if as a ternary operation; those would be 1) the stack item, 2) the true destination, 3) the false destination? 2023-11-19 12:16:16 ACTION defined ?: for that kind of stuff 2023-11-19 12:16:26 These days I use conditional return words for my control flow. 2023-11-19 12:16:31 KipIngram: Exactly 2023-11-19 12:16:41 Either conditional return or conditional recursion. 2023-11-19 12:18:09 So for IF THEN I factor into a separate word with a conditional return at the beginning. 2023-11-19 12:18:26 : ?: ( altern conseq cond -- conseq | altern ) SKipifZero SWAP DROP ; 2023-11-19 12:19:00 That's nice. :-) 2023-11-19 12:19:48 I may start using that; I like it. 2023-11-19 12:20:27 I like the name too - it evokes conditional expression syntax we're already familiar with. 2023-11-19 12:20:41 I use that in the defintion of (BRZ) as SKZ is much easier to implement in hardware than BRZ 2023-11-19 12:26:04 but I think you can implement SKZ thusly: : SKZ CELLwideOR 1 AND 1 XOR R> + >R ; 2023-11-19 12:26:35 no branching primitive used there 2023-11-19 12:30:21 Yes, skip is easy to do. 2023-11-19 12:30:44 It's probably the "most sensible" control flow construct in Forth. 2023-11-19 12:30:52 Conditional return is similarly easy, though. 2023-11-19 12:31:21 You could do either one using the other pretty easily. 2023-11-19 12:31:26 what about jumptables? 2023-11-19 12:31:47 I guess skip would work quite well for that. 2023-11-19 12:32:00 conditional return is inherently a "two options" thing. 2023-11-19 12:32:24 Maybe that gives skip an edge. 2023-11-19 12:32:39 the thing is, SKZ can be used to implement the rest of the control flow 2023-11-19 12:32:47 Yes, I like it. 2023-11-19 12:32:47 words. 2023-11-19 12:33:40 I could have written many of my conditional returns in terms of a "basic" one, but I made them all primitives for performance. 2023-11-19 12:34:00 If portability was the goal I'd have done that very differently. 2023-11-19 12:34:58 I like how small the set of primitives one can get away with in Forth, yet have the option of expanding the set for performance 2023-11-19 12:35:41 Yeah, I see the "beauty" in a really small set of primitives. 2023-11-19 12:36:04 in this case I generated the asm source with Python, so I didn't have to write all those myself and carry risk of mistakes on each one. 2023-11-19 12:36:31 So, when you do a jump table, I see how skip gets you down into it. How do you then avoid the "remainder" of the table that follows your chosen item? 2023-11-19 12:37:29 nope, I use SKZ for (BRZ) and such and then use those to implement jumptable words 2023-11-19 12:40:22 basically : (jmptbl) ( idx -- | idx ) DUP R@ @ < IF R> 1+ + @ >R ELSE R@ @ R> 1+ >R THEN ; 2023-11-19 12:40:58 which means if idx given it will jump over the table. Kind of default case. 2023-11-19 12:41:52 s/|// 2023-11-19 12:43:05 s/1+ >R/1+ + >R/ ugh 2023-11-19 12:53:04 Ah - ok, that's a little complex than I was imagining. 2023-11-19 12:53:50 I was think of skip just hopping over the first part of the table and landing right there in the middle of it. So the word it landed ON would be the one you chose, but then there would still be those other words following that would need to be avoided somehow. 2023-11-19 12:54:32 I actually have done that sort of thing< but I factor the jump table into its own word, and each table case ends with a double-return, which returns back "over" the table word. 2023-11-19 12:54:56 Have you seen Julian Noble's FSM implementation? 2023-11-19 12:55:05 One thing I like about this particular approach to control flow is that it encourages further factoring. 2023-11-19 12:55:15 http://forth.org/literature/noble.html 2023-11-19 12:55:41 No, I haven't, but I'd like to. If you've got a link easily accessible please share; otherwise there ought to be enough there to search for. 2023-11-19 12:55:58 I like state machines. 2023-11-19 12:57:28 I found it - http://www.forth.org/literature/noble.html 2023-11-19 13:09:34 It came to mind because you were talking about jump tables 2023-11-19 13:28:56 More often than not I just write code to implement such logic, rather than having a "data structure" that I interpret. 2023-11-19 13:30:29 I've always viewed it as an applications of Chuck's "calculate, don't choose" guidance. 2023-11-19 13:32:37 I did use a jump table in a Forth printf I wrote once, but even there it was really a small virtual machine that "executed" the format string. 2023-11-19 13:39:19 KipIngram: Do you still have the printf code? 2023-11-19 13:39:42 I'd like to read it. 2023-11-19 13:43:31 I will see if I can find it. It was on "system before last," and I don't recall whether it was source in a block file or hand-compiled Forth in the asm file. 2023-11-19 13:43:36 But I'll see what I can do. 2023-11-19 13:43:51 If I can't find it I'll send you a little description of how it worked. 2023-11-19 13:44:08 That's great too, thanks. 2023-11-19 13:44:26 When you think about it, Forth code has more power than a plain vanilla state machine. You can think of the instruction pointer as the "state,' but then on top of that Forth has a return stack. 2023-11-19 13:44:50 printf is usually a state machine over on the C side (and a mess of macros you might step into by mistake) 2023-11-19 13:44:52 So it can make moves a state machine couldn't make as readily. 2023-11-19 13:45:18 That doesn't surprise me (that it's a state machine) - it felt like a very natural way of doing it. 2023-11-19 13:53:43 Ok, that doesn't seem to be on this computer. It may be on my work computer, which I expect to have open for a bit this evening. Technically I'm on vacation this coming week, but I always do try to keep the test stands busy. I kicked off two jobs earlier this morning that will take over four days, and I want to start two more of those tonight. I'll look around on that one later. 2023-11-19 13:55:36 ACTION is reading about crazy USA inflation and sees "inflation abatement fee" and lols 2023-11-19 14:04:34 Printf seems surprisingly complex 2023-11-19 14:04:57 A printf format string is a DSL 2023-11-19 14:06:08 and often a source of security bugs 2023-11-19 14:06:43 Just like every other language :P 2023-11-19 14:07:25 I'd imagine forth wouldn't have this problem, but it may also get a premature NUL character, e.g "hello \x00%s" world 2023-11-19 14:08:21 Is there any resources on a list of the simpler words that you need to implement in your "interpreter" for forth to then implement the rest with? I am trying to implement something forth-like without knowing enough about it (I think) 2023-11-19 14:11:23 That's a really good question. I'm not aware of such a list just floating around out there. I usually start at the top. The outermost loop in a Forth system often has its entry point named QUIT. It's an infinite loop over a read/execute/print loop. That usually calls INTERPRET, which is responsible for executing one line of console input. THAT uses WORD to parse out each word, has some logic, and 2023-11-19 14:11:25 eventually compiles or executes. And so on down - I just push my way down until eventually 'm implementing primitives. 2023-11-19 14:11:37 Then before QUIT gets called the first time there's some initialization that has to be done. 2023-11-19 14:12:31 One issue you face is that if you took as your goal the "minimum possible set of words" then you might wind up with some implementations in there that weren't "optimally efficient," whereas getting the cleanest best implementation overall might require more words. 2023-11-19 14:12:51 Anyway, I have found this top down reasoning process to be helpful. 2023-11-19 14:13:54 Even after you get this in place, though, as you go off to do the remaining words there may be many of those that you want to make primitives, rather than doing a Forth implementation, for performance. 2023-11-19 14:13:59 You could look at Jones Forth which builds up from asm to Forth 2023-11-19 14:20:03 My goal on this next system is for it to be able to metacompile itself. I want to write the whole thing in *Forth source*, exactly the way it would need to be for a metacompile. Then I want to write something separate, in C most likely, that can "emulate" that metacompile process for the initial system creation. 2023-11-19 14:20:33 And I want to TRY to do that in such a way as to allow me to gradually hand of functionality from the emulation to the real system, as each new bit of it comes online. 2023-11-19 14:21:03 hand "off" 2023-11-19 14:22:23 That way I'll only have to troubleshoot the piece I'm trying to hand off at any given time. 2023-11-19 14:24:47 ripsquid: ftp://ftp.taygeta.com/pub/Forth/Applications/ANS/maf1v02.zip might also be worth a look 2023-11-19 14:26:07 ripsquid: http://www.bradrodriguez.com/papers/moving1.htm 2023-11-19 14:33:13 I have a REPL loop defined in my rust project because its a whole operating system. My plan is to allow jumping with indexes and getting a word name as a string on the stack with index. So loops could search for its counterpart end or something like that. I am not sure if I am thinking correctly or if my implementation is even done as a normal forth implementation 2023-11-19 14:33:25 Not sure what the simplest loop/logic needed to implement the rest of them 2023-11-19 14:36:07 Traditionally, the dictionary is a linked list of names 2023-11-19 14:36:20 Simple string comparison is all that's needed 2023-11-19 14:42:21 I have a hashmap of pre-defined words on compile time. That is a hashmap from String to Function. Then defined words on compile time is just a hashmap of String -> List of Words. It is the easiest but probably worst way to do it. Then I have to figure out how to define words that are like if logic and loops. I am not sure if that could be done inside forth easily or if it needs to be done inside Rust 2023-11-19 14:43:59 https://forth.sourceforge.net/standard/dpans/dpansa3.htm#A.3.2.3.2 2023-11-19 14:47:13 ripsquid: My latest thinking on that is a very simple linearly searchable list (not quite a conventional linked list, but close), but one to which an external hash table can be associated. It'll totally work without the hash and offer O(N) performance. But adding the hash table later would bring it to ammortized O(1). 2023-11-19 14:48:12 I think it's general enough to allow the same code to support it for vocabularies, file system directories, and whatever else I happen to want to do later (general dictionary structures in the Python sense). 2023-11-19 15:31:10 So I'm thinking about a "twist" on local variables (which have kind of been done to death in Forth over the years). Usually attempt is made to associate the local names with each individual Forth word. That seems too fine-grained to me. Forth words are supposed to be short, and their temporary stack space is supposed to be on the data stack. 2023-11-19 15:31:32 So what I'm thinking of here is a way to have "local data frames" but have a frame be accessible to a group of words. 2023-11-19 15:32:02 There would be two phases - somewhere you have to define the layout of these words, which gives you a vocabularly you can use to map word names onto offsets in the local frame. 2023-11-19 15:32:13 And then there would be a way to "open" and "close" local frames. 2023-11-19 15:33:12 Opening a frame would allocate space on the return stack for that frame, and as long as it's the "currently open frame" references would go to that frame. But you could, if you wanted to, open another copy of the same frame, and that would... well, it would do that. Closing that second copy would re-engage the first one. 2023-11-19 15:33:25 All of a group of words would work on the currently open copy. 2023-11-19 15:33:38 I don't know if this is useful or not - just thinking about it right now.