bit ops
This commit is contained in:
@@ -192,6 +192,20 @@ END;
|
|||||||
print("band: {}\n", 0xFF & 0x0F);
|
print("band: {}\n", 0xFF & 0x0F);
|
||||||
print("bor: {}\n", 1 | 2 | 4);
|
print("bor: {}\n", 1 | 2 | 4);
|
||||||
|
|
||||||
|
// Bitwise XOR
|
||||||
|
print("bxor: {}\n", 0xFF ^ 0x0F);
|
||||||
|
print("bxor2: {}\n", 6 ^ 3);
|
||||||
|
|
||||||
|
// Bitwise NOT
|
||||||
|
print("bnot: {}\n", ~0);
|
||||||
|
print("bnot2: {}\n", ~1);
|
||||||
|
|
||||||
|
// Shifts
|
||||||
|
print("shl: {}\n", 1 << 4);
|
||||||
|
print("shr: {}\n", 256 >> 4);
|
||||||
|
print("shl2: {}\n", 3 << 3);
|
||||||
|
print("shr2: {}\n", 255 >> 1);
|
||||||
|
|
||||||
// Bitwise on variables
|
// Bitwise on variables
|
||||||
bv1 := 0xFF;
|
bv1 := 0xFF;
|
||||||
bv2 := 0x0F;
|
bv2 := 0x0F;
|
||||||
@@ -199,6 +213,27 @@ END;
|
|||||||
bv3 := 1;
|
bv3 := 1;
|
||||||
bv4 := 6;
|
bv4 := 6;
|
||||||
print("bor-var: {}\n", bv3 | bv4);
|
print("bor-var: {}\n", bv3 | bv4);
|
||||||
|
print("bxor-var: {}\n", bv1 ^ bv2);
|
||||||
|
print("shl-var: {}\n", bv3 << 4);
|
||||||
|
print("shr-var: {}\n", bv1 >> 4);
|
||||||
|
print("bnot-var: {}\n", ~bv2);
|
||||||
|
|
||||||
|
// Bitwise compound assignment
|
||||||
|
bca := 0xFF;
|
||||||
|
bca &= 0x0F;
|
||||||
|
print("and-assign: {}\n", bca);
|
||||||
|
bco := 0x0F;
|
||||||
|
bco |= 0xF0;
|
||||||
|
print("or-assign: {}\n", bco);
|
||||||
|
bcx := 0xFF;
|
||||||
|
bcx ^= 0x0F;
|
||||||
|
print("xor-assign: {}\n", bcx);
|
||||||
|
bcs := 1;
|
||||||
|
bcs <<= 8;
|
||||||
|
print("shl-assign: {}\n", bcs);
|
||||||
|
bcr := 256;
|
||||||
|
bcr >>= 4;
|
||||||
|
print("shr-assign: {}\n", bcr);
|
||||||
|
|
||||||
// Modulo on variables
|
// Modulo on variables
|
||||||
mv1 := 17;
|
mv1 := 17;
|
||||||
|
|||||||
42
specs.md
42
specs.md
@@ -65,6 +65,10 @@ GLSL;
|
|||||||
| `>=` | greater or equal |
|
| `>=` | greater or equal |
|
||||||
| `&` | bitwise AND |
|
| `&` | bitwise AND |
|
||||||
| `\|` | bitwise OR |
|
| `\|` | bitwise OR |
|
||||||
|
| `^` | bitwise XOR |
|
||||||
|
| `~` | bitwise NOT (unary) |
|
||||||
|
| `<<` | left shift |
|
||||||
|
| `>>` | right shift (arithmetic for signed, logical for unsigned) |
|
||||||
| `and` | logical AND (short-circuit) |
|
| `and` | logical AND (short-circuit) |
|
||||||
| `or` | logical OR (short-circuit) |
|
| `or` | logical OR (short-circuit) |
|
||||||
| `in` | membership test (tuples) |
|
| `in` | membership test (tuples) |
|
||||||
@@ -72,6 +76,11 @@ GLSL;
|
|||||||
| `-=` | sub-assign |
|
| `-=` | sub-assign |
|
||||||
| `*=` | mul-assign |
|
| `*=` | mul-assign |
|
||||||
| `/=` | div-assign |
|
| `/=` | div-assign |
|
||||||
|
| `&=` | bitwise AND assign |
|
||||||
|
| `\|=` | bitwise OR assign |
|
||||||
|
| `^=` | bitwise XOR assign |
|
||||||
|
| `<<=` | left shift assign |
|
||||||
|
| `>>=` | right shift assign |
|
||||||
|
|
||||||
### Delimiters and Punctuation
|
### Delimiters and Punctuation
|
||||||
|
|
||||||
@@ -768,11 +777,27 @@ Restrictions:
|
|||||||
|
|
||||||
### Bitwise Operators
|
### Bitwise Operators
|
||||||
|
|
||||||
`&` (bitwise AND) and `|` (bitwise OR) work on all integer types, not just flags. They sit at precedence level 3, between comparisons and logical operators.
|
All bitwise operators work on integer types. `>>` is arithmetic (sign-extending) for signed types and logical (zero-filling) for unsigned types.
|
||||||
|
|
||||||
```sx
|
```sx
|
||||||
x := 0xFF & 0x0F; // 15
|
x := 0xFF & 0x0F; // 15 — AND
|
||||||
y := 1 | 2 | 4; // 7
|
y := 1 | 2 | 4; // 7 — OR
|
||||||
|
z := 0xFF ^ 0x0F; // 240 — XOR
|
||||||
|
w := ~0; // -1 — NOT
|
||||||
|
a := 1 << 4; // 16 — left shift
|
||||||
|
b := 256 >> 4; // 16 — right shift
|
||||||
|
```
|
||||||
|
|
||||||
|
Compound assignment forms: `&=`, `|=`, `^=`, `<<=`, `>>=`.
|
||||||
|
|
||||||
|
```sx
|
||||||
|
x := 0xFF;
|
||||||
|
x &= 0x0F; // 15
|
||||||
|
x |= 0xF0; // 255
|
||||||
|
x ^= 0x0F; // 240
|
||||||
|
y := 1;
|
||||||
|
y <<= 8; // 256
|
||||||
|
y >>= 4; // 16
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -785,10 +810,13 @@ Everything in `sx` is expression-oriented where possible.
|
|||||||
|
|
||||||
| Prec | Operators | Notes |
|
| Prec | Operators | Notes |
|
||||||
|------|-----------|-------|
|
|------|-----------|-------|
|
||||||
| 6 (highest) | `*`, `/`, `%` | multiplication, division, modulo |
|
| 9 (highest) | `*`, `/`, `%` | multiplication, division, modulo |
|
||||||
| 5 | `+`, `-` | addition, subtraction |
|
| 8 | `+`, `-` | addition, subtraction |
|
||||||
| 4 | `<`, `<=`, `>`, `>=`, `==`, `!=` | comparisons (chainable) |
|
| 7 | `<<`, `>>` | shifts |
|
||||||
| 3 | `&`, `\|` | bitwise AND, bitwise OR |
|
| 6 | `<`, `<=`, `>`, `>=`, `==`, `!=` | comparisons (chainable) |
|
||||||
|
| 5 | `&` | bitwise AND |
|
||||||
|
| 4 | `^` | bitwise XOR |
|
||||||
|
| 3 | `\|` | bitwise OR |
|
||||||
| 2 | `and` | logical AND (short-circuit) |
|
| 2 | `and` | logical AND (short-circuit) |
|
||||||
| 1 (lowest) | `or` | logical OR (short-circuit) |
|
| 1 (lowest) | `or` | logical OR (short-circuit) |
|
||||||
|
|
||||||
|
|||||||
@@ -157,6 +157,9 @@ pub const BinaryOp = struct {
|
|||||||
or_op,
|
or_op,
|
||||||
bit_and,
|
bit_and,
|
||||||
bit_or,
|
bit_or,
|
||||||
|
bit_xor,
|
||||||
|
shl,
|
||||||
|
shr,
|
||||||
in_op,
|
in_op,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -173,6 +176,7 @@ pub const UnaryOp = struct {
|
|||||||
pub const Op = enum {
|
pub const Op = enum {
|
||||||
negate,
|
negate,
|
||||||
not,
|
not,
|
||||||
|
bit_not,
|
||||||
xx,
|
xx,
|
||||||
address_of,
|
address_of,
|
||||||
};
|
};
|
||||||
@@ -231,6 +235,11 @@ pub const Assignment = struct {
|
|||||||
mul_assign,
|
mul_assign,
|
||||||
div_assign,
|
div_assign,
|
||||||
mod_assign,
|
mod_assign,
|
||||||
|
and_assign,
|
||||||
|
or_assign,
|
||||||
|
xor_assign,
|
||||||
|
shl_assign,
|
||||||
|
shr_assign,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3108,6 +3108,11 @@ pub const CodeGen = struct {
|
|||||||
.mul_assign => if (ty.isFloat()) c.LLVMBuildFMul(self.builder, cur, rhs, "multmp") else c.LLVMBuildMul(self.builder, cur, rhs, "multmp"),
|
.mul_assign => if (ty.isFloat()) c.LLVMBuildFMul(self.builder, cur, rhs, "multmp") else c.LLVMBuildMul(self.builder, cur, rhs, "multmp"),
|
||||||
.div_assign => if (ty.isFloat()) c.LLVMBuildFDiv(self.builder, cur, rhs, "divtmp") else if (ty.isUnsigned()) c.LLVMBuildUDiv(self.builder, cur, rhs, "divtmp") else c.LLVMBuildSDiv(self.builder, cur, rhs, "divtmp"),
|
.div_assign => if (ty.isFloat()) c.LLVMBuildFDiv(self.builder, cur, rhs, "divtmp") else if (ty.isUnsigned()) c.LLVMBuildUDiv(self.builder, cur, rhs, "divtmp") else c.LLVMBuildSDiv(self.builder, cur, rhs, "divtmp"),
|
||||||
.mod_assign => if (ty.isFloat()) c.LLVMBuildFRem(self.builder, cur, rhs, "modtmp") else if (ty.isUnsigned()) c.LLVMBuildURem(self.builder, cur, rhs, "modtmp") else c.LLVMBuildSRem(self.builder, cur, rhs, "modtmp"),
|
.mod_assign => if (ty.isFloat()) c.LLVMBuildFRem(self.builder, cur, rhs, "modtmp") else if (ty.isUnsigned()) c.LLVMBuildURem(self.builder, cur, rhs, "modtmp") else c.LLVMBuildSRem(self.builder, cur, rhs, "modtmp"),
|
||||||
|
.and_assign => c.LLVMBuildAnd(self.builder, cur, rhs, "bandtmp"),
|
||||||
|
.or_assign => c.LLVMBuildOr(self.builder, cur, rhs, "bortmp"),
|
||||||
|
.xor_assign => c.LLVMBuildXor(self.builder, cur, rhs, "bxortmp"),
|
||||||
|
.shl_assign => c.LLVMBuildShl(self.builder, cur, rhs, "shltmp"),
|
||||||
|
.shr_assign => if (ty.isUnsigned()) c.LLVMBuildLShr(self.builder, cur, rhs, "shrtmp") else c.LLVMBuildAShr(self.builder, cur, rhs, "shrtmp"),
|
||||||
.assign => unreachable,
|
.assign => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -3560,6 +3565,7 @@ pub const CodeGen = struct {
|
|||||||
c.LLVMBuildNeg(self.builder, operand, "negtmp");
|
c.LLVMBuildNeg(self.builder, operand, "negtmp");
|
||||||
},
|
},
|
||||||
.not => c.LLVMBuildNot(self.builder, operand, "nottmp"),
|
.not => c.LLVMBuildNot(self.builder, operand, "nottmp"),
|
||||||
|
.bit_not => c.LLVMBuildNot(self.builder, operand, "bnottmp"),
|
||||||
.xx, .address_of => unreachable,
|
.xx, .address_of => unreachable,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -4460,15 +4466,17 @@ pub const CodeGen = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bitwise op on enum type: recursively generate both sides with enum context
|
// Bitwise op on enum type: recursively generate both sides with enum context
|
||||||
if (node.data == .binary_op and (node.data.binary_op.op == .bit_or or node.data.binary_op.op == .bit_and) and target_ty.isEnum()) {
|
if (node.data == .binary_op and (node.data.binary_op.op == .bit_or or node.data.binary_op.op == .bit_and or node.data.binary_op.op == .bit_xor) and target_ty.isEnum()) {
|
||||||
const binop = node.data.binary_op;
|
const binop = node.data.binary_op;
|
||||||
const lhs = try self.genExprAsType(binop.lhs, target_ty);
|
const lhs = try self.genExprAsType(binop.lhs, target_ty);
|
||||||
const rhs = try self.genExprAsType(binop.rhs, target_ty);
|
const rhs = try self.genExprAsType(binop.rhs, target_ty);
|
||||||
const b = self.builder;
|
const b = self.builder;
|
||||||
return if (binop.op == .bit_or)
|
return switch (binop.op) {
|
||||||
c.LLVMBuildOr(b, lhs, rhs, "bortmp")
|
.bit_or => c.LLVMBuildOr(b, lhs, rhs, "bortmp"),
|
||||||
else
|
.bit_and => c.LLVMBuildAnd(b, lhs, rhs, "bandtmp"),
|
||||||
c.LLVMBuildAnd(b, lhs, rhs, "bandtmp");
|
.bit_xor => c.LLVMBuildXor(b, lhs, rhs, "bxortmp"),
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enum/union literal assigned to union type: construct tagged enum
|
// Enum/union literal assigned to union type: construct tagged enum
|
||||||
@@ -5714,6 +5722,9 @@ pub const CodeGen = struct {
|
|||||||
.gte => if (is_float) c.LLVMBuildFCmp(b, c.LLVMRealOGE, lhs, rhs, "getmp") else if (is_unsigned) c.LLVMBuildICmp(b, c.LLVMIntUGE, lhs, rhs, "getmp") else c.LLVMBuildICmp(b, c.LLVMIntSGE, lhs, rhs, "getmp"),
|
.gte => if (is_float) c.LLVMBuildFCmp(b, c.LLVMRealOGE, lhs, rhs, "getmp") else if (is_unsigned) c.LLVMBuildICmp(b, c.LLVMIntUGE, lhs, rhs, "getmp") else c.LLVMBuildICmp(b, c.LLVMIntSGE, lhs, rhs, "getmp"),
|
||||||
.bit_and => c.LLVMBuildAnd(b, lhs, rhs, "bandtmp"),
|
.bit_and => c.LLVMBuildAnd(b, lhs, rhs, "bandtmp"),
|
||||||
.bit_or => c.LLVMBuildOr(b, lhs, rhs, "bortmp"),
|
.bit_or => c.LLVMBuildOr(b, lhs, rhs, "bortmp"),
|
||||||
|
.bit_xor => c.LLVMBuildXor(b, lhs, rhs, "bxortmp"),
|
||||||
|
.shl => c.LLVMBuildShl(b, lhs, rhs, "shltmp"),
|
||||||
|
.shr => if (is_unsigned) c.LLVMBuildLShr(b, lhs, rhs, "shrtmp") else c.LLVMBuildAShr(b, lhs, rhs, "shrtmp"),
|
||||||
.and_op, .or_op, .in_op => unreachable,
|
.and_op, .or_op, .in_op => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,6 +217,10 @@ pub const Instruction = union(enum) {
|
|||||||
// Bitwise
|
// Bitwise
|
||||||
bit_and,
|
bit_and,
|
||||||
bit_or,
|
bit_or,
|
||||||
|
bit_xor,
|
||||||
|
bit_not,
|
||||||
|
shl,
|
||||||
|
shr,
|
||||||
|
|
||||||
// Logic
|
// Logic
|
||||||
not,
|
not,
|
||||||
@@ -608,6 +612,9 @@ pub const Compiler = struct {
|
|||||||
.gte => .gte,
|
.gte => .gte,
|
||||||
.bit_and => .bit_and,
|
.bit_and => .bit_and,
|
||||||
.bit_or => .bit_or,
|
.bit_or => .bit_or,
|
||||||
|
.bit_xor => .bit_xor,
|
||||||
|
.shl => .shl,
|
||||||
|
.shr => .shr,
|
||||||
.and_op, .or_op, .in_op => unreachable,
|
.and_op, .or_op, .in_op => unreachable,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -667,6 +674,7 @@ pub const Compiler = struct {
|
|||||||
switch (unop.op) {
|
switch (unop.op) {
|
||||||
.negate => try self.emit(.negate),
|
.negate => try self.emit(.negate),
|
||||||
.not => try self.emit(.not),
|
.not => try self.emit(.not),
|
||||||
|
.bit_not => try self.emit(.bit_not),
|
||||||
.xx => try self.emit(.unwrap_any), // autocast — unwraps any_val to inner value
|
.xx => try self.emit(.unwrap_any), // autocast — unwraps any_val to inner value
|
||||||
.address_of => unreachable, // handled above
|
.address_of => unreachable, // handled above
|
||||||
}
|
}
|
||||||
@@ -735,6 +743,11 @@ pub const Compiler = struct {
|
|||||||
.mul_assign => .mul,
|
.mul_assign => .mul,
|
||||||
.div_assign => .div,
|
.div_assign => .div,
|
||||||
.mod_assign => .mod,
|
.mod_assign => .mod,
|
||||||
|
.and_assign => .bit_and,
|
||||||
|
.or_assign => .bit_or,
|
||||||
|
.xor_assign => .bit_xor,
|
||||||
|
.shl_assign => .shl,
|
||||||
|
.shr_assign => .shr,
|
||||||
.assign => unreachable,
|
.assign => unreachable,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -1248,6 +1261,35 @@ pub const VM = struct {
|
|||||||
try self.push(.{ .int_val = a.int_val | b.int_val });
|
try self.push(.{ .int_val = a.int_val | b.int_val });
|
||||||
} else return error.TypeError;
|
} else return error.TypeError;
|
||||||
},
|
},
|
||||||
|
.bit_xor => {
|
||||||
|
const b = try self.pop();
|
||||||
|
const a = try self.pop();
|
||||||
|
if (a == .int_val and b == .int_val) {
|
||||||
|
try self.push(.{ .int_val = a.int_val ^ b.int_val });
|
||||||
|
} else return error.TypeError;
|
||||||
|
},
|
||||||
|
.shl => {
|
||||||
|
const b = try self.pop();
|
||||||
|
const a = try self.pop();
|
||||||
|
if (a == .int_val and b == .int_val) {
|
||||||
|
const shift: u6 = @intCast(@as(u64, @bitCast(b.int_val)) & 63);
|
||||||
|
try self.push(.{ .int_val = a.int_val << shift });
|
||||||
|
} else return error.TypeError;
|
||||||
|
},
|
||||||
|
.shr => {
|
||||||
|
const b = try self.pop();
|
||||||
|
const a = try self.pop();
|
||||||
|
if (a == .int_val and b == .int_val) {
|
||||||
|
const shift: u6 = @intCast(@as(u64, @bitCast(b.int_val)) & 63);
|
||||||
|
try self.push(.{ .int_val = a.int_val >> shift });
|
||||||
|
} else return error.TypeError;
|
||||||
|
},
|
||||||
|
.bit_not => {
|
||||||
|
const v = try self.pop();
|
||||||
|
if (v == .int_val) {
|
||||||
|
try self.push(.{ .int_val = ~v.int_val });
|
||||||
|
} else return error.TypeError;
|
||||||
|
},
|
||||||
.negate => {
|
.negate => {
|
||||||
const v = try self.pop();
|
const v = try self.pop();
|
||||||
try self.push(switch (v) {
|
try self.push(switch (v) {
|
||||||
|
|||||||
@@ -173,9 +173,29 @@ pub const Lexer = struct {
|
|||||||
}
|
}
|
||||||
return self.makeToken(.percent, start, self.index);
|
return self.makeToken(.percent, start, self.index);
|
||||||
},
|
},
|
||||||
'&' => return self.makeToken(.ampersand, start, self.index),
|
'&' => {
|
||||||
|
if (self.peek() == '=') {
|
||||||
|
self.index += 1;
|
||||||
|
return self.makeToken(.ampersand_equal, start, self.index);
|
||||||
|
}
|
||||||
|
return self.makeToken(.ampersand, start, self.index);
|
||||||
|
},
|
||||||
'@' => return self.makeToken(.at, start, self.index),
|
'@' => return self.makeToken(.at, start, self.index),
|
||||||
'|' => return self.makeToken(.pipe, start, self.index),
|
'|' => {
|
||||||
|
if (self.peek() == '=') {
|
||||||
|
self.index += 1;
|
||||||
|
return self.makeToken(.pipe_equal, start, self.index);
|
||||||
|
}
|
||||||
|
return self.makeToken(.pipe, start, self.index);
|
||||||
|
},
|
||||||
|
'^' => {
|
||||||
|
if (self.peek() == '=') {
|
||||||
|
self.index += 1;
|
||||||
|
return self.makeToken(.caret_equal, start, self.index);
|
||||||
|
}
|
||||||
|
return self.makeToken(.caret, start, self.index);
|
||||||
|
},
|
||||||
|
'~' => return self.makeToken(.tilde, start, self.index),
|
||||||
'!' => {
|
'!' => {
|
||||||
if (self.peek() == '=') {
|
if (self.peek() == '=') {
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
@@ -184,6 +204,14 @@ pub const Lexer = struct {
|
|||||||
return self.makeToken(.bang, start, self.index);
|
return self.makeToken(.bang, start, self.index);
|
||||||
},
|
},
|
||||||
'<' => {
|
'<' => {
|
||||||
|
if (self.peek() == '<') {
|
||||||
|
self.index += 1;
|
||||||
|
if (self.peek() == '=') {
|
||||||
|
self.index += 1;
|
||||||
|
return self.makeToken(.less_less_equal, start, self.index);
|
||||||
|
}
|
||||||
|
return self.makeToken(.less_less, start, self.index);
|
||||||
|
}
|
||||||
if (self.peek() == '=') {
|
if (self.peek() == '=') {
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
return self.makeToken(.less_equal, start, self.index);
|
return self.makeToken(.less_equal, start, self.index);
|
||||||
@@ -191,6 +219,14 @@ pub const Lexer = struct {
|
|||||||
return self.makeToken(.less, start, self.index);
|
return self.makeToken(.less, start, self.index);
|
||||||
},
|
},
|
||||||
'>' => {
|
'>' => {
|
||||||
|
if (self.peek() == '>') {
|
||||||
|
self.index += 1;
|
||||||
|
if (self.peek() == '=') {
|
||||||
|
self.index += 1;
|
||||||
|
return self.makeToken(.greater_greater_equal, start, self.index);
|
||||||
|
}
|
||||||
|
return self.makeToken(.greater_greater, start, self.index);
|
||||||
|
}
|
||||||
if (self.peek() == '=') {
|
if (self.peek() == '=') {
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
return self.makeToken(.greater_equal, start, self.index);
|
return self.makeToken(.greater_equal, start, self.index);
|
||||||
|
|||||||
@@ -1028,8 +1028,17 @@ pub const Server = struct {
|
|||||||
.percent,
|
.percent,
|
||||||
.percent_equal,
|
.percent_equal,
|
||||||
.ampersand,
|
.ampersand,
|
||||||
|
.ampersand_equal,
|
||||||
.at,
|
.at,
|
||||||
.pipe,
|
.pipe,
|
||||||
|
.pipe_equal,
|
||||||
|
.caret,
|
||||||
|
.caret_equal,
|
||||||
|
.tilde,
|
||||||
|
.less_less,
|
||||||
|
.less_less_equal,
|
||||||
|
.greater_greater,
|
||||||
|
.greater_greater_equal,
|
||||||
.arrow,
|
.arrow,
|
||||||
.fat_arrow,
|
.fat_arrow,
|
||||||
.colon_colon,
|
.colon_colon,
|
||||||
|
|||||||
@@ -1025,7 +1025,7 @@ pub const Parser = struct {
|
|||||||
// ---- Expression parsing (Pratt / precedence climbing) ----
|
// ---- Expression parsing (Pratt / precedence climbing) ----
|
||||||
|
|
||||||
pub fn parseExpr(self: *Parser) anyerror!*Node {
|
pub fn parseExpr(self: *Parser) anyerror!*Node {
|
||||||
return self.parseBinary(0);
|
return self.parseBinary(Prec.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseBinary(self: *Parser, min_prec: u8) anyerror!*Node {
|
fn parseBinary(self: *Parser, min_prec: u8) anyerror!*Node {
|
||||||
@@ -1088,6 +1088,12 @@ pub const Parser = struct {
|
|||||||
const operand = try self.parseUnary();
|
const operand = try self.parseUnary();
|
||||||
return try self.createNode(start, .{ .unary_op = .{ .op = .not, .operand = operand } });
|
return try self.createNode(start, .{ .unary_op = .{ .op = .not, .operand = operand } });
|
||||||
}
|
}
|
||||||
|
if (self.current.tag == .tilde) {
|
||||||
|
const start = self.current.loc.start;
|
||||||
|
self.advance();
|
||||||
|
const operand = try self.parseUnary();
|
||||||
|
return try self.createNode(start, .{ .unary_op = .{ .op = .bit_not, .operand = operand } });
|
||||||
|
}
|
||||||
if (self.current.tag == .kw_xx) {
|
if (self.current.tag == .kw_xx) {
|
||||||
const start = self.current.loc.start;
|
const start = self.current.loc.start;
|
||||||
self.advance();
|
self.advance();
|
||||||
@@ -1435,9 +1441,9 @@ pub const Parser = struct {
|
|||||||
const start = self.current.loc.start;
|
const start = self.current.loc.start;
|
||||||
self.advance(); // skip 'if'
|
self.advance(); // skip 'if'
|
||||||
|
|
||||||
// Parse condition at prec 5 (arithmetic+), leaving all comparisons
|
// Parse condition above comparison level, leaving comparisons
|
||||||
// unconsumed for manual handling with match disambiguation.
|
// unconsumed for manual handling with match disambiguation.
|
||||||
var condition = try self.parseBinary(5);
|
var condition = try self.parseBinary(Prec.shift);
|
||||||
|
|
||||||
// Handle comparisons with chain detection and match disambiguation.
|
// Handle comparisons with chain detection and match disambiguation.
|
||||||
// All comparisons (< <= > >= == !=) are at the same precedence.
|
// All comparisons (< <= > >= == !=) are at the same precedence.
|
||||||
@@ -1459,13 +1465,13 @@ pub const Parser = struct {
|
|||||||
// Chain followed by == { is an error — fall through to
|
// Chain followed by == { is an error — fall through to
|
||||||
// regular comparison (will likely fail at parse time)
|
// regular comparison (will likely fail at parse time)
|
||||||
}
|
}
|
||||||
const rhs = try self.parseBinary(5);
|
const rhs = try self.parseBinary(Prec.shift);
|
||||||
try operands.append(self.allocator, rhs);
|
try operands.append(self.allocator, rhs);
|
||||||
try ops.append(self.allocator, .eq);
|
try ops.append(self.allocator, .eq);
|
||||||
} else {
|
} else {
|
||||||
const cmp_op = self.binaryOp() orelse break;
|
const cmp_op = self.binaryOp() orelse break;
|
||||||
self.advance();
|
self.advance();
|
||||||
const rhs = try self.parseBinary(5);
|
const rhs = try self.parseBinary(Prec.shift);
|
||||||
try operands.append(self.allocator, rhs);
|
try operands.append(self.allocator, rhs);
|
||||||
try ops.append(self.allocator, cmp_op);
|
try ops.append(self.allocator, cmp_op);
|
||||||
}
|
}
|
||||||
@@ -1488,7 +1494,7 @@ pub const Parser = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle and/or with proper Pratt precedence
|
// Handle and/or with proper Pratt precedence
|
||||||
condition = try self.parseBinaryRhs(condition, 1);
|
condition = try self.parseBinaryRhs(condition, Prec.logical_or);
|
||||||
|
|
||||||
// Inline form: if cond then expr [else expr]
|
// Inline form: if cond then expr [else expr]
|
||||||
if (self.current.tag == .kw_then) {
|
if (self.current.tag == .kw_then) {
|
||||||
@@ -1771,7 +1777,9 @@ pub const Parser = struct {
|
|||||||
|
|
||||||
fn isAssignOp(self: *const Parser) bool {
|
fn isAssignOp(self: *const Parser) bool {
|
||||||
return switch (self.current.tag) {
|
return switch (self.current.tag) {
|
||||||
.equal, .plus_equal, .minus_equal, .star_equal, .slash_equal, .percent_equal => true,
|
.equal, .plus_equal, .minus_equal, .star_equal, .slash_equal, .percent_equal,
|
||||||
|
.ampersand_equal, .pipe_equal, .caret_equal, .less_less_equal, .greater_greater_equal,
|
||||||
|
=> true,
|
||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1784,6 +1792,11 @@ pub const Parser = struct {
|
|||||||
.star_equal => .mul_assign,
|
.star_equal => .mul_assign,
|
||||||
.slash_equal => .div_assign,
|
.slash_equal => .div_assign,
|
||||||
.percent_equal => .mod_assign,
|
.percent_equal => .mod_assign,
|
||||||
|
.ampersand_equal => .and_assign,
|
||||||
|
.pipe_equal => .or_assign,
|
||||||
|
.caret_equal => .xor_assign,
|
||||||
|
.less_less_equal => .shl_assign,
|
||||||
|
.greater_greater_equal => .shr_assign,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1827,16 +1840,31 @@ pub const Parser = struct {
|
|||||||
} });
|
} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Prec = struct {
|
||||||
|
const none: u8 = 0;
|
||||||
|
const logical_or: u8 = 1; // or
|
||||||
|
const logical_and: u8 = 2; // and
|
||||||
|
const bit_or: u8 = 3; // |
|
||||||
|
const bit_xor: u8 = 4; // ^
|
||||||
|
const bit_and: u8 = 5; // &
|
||||||
|
const comparison: u8 = 6; // == != < <= > >= in
|
||||||
|
const shift: u8 = 7; // << >>
|
||||||
|
const additive: u8 = 8; // + -
|
||||||
|
const multiplicative: u8 = 9; // * / %
|
||||||
|
};
|
||||||
|
|
||||||
fn binaryPrec(self: *const Parser) u8 {
|
fn binaryPrec(self: *const Parser) u8 {
|
||||||
return switch (self.current.tag) {
|
return switch (self.current.tag) {
|
||||||
.kw_or => 1,
|
.kw_or => Prec.logical_or,
|
||||||
.kw_and => 2,
|
.kw_and => Prec.logical_and,
|
||||||
.pipe => 3,
|
.pipe => Prec.bit_or,
|
||||||
.ampersand => 3,
|
.caret => Prec.bit_xor,
|
||||||
.equal_equal, .bang_equal, .less, .less_equal, .greater, .greater_equal, .kw_in => 4,
|
.ampersand => Prec.bit_and,
|
||||||
.plus, .minus => 5,
|
.equal_equal, .bang_equal, .less, .less_equal, .greater, .greater_equal, .kw_in => Prec.comparison,
|
||||||
.star, .slash, .percent => 6,
|
.less_less, .greater_greater => Prec.shift,
|
||||||
else => 0,
|
.plus, .minus => Prec.additive,
|
||||||
|
.star, .slash, .percent => Prec.multiplicative,
|
||||||
|
else => Prec.none,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1845,6 +1873,7 @@ pub const Parser = struct {
|
|||||||
.kw_and => .and_op,
|
.kw_and => .and_op,
|
||||||
.kw_or => .or_op,
|
.kw_or => .or_op,
|
||||||
.pipe => .bit_or,
|
.pipe => .bit_or,
|
||||||
|
.caret => .bit_xor,
|
||||||
.ampersand => .bit_and,
|
.ampersand => .bit_and,
|
||||||
.plus => .add,
|
.plus => .add,
|
||||||
.minus => .sub,
|
.minus => .sub,
|
||||||
@@ -1857,6 +1886,8 @@ pub const Parser = struct {
|
|||||||
.less_equal => .lte,
|
.less_equal => .lte,
|
||||||
.greater => .gt,
|
.greater => .gt,
|
||||||
.greater_equal => .gte,
|
.greater_equal => .gte,
|
||||||
|
.less_less => .shl,
|
||||||
|
.greater_greater => .shr,
|
||||||
.kw_in => .in_op,
|
.kw_in => .in_op,
|
||||||
else => null,
|
else => null,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -63,8 +63,17 @@ pub const Tag = enum {
|
|||||||
percent, // %
|
percent, // %
|
||||||
percent_equal, // %=
|
percent_equal, // %=
|
||||||
ampersand, // &
|
ampersand, // &
|
||||||
|
ampersand_equal, // &=
|
||||||
at, // @
|
at, // @
|
||||||
pipe, // |
|
pipe, // |
|
||||||
|
pipe_equal, // |=
|
||||||
|
caret, // ^
|
||||||
|
caret_equal, // ^=
|
||||||
|
tilde, // ~
|
||||||
|
less_less, // <<
|
||||||
|
less_less_equal, // <<=
|
||||||
|
greater_greater, // >>
|
||||||
|
greater_greater_equal, // >>=
|
||||||
|
|
||||||
// Delimiters
|
// Delimiters
|
||||||
l_paren, // (
|
l_paren, // (
|
||||||
@@ -121,8 +130,17 @@ pub const Tag = enum {
|
|||||||
.percent => "%",
|
.percent => "%",
|
||||||
.percent_equal => "%=",
|
.percent_equal => "%=",
|
||||||
.ampersand => "&",
|
.ampersand => "&",
|
||||||
|
.ampersand_equal => "&=",
|
||||||
.at => "@",
|
.at => "@",
|
||||||
.pipe => "|",
|
.pipe => "|",
|
||||||
|
.pipe_equal => "|=",
|
||||||
|
.caret => "^",
|
||||||
|
.caret_equal => "^=",
|
||||||
|
.tilde => "~",
|
||||||
|
.less_less => "<<",
|
||||||
|
.less_less_equal => "<<=",
|
||||||
|
.greater_greater => ">>",
|
||||||
|
.greater_greater_equal => ">>=",
|
||||||
.kw_null => "null",
|
.kw_null => "null",
|
||||||
.l_paren => "(",
|
.l_paren => "(",
|
||||||
.r_paren => ")",
|
.r_paren => ")",
|
||||||
|
|||||||
@@ -36,8 +36,25 @@ eq-chain: true
|
|||||||
eq-chain-f: false
|
eq-chain-f: false
|
||||||
band: 15
|
band: 15
|
||||||
bor: 7
|
bor: 7
|
||||||
|
bxor: 240
|
||||||
|
bxor2: 5
|
||||||
|
bnot: -1
|
||||||
|
bnot2: -2
|
||||||
|
shl: 16
|
||||||
|
shr: 16
|
||||||
|
shl2: 24
|
||||||
|
shr2: 127
|
||||||
band-var: 15
|
band-var: 15
|
||||||
bor-var: 7
|
bor-var: 7
|
||||||
|
bxor-var: 240
|
||||||
|
shl-var: 16
|
||||||
|
shr-var: 15
|
||||||
|
bnot-var: -16
|
||||||
|
and-assign: 15
|
||||||
|
or-assign: 255
|
||||||
|
xor-assign: 240
|
||||||
|
shl-assign: 256
|
||||||
|
shr-assign: 16
|
||||||
mod-var: 2
|
mod-var: 2
|
||||||
and: true
|
and: true
|
||||||
and-false: false
|
and-false: false
|
||||||
|
|||||||
Reference in New Issue
Block a user