Agreed for the second option where the numeric is specified.
I like being able to start at non-zero even tho you’re really saying for example CAST n-x
What is the pasm result? A table of addresses and you add ‘n’ to the base address? It’s sort of like using an XBYTE instruction with the skip bits all zero.
All functions have to be of the same input/output, if you don't need that just use void void.
C is strong type cast for your own safety, so to create a jump table takes some weird looking point type casting.
Example 1
pf[] is a static array of pointers to functions that take an INT as an argument and return void.
void fna(INT); // Example prototype of a function to be called
// Declaration using typedef
typedef void (* const PFV_I)(INT);
static PFV_I pf[] = {fna, fnb, fnc, ..., fnz);
// Direct declaration
static void (* const pf[])(INT) = {fna, fnb, fnc, ..., fnz};
// Example use
INT a = 6;
pf[jump_index](a); // Calling method 1
(*pf[jump_index])(a); // Calling method 2
A potentially cool name might be "gate." It's four letters, which many are leaning towards. Although it's more often a noun than a verb, "case" is also a noun. It could make the choices easy to talk about: For example, gate 3 moves the sprite to the right. Obviously, it's a play on airport terminal gates, which makes it memorable.
I strongly suggest the instruction be named either SELECT (BASIC) or SWITCH (C), CAST should be used for something like variable type casting, as that's the reserved usage in SQL.
I like PICK, SELECT, or CASETABLE, CAST has the wrong meaning. I also think it should be OTHER and absolutely not ? or >.
I think it should require a number for each entry, but you can leave some out. The compiler can just fill in the unused table entries with the "other" entry.
If there is no OTHER entry specified, then it can be implied as the next statement after the table.
I agree with ersmith that making everything short cryptic symbols makes the code harder to read. Please don't go down that route.
I also like the idea of the compiler just being able to do this with the regular CASE instruction. That's how C/C++ works when the compiler is decent, it produces jump table for switch/case constructs. I've even seen simple cases reduces to branchless code (but that gets pretty complex at the compiler level).
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.
Surely that is easily the best solution, which is how other languages already do this ?
The computer does the work, and you do not add more words, or more syntatic chaff, thus keeping the programmer's life simpler.
Some compilers will give a choice of size or speed, and there is also a case form that a compiler arranges like
case x
0: block_0
1: block_1
2: block_2
3: block_3
4: block_4
5: block_5
6: block_6
else: block_other
Where each line compiles not to a compare then jump, but to a chain of DNJZ, or DJNF
This only works if the numeric list is simply sequential, and starts at 1/0 - common to see this in Assembler code.
I kind of like everything being numbered, as well, because it's immediately clear. It's just a pain to rearrange..
I'm not following the 'It's just a pain to rearrange' ?
Code is always best to be able to add and remove lines, in an editor at will, and should tolerate lines added/commented without change of meaning.
As an assist to compilers, FreeBasic takes this approach http://bourabai.kz/einf/freebasic/KeyPgSelectcase.html
Here they use 'If As Const is used, only Integer constants can be evaluated and the expression list supports single constants and enumerations only. "To" ranges are supported, but "Is" relational operators are not.
With As Const, a jump table is created to contain the full range of integer Cases handled. This allows Select Case As Const to be faster than Select Case.
However, the size of the range of values is limited, and the largest value in the range may be no higher than the smallest value + 4096."
and Python has this approach possible
if x == 1: print('first')
elif x == 2: print('second')
elif x == 3: print('third')
else: print('did not place')
The "As Const" approach should work quite well: we could just add "CON" or "CONSTANT" after the "CASE" keyword to let the compiler know that it can use a jump table:
CASE CONSTANT x
0: block_0
1: block_1
other: block_other
This is nice and simple for the programmer: it's just like "CASE", but the "CONSTANT" is a hint that the compiler can use to verify that all the case labels are constants. Fancier compilers can just ignore the "CONSTANT" and optimize to a jump table as they see fit.
Would be nice for ELSE: instead of, or an alternative to, OTHER:
This is just a simplification of the language. I never understood why new languages had to invent new terms for existing operations eg SWITCH, SELECT, VOID, and so on.
Would be nice for ELSE: instead of, or an alternative to, OTHER:
This is just a simplification of the language. I never understood why new languages had to invent new terms for existing operations eg SWITCH, SELECT, VOID, and so on.
I like ELSE better than OTHER, too. I'll get rid of OTHER and keep ELSE. We want a short name because it must be at the same indention level as the numbers.
I like GOTO x followed by all the choices. It reminds me of the ancient BASIC days
Finish the list with ELSE.
Simple & Easy.
I like GOTO maybe best, too.
..but that adds a new keyword, and GOTO X is not actually 'going to X' at all, which is why BASIC uses ON X GOSUB/GOTO
Seems best to keep existing keywords, and use the CONST modifier, if a compiler needs help to see a table can be used.
Smarter compilers do not even need CONST, making things even simpler for the programmer.
ELSE is widely understood, so is better than OTHER
I like GOTO x followed by all the choices. It reminds me of the ancient BASIC days
Finish the list with ELSE.
Simple & Easy.
I like GOTO maybe best, too.
..but that adds a new keyword, and GOTO X is not actually 'going to X' at all, which is why BASIC uses ON X GOSUB/GOTO
Seems best to keep existing keywords, and use the CONST modifier, if a compiler needs help to see a table can be used.
Smarter compilers do not even need CONST, making things even simpler for the programmer.
ELSE is widely understood, so is better than OTHER
I like GOTO x followed by all the choices. It reminds me of the ancient BASIC days
Finish the list with ELSE.
Simple & Easy.
I like GOTO maybe best, too.
..but that adds a new keyword, and GOTO X is not actually 'going to X' at all, which is why BASIC uses ON X GOSUB/GOTO
Seems best to keep existing keywords, and use the CONST modifier, if a compiler needs help to see a table can be used.
Smarter compilers do not even need CONST, making things even simpler for the programmer.
ELSE is widely understood, so is better than OTHER
Well, maybe CASEX, then. Some single word.
Why a single word?
Thinking about this some more I've come to the conclusion that the very best solution is to change the compiler to recognize when it can optimize CASE into a jump table. That way users don't have to learn two separate keywords (CASE and CASEX) and figure out when to use which one. It's better to do the hard work once (in the compiler) than to make every user do the optimization themselves every time they write a CASE statement.
Second best, IMHO, is to add a modifier to the CASE statement (something like CONST) as a promise to the compiler that yes, it can optimize this case into a jump table, and throw an error if it can't.
I think adding a new keyword is the worst solution, since it complicates the language without really adding any new functionality.
If changing the compiler seems like a big task, could it not be postponed? After all this is just an optimization, it doesn't actually change what the language can do. In that case, why not just get something working (for now) and then go back and make it better later? People managed to get quite a lot of work done in Spin1 without this feature, after all .
That's tolerable, but still less than ideal to me, as it adds another keyword, and other language users will says "why does this Spin language have two case words ?"
Best is to have compilers work like Verilog does, where it infers better structures, and reports what it has done. If that is too difficult, use the (optional) CONST as ersmith outlined
ie ideally, programmers simply use CASE, and the compiler confirms it did see a table Case structure.
A very smart compiler would see very short case lists are better done with a few DJNZ/DJNF than creating and loading a table, but larger case lists benefit from a table.
I noticed FreeBASIC above puts a limit on the size of the table.
fge x,#7 ' max value
shl x,#2 ' convert to long offset
add j,x
nop
nop
j jmp table-0 ' indirect jump
table long label0
long label1
long label2
long label2
long label2
long label2
long label6
long label7
CASEJMP, CASETBL, CASETABLE, JMPTABLE or something similar might work?
Thinking about this, CASE optimization during compilation could be a whole sub-science of its own. We currently have a most-flexible implementation and a fastest implementation. Everything else would be in-between.
fge x,#7 ' max value
shl x,#2 ' convert to long offset
add j,x
nop
nop
j jmp table-0 ' indirect jump
table long label0
long label1
long label2
long label2
long label2
long label2
long label6
long label7
CASEJMP, CASETBL, CASETABLE, JMPTABLE or something similar might work?
That is what the code looks like, all right. the implication of using words is that the entire CASE must fit within 64KB, which should never be a problem.
Please don't reuse else. else is part of the if/then/else stuff.
I also don't understand this desire to shorten everything. There is no need for it.
Use DEFAULT for the other case, that will match what C/C++ does with switch/case stuff, and not be reusing something that already exists in the language (as far as I know).
also, why not CASE JUMP or CASE CONSTANT? it's a special case of CASE, why not have it be a keyword following it instead of mashing it up like an ASM instruction? This is not ASM.
also, why not CASE JUMP or CASE CONSTANT? it's a special case of CASE, why not have it be a keyword following it instead of mashing it up like an ASM instruction? This is not ASM.
I'm fine with either of those, but a better compiler would use a TABLE depending where it was told to choose speed or size, and not need CONSTANT
In the code above, less than somewhere about 6 case choices, would be smaller and faster via DJNF opcode, assuming the reach is ok, whilst above that, table, is larger but faster, for later choices,
also, why not CASE JUMP or CASE CONSTANT? it's a special case of CASE, why not have it be a keyword following it instead of mashing it up like an ASM instruction? This is not ASM.
I'm fine with either of those, but a better compiler would use a TABLE depending where it was told to choose speed or size, and not need CONSTANT
In the code above, less than somewhere about 6 case choices, would be smaller and faster via DJNF opcode, assuming the reach is ok, whilst above that, table, is larger but faster, for later choices,
To inform the compiler the optional keyword following CASE could be a member of a set like:
SMALL - uses the smallest memory footprint, regardless of performance
FAST - uses fastest method regardless of memory size
TABLE - directs the use of a table, even if other methods might be smaller or faster.
The absence of the optional keyword could then employ a heuristic to determine which structure to use in the situation. Some fairly simple analysis should identify which method is best for the specific code being compiled.
This gives beginners the opportunity to ignore the compiler directive keywords until they run across a case (pun unintended) where they need them.
also, why not CASE JUMP or CASE CONSTANT? it's a special case of CASE, why not have it be a keyword following it instead of mashing it up like an ASM instruction? This is not ASM.
I'm fine with either of those, but a better compiler would use a TABLE depending where it was told to choose speed or size, and not need CONSTANT
In the code above, less than somewhere about 6 case choices, would be smaller and faster via DJNF opcode, assuming the reach is ok, whilst above that, table, is larger but faster, for later choices,
To inform the compiler the optional keyword following CASE could be a member of a set like:
SMALL - uses the smallest memory footprint, regardless of performance
FAST - uses fastest method regardless of memory size
TABLE - directs the use of a table, even if other methods might be smaller or faster.
The absence of the optional keyword could then employ a heuristic to determine which structure to use in the situation. Some fairly simple analysis should identify which method is best for the specific code being compiled.
This gives beginners the opportunity to ignore the compiler directive keywords until they run across a case (pun unintended) where they need them.
Yes, that's quite a good idea.
I can think of one case where you might want a TABLE, even tho it may be slower in some paths, and that would be for a known & fixed delay path choice.
">" is supposed to mean "out-of-bounds" or "greater-than".
I like other better. But cast? Not so much. How about index?
But why do we need a separate keyword to begin with? Can't the case parsing decide between a high-maintenance case situation and something more efficient? This is a problem for the compiler, not for the Spin language, per se.
IOW make your job complicated to keep the language simple -- please.
">" is supposed to mean "out-of-bounds" or "greater-than".
I like other better. But cast? Not so much. How about index?
But why do we need a separate keyword to begin with? Can't the case parsing decide between a high-maintenance case situation and something more efficient? This is a problem for the compiler, not for the Spin language, per se.
IOW make your job complicated to keep the language simple -- please.
-Phil
The thing is, this is the highest-performance CASE possible. There are some strict requirements about how it needs to be set up by the user. It's possible that the user could miss some requirement and end up with a normal, much slower CASE. Therefore, I think it's appropriate to give this a separate name, to be sure that its requirements are met.
Comments
I like being able to start at non-zero even tho you’re really saying for example CAST n-x
What is the pasm result? A table of addresses and you add ‘n’ to the base address? It’s sort of like using an XBYTE instruction with the skip bits all zero.
What about JTABLE ?
Postedit
Use JTABLE n-5 in the above example and code for 0... and forget LOWER:
All functions have to be of the same input/output, if you don't need that just use void void.
C is strong type cast for your own safety, so to create a jump table takes some weird looking point type casting.
Example 1 https://barrgroup.com/Embedded-Systems/How-To/C-Function-Pointers
ongt x
0: block_0
1: block_1
2: block_2
3: block_3
4: block_4
5: block_5
6: block_6
>: block_other
Finish the list with ELSE.
Simple & Easy.
I think it should require a number for each entry, but you can leave some out. The compiler can just fill in the unused table entries with the "other" entry.
If there is no OTHER entry specified, then it can be implied as the next statement after the table.
I agree with ersmith that making everything short cryptic symbols makes the code harder to read. Please don't go down that route.
I also like the idea of the compiler just being able to do this with the regular CASE instruction. That's how C/C++ works when the compiler is decent, it produces jump table for switch/case constructs. I've even seen simple cases reduces to branchless code (but that gets pretty complex at the compiler level).
Surely that is easily the best solution, which is how other languages already do this ?
The computer does the work, and you do not add more words, or more syntatic chaff, thus keeping the programmer's life simpler.
Some compilers will give a choice of size or speed, and there is also a case form that a compiler arranges like
Where each line compiles not to a compare then jump, but to a chain of DNJZ, or DJNF
This only works if the numeric list is simply sequential, and starts at 1/0 - common to see this in Assembler code.
Yes, cast is already widely used elsewhere, where it means something else, so is best avoided
It's no smaller than 'if'
In BASIC that looks like this I'm not following the 'It's just a pain to rearrange' ?
Code is always best to be able to add and remove lines, in an editor at will, and should tolerate lines added/commented without change of meaning.
As an assist to compilers, FreeBasic takes this approach
http://bourabai.kz/einf/freebasic/KeyPgSelectcase.html
Here they use
'If As Const is used, only Integer constants can be evaluated and the expression list supports single constants and enumerations only. "To" ranges are supported, but "Is" relational operators are not.
With As Const, a jump table is created to contain the full range of integer Cases handled. This allows Select Case As Const to be faster than Select Case.
However, the size of the range of values is limited, and the largest value in the range may be no higher than the smallest value + 4096."
and Python has this approach possible
Would be nice for ELSE: instead of, or an alternative to, OTHER:
This is just a simplification of the language. I never understood why new languages had to invent new terms for existing operations eg SWITCH, SELECT, VOID, and so on.
I like ELSE better than OTHER, too. I'll get rid of OTHER and keep ELSE. We want a short name because it must be at the same indention level as the numbers.
I like GOTO maybe best, too.
..but that adds a new keyword, and GOTO X is not actually 'going to X' at all, which is why BASIC uses ON X GOSUB/GOTO
Seems best to keep existing keywords, and use the CONST modifier, if a compiler needs help to see a table can be used.
Smarter compilers do not even need CONST, making things even simpler for the programmer.
ELSE is widely understood, so is better than OTHER
Well, maybe CASEX, then. Some single word.
Why a single word?
Thinking about this some more I've come to the conclusion that the very best solution is to change the compiler to recognize when it can optimize CASE into a jump table. That way users don't have to learn two separate keywords (CASE and CASEX) and figure out when to use which one. It's better to do the hard work once (in the compiler) than to make every user do the optimization themselves every time they write a CASE statement.
Second best, IMHO, is to add a modifier to the CASE statement (something like CONST) as a promise to the compiler that yes, it can optimize this case into a jump table, and throw an error if it can't.
I think adding a new keyword is the worst solution, since it complicates the language without really adding any new functionality.
If changing the compiler seems like a big task, could it not be postponed? After all this is just an optimization, it doesn't actually change what the language can do. In that case, why not just get something working (for now) and then go back and make it better later? People managed to get quite a lot of work done in Spin1 without this feature, after all .
Regards,
Eric
Best is to have compilers work like Verilog does, where it infers better structures, and reports what it has done. If that is too difficult, use the (optional) CONST as ersmith outlined
ie ideally, programmers simply use CASE, and the compiler confirms it did see a table Case structure.
A very smart compiler would see very short case lists are better done with a few DJNZ/DJNF than creating and loading a table, but larger case lists benefit from a table.
I noticed FreeBASIC above puts a limit on the size of the table.
So this will expand to something like this
CASEJMP, CASETBL, CASETABLE, JMPTABLE or something similar might work?
That is what the code looks like, all right. the implication of using words is that the entire CASE must fit within 64KB, which should never be a problem.
I also don't understand this desire to shorten everything. There is no need for it.
Use DEFAULT for the other case, that will match what C/C++ does with switch/case stuff, and not be reusing something that already exists in the language (as far as I know).
also, why not CASE JUMP or CASE CONSTANT? it's a special case of CASE, why not have it be a keyword following it instead of mashing it up like an ASM instruction? This is not ASM.
ELSE is in common use, in many languages, outside of if statements alone.
I'm fine with either of those, but a better compiler would use a TABLE depending where it was told to choose speed or size, and not need CONSTANT
In the code above, less than somewhere about 6 case choices, would be smaller and faster via DJNF opcode, assuming the reach is ok, whilst above that, table, is larger but faster, for later choices,
To inform the compiler the optional keyword following CASE could be a member of a set like:
SMALL - uses the smallest memory footprint, regardless of performance
FAST - uses fastest method regardless of memory size
TABLE - directs the use of a table, even if other methods might be smaller or faster.
The absence of the optional keyword could then employ a heuristic to determine which structure to use in the situation. Some fairly simple analysis should identify which method is best for the specific code being compiled.
This gives beginners the opportunity to ignore the compiler directive keywords until they run across a case (pun unintended) where they need them.
Yes, that's quite a good idea.
I can think of one case where you might want a TABLE, even tho it may be slower in some paths, and that would be for a known & fixed delay path choice.
I like other better. But cast? Not so much. How about index?
But why do we need a separate keyword to begin with? Can't the case parsing decide between a high-maintenance case situation and something more efficient? This is a problem for the compiler, not for the Spin language, per se.
IOW make your job complicated to keep the language simple -- please.
-Phil
The thing is, this is the highest-performance CASE possible. There are some strict requirements about how it needs to be set up by the user. It's possible that the user could miss some requirement and end up with a normal, much slower CASE. Therefore, I think it's appropriate to give this a separate name, to be sure that its requirements are met.