Thinking about it more, I agree about having >> be non-arithmetic in the context of Spin and typical usage there. Also, having SAR/SAL makes it very clear that it's not a normal shift.
I have sometimes wished that C would let you more explicitly do shifts and arithmetic shifts. Usually you have to do some casting to get what you want if it's not the defined behavior.
Chip, re: precedence
As others have said, I don't really think about precedence order beyond the basics for the simple math ops. I typically just put in the ( )'s just so it's very clear.
Keep the Spin precedence order, Chip. I concur with your assessment that C's is a bit off.
Thanks,
-Phil
Phil, thanks for thinking about all this. I know this is a bit of a roller coaster.
All of your guys' input has been really helpful here. I think we've maybe got it nailed now.
My only wish, at this point, is that we could do this, but it really flies in the face of convention:
>> rotate right
<< rotate left
-> shift right
<- shift left
~> shift arithmetic right
That would have been elegant, but >> and << got used for shift a long time ago, even though, graphically, they are ideal for rotates. And they leave nice solutions for shifts: <-, ->, ~>
Keep the Spin precedence order, Chip. I concur with your assessment that C's is a bit off.
Thanks,
-Phil
Phil, thanks for thinking about all this. I know this is a bit of a roller coaster.
All of your guys' input has been really helpful here. I think we've maybe got it nailed now.
My only wish, at this point, is that we could do this, but it really flies in the face of convention:
>> rotate right
<< rotate left
-> shift right
<- shift left
~> shift arithmetic right
That would have been elegant, but >> and << got used for shift a long time ago, even though, graphically, they are ideal for rotate. And they leave nice solutions for shifts: <-, ->, ~>
I'm not opposed, and it's nice. Agreed. But, I also think this is an opportunity missed too.
IMHO, basically everybody will stumble on >> and friends, if we don't leave them as a simple shift. Gets used all the time.
Thinking about it more, I agree about having >> be non-arithmetic in the context of Spin and typical usage there. Also, having SAR/SAL makes it very clear that it's not a normal shift.
I have sometimes wished that C would let you more explicitly do shifts and arithmetic shifts. Usually you have to do some casting to get what you want if it's not the defined behavior.
Chip, re: precedence
As others have said, I don't really think about precedence order beyond the basics for the simple math ops. I typically just put in the ( )'s just so it's very clear.
SAL is the usually the same as SHL, and many micros don't include SAL. From wikipedia
Equivalence of arithmetic left shift and multiplication
Arithmetic left shifts are equivalent to multiplication by a (positive, integral) power of the radix (e.g., a multiplication by a power of 2 for binary numbers). Arithmetic left shifts are, with two exceptions, identical in effect to logical left shifts. Exception one is the minor trap that arithmetic shifts may trigger arithmetic overflow whereas logical shifts do not. Obviously, that exception occurs in real world use cases only if a trigger signal for such an overflow is needed by the design it is used for. Exception two is the MSB is preserved. Processors usually do not offer logical and arithmetic left shift operations with a significant difference, if any.
So, IMHO no need for SAL. Otherwise, totally agreed.
Chip,
Maybe you could use the following descriptions...
#> x #> y x #>= y binary signed 9, 16 Greater/Maximum (limit minimum)/Ensure x => y
<# x <# y x <#= y binary signed 9, 16 Lesser /Minimum (limit maximum)/Ensure x <= y
does the foo() function get called? In C it doesn't for &&, but of course does for &.
jmg: boolean AND and binary AND are different, so they should have different operators. If Spin had a boolean type then perhaps we could rely on the compiler to infer which is intended, but with all variables being 32 bit integers this isn't really possible.
We could do it that way. That would really be a compiler thing. In fact, if we do it that way, there's not even a need for the boolean AND/OR, as actual operator routines.
= assignement operator - in addition to :=
; for statement terminator
// for comments (in addition to ')
/* ... */ for block/multi-line comments - in addition to {...} and {{...}}
Keep the Spin precedence order, Chip. I concur with your assessment that C's is a bit off.
Thanks,
-Phil
I was just now looking at the original Spin precedence list and I see that I wound up replicating it exactly today, without even remembering what the original Spin did. I just thought it through. The only difference is that the OR's and XOR's switched places. I was influenced by looking at C's precedence today. Don't know what is actually better. Kind of seems like XOR should be the highest to me. Someone pointed out that the new way is proper, though.
= assignement operator - in addition to :=
; for statement terminator
// for comments (in addition to ')
/* ... */ for block/multi-line comments - in addition to {...} and {{...}}
And maybe { } for scope, since they wouldn't be needed for comments.
Chip,
I really wish we had normal MIN and MAX, the limit variants are just odd to me. I just want x := MAX(a,b) where x is assigned the higher of a and b. and vice versa for MIN.
I have never used <# or #> myself, they just look foreign to me, and I have to think a long time about them when I see them in code.
Same for the PASM MIN and MAX instructions, they are just backwards to me. REALLY confusing to use, when I'm so used to MAX and MIN in other CPUs/MCUs, and languages working like I said above.
= assignement operator - in addition to :=
; for statement terminator
// for comments (in addition to ')
/* ... */ for block/multi-line comments - in addition to {...} and {{...}}
That would have been elegant, but >> and << got used for shift a long time ago, even though, graphically, they are ideal for rotates.
Yes, history did chose another path, which is why the alias coverage is important, as a means to give an explicit way to cover all the bases.
Ideally, the HLL should be able to support all the SHIFTS available in Assembler, and you can see laments from C coders where it lacks that.
The aliases you have already listed SAR, SHL, SHR, ROR, ROL give that coverage & follow established conventions
Chip,
I really wish we had normal MIN and MAX, the limit variants are just odd to me. I just want x := MAX(a,b) where x is assigned the higher of a and b. and vice versa for MIN.
I have never used <# or #> myself, they just look foreign to me, and I have to think a long time about them when I see them in code.
Same for the PASM MIN and MAX instructions, they are just backwards to me. REALLY confusing to use, when I'm so used to MAX and MIN in other CPUs/MCUs, and languages working like I said above.
I hear you and I think I know the problem. We are doing MIN and MAX with one implied term, in sequence.
Using MIN(x,y) and MAX(x,y) is common in the world, but you can't use them as assignment operators cleanly. Those are 'batch', while ours are 'flow'.
If it wasn't for people's experiential bias, I think the #> and <# way would be seen as better. As those operators execute, I imagine a carpenter doing some trimming as he moves along. With MIN(x,y), I imagine something in each hand being evaluated and one getting kept, while the other gets tossed.
Here is the updated list with SHL and SHR gone, as they are no longer needed. SAR sits where SHR used to:
Operator Term Usage Assign Usage Type Prior Description
-------------------------------------------------------------------------------------------------------------------------
++ (pre) ++x ++x var prefix 1 Pre-increment
-- (pre) --x --x var prefix 1 Pre-decrement
?? (pre) ??x ??x var prefix 1 PRNG reverse (var must be long)
++ (post) x++ x++ var postfix 1 Post-increment
-- (post) x-- x-- var postfix 1 Post-decrement
?? (post) x?? x?? var postfix 1 PRNG forward (var must be long)
!! (post) x!! x!! var postfix 1 Post-boolean NOT
! (post) x! x! var postfix 1 Post-bitwise NOT
\ (post) x\y x\y var postfix 1 Post-set to y
! !x != x unary 2 Bitwise NOT, 1's complement
- -x -= x unary 2 Negation, 2's complement
ABS ABS x ABS= x unary 2 Absolute value
NCOD NCOD x NCOD= x unary 2 Encode MSB, 31..0
DCOD DCOD x DCOD= x unary 2 Decode, 1<<(x & $1F)
SQRT SQRT x SQRT= x unary 2 Square root
LOG2 LOG2 x LOG2= x unary 2 Unsigned to logarithm-base2
EXP2 EXP2 x EXP2= x unary 2 Logarithm-base2 to unsigned
>> x >> y x >>= y binary 3, 16 Shift right, insert 0's
<< x << y x <<= y binary 3, 16 Shift left
SAR x SAR y x SAR= y binary 3, 16 Shift right, insert MSB's
ROR x ROR y x ROR= y binary 3, 16 Rotate right
ROL x ROL y x ROL= y binary 3, 16 Rotate left
REV x REV y x REV= y binary 3, 16 Reverse y LSBs of x and zero-extend
SIGNX x SIGNX y x SIGNX= y binary 3, 16 Sign-extend from bit y
& x & y x &= y binary 4, 16 Bitwise AND
^ x ^ y x ^= y binary 5, 16 Bitwise XOR
| x | y x |= y binary 6, 16 Bitwise OR
* x * y x *= y binary 7, 16 Multiply
/ x / y x /= y binary 7, 16 Divide, return quotient
MOD x MOD y x MOD= y binary 7, 16 Divide, return remainder
SCAL x SCAL y x SCAL= y binary 7, 16 Scale, unsigned (x * y) >> 32
FRAC x FRAC y x FRAC= y binary 7, 16 Fraction, unsigned {x, 32'b0}/y
+ x + y x += y binary 8, 16 Add
- x - y x -= y binary 8, 16 Subtract
MIN x MIN y x MIN= y binary 9, 16 Ensure x => y, signed
MAX x MAX y x MAX= y binary 9, 16 Ensure x <= y, signed
< x < y n/a equality 10 Check less than (returns 0 or -1)
<= x <= y n/a equality 10 Check less than or equal (returns 0 or -1)
== x == y n/a equality 10 Check equal (returns 0 or -1)
<> x <> y n/a equality 10 Check not equal (returns 0 or -1)
>= x >= y n/a equality 10 Check greater than or equal (returns 0 or -1)
> x > y n/a equality 10 Check greater than (returns 0 or -1)
!!, NOT !!x !!= x unary 11 Boolean NOT (x == 0, returns 0 or -1)
&&, AND x && y x &&= y binary 12 Boolean AND (x <> 0 AND y <> 0, returns 0 or -1)
^^, XOR x ^^ y x ^^= y binary 13 Boolean XOR (x <> 0 XOR y <> 0, returns 0 or -1)
||, OR x || y x ||= y binary 14 Boolean OR (x <> 0 OR y <> 0, returns 0 or -1)
? : x ? y : z n/a ternary 15 Choose between y and z
:= x := y x := y assign 16 Set x to y
Among the unary and binary operators, the only non-standard, non-alpha operators are #> and <#. I kind of just want to call them MIN and MAX and be done with it... Okay, I just did it.
I really wish we had normal MIN and MAX, the limit variants are just odd to me. I just want x := MAX(a,b) where x is assigned the higher of a and b. and vice versa for MIN.
I have never used <# or #> myself, they just look foreign to me, and I have to think a long time about them when I see them in code.
Same for the PASM MIN and MAX instructions, they are just backwards to me. REALLY confusing to use, when I'm so used to MAX and MIN in other CPUs/MCUs, and languages working like I said above.
Agreed, those are backwards, but they are also now established in the field, so a new form is required for the fix, where pfxMAX or MAXsfx mean what the English says. (pfx,sfx = some tbf prefix of suffix)
Thinking about it more, I agree about having >> be non-arithmetic in the context of Spin and typical usage there. Also, having SAR/SAL makes it very clear that it's not a normal shift.
I have sometimes wished that C would let you more explicitly do shifts and arithmetic shifts. Usually you have to do some casting to get what you want if it's not the defined behavior.
Chip, re: precedence
As others have said, I don't really think about precedence order beyond the basics for the simple math ops. I typically just put in the ( )'s just so it's very clear.
I also tend to assume that && and || are almost the lowest precedence so you can write expressions like "x >= 0 && x < 10". Other than that, I use parens.
Here is the updated list with SHL and SHR gone, as they are no longer needed. SAR sits where SHR used to:
Operator Term Usage Assign Usage Type Prior Description
-------------------------------------------------------------------------------------------------------------------------
++ (pre) ++x ++x var prefix 1 Pre-increment
-- (pre) --x --x var prefix 1 Pre-decrement
?? (pre) ??x ??x var prefix 1 PRNG reverse (var must be long)
++ (post) x++ x++ var postfix 1 Post-increment
-- (post) x-- x-- var postfix 1 Post-decrement
?? (post) x?? x?? var postfix 1 PRNG forward (var must be long)
!! (post) x!! x!! var postfix 1 Post-boolean NOT
! (post) x! x! var postfix 1 Post-bitwise NOT
\ (post) x\y x\y var postfix 1 Post-set to y
! !x != x unary 2 Bitwise NOT, 1's complement
- -x -= x unary 2 Negation, 2's complement
ABS ABS x ABS= x unary 2 Absolute value
NCOD NCOD x NCOD= x unary 2 Encode MSB, 31..0
DCOD DCOD x DCOD= x unary 2 Decode, 1<<(x & $1F)
SQRT SQRT x SQRT= x unary 2 Square root
LOG2 LOG2 x LOG2= x unary 2 Unsigned to logarithm-base2
EXP2 EXP2 x EXP2= x unary 2 Logarithm-base2 to unsigned
>> x >> y x >>= y binary 3, 16 Shift right, insert 0's
<< x << y x <<= y binary 3, 16 Shift left
SAR x SAR y x SAR= y binary 3, 16 Shift right, insert MSB's
ROR x ROR y x ROR= y binary 3, 16 Rotate right
ROL x ROL y x ROL= y binary 3, 16 Rotate left
REV x REV y x REV= y binary 3, 16 Reverse y LSBs of x and zero-extend
SIGNX x SIGNX y x SIGNX= y binary 3, 16 Sign-extend from bit y
& x & y x &= y binary 4, 16 Bitwise AND
^ x ^ y x ^= y binary 5, 16 Bitwise XOR
| x | y x |= y binary 6, 16 Bitwise OR
* x * y x *= y binary 7, 16 Multiply
/ x / y x /= y binary 7, 16 Divide, return quotient
MOD x MOD y x MOD= y binary 7, 16 Divide, return remainder
SCAL x SCAL y x SCAL= y binary 7, 16 Scale, unsigned (x * y) >> 32
FRAC x FRAC y x FRAC= y binary 7, 16 Fraction, unsigned {x, 32'b0}/y
+ x + y x += y binary 8, 16 Add
- x - y x -= y binary 8, 16 Subtract
MIN x MIN y x MIN= y binary 9, 16 Ensure x => y, signed
MAX x MAX y x MAX= y binary 9, 16 Ensure x <= y, signed
< x < y n/a equality 10 Check less than (returns 0 or -1)
<= x <= y n/a equality 10 Check less than or equal (returns 0 or -1)
== x == y n/a equality 10 Check equal (returns 0 or -1)
<> x <> y n/a equality 10 Check not equal (returns 0 or -1)
>= x >= y n/a equality 10 Check greater than or equal (returns 0 or -1)
> x > y n/a equality 10 Check greater than (returns 0 or -1)
!!, NOT !!x !!= x unary 11 Boolean NOT (x == 0, returns 0 or -1)
&&, AND x && y x &&= y binary 12 Boolean AND (x <> 0 AND y <> 0, returns 0 or -1)
^^, XOR x ^^ y x ^^= y binary 13 Boolean XOR (x <> 0 XOR y <> 0, returns 0 or -1)
||, OR x || y x ||= y binary 14 Boolean OR (x <> 0 OR y <> 0, returns 0 or -1)
? : x ? y : z n/a ternary 15 Choose between y and z
:= x := y x := y assign 16 Set x to y
Among the unary and binary operators, the only non-standard, non-alpha operators are #> and <#. I kind of just want to call them MIN and MAX and be done with it... Okay, I just did it.
I really think you should use MINS and MAXS rather than MIN and MAX to at least be consistent with PASM.
The wording of "Ensure x => y" for the MIN operator seems confusing. It sounds like the value of x is changed to equal the value of y if y is greater than x. The description for "x MIN y" should say exactly what it does, which is "returns the maximum value of x and y". And the description of MAX should be "returns the minimum value of x and y". Of course, those accurate descriptions will make the novice programmer wonder why the operator does the opposite of what the name implies, but I think all of us have wondered that ourselves.
EDIT: I would suggest not having aliases for #> and <#, or if you do want to use MIN and MAX for the aliases then swap them so their use is obvious instead of obfuscating the meaning. Of course, if you do that you would probably need to fix the meaning in assembly as well. Maybe better aliases would be CLIPHI and CLIPLO, or CLPH and CLPL. I think it's better to avoid using the terms MIN and MAX because of the misleading connotation of the operators.
I always have to look up #> and <# to remember what they mean. Why not something simple like X := LimMax(Y, maxval) or X := LimMax(Y, 100) then you see exactly what the maximum value will be and there is zero confusion about it.
I've always liked the #> and <# as opposed to MIN and MAX. To me they mean "force greater-than-or-equal" and "force-less-than-or-equal."
To those of you who've never programmed in PBASIC, MIN and MAX there act as limit operators, too, not as traditional MIN and MAX functions. Those mnemonics can be confusing if you're expecting the opposite behavior.
And maybe { } for scope, since they wouldn't be needed for comments.
Oh heavens, no! Please keep the indentation.
When I taught C for robotics, the biggest thing that slowed the class down was helping the kids track down unmatched braces. And with braces they were slow to use proper indentation style, which made things even worse. Spin solved both issues with indentation scoping.
So keep it clean, please. We don't need more syntactic gingerbread in Spin.
I don't enjoy brackets, either. It would be nice to maybe get more than one statement on a line, though. But, I don't like semicolons any more than brackets.
As much as I am unhappy with white space block delimiting I vote against changing Spin so drastically as to add brace delimiters or "begin", "end" etc. That is going to far.
I also love semicolons, but not in Spin for Pete's sake.
I really hate the practice of stuffing multiple statements on a line separated by semi-colons. I know ace programmers love it. But I'm sure it's just another confusion and makes things harder to read for beginners.
In short I'm all for keeping that syntactic noise out of Spin.
I always have to look up #> and <# to remember what they mean. Why not something simple like X := LimMax(Y, maxval) or X := LimMax(Y, 100) then you see exactly what the maximum value will be and there is zero confusion about it.
In cases where you want to constrain the top and bottom, doing "somevalue min 0 max 99" is very simple. It would take two separate statements to achieve that using something like LimMax(,) and LimMin(,), and only an optimizing compiler could reduce it what it only really needs to be. Having these as binary operators allows much more efficiency.
Comments
I have sometimes wished that C would let you more explicitly do shifts and arithmetic shifts. Usually you have to do some casting to get what you want if it's not the defined behavior.
Chip, re: precedence
As others have said, I don't really think about precedence order beyond the basics for the simple math ops. I typically just put in the ( )'s just so it's very clear.
Phil, thanks for thinking about all this. I know this is a bit of a roller coaster.
All of your guys' input has been really helpful here. I think we've maybe got it nailed now.
My only wish, at this point, is that we could do this, but it really flies in the face of convention:
That would have been elegant, but >> and << got used for shift a long time ago, even though, graphically, they are ideal for rotates. And they leave nice solutions for shifts: <-, ->, ~>
I'm not opposed, and it's nice. Agreed. But, I also think this is an opportunity missed too.
IMHO, basically everybody will stumble on >> and friends, if we don't leave them as a simple shift. Gets used all the time.
From wikipedia
So, IMHO no need for SAL. Otherwise, totally agreed.
Maybe you could use the following descriptions...
We could do it that way. That would really be a compiler thing. In fact, if we do it that way, there's not even a need for the boolean AND/OR, as actual operator routines.
The #> and <# are signed. I made a note in the operator list.
I would code that last example as:
x #> min <# max
What you wrote looks like it would work, but it looks strange to me.
I was just now looking at the original Spin precedence list and I see that I wound up replicating it exactly today, without even remembering what the original Spin did. I just thought it through. The only difference is that the OR's and XOR's switched places. I was influenced by looking at C's precedence today. Don't know what is actually better. Kind of seems like XOR should be the highest to me. Someone pointed out that the new way is proper, though.
And maybe { } for scope, since they wouldn't be needed for comments.
I really wish we had normal MIN and MAX, the limit variants are just odd to me. I just want x := MAX(a,b) where x is assigned the higher of a and b. and vice versa for MIN.
I have never used <# or #> myself, they just look foreign to me, and I have to think a long time about them when I see them in code.
Same for the PASM MIN and MAX instructions, they are just backwards to me. REALLY confusing to use, when I'm so used to MAX and MIN in other CPUs/MCUs, and languages working like I said above.
Yes, history did chose another path, which is why the alias coverage is important, as a means to give an explicit way to cover all the bases.
Ideally, the HLL should be able to support all the SHIFTS available in Assembler, and you can see laments from C coders where it lacks that.
The aliases you have already listed SAR, SHL, SHR, ROR, ROL give that coverage & follow established conventions
I hear you and I think I know the problem. We are doing MIN and MAX with one implied term, in sequence.
Using MIN(x,y) and MAX(x,y) is common in the world, but you can't use them as assignment operators cleanly. Those are 'batch', while ours are 'flow'.
If it wasn't for people's experiential bias, I think the #> and <# way would be seen as better. As those operators execute, I imagine a carpenter doing some trimming as he moves along. With MIN(x,y), I imagine something in each hand being evaluated and one getting kept, while the other gets tossed.
Isn't this nice, though: Maybe we should just use names, instead:
Among the unary and binary operators, the only non-standard, non-alpha operators are #> and <#. I kind of just want to call them MIN and MAX and be done with it... Okay, I just did it.
I really think you should use MINS and MAXS rather than MIN and MAX to at least be consistent with PASM.
EDIT: I would suggest not having aliases for #> and <#, or if you do want to use MIN and MAX for the aliases then swap them so their use is obvious instead of obfuscating the meaning. Of course, if you do that you would probably need to fix the meaning in assembly as well. Maybe better aliases would be CLIPHI and CLIPLO, or CLPH and CLPL. I think it's better to avoid using the terms MIN and MAX because of the misleading connotation of the operators.
To those of you who've never programmed in PBASIC, MIN and MAX there act as limit operators, too, not as traditional MIN and MAX functions. Those mnemonics can be confusing if you're expecting the opposite behavior.
-Phil
When I taught C for robotics, the biggest thing that slowed the class down was helping the kids track down unmatched braces. And with braces they were slow to use proper indentation style, which made things even worse. Spin solved both issues with indentation scoping.
So keep it clean, please. We don't need more syntactic gingerbread in Spin.
-Phil
I have much more of a beef with the PASM MIN/MAX instructions being backwards...
I also love semicolons, but not in Spin for Pete's sake.
I really hate the practice of stuffing multiple statements on a line separated by semi-colons. I know ace programmers love it. But I'm sure it's just another confusion and makes things harder to read for beginners.
In short I'm all for keeping that syntactic noise out of Spin.
It's got enough already
Given that PASM instructions MIN and MAX potentially modify D, what names would be appropriate?
In cases where you want to constrain the top and bottom, doing "somevalue min 0 max 99" is very simple. It would take two separate statements to achieve that using something like LimMax(,) and LimMin(,), and only an optimizing compiler could reduce it what it only really needs to be. Having these as binary operators allows much more efficiency.