Skip to content

Commit 011ba76

Browse files
authored
Add the bitwise operators (#35)
1 parent a2b998d commit 011ba76

File tree

7 files changed

+95
-23
lines changed

7 files changed

+95
-23
lines changed

src/main/java/org/meteordev/starscript/Instruction.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ public enum Instruction {
1414
Modulo,
1515
Power,
1616

17+
BitwiseAnd,
18+
BitwiseOr,
19+
BitwiseXor,
20+
BitwiseNot,
21+
LeftShift,
22+
RightShift,
23+
UnsignedRightShift,
24+
1725
AddConstant,
1826

1927
Pop,

src/main/java/org/meteordev/starscript/Starscript.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,26 @@ public Section run(Script script, StringBuilder sb) {
5050
case Modulo: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.number(a.getNumber() % b.getNumber())); else error("Can only modulo 2 numbers."); break; }
5151
case Power: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.number(Math.pow(a.getNumber(), b.getNumber()))); else error("Can only power 2 numbers."); break; }
5252

53+
case BitwiseAnd: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.number((long) a.getNumber() & (long) b.getNumber())); else error("This operation requires 2 numbers."); break; }
54+
case BitwiseOr: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.number((long) a.getNumber() | (long) b.getNumber())); else error("This operation requires 2 numbers."); break; }
55+
case BitwiseXor: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.number((long) a.getNumber() ^ (long) b.getNumber())); else error("This operation requires 2 numbers."); break; }
56+
case LeftShift: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.number((long) a.getNumber() << (long) b.getNumber())); else error("This operation requires 2 numbers."); break; }
57+
case RightShift: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.number((long) a.getNumber() >> (long) b.getNumber())); else error("This operation requires 2 numbers."); break; }
58+
case UnsignedRightShift: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.number((long) a.getNumber() >>> (long) b.getNumber())); else error("This operation requires 2 numbers."); break; }
59+
5360
case AddConstant: { Value b = script.constants.get(script.code[ip++] & 0xFF); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.number(a.getNumber() + b.getNumber())); else if (a.isString()) push(Value.string(a.getString() + b.toString())); else error("Can only add 2 numbers or 1 string and other value."); break; }
5461

5562
case Pop: pop(); break;
5663
case Not: push(Value.bool(!pop().isTruthy())); break;
5764
case Negate: { Value a = pop(); if (a.isNumber()) push(Value.number(-a.getNumber())); else error("This operation requires a number."); break; }
65+
case BitwiseNot: { Value a = pop(); if (a.isNumber()) push(Value.number(~((long) a.getNumber()))); else error("This operation requires a number."); break; }
5866

5967
case Equals: push(Value.bool(pop().equals(pop()))); break;
6068
case NotEquals: push(Value.bool(!pop().equals(pop()))); break;
61-
case Greater: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.bool(a.getNumber() > b.getNumber())); else error("This operation requires 2 number."); break; }
62-
case GreaterEqual: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.bool(a.getNumber() >= b.getNumber())); else error("This operation requires 2 number."); break; }
63-
case Less: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.bool(a.getNumber() < b.getNumber())); else error("This operation requires 2 number."); break; }
64-
case LessEqual: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.bool(a.getNumber() <= b.getNumber())); else error("This operation requires 2 number."); break; }
69+
case Greater: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.bool(a.getNumber() > b.getNumber())); else error("This operation requires 2 numbers."); break; }
70+
case GreaterEqual: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.bool(a.getNumber() >= b.getNumber())); else error("This operation requires 2 numbers."); break; }
71+
case Less: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.bool(a.getNumber() < b.getNumber())); else error("This operation requires 2 numbers."); break; }
72+
case LessEqual: { Value b = pop(); Value a = pop(); if (a.isNumber() && b.isNumber()) push(Value.bool(a.getNumber() <= b.getNumber())); else error("This operation requires 2 numbers."); break; }
6573

6674
case Variable: { String name = script.constants.get(script.code[ip++] & 0xFF).getString(); Supplier<Value> s = globals.getRaw(name); push(s != null ? s.get() : Value.null_()); break; }
6775
case Get: { String name = script.constants.get(script.code[ip++] & 0xFF).getString(); Value v = pop(); if (!v.isMap()) { push(Value.null_()); break; } Supplier<Value> s = v.getMap().getRaw(name); push(s != null ? s.get() : Value.null_()); break; }

src/main/java/org/meteordev/starscript/compiler/Compiler.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,26 @@ public void visitBinary(Expr.Binary expr) {
8888
else compile(expr.getRight());
8989

9090
switch (expr.op) {
91-
case Plus: script.write(Instruction.Add); break;
92-
case Minus: script.write(Instruction.Subtract); break;
93-
case Star: script.write(Instruction.Multiply); break;
94-
case Slash: script.write(Instruction.Divide); break;
95-
case Percentage: script.write(Instruction.Modulo); break;
96-
case UpArrow: script.write(Instruction.Power); break;
97-
98-
case EqualEqual: script.write(Instruction.Equals); break;
99-
case BangEqual: script.write(Instruction.NotEquals); break;
100-
case Greater: script.write(Instruction.Greater); break;
101-
case GreaterEqual: script.write(Instruction.GreaterEqual); break;
102-
case Less: script.write(Instruction.Less); break;
103-
case LessEqual: script.write(Instruction.LessEqual); break;
91+
case Plus: script.write(Instruction.Add); break;
92+
case Minus: script.write(Instruction.Subtract); break;
93+
case Star: script.write(Instruction.Multiply); break;
94+
case Slash: script.write(Instruction.Divide); break;
95+
case Percentage: script.write(Instruction.Modulo); break;
96+
case UpArrow: script.write(Instruction.Power); break;
97+
98+
case EqualEqual: script.write(Instruction.Equals); break;
99+
case BangEqual: script.write(Instruction.NotEquals); break;
100+
case Greater: script.write(Instruction.Greater); break;
101+
case GreaterEqual: script.write(Instruction.GreaterEqual); break;
102+
case Less: script.write(Instruction.Less); break;
103+
case LessEqual: script.write(Instruction.LessEqual); break;
104+
105+
case Ampersand: script.write(Instruction.BitwiseAnd); break;
106+
case VBar: script.write(Instruction.BitwiseOr); break;
107+
case DoubleUpArrow: script.write(Instruction.BitwiseXor); break;
108+
case DoubleLess: script.write(Instruction.LeftShift); break;
109+
case DoubleGreater: script.write(Instruction.RightShift); break;
110+
case TripleGreater: script.write(Instruction.UnsignedRightShift); break;
104111
}
105112
}
106113

@@ -110,6 +117,7 @@ public void visitUnary(Expr.Unary expr) {
110117

111118
if (expr.op == Token.Bang) script.write(Instruction.Not);
112119
else if (expr.op == Token.Minus) script.write(Instruction.Negate);
120+
else if (expr.op == Token.Tilde) script.write(Instruction.BitwiseNot);
113121
}
114122

115123
@Override

src/main/java/org/meteordev/starscript/compiler/Lexer.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,33 @@ public void next() {
4747

4848
case '=': if (match('=')) createToken(Token.EqualEqual); else unexpected(); break;
4949
case '!': createToken(match('=') ? Token.BangEqual : Token.Bang); break;
50-
case '>': createToken(match('=') ? Token.GreaterEqual : Token.Greater); break;
51-
case '<': createToken(match('=') ? Token.LessEqual : Token.Less); break;
50+
case '>':
51+
if (match('=')) {
52+
createToken(Token.GreaterEqual);
53+
break;
54+
}
55+
if (match('>')) {
56+
createToken(match('>') ? Token.TripleGreater : Token.DoubleGreater);
57+
break;
58+
}
59+
createToken(Token.Greater); break;
60+
case '<':
61+
if (match('=')) {
62+
createToken(Token.LessEqual);
63+
break;
64+
}
65+
if (match('<')) {
66+
createToken(Token.DoubleLess);
67+
break;
68+
}
69+
createToken(Token.Less); break;
5270

5371
case '+': createToken(Token.Plus); break;
5472
case '-': createToken(Token.Minus); break;
5573
case '*': createToken(Token.Star); break;
5674
case '/': createToken(Token.Slash); break;
5775
case '%': createToken(Token.Percentage); break;
58-
case '^': createToken(Token.UpArrow); break;
76+
case '^': createToken(match('^') ? Token.DoubleUpArrow : Token.UpArrow); break;
5977

6078
case '.': createToken(Token.Dot); break;
6179
case ',': createToken(Token.Comma); break;
@@ -71,6 +89,10 @@ public void next() {
7189
createToken(Token.Section, source.substring(start + 1, current));
7290
break;
7391

92+
case '&': createToken(Token.Ampersand); break;
93+
case '|': createToken(Token.VBar); break;
94+
case '~': createToken(Token.Tilde); break;
95+
7496
default: unexpected();
7597
}
7698
}

src/main/java/org/meteordev/starscript/compiler/Parser.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,22 @@ private Expr term() {
148148

149149
private Expr factor() {
150150
int start = previous.start;
151-
Expr expr = unary();
151+
Expr expr = bitwise();
152152

153153
while (match(Token.Star, Token.Slash, Token.Percentage, Token.UpArrow)) {
154+
Token op = previous.token;
155+
Expr right = bitwise();
156+
expr = new Expr.Binary(start, previous.end, expr, op, right);
157+
}
158+
159+
return expr;
160+
}
161+
162+
private Expr bitwise() {
163+
int start = previous.start;
164+
Expr expr = unary();
165+
166+
while (match(Token.Ampersand, Token.VBar, Token.DoubleUpArrow, Token.DoubleLess, Token.DoubleGreater, Token.TripleGreater)) {
154167
Token op = previous.token;
155168
Expr right = unary();
156169
expr = new Expr.Binary(start, previous.end, expr, op, right);
@@ -160,7 +173,7 @@ private Expr factor() {
160173
}
161174

162175
private Expr unary() {
163-
if (match(Token.Bang, Token.Minus)) {
176+
if (match(Token.Bang, Token.Minus, Token.Tilde)) {
164177
int start = previous.start;
165178

166179
Token op = previous.token;
@@ -353,7 +366,7 @@ public static class Result {
353366

354367
/** Helper method that returns true if there was 1 or more errors. */
355368
public boolean hasErrors() {
356-
return errors.size() > 0;
369+
return !errors.isEmpty();
357370
}
358371

359372
public void accept(Expr.Visitor visitor) {

src/main/java/org/meteordev/starscript/compiler/Token.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,11 @@ public enum Token {
2323

2424
Section,
2525

26+
Ampersand, VBar,
27+
Tilde,
28+
DoubleUpArrow,
29+
DoubleGreater, TripleGreater,
30+
DoubleLess,
31+
2632
Error, EOF
2733
}

src/main/java/org/meteordev/starscript/utils/SemanticTokenProvider.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ public static void get(String source, List<SemanticToken> tokens) {
4444
case Bang:
4545
case QuestionMark:
4646
case Colon:
47+
case Ampersand:
48+
case VBar:
49+
case DoubleUpArrow:
50+
case DoubleLess:
51+
case DoubleGreater:
52+
case TripleGreater:
53+
case Tilde:
4754
tokens.add(new SemanticToken(SemanticTokenType.Operator, lexer.start, lexer.current));
4855
break;
4956

0 commit comments

Comments
 (0)