Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support bitwise operations #8903

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,18 @@ private static Object evaluate(
case PIPE:
return pipe(x, y, env, location);

case AMPERSAND:
return (Integer) x & (Integer) y;
Quarz0 marked this conversation as resolved.
Show resolved Hide resolved

case CARET:
return (Integer) x ^ (Integer) y;

case GREATER_GREATER:
Quarz0 marked this conversation as resolved.
Show resolved Hide resolved
return (Integer) x >> (Integer) y;

case LESS_LESS:
return (Integer) x << (Integer) y;
Quarz0 marked this conversation as resolved.
Show resolved Hide resolved

case MINUS:
return minus(x, y, location);

Expand Down Expand Up @@ -346,7 +358,9 @@ private static Object plus(
/** Implements 'x | y'. */
private static Object pipe(Object x, Object y, Environment env, Location location)
throws EvalException {
if (x instanceof SkylarkNestedSet) {
if (x instanceof Integer && y instanceof Integer) {
return ((Integer) x) | ((Integer) y);
} else if (x instanceof SkylarkNestedSet) {
if (env.getSemantics().incompatibleDepsetUnion()) {
throw new EvalException(
location,
Expand Down
32 changes: 30 additions & 2 deletions src/main/java/com/google/devtools/build/lib/syntax/Lexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public final class Lexer {
.put('*', TokenKind.STAR_EQUALS)
.put('/', TokenKind.SLASH_EQUALS)
.put('%', TokenKind.PERCENT_EQUALS)
.put('^', TokenKind.CARET_EQUALS)
.put('&', TokenKind.AMPERSAND_EQUALS)
.put('|', TokenKind.PIPE_EQUALS)
.build();

private final EventHandler eventHandler;
Expand Down Expand Up @@ -809,10 +812,26 @@ private void tokenize() {
popParen();
break;
case '>':
setToken(TokenKind.GREATER, pos - 1, pos);
if (lookaheadIs(0, '>') && lookaheadIs(1, '=')) {
Quarz0 marked this conversation as resolved.
Show resolved Hide resolved
setToken(TokenKind.GREATER_GREATER_EQUALS, pos - 1, pos + 2);
pos += 2;
} else if (lookaheadIs(0, '>')) {
setToken(TokenKind.GREATER_GREATER, pos - 1, pos + 1);
pos += 1;
} else {
setToken(TokenKind.GREATER, pos - 1, pos);
}
break;
case '<':
setToken(TokenKind.LESS, pos - 1, pos);
if (lookaheadIs(0, '<') && lookaheadIs(1, '=')) {
setToken(TokenKind.LESS_LESS_EQUALS, pos - 1, pos + 2);
pos += 2;
} else if (lookaheadIs(0, '<')) {
setToken(TokenKind.LESS_LESS, pos - 1, pos + 1);
pos += 1;
} else {
setToken(TokenKind.LESS, pos - 1, pos);
}
break;
case ':':
setToken(TokenKind.COLON, pos - 1, pos);
Expand All @@ -835,6 +854,15 @@ private void tokenize() {
case '%':
setToken(TokenKind.PERCENT, pos - 1, pos);
break;
case '~':
setToken(TokenKind.TILDE, pos - 1, pos);
break;
case '&':
setToken(TokenKind.AMPERSAND, pos - 1, pos);
break;
case '^':
setToken(TokenKind.CARET, pos - 1, pos);
break;
case '/':
if (lookaheadIs(0, '/') && lookaheadIs(1, '=')) {
setToken(TokenKind.SLASH_SLASH_EQUALS, pos - 1, pos + 2);
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/google/devtools/build/lib/syntax/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ public enum ParsingLevel {
.put(TokenKind.SLASH_EQUALS, TokenKind.SLASH)
.put(TokenKind.SLASH_SLASH_EQUALS, TokenKind.SLASH_SLASH)
.put(TokenKind.PERCENT_EQUALS, TokenKind.PERCENT)
.put(TokenKind.AMPERSAND_EQUALS, TokenKind.AMPERSAND)
.put(TokenKind.CARET_EQUALS, TokenKind.CARET)
.put(TokenKind.PIPE_EQUALS, TokenKind.PIPE)
.put(TokenKind.GREATER_GREATER_EQUALS, TokenKind.GREATER_GREATER)
.put(TokenKind.LESS_LESS_EQUALS, TokenKind.LESS_LESS)
.build();

/**
Expand All @@ -155,6 +160,9 @@ public enum ParsingLevel {
TokenKind.IN,
TokenKind.NOT_IN),
EnumSet.of(TokenKind.PIPE),
EnumSet.of(TokenKind.CARET),
EnumSet.of(TokenKind.AMPERSAND),
EnumSet.of(TokenKind.GREATER_GREATER, TokenKind.LESS_LESS),
EnumSet.of(TokenKind.MINUS, TokenKind.PLUS),
EnumSet.of(TokenKind.SLASH, TokenKind.SLASH_SLASH, TokenKind.STAR, TokenKind.PERCENT));

Expand Down Expand Up @@ -673,6 +681,13 @@ private Expression parsePrimary() {
UnaryOperatorExpression minus = new UnaryOperatorExpression(TokenKind.MINUS, expr);
return setLocation(minus, start, expr);
}
case TILDE:
{
nextToken();
Expression expr = parsePrimaryWithSuffix();
UnaryOperatorExpression tilde = new UnaryOperatorExpression(TokenKind.TILDE, expr);
return setLocation(tilde, start, expr);
}
default:
{
syntaxError("expected expression");
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/google/devtools/build/lib/syntax/TokenKind.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@
*/
public enum TokenKind {

AMPERSAND("&"),
AMPERSAND_EQUALS("&="),
AND("and"),
AS("as"),
ASSERT("assert"),
BREAK("break"),
CARET("^"),
CARET_EQUALS("^="),
CLASS("class"),
COLON(":"),
COMMA(","),
Expand All @@ -42,6 +46,8 @@ public enum TokenKind {
GLOBAL("global"),
GREATER(">"),
GREATER_EQUALS(">="),
GREATER_GREATER(">>"),
GREATER_GREATER_EQUALS(">>="),
IDENTIFIER("identifier"),
IF("if"),
ILLEGAL("illegal character"),
Expand All @@ -55,6 +61,8 @@ public enum TokenKind {
LBRACKET("["),
LESS("<"),
LESS_EQUALS("<="),
LESS_LESS("<<"),
LESS_LESS_EQUALS("<<="),
LOAD("load"),
LPAREN("("),
MINUS("-"),
Expand All @@ -70,6 +78,7 @@ public enum TokenKind {
PERCENT("%"),
PERCENT_EQUALS("%="),
PIPE("|"),
PIPE_EQUALS("|="),
PLUS("+"),
PLUS_EQUALS("+="),
RAISE("raise"),
Expand All @@ -86,6 +95,7 @@ public enum TokenKind {
STAR_EQUALS("*="),
STAR_STAR("**"),
STRING("string literal"),
TILDE("~"),
TRY("try"),
WHILE("while"),
WITH("with"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
/** A UnaryOperatorExpression represents a unary operator expression, 'op x'. */
public final class UnaryOperatorExpression extends Expression {

private final TokenKind op; // NOT or MINUS
private final TokenKind op; // NOT, TILDE or MINUS
private final Expression x;

public UnaryOperatorExpression(TokenKind op, Expression x) {
Expand Down Expand Up @@ -75,6 +75,14 @@ private static Object evaluate(TokenKind op, Object value, Location loc)
// Fails for -MIN_INT.
throw new EvalException(loc, e.getMessage());
}
case TILDE:
Quarz0 marked this conversation as resolved.
Show resolved Hide resolved
if (!(value instanceof Integer)) {
throw new EvalException(
loc,
String.format(
"unsupported operand type for ~: '%s'", EvalUtils.getDataTypeName(value)));
}
Quarz0 marked this conversation as resolved.
Show resolved Hide resolved
return ~((Integer) value);

default:
Quarz0 marked this conversation as resolved.
Show resolved Hide resolved
throw new AssertionError("Unsupported unary operator: " + op);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,31 @@ public void testFloorDivision() throws Exception {
.testIfExactError("integer division by zero", "5 // 0");
}

@Test
public void testBitwiseOperations() throws Exception {
Quarz0 marked this conversation as resolved.
Show resolved Hide resolved
newTest()
.testStatement("-6 ^ 0", -6)
.testStatement("6 ^ 6", 0)
.testStatement("1 ^ 6", 7)
.testStatement("7 & 0", 0)
.testStatement("7 & 7", 7)
.testStatement("7 & 2", 2)
.testStatement("7 | 0", 7)
.testStatement("7 | 7", 7)
.testStatement("5 | 2", 7)
.testStatement("2 >> 1", 1)
.testStatement("7 >> 1", 3)
.testStatement("7 >> 0", 7)
.testStatement("0 >> 0", 0)
.testStatement("2 << 1", 4)
.testStatement("7 << 1", 14)
.testStatement("7 << 0", 7)
.testStatement("2147483647 << 2147483647", -2147483648)
.testStatement("~6", -7)
.testStatement("~0", -1)
.testStatement("~2147483647", -2147483648);
}

@Test
public void testCheckedArithmetic() throws Exception {
new SkylarkTest()
Expand All @@ -230,7 +255,9 @@ public void testOperatorPrecedence() throws Exception {
newTest()
.testStatement("2 + 3 * 4", 14)
.testStatement("2 + 3 // 4", 2)
.testStatement("2 * 3 + 4 // -2", 4);
.testStatement("2 * 3 + 4 // -2", 4)
.testStatement("8 | 3 ^ 4 & -2", 15)
.testStatement("~8 >> 1 | 3 ^ 4 & -2 << 2 * 3 + 4 // -2", -5);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,10 @@ public void testAugmentedAssign() throws Exception {
assertThat(parseFile("x *= 1").toString()).isEqualTo("[x *= 1\n]");
assertThat(parseFile("x /= 1").toString()).isEqualTo("[x /= 1\n]");
assertThat(parseFile("x %= 1").toString()).isEqualTo("[x %= 1\n]");
assertThat(parseFile("x |= 1").toString()).isEqualTo("[x |= 1\n]");
assertThat(parseFile("x &= 1").toString()).isEqualTo("[x &= 1\n]");
assertThat(parseFile("x <<= 1").toString()).isEqualTo("[x <<= 1\n]");
assertThat(parseFile("x >>= 1").toString()).isEqualTo("[x >>= 1\n]");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1508,7 +1508,7 @@ public void testUnionSet() throws Exception {
new SkylarkTest("--incompatible_depset_union=false")
.testStatement("str(depset([1, 3]) | depset([1, 2]))", "depset([1, 2, 3])")
.testStatement("str(depset([1, 2]) | [1, 3])", "depset([1, 2, 3])")
.testIfExactError("unsupported operand type(s) for |: 'int' and 'int'", "2 | 4");
.testIfExactError("unsupported operand type(s) for |: 'int' and 'bool'", "2 | False");
Quarz0 marked this conversation as resolved.
Show resolved Hide resolved
}

@Test
Expand Down
31 changes: 31 additions & 0 deletions src/test/starlark/testdata/int.sky
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,34 @@ compound()
1 // 0 ### integer division by zero
---
1 % 0 ### integer modulo by zero
---

# bitwise

def f():
x = 2
x &= 1
assert_eq(x, 0)
x = 0
x |= 2
assert_eq(x, 2)
x ^= 3
assert_eq(x, 1)
x <<= 2
assert_eq(x, 4)
x >>=2
assert_eq(x, 1)

f()
---
assert_eq(1|2, 3)
assert_eq(3|6, 7)
assert_eq((1|2) & (2|4), 2)
assert_eq(1 ^ 2, 3)
assert_eq(2 ^ 2, 0)
assert_eq(1 | 0 ^ 1, 1) # check | and ^ operators precedence
assert_eq(~1, -2)
assert_eq(~-2, 1)
assert_eq(~0, -1)
assert_eq(1 << 2, 4)
assert_eq(2 >> 1, 1)