Clean up behavior and syntax for unsigned bit shifts (<<<, >>>)

This commit is contained in:
Bruce Hill 2024-11-03 16:06:26 -05:00
parent 3743913ce2
commit 39a58bc129
8 changed files with 45 additions and 28 deletions

4
ast.c
View File

@ -15,8 +15,8 @@ static const char *OP_NAMES[] = {
[BINOP_UNKNOWN]="unknown",
[BINOP_POWER]="^", [BINOP_MULT]="*", [BINOP_DIVIDE]="/",
[BINOP_MOD]="mod", [BINOP_MOD1]="mod1", [BINOP_PLUS]="+", [BINOP_MINUS]="minus",
[BINOP_CONCAT]="++", [BINOP_LSHIFT]="<<", [BINOP_ULSHIFT]="<<[u]",
[BINOP_RSHIFT]=">>", [BINOP_URSHIFT]=">>[u]", [BINOP_MIN]="min",
[BINOP_CONCAT]="++", [BINOP_LSHIFT]="<<", [BINOP_ULSHIFT]="<<<",
[BINOP_RSHIFT]=">>", [BINOP_URSHIFT]=">>>", [BINOP_MIN]="min",
[BINOP_MAX]="max", [BINOP_EQ]="==", [BINOP_NE]="!=", [BINOP_LT]="<",
[BINOP_LE]="<=", [BINOP_GT]=">", [BINOP_GE]=">=", [BINOP_CMP]="<>",
[BINOP_AND]="and", [BINOP_OR]="or", [BINOP_XOR]="xor",

View File

@ -725,6 +725,14 @@ CORD compile_statement(env_t *env, ast_t *ast)
if (lhs_t->tag != IntType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a shift assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all(lhs, " >>= ", rhs, ";");
case BINOP_ULSHIFT:
if (lhs_t->tag != IntType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a shift assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all("{ ", compile_unsigned_type(lhs_t), " *dest = (void*)&(", lhs, "); *dest <<= ", rhs, "; }");
case BINOP_URSHIFT:
if (lhs_t->tag != IntType && lhs_t->tag != ByteType)
code_err(ast, "I can't do a shift assignment with this operator between %T and %T", lhs_t, rhs_t);
return CORD_all("{ ", compile_unsigned_type(lhs_t), " *dest = (void*)&(", lhs, "); *dest >>= ", rhs, "; }");
case BINOP_AND: {
if (lhs_t->tag == BoolType)
return CORD_all("if (", lhs, ") ", lhs, " = ", rhs, ";");

View File

@ -21,8 +21,9 @@ truncated form of the input value.
Integers support the standard math operations (`x+y`, `x-y`, `x*y`, `x/y`) as
well as powers/exponentiation (`x^y`), modulus (`x mod y` and `x mod1 y`), and
bitwise operations: `x and y`, `x or y`, `x xor y`, `x << y`, and `x >> y`. The
operators `and`, `or`, and `xor` are _bitwise_, not logical operators.
bitwise operations: `x and y`, `x or y`, `x xor y`, `x << y`, `x >> y`, `x >>>
y` (unsigned right shift), and `x <<< y` (unsighted left shift). The operators
`and`, `or`, and `xor` are _bitwise_, not logical operators.
# Integer Functions

View File

@ -10,6 +10,7 @@ Tomo supports a number of operators, both infix and prefix:
is particularly useful for doing wraparound behavior on 1-indexed arrays.
- `++`: concatenation (for text and arrays)
- `<<`, `>>`: bitwise left shift and right shift for integers
- `<<<`, `>>>`: unsigned bitwise left shift and right shift for integers
- `_min_`/`_max_`: minimum and maximum (see below)
- `<`, `<=`, `>`, `>=`, `==`, `!=`: comparisons
- `<>`: the signed comparison operator (see below)
@ -283,8 +284,10 @@ and will return a value of the same type.
#### Bit Operations
```
func left_shift(T, Int)->T
func right_shift(T, Int)->T
func left_shifted(T, Int)->T
func right_shifted(T, Int)->T
func unsigned_left_shifted(T, Int)->T
func unsigned_right_shifted(T, Int)->T
func bit_and(T, T)->T
func bit_or(T, T)->T
func bit_xor(T, T)->T
@ -292,7 +295,8 @@ func bit_xor(T, T)->T
In a bit shifting expression, `a >> b` or `a << b`, if `a` has type `T` and `b`
is an `Int`, then the method `left_shift()` or `right_shift()` will be invoked.
A value of type `T` will be returned.
A value of type `T` will be returned. The same is true for `>>>`
(`unsigned_right_shift()`) and `<<<` (`unsigned_left_shift`).
In a bitwise binary operation `a and b`, `a or b`, or `a xor b`, then the
method `bit_and()`, `bit_or()`, or `bit_xor()` will be invoked, assuming that

View File

@ -138,8 +138,8 @@ env_t *new_compilation_unit(CORD libname)
{"modulo1", "Int64$modulo1", "func(x,y:Int64 -> Int64)"},
{"octal", "Int64$octal", "func(i:Int64, digits=0, prefix=yes -> Text)"},
{"to", "Int64$to", "func(from:Int64,to:Int64 -> Range)"},
{"unsigned_left_shift", "Int64$unsigned_left_shift", "func(x:Int64,y:Int64 -> Int64)"},
{"unsigned_right_shift", "Int64$unsigned_right_shift", "func(x:Int64,y:Int64 -> Int64)"},
{"unsigned_left_shifted", "Int64$unsigned_left_shifted", "func(x:Int64,y:Int64 -> Int64)"},
{"unsigned_right_shifted", "Int64$unsigned_right_shifted", "func(x:Int64,y:Int64 -> Int64)"},
{"wrapping_minus", "Int64$wrapping_minus", "func(x:Int64,y:Int64 -> Int64)"},
{"wrapping_plus", "Int64$wrapping_plus", "func(x:Int64,y:Int64 -> Int64)"},
// Must be defined after min/max:
@ -159,8 +159,8 @@ env_t *new_compilation_unit(CORD libname)
{"modulo1", "Int32$modulo1", "func(x,y:Int32 -> Int32)"},
{"octal", "Int32$octal", "func(i:Int32, digits=0, prefix=yes -> Text)"},
{"to", "Int32$to", "func(from:Int32,to:Int32 -> Range)"},
{"unsigned_left_shift", "Int32$unsigned_left_shift", "func(x:Int32,y:Int32 -> Int32)"},
{"unsigned_right_shift", "Int32$unsigned_right_shift", "func(x:Int32,y:Int32 -> Int32)"},
{"unsigned_left_shifted", "Int32$unsigned_left_shifted", "func(x:Int32,y:Int32 -> Int32)"},
{"unsigned_right_shifted", "Int32$unsigned_right_shifted", "func(x:Int32,y:Int32 -> Int32)"},
{"wrapping_minus", "Int32$wrapping_minus", "func(x:Int32,y:Int32 -> Int32)"},
{"wrapping_plus", "Int32$wrapping_plus", "func(x:Int32,y:Int32 -> Int32)"},
// Must be defined after min/max:
@ -180,8 +180,8 @@ env_t *new_compilation_unit(CORD libname)
{"modulo1", "Int16$modulo1", "func(x,y:Int16 -> Int16)"},
{"octal", "Int16$octal", "func(i:Int16, digits=0, prefix=yes -> Text)"},
{"to", "Int16$to", "func(from:Int16,to:Int16 -> Range)"},
{"unsigned_left_shift", "Int16$unsigned_left_shift", "func(x:Int16,y:Int16 -> Int16)"},
{"unsigned_right_shift", "Int16$unsigned_right_shift", "func(x:Int16,y:Int16 -> Int16)"},
{"unsigned_left_shifted", "Int16$unsigned_left_shifted", "func(x:Int16,y:Int16 -> Int16)"},
{"unsigned_right_shifted", "Int16$unsigned_right_shifted", "func(x:Int16,y:Int16 -> Int16)"},
{"wrapping_minus", "Int16$wrapping_minus", "func(x:Int16,y:Int16 -> Int16)"},
{"wrapping_plus", "Int16$wrapping_plus", "func(x:Int16,y:Int16 -> Int16)"},
// Must be defined after min/max:
@ -201,8 +201,8 @@ env_t *new_compilation_unit(CORD libname)
{"modulo1", "Int8$modulo1", "func(x,y:Int8 -> Int8)"},
{"octal", "Int8$octal", "func(i:Int8, digits=0, prefix=yes -> Text)"},
{"to", "Int8$to", "func(from:Int8,to:Int8 -> Range)"},
{"unsigned_left_shift", "Int8$unsigned_left_shift", "func(x:Int8,y:Int8 -> Int8)"},
{"unsigned_right_shift", "Int8$unsigned_right_shift", "func(x:Int8,y:Int8 -> Int8)"},
{"unsigned_left_shifted", "Int8$unsigned_left_shifted", "func(x:Int8,y:Int8 -> Int8)"},
{"unsigned_right_shifted", "Int8$unsigned_right_shifted", "func(x:Int8,y:Int8 -> Int8)"},
{"wrapping_minus", "Int8$wrapping_minus", "func(x:Int8,y:Int8 -> Int8)"},
{"wrapping_plus", "Int8$wrapping_plus", "func(x:Int8,y:Int8 -> Int8)"},
// Must be defined after min/max:

22
parse.c
View File

@ -1706,21 +1706,21 @@ binop_e match_binary_operator(const char **pos)
case '^': *pos += 1; return BINOP_POWER;
case '<': {
*pos += 1;
if (match(pos, "=")) return BINOP_LE;
else if (match(pos, ">")) return BINOP_CMP;
if (match(pos, "=")) return BINOP_LE; // "<="
else if (match(pos, ">")) return BINOP_CMP; // "<>"
else if (match(pos, "<")) {
if (match(pos, "[u]"))
return BINOP_ULSHIFT;
return BINOP_LSHIFT;
if (match(pos, "<"))
return BINOP_ULSHIFT; // "<<<"
return BINOP_LSHIFT; // "<<"
} else return BINOP_LT;
}
case '>': {
*pos += 1;
if (match(pos, "=")) return BINOP_GE;
if (match(pos, "=")) return BINOP_GE; // ">="
if (match(pos, ">")) {
if (match(pos, "[u]"))
return BINOP_URSHIFT;
return BINOP_RSHIFT;
if (match(pos, ">"))
return BINOP_URSHIFT; // ">>>"
return BINOP_RSHIFT; // ">>"
}
return BINOP_GT;
}
@ -1814,6 +1814,10 @@ PARSER(parse_update) {
else if (match(&pos, "*=")) op = BINOP_MULT;
else if (match(&pos, "/=")) op = BINOP_DIVIDE;
else if (match(&pos, "^=")) op = BINOP_POWER;
else if (match(&pos, "<<=")) op = BINOP_LSHIFT;
else if (match(&pos, "<<<=")) op = BINOP_ULSHIFT;
else if (match(&pos, ">>=")) op = BINOP_RSHIFT;
else if (match(&pos, ">>>=")) op = BINOP_URSHIFT;
else if (match(&pos, "and=")) op = BINOP_AND;
else if (match(&pos, "or=")) op = BINOP_OR;
else if (match(&pos, "xor=")) op = BINOP_XOR;

View File

@ -67,10 +67,10 @@
MACROLIKE PUREFUNC c_type type_name ## $wrapping_minus(c_type x, c_type y) { \
return (c_type)((u##c_type)x + (u##c_type)y); \
} \
MACROLIKE PUREFUNC c_type type_name ## $unsigned_left_shift(c_type x, c_type y) { \
MACROLIKE PUREFUNC c_type type_name ## $unsigned_left_shifted(c_type x, c_type y) { \
return (c_type)((u##c_type)x << y); \
} \
MACROLIKE PUREFUNC c_type type_name ## $unsigned_right_shift(c_type x, c_type y) { \
MACROLIKE PUREFUNC c_type type_name ## $unsigned_right_shifted(c_type x, c_type y) { \
return (c_type)((u##c_type)x >> y); \
}

View File

@ -20,11 +20,11 @@ func main():
!! Signed and unsigned bit shifting:
>> -2[64] << 1
= -4[64]
>> -2[64] <<[u] 1
>> -2[64] <<< 1
= -4[64]
>> -2[64] >> 1
= -1[64]
>> -2[64] >>[u] 1
>> -2[64] >>> 1
= 9223372036854775807[64]
>> 3 and 2