diff --git a/ast.c b/ast.c index 5087e91..a17e64a 100644 --- a/ast.c +++ b/ast.c @@ -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", diff --git a/compile.c b/compile.c index 383a4e0..1f67156 100644 --- a/compile.c +++ b/compile.c @@ -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, ";"); diff --git a/docs/integers.md b/docs/integers.md index ef30b74..1c30aee 100644 --- a/docs/integers.md +++ b/docs/integers.md @@ -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 diff --git a/docs/operators.md b/docs/operators.md index 4afa3ad..25f7868 100644 --- a/docs/operators.md +++ b/docs/operators.md @@ -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 diff --git a/environment.c b/environment.c index 2cd94f7..7eb1368 100644 --- a/environment.c +++ b/environment.c @@ -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: diff --git a/parse.c b/parse.c index 1e76f82..0130673 100644 --- a/parse.c +++ b/parse.c @@ -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; diff --git a/stdlib/integers.h b/stdlib/integers.h index ea28620..e7b5b0e 100644 --- a/stdlib/integers.h +++ b/stdlib/integers.h @@ -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); \ } diff --git a/test/integers.tm b/test/integers.tm index 225baab..9b5b6b4 100644 --- a/test/integers.tm +++ b/test/integers.tm @@ -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