2024-04-29 12:40:39 i have a word that pop from the stack an operand, mutate it locally (e.g. convert string to integer), and then push it again into the stack. As an optimization, i don't pop/push the operand from/to the stack, otherwise i don't move it, and i get its reference from the top of the stack, as an optimization. Now the problem is: when i catch an error, e.g. an integer can't be parsed from string, in my try/catch mechanism execution is interrupted, so the s 2024-04-29 12:40:39 inconsistent: the operand will be still there, since it has never been popped, due to the optimization 2024-04-29 12:41:18 what are your advice? to eliminate that optimization, so the stack will be coherent, or to drop the operand from the top of the stack only in the case of error? 2024-04-29 14:32:44 it, or something, should still be there after catch returns nonzero 2024-04-29 14:37:20 when catch returns nonzero, the stack depth should be the same as it was when catch was called 2024-04-29 15:40:54 zelgomer, but if a word pops an operand from the stack, then convert it and there is an error there 2024-04-29 15:41:00 that operand shouldn't be in the stack! 2024-04-29 15:41:03 or, am i wrong?! 2024-04-29 15:43:52 for instance: word `str>int` pops a string from the stack, then convert that string into an integer then push the integer into the stack. If it pops the string 'ssssddd' which cannot be converted into an integer, an error is triggered, but the string has been already popped from the stack, right? 2024-04-29 16:26:18 yes, but the stack depth must be restored 2024-04-29 16:26:40 the values in the stack positions that were manipulated in the meantime are undefined 2024-04-29 17:54:50 zelgomer, uhm, why stack depth must be restored? i mean, if there is an error after the operand has been popped off, you lose that operand, right? 2024-04-29 17:56:24 rendar: because i don't want to return from catch and not know the depth of the stack. that's an insane condition that you can't reliably recover from. 2024-04-29 17:57:52 when catch returns, the stack depth has to be knowable. if catch returns success, then you can expect for the depth to be whatever it was supposed to be according to the stack effects of the word that you called. if it failed, then who knows where it failed, so the only sane thing for catch to is to restore it to a known depth, which would be the depth it was when catch was entered. 2024-04-29 17:59:43 well, i don't have classic catch, i have special words which start with `#`, e.g.: #try 'abc' >int #err* 'error!' log,err #done 2024-04-29 18:00:15 with special word #try it enters a try block, tries to convert 'abc' to an integer, it fails, so it runs words inside the #err* block 2024-04-29 18:00:56 what if >int fails after pushing 3 values? 2024-04-29 18:01:18 it can't, every words push a definite numbers of values 2024-04-29 18:01:33 but its inner workings might 2024-04-29 18:01:46 as the user, i don't know how >int is implemented 2024-04-29 18:02:37 hmm 2024-04-29 18:13:48 zelgomer, but as the user, >int is a very well documented word, with a stack_in and stack_out integer well set.. 2024-04-29 18:14:11 in my Forth every words has a structure containing information about how the word works 2024-04-29 18:14:40 https://bpa.st/TCBQ <- check this out 2024-04-29 18:42:29 rendar: if >int generated an error, then it didn't complete. so its stack effect is not necessarily consistent with what's advertised. 2024-04-29 18:44:04 rendar: e.g., : foo ( a b c -- d) / / ; : bar #try foo #err* ( what is the depth here?) #done ; 1 1 0 bar 1 0 1 bar 2024-04-29 18:44:41 but, again, the error happened after the operands have been popped off from the stack.. let's consider a word which pops an integer N from the stack, then pops N operands from the stack to put them into a list.. in there is an error at K-th operand, some operands have been already inserted into the list, it can't remove them to restore the stack at its initial state, it would be folly 2024-04-29 18:45:04 the depth here is very well defined: 1 2024-04-29 18:45:30 because the word foo popped 2 operands to add them, if foo is `+` for instance 2024-04-29 18:45:56 then,if there is an overflow error, it can't restore the 'b' and 'c' operands, for the same reason of my list example of above ^ 2024-04-29 18:46:36 i'm not using the right words. somebody else do it. 2024-04-29 18:52:31 the right words? 2024-04-29 19:28:11 rendar: I think your situation deserves some mulling over, but my first thought on it is that the state that existed before the error probably ought to be retained. 2024-04-29 19:28:38 In my system I save the system state that exists before I start interpreting a line of input, and if anything in that line throws an error I restore it. 2024-04-29 19:29:01 It puts me in a position to "cursor up" to get that line back from the command history, edit it to fix the error, and then just hit enter to run it again. 2024-04-29 19:30:01 KipIngram, but how? let's consider this stack: x1 x2 x3 ... x1000 1000 <- there is 1 integer representing the depth of the stack, and 1000 elements, i call add-n to add all of them into a list, it start iterating, then at 424th element it raise an error 2024-04-29 19:30:05 it can't restore the stack.. 2024-04-29 19:30:09 So there I'm not trying to do something that's "logically consistent at the word level" - I'm just trying to establish the most convenient for the user global situation. 2024-04-29 19:30:19 it should move back all the elements, but that would be absurd 2024-04-29 19:30:57 Yes, it's quite expensive and no way would it be feasible in a resource constrained system (what I do, I mean). 2024-04-29 19:31:12 I snapshot the entire system. 2024-04-29 19:31:27 But, I'm on a big machine I'm not using nearly all of the resources on anyway. 2024-04-29 19:31:28 i can't snapshot it.. 2024-04-29 19:31:59 Yeah - I get it. In your situation I think I would leave the string on the stack, but i do see arguments in both directions. 2024-04-29 19:32:19 The thing is, if you've had an error then you can't really expect to "continue," so you're facing some kind of a recovery situation. 2024-04-29 19:32:37 yes 2024-04-29 19:33:38 my philosophy on this has been: in normal repl interpreter, stack is erased on error, and the raised error is thrown 2024-04-29 19:33:43 So I think "whatever makes recovery easiest for the user" is the right answer. I paid a ridiculously high price to push that all the way to what I saw as a limit, but like I said, that wouldn't always be feasible. 2024-04-29 19:34:09 in defined word, inside a try/err block, the stack is untouched, and you have the chance to recover something inside the err block 2024-04-29 19:34:09 Yes, I think that's common. The interpreter just "restarts," and tries to tell you what happened. 2024-04-29 19:34:17 indeed 2024-04-29 19:34:48 but you can't expect to have in the stack ALL the data before the error is thrown, because possibly an enormous amount of data has been moved 2024-04-29 19:35:02 The rub in my system is that I do not snapshot the disk blocks, so if the line modified one of those before failing, I have a less pleasant situation on my hands. 2024-04-29 19:35:43 yeah 2024-04-29 19:53:54 rendar> it should move back all the elements, but that would be absurd 2024-04-29 19:54:14 no, it only has to restore the stack pointer. as i said before, the /values/ in those stack positions are undefined 2024-04-29 19:54:52 zelgomer, in my Forth i don't have a stack pointer, my Forth is a higher level Forth, my stack is create by a `Vec` 2024-04-29 19:54:56 which is a vector of Atom operands 2024-04-29 19:55:49 :0 2024-04-29 19:55:54 rendar: in your example, consider the error path. if you can't know whether the error occurred on item 1 or item 424. so if the depth of the stack is unpredictable in your #err*..#done block, how can you possibly recover? 2024-04-29 19:55:55 a high level forth 2024-04-29 19:56:04 zelgomer, so, my stack will always contain *defined* Atom operands, it won't contain any undefined space 2024-04-29 19:56:16 rendar written in rust? 2024-04-29 19:56:18 that's an implementation problem 2024-04-29 19:56:21 vms14, yep 2024-04-29 19:56:29 zelgomer, wait 2024-04-29 19:56:36 if you want to say they're defined, then fine 2024-04-29 19:56:56 :0 2024-04-29 19:56:56 (they really aren't, though. you can initialize them if you want, but from the user's standpoint they're undefined) 2024-04-29 19:56:59 source code? 2024-04-29 19:57:12 zelgomer, in the #err* block, you have the stack with some operands still there, you can do `depth add-to-another-list` to save that data 2024-04-29 19:57:25 vms14, will be released in the next months 2024-04-29 19:57:26 are you faking memory rendar? 2024-04-29 19:57:34 faking memory? 2024-04-29 19:57:39 allot and alike 2024-04-29 19:57:50 how are them implemented without malloc? 2024-04-29 19:58:12 vms14, huh? Vec calls malloc under the hoods 2024-04-29 19:58:12 rendar: then you pretty much force all #err* paths to use "depth" and manage a variable depth stack 2024-04-29 19:58:31 and i guarantee you most of the time what they're going to do is restore it to a predictable depth. so all you've done is move the logic out of catch onto the user. 2024-04-29 19:58:46 zelgomer, not in all circumstances, only when the error happens on operations that move N operands, with that N took from the stack itself 2024-04-29 19:58:50 not all operations do that 2024-04-29 19:59:34 rendar: do you understand that when i write : foo ( a -- b) ... ; although foo advertises that it pops one item and pushes one item, it may push and pop any number of items internally before it returns? 2024-04-29 20:00:59 zelgomer, the thing is, we don't have many options, if an error happens in the 343-th element, we can: 1) completely clear the stack, so the user will receive a completely empty stack, and they cannot do anything 2) stop any operations and give the user the current stack (what i do) 3) restore ALL the moved elements to the stack with absurdity in computations, the user will receive the stack as was before, with a very low computational speed 2024-04-29 20:01:33 you don't have to restore all of the moved elements. nobody said that but you. 2024-04-29 20:01:44 ok 2024-04-29 20:01:50 you don't have to restore values, only the /depth/. 2024-04-29 20:02:05 this is why "the values in those positions are undefined" 2024-04-29 20:02:45 zelgomer, i see, you mean restoring the depth with operands or something? 2024-04-29 20:03:00 with whatever you want 2024-04-29 20:03:10 hmm, i see 2024-04-29 20:03:38 i will explore that, if it would be feasible 2024-04-29 20:04:09 from the caller's perspective, it's all undefined because the caller has no idea which stack positions are the original values and which have been manipulated by internal logic and which are filler that you populated as part of the throw 2024-04-29 20:04:39 zelgomer, let me explain one more detail, my actual logic is this: 2024-04-29 20:06:21 1) in the case of error, operations will stop, so in the err block, the stack is left untouched 2) if the initial stack is `x1 x2 x3 x4 x5 5` and i do a word add-n which pops an integer N from the stack (5) and pops N elements to put into a queue, it starts with x5, x4, x3, then the queue is full! so an error is thrown and the user will get this stack: `x1 x2` 2024-04-29 20:07:15 so inside the error block, the user can do `depth` which will do this stack: `x1 x2 2` and add-to-another-queue-n 2024-04-29 20:07:20 x3 will be lost 2024-04-29 20:09:23 i wouldn't do this. i would restore the stack to its original depth, so when catch returns it sees `? ? ? ? ? ? error` where the question marks are undefined values and "error" is some error code or object. if you want to give the user a chance to handle items that weren't handled, then pass them through the error object or some other means. 2024-04-29 20:10:14 or don't use an error to signal this condition 2024-04-29 20:11:03 use an error when enqueuing a single object, and when you want to enqueue many, then the interface should be ( i*x num-to-enqueue -- j*x num-not-enqueued) or something like that 2024-04-29 20:11:45 errors should be reserved for unrecoverable situations. you're trying to use throw-catch for logic. 2024-04-29 20:13:18 "unrecoverable" might not be the right word. maybe "unavoidable unexpected situations" 2024-04-29 20:13:25 "uncontinuable" 2024-04-29 20:21:31 I'm somewhat uncomfortable thinking about stacks with hundreds or thousands of values on them 2024-04-29 20:23:12 Me too. 2024-04-29 20:23:29 "Nobody should ever need more than 640K of memory." 2024-04-29 20:23:34 It's a scratchpad. 2024-04-29 20:23:38 if writing a system where things like this are possible, I'd assume it's a larger target, and probably just make a snapshot of the state to restore if necessary 2024-04-29 21:38:44 hi, just curious if anybody had work with Time Processor Units like in this: 2024-04-29 21:38:47 https://www.nxp.com/docs/en/user-guide/MC68336376UM11.pdf 2024-04-29 21:39:13 I had never heard of TPU before, apparently meant as an alternative to interrupts 2024-04-29 21:39:58 there is great significance to the passage of time 2024-04-29 21:40:18 I've got an old board with an MC68376 which has a TPU but it is not used on the board 2024-04-29 23:03:54 zelgomer, ok i see your point 2024-04-29 23:04:16 zelgomer, the fact is that my try/err system doesn't work in the way you described, it doesn't push an error to the top of the stack 2024-04-29 23:13:56 crc, well i also see your point, but my Forth version is specific to manipulate data, so a stack with a lot of entries could be possible.. 2024-04-29 23:16:53 if your response to everything is going to be "my forth version is unique in all ways and cannot be considered in the same way as other forths" then i'm not sure how we can help 2024-04-29 23:19:07 zelgomer, you're right, but the thing stands, i don't have any error on the stack 2024-04-29 23:19:25 i ask here to learn the best practices that Forth programmers have 2024-04-29 23:19:56 actually, i don't see how restoring ? ? ? ? ? into the stack could help to "better" solve the error 2024-04-29 23:20:07 i will only have the stack clobbered with nil items 2024-04-29 23:21:06 rendar: because i suspect you're skimming my responses and not actually considering them fully. 2024-04-29 23:21:37 no no, i'm here to study and learn, so i'm trying hard to grasp concepts, consider that i'm pretty new to Forth 2024-04-29 23:21:37 zelgomer> rendar: e.g., : foo ( a b c -- d) / / ; : bar #try foo #err* ( what is the depth here?) #done ; 1 1 0 bar 1 0 1 bar 2024-04-29 23:21:44 you'll have a decennal experience compared to me 2024-04-29 23:21:57 consider those two cases and how they're different 2024-04-29 23:22:04 let's see 2024-04-29 23:23:32 ok, now i see it, you mean that foo promises to eat 3 from stack and produce 1, so inside the #try block i have to *ensure* that stack balance 2024-04-29 23:24:41 you have to ensure some stack balance, but if you're a traditional forth you don't actually know that foo produces 1 because the stack effect is only a comment. the best you can do is ensure that the stack depth will be restored to what it was before #try 2024-04-29 23:25:53 actually in my Forth foo will eat a,b,c from the stack, then it will use them to do computation, if an error occurs, the depth of the stack in your question is simply this: `depth 3 -` if you get what i mean 2024-04-29 23:26:29 your point is to ensure that in the stack there are 3 elements, my point is to ensure that foo ate exactly its 3 elements, so the depth is very well defined: `depth 3 -` 2024-04-29 23:26:31 are you sure about that? 2024-04-29 23:27:21 pretty sure..but i repeat that i'm new to Forth 2024-04-29 23:27:33 in the first case, the first / throws a divide-by-0 error. the second / has not executed yet. how many items are on the stack there? 2024-04-29 23:28:20 in the second case, the second / throws a divide-by-0 error. the first / has already consumed two and produced 1. how many items are on the stack there? 2024-04-29 23:28:41 hmm, ok, now its clear 2024-04-29 23:30:38 my answer: if foo is a builtin word, it will pop all the 3 elements, then do any computations that can lead to error, if foo is defined, and we have: `: foo / / ;` there is a stack inbalance, right.. 2024-04-29 23:34:30 yes, that will produce stack imbalance, but ...how to fix it?! 2024-04-29 23:34:58 i guess nobody knows 2024-04-29 23:35:10 generally speaking when / encounter an error, should push back operands into the stack 2024-04-29 23:35:43 zelgomer, you really opened my mind with that example, thank you, really 2024-04-29 23:36:00 i have an idea 2024-04-29 23:36:20 it's a little bit out there, so bare with me 2024-04-29 23:36:35 ok 2024-04-29 23:37:23 what if #try recorded the depth of the stack somewhere, and then something throws an error, it either drops values or pushes filler values to ensure that the stack is restored to the depth that #try previously saved? 2024-04-29 23:37:52 yeah, i was thinking to something similar 2024-04-29 23:39:33 well, if it stores the actual stack depth, it could only push filler values if stack depth is < or drop them is its > than the original value? 2024-04-29 23:40:02 or, it could give the user's a variable EXDEPTH so the user can do it himself 2024-04-29 23:42:53 zelgomer, i will think about this, for now, many thanks! i owe you a lot, thanks for having insisted to explain your points, they are fundamental for this kind of stuff 2024-04-29 23:43:24 ladies and gentlemen, i think we got there :) 2024-04-29 23:47:28 rendar: I'll be curious to see what words you come up with for managing large amounts of data on the stack. 2024-04-29 23:50:46 BLOAT EMBIGGEN MOAR 2024-04-29 23:58:29 making a list in forth should be easy 2024-04-29 23:58:33 just use the comma