From 5963a5f78bb5bf06e5c7993f4e2fc07e3d3f2d64 Mon Sep 17 00:00:00 2001 From: LunaStev Date: Wed, 17 Dec 2025 19:48:49 +0900 Subject: [PATCH] feat: implement bitwise operations and unary operators Add support for bitwise operations (shift, XOR, NOT) and unary operators (logical NOT, bitwise NOT) with proper operator precedence. Changes: - Update lexer to use C-style shift operators: - Change "rol" keyword to "<<" (shift left) - Change "ror" keyword to ">>" (shift right) - Maintain TokenType::Rol and TokenType::Ror internally - Add new operators to AST: - ShiftLeft (<<), ShiftRight (>>) - BitwiseXor (^), BitwiseNot (~) - LogicalNot (!) - Implement unary expression support: - Add Expression::Unary variant with operator and inner expression - Parse unary prefix operators (!, ~) in parse_expression - Generate LLVM IR for unary operations - Refactor expression parsing precedence hierarchy: - parse_logical_expression now calls parse_bitwise_expression - Add parse_bitwise_expression for |, ^ operators - Add parse_shift_expression for <<, >> operators - Maintain proper precedence: logical > bitwise > shift > relational - Implement LLVM code generation for new operators: - build_left_shift / build_right_shift for << and >> - build_not for bitwise NOT (~) - build_xor with const_int(1) for logical NOT (!) - Add comprehensive operator test (test70.wave): - Arithmetic operators (+, -, *, /, %) - Compound assignment (+=, -=, *=, /=) - Comparison operators (==, !=, <, <=, >, >=) - Logical operators (&&, ||, !) - Bitwise operators (|, ^, ~) - Shift operators (<<, >>) - Operator precedence and grouping Example usage: var x: i32 = 10; var y: i32 = x << 2; // 40 var z: i32 = ~x; // -11 (two's complement) var w: bool = !(x > 5); // false This completes Wave's operator set with industry-standard bitwise manipulation and unary operations, matching C/C++ operator syntax. Signed-off-by: LunaStev --- front/lexer/src/lexer/lexer.rs | 8 +-- front/parser/src/parser/ast.rs | 9 +++ front/parser/src/parser/format.rs | 68 ++++++++++++++++++- .../src/llvm_temporary/expression.rs | 30 ++++++++ test/test70.wave | 53 +++++++++++++++ 5 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 test/test70.wave diff --git a/front/lexer/src/lexer/lexer.rs b/front/lexer/src/lexer/lexer.rs index 57234889..10672cd3 100644 --- a/front/lexer/src/lexer/lexer.rs +++ b/front/lexer/src/lexer/lexer.rs @@ -638,17 +638,17 @@ impl<'a> Lexer<'a> { line: self.line, } }, - "rol" => { + "<<" => { Token { token_type: TokenType::Rol, - lexeme: "rol".to_string(), + lexeme: "<<".to_string(), line: self.line, } }, - "ror" => { + ">>" => { Token { token_type: TokenType::Ror, - lexeme: "ror".to_string(), + lexeme: ">>".to_string(), line: self.line, } }, diff --git a/front/parser/src/parser/ast.rs b/front/parser/src/parser/ast.rs index 3d08857a..5377c6b7 100644 --- a/front/parser/src/parser/ast.rs +++ b/front/parser/src/parser/ast.rs @@ -122,6 +122,10 @@ pub enum Expression { object: Box, field: String, }, + Unary { + operator: Operator, + expr: Box, + }, } #[derive(Debug, Clone)] @@ -149,6 +153,11 @@ pub enum Operator { LogicalOr, BitwiseOr, Assign, + ShiftLeft, // << + ShiftRight, // >> + BitwiseXor, + LogicalNot, + BitwiseNot, } #[derive(Debug, Clone)] diff --git a/front/parser/src/parser/format.rs b/front/parser/src/parser/format.rs index 5a701346..5f37382e 100644 --- a/front/parser/src/parser/format.rs +++ b/front/parser/src/parser/format.rs @@ -37,6 +37,24 @@ pub fn parse_expression<'a, T>(tokens: &mut Peekable) -> Option where T: Iterator, { + if let Some(Token { token_type: TokenType::Not, .. }) = tokens.peek() { + tokens.next(); + let inner = parse_expression(tokens)?; + return Some(Expression::Unary { + operator: Operator::LogicalNot, + expr: Box::new(inner), + }); + } + + if let Some(Token { token_type: TokenType::BitwiseNot, .. }) = tokens.peek() { + tokens.next(); + let inner = parse_expression(tokens)?; + return Some(Expression::Unary { + operator: Operator::BitwiseNot, + expr: Box::new(inner), + }); + } + if let Some(Token { token_type: TokenType::AddressOf, .. }) = tokens.peek() { tokens.next(); // consume '&' let inner = parse_expression(tokens)?; @@ -86,7 +104,7 @@ pub fn parse_logical_expression<'a, T>(tokens: &mut Peekable) -> Option, { - let mut left = parse_relational_expression(tokens)?; + let mut left = parse_bitwise_expression(tokens)?; while let Some(token) = tokens.peek() { match token.token_type { @@ -618,4 +636,52 @@ where None } } +} + +pub fn parse_shift_expression<'a, T>(tokens: &mut Peekable) -> Option +where + T: Iterator, +{ + let mut left = parse_relational_expression(tokens)?; + + while let Some(token) = tokens.peek() { + let op = match token.token_type { + TokenType::Rol => Operator::ShiftLeft, + TokenType::Ror => Operator::ShiftRight, + _ => break, + }; + + tokens.next(); + let right = parse_relational_expression(tokens)?; + left = Expression::BinaryExpression { + left: Box::new(left), + operator: op, + right: Box::new(right), + }; + } + Some(left) +} + +pub fn parse_bitwise_expression<'a, T>(tokens: &mut Peekable) -> Option +where + T: Iterator, +{ + let mut left = parse_shift_expression(tokens)?; + + while let Some(token) = tokens.peek() { + let op = match token.token_type { + TokenType::BitwiseOr => Operator::BitwiseOr, + TokenType::Xor => Operator::BitwiseXor, + _ => break, + }; + + tokens.next(); + let right = parse_shift_expression(tokens)?; + left = Expression::BinaryExpression { + left: Box::new(left), + operator: op, + right: Box::new(right), + }; + } + Some(left) } \ No newline at end of file diff --git a/llvm_temporary/src/llvm_temporary/expression.rs b/llvm_temporary/src/llvm_temporary/expression.rs index 40590229..cb111c9c 100644 --- a/llvm_temporary/src/llvm_temporary/expression.rs +++ b/llvm_temporary/src/llvm_temporary/expression.rs @@ -440,6 +440,8 @@ pub fn generate_expression_ir<'ctx>( Operator::Multiply => builder.build_int_mul(l_casted, r_casted, "multmp"), Operator::Divide => builder.build_int_signed_div(l_casted, r_casted, "divtmp"), Operator::Remainder => builder.build_int_signed_rem(l_casted, r_casted, "modtmp"), + Operator::ShiftLeft => builder.build_left_shift(l_casted, r_casted, "shl"), + Operator::ShiftRight => builder.build_right_shift(l_casted, r_casted, true, "shr"), Operator::Greater => builder.build_int_compare(IntPredicate::SGT, l_casted, r_casted, "cmptmp"), Operator::Less => builder.build_int_compare(IntPredicate::SLT, l_casted, r_casted, "cmptmp"), Operator::Equal => builder.build_int_compare(IntPredicate::EQ, l_casted, r_casted, "cmptmp"), @@ -731,6 +733,34 @@ pub fn generate_expression_ir<'ctx>( .as_basic_value_enum() } + Expression::Unary { operator, expr } => { + let val = generate_expression_ir( + context, + builder, + expr, + variables, + module, + None, + global_consts, + struct_types, + struct_field_indices, + ); + + match (operator, val) { + // ! (logical not) + (Operator::LogicalNot, BasicValueEnum::IntValue(v)) => { + let one = v.get_type().const_int(1, false); + builder.build_xor(v, one, "logical_not").unwrap().as_basic_value_enum() + } + + (Operator::BitwiseNot, BasicValueEnum::IntValue(v)) => { + builder.build_not(v, "bitwise_not").unwrap().as_basic_value_enum() + } + + _ => panic!("Unsupported unary operator {:?} for value {:?}", operator, val), + } + } + _ => unimplemented!("Unsupported expression type"), } } \ No newline at end of file diff --git a/test/test70.wave b/test/test70.wave new file mode 100644 index 00000000..0b22d6b2 --- /dev/null +++ b/test/test70.wave @@ -0,0 +1,53 @@ +fun main() { + var a: i32 = 10; + var b: i32 = 3; + + println("a + b = {}", a + b); + println("a - b = {}", a - b); + println("a * b = {}", a * b); + println("a / b = {}", a / b); + println("a % b = {}", a % b); + + var x: i32 = 5; + x += 2; + println("x += 2 -> {}", x); + x -= 1; + println("x -= 1 -> {}", x); + x *= 3; + println("x *= 3 -> {}", x); + x /= 2; + println("x /= 2 -> {}", x); + + println("a == b -> {}", a == b); + println("a != b -> {}", a != b); + println("a < b -> {}", a < b); + println("a <= b -> {}", a <= b); + println("a > b -> {}", a > b); + println("a >= b -> {}", a >= b); + + var t: bool = true; + var f: bool = false; + + println("t && f -> {}", t && f); + println("t || f -> {}", t || f); + println("!t -> {}", !t); + + var m: i32 = 0b1010; // 10 + var n: i32 = 0b0110; // 6 + + println("m | n -> {}", m | n); + println("m ^ n -> {}", m ^ n); + println("~m -> {}", ~m); + + println("a << 1 -> {}", a << 1); + println("a >> 1 -> {}", a >> 1); + + var y: i32 = a + b * 2; + println("a + b * 2 -> {}", y); + + var z: bool = a > b && b < 5; + println("a > b && b < 5 -> {}", z); + + var w: i32 = (a + b) << 2; + println("(a + b) << 2 -> {}", w); +}