New CAST instruction for Spin2
cgracey
Posts: 14,153
in Propeller 2
It's been bugging me for a long time that there's no way in Spin to do a deterministic one-of-n branch.
The CASE instruction serially tests cases and then jumps to a code block when a match is found, but that's slow for many cases.
When you know you want to run a particular block of code based on an index value, it would be nice to have something akin to a jump table, but in a structured format.
I made a new instruction called CAST, which is kind of like CASE, but takes an index value and uses it to look up an offset word to select the routine to run. This means only one decision has to be made, instead of a bunch of serial decisions.
What syntax looks best?
The compiler tallies up the cases and produces an internal FLE (force less-than-or-equal) value to make sure the branching is bounded.
The OTHER case, which is optional, could be expressed with OTHER or maybe ">". And the values can be implied sequentially, so they are not requisite. Might it be good to allow expected values (0,1,2...), but not require them?
The 2nd example which lists every value is easiest to read, but a pain to rearrange. The 1st example can be rearranged, but the case numbers are not immediately apparent. The 3rd and 4th examples show the optional numbering with different OTHER approaches.
Which is best, if any?
The CASE instruction serially tests cases and then jumps to a code block when a match is found, but that's slow for many cases.
When you know you want to run a particular block of code based on an index value, it would be nice to have something akin to a jump table, but in a structured format.
I made a new instruction called CAST, which is kind of like CASE, but takes an index value and uses it to look up an offset word to select the routine to run. This means only one decision has to be made, instead of a bunch of serial decisions.
What syntax looks best?
cast x : block_0 : block_1 : block_2 : block_3 : block_4 : block_5 : block_6 > block_other cast x 0: block_0 1: block_1 2: block_2 3: block_3 4: block_4 5: block_5 6: block_6 >: block_other cast x : block_0 : block_1 2 : block_2 : block_3 4 : block_4 : block_5 6 : block_6 > : block_other cast x : block_0 : block_1 2 : block_2 : block_3 4 : block_4 : block_5 6 : block_6 OTHER : block_other
The compiler tallies up the cases and produces an internal FLE (force less-than-or-equal) value to make sure the branching is bounded.
The OTHER case, which is optional, could be expressed with OTHER or maybe ">". And the values can be implied sequentially, so they are not requisite. Might it be good to allow expected values (0,1,2...), but not require them?
The 2nd example which lists every value is easiest to read, but a pain to rearrange. The 1st example can be rearranged, but the case numbers are not immediately apparent. The 3rd and 4th examples show the optional numbering with different OTHER approaches.
Which is best, if any?
Comments
Also you could skip some numbers if you don't want anything to happen for them.
The ">" selector looks a bit odd, but I guess you have to use something.
Bean
">" is supposed to mean "out-of-bounds" or "greater-than".
This is internally a jump table, so there are no dead spots, as each entry needs an address. I suppose I could have empty blocks vector right to the last terminator.
Optionally being able to leave out some numbers may be ok, but totally unnumbered may be confusing for long lists.
I remember having counted long "on var goto ..." lists far too often in that 80ers era of MSBASIC and friends...
For the compiler listing the indices in the source is redundant but it may help spotting a forgotten "case" for the human and the compiler.
Would another option be to accept optional range numbering?
If it's slow why not replace it?
Sure, one can solely rely on the odd or even-numbered indexes, but them it'll have to depend on the parser, in order to verify the consistence of the sequence; there would be some errors to check for and correct.
Henrique
I kind of like everything being numbered, as well, because it's immediately clear. It's just a pain to rearrange.
I will see about doing what VonSzarvas says. It starts to get back to CASE functionality pretty quickly.
You can also call methods with indexes: BaseMethod[index](params). This is a runtime thing, so you need to know what you're doing. Same with using method pointers, which also have indexes, by the way.
If we had a smart editor, it could dynamically show the numbering without your needing to type it. Maybe in the future.
CAST is a faster/simpler cousin to CASE. CASE evaluates every case, whereas CAST just jumps. CASE is needed sometimes, while CAST is much more efficient in cases where an integer succession exists.
Even better might be to recognize a restricted set of CASE and automatically use a jump table if the cases are simple enough (and disjoint). That's more work, but might be nicer for the users.
My first impulse was to call it FORK, but that means multi-threading in C.
JUMP might be best, actually.
@cgracey, I would prefer something like jl(cast) var label.
That way, since you have an jump-list's ending label, if others are in between these can be not considered and thus become optional, so anyone is free to write them or not.
Can't think of anything better really. Hmm.
Maybe CASEn or CASEN (ie. CASE-Numeric)
Yes, I thought about making the compiler smart enough to compile this more-efficient method when circumstances allowed, but it will take some work and maybe new parsing approaches. I was thinking your compiler could probably handle that, easily.
Unless "ON" has another meaning, I think "ON x" instead of "CAST x" would make more sense (at least to BASIC programmers).
Just my opinion....
Bean
I've just been searching and there's no ~4-letter word in English that means what we need a word to mean.
"ON" is pretty good, but it's an awfully small word, almost gets looked past:
We need a word that means "pick one and do it".
Another good choice might be "GOTO", which isn't a reserved word but is probably less likely to conflict with anything:
XPICK, YPICK; XJMP; YJMP (not to be confused with Z80's IX and IY-based indexing...)
Bean
In that case, using ">" may be confusing, as the index could also be less than minimum.
OTHER or ? may suit both cases of less or more,
As a shorthand I prefer "?". ie. something else or something unknown.
"other" is already there, and is reasonably clear to the reader, so I think we should use it.
I know that experienced Spin users like to save keystrokes, but it's a false economy -- code is read a lot more often than it is written, and having cryptic short aliases for everything makes it harder for new users to come to grips with code.
Agreed.
Maybe "Exe#" or "Execute#" ??
J
I agree about using OTHER: or even suggest ELSE: