diff --git a/app_llvm_test b/app_llvm_test new file mode 100644 index 00000000..a21c6c33 Binary files /dev/null and b/app_llvm_test differ diff --git a/src/ast.rs b/src/ast.rs index 518af381..220652e1 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -333,6 +333,7 @@ pub enum BinaryOperator { Multiply, Divide, Modulo, + Shl, // << shift-left (Phase 1) Equal, NotEqual, Less, @@ -361,6 +362,7 @@ impl fmt::Display for BinaryOperator { BinaryOperator::Multiply => "*", BinaryOperator::Divide => "/", BinaryOperator::Modulo => "%", + BinaryOperator::Shl => "<<", BinaryOperator::Equal => "==", BinaryOperator::NotEqual => "!=", BinaryOperator::Less => "<", diff --git a/src/interpreter/expressions/operators.rs b/src/interpreter/expressions/operators.rs index 7f0ceed4..d49c250a 100644 --- a/src/interpreter/expressions/operators.rs +++ b/src/interpreter/expressions/operators.rs @@ -323,6 +323,19 @@ impl NyashInterpreter { } } + BinaryOperator::Shl => { + // Integer-only left shift + if let (Some(li), Some(ri)) = ( + crate::runtime::semantics::coerce_to_i64(left_val.as_ref()), + crate::runtime::semantics::coerce_to_i64(right_val.as_ref()), + ) { + return Ok(Box::new(IntegerBox::new(li.wrapping_shl(ri as u32)))); + } + Err(RuntimeError::TypeError { + message: format!("Shift-left '<<' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) + }) + } + BinaryOperator::Less => { let result = CompareBox::less(left_val.as_ref(), right_val.as_ref()); Ok(Box::new(result)) diff --git a/src/interpreter/operators.rs b/src/interpreter/operators.rs index fbf3f205..fbb78b77 100644 --- a/src/interpreter/operators.rs +++ b/src/interpreter/operators.rs @@ -256,6 +256,17 @@ impl NyashInterpreter { Ok(Box::new(BoolBox::new(self.is_truthy(right_val)))) } }, + + BinaryOperator::Shl => { + if let (Some(li), Some(ri)) = ( + left_val.as_any().downcast_ref::(), + right_val.as_any().downcast_ref::(), + ) { + Ok(Box::new(IntegerBox::new(li.value.wrapping_shl(ri.value as u32)))) + } else { + Err(RuntimeError::InvalidOperation { message: format!("Shift-left '<<' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) }) + } + }, } } diff --git a/src/mir/builder/ops.rs b/src/mir/builder/ops.rs index 88004a31..1235df25 100644 --- a/src/mir/builder/ops.rs +++ b/src/mir/builder/ops.rs @@ -114,6 +114,7 @@ impl super::MirBuilder { BinaryOperator::Multiply => Ok(BinaryOpType::Arithmetic(BinaryOp::Mul)), BinaryOperator::Divide => Ok(BinaryOpType::Arithmetic(BinaryOp::Div)), BinaryOperator::Modulo => Ok(BinaryOpType::Arithmetic(BinaryOp::Mod)), + BinaryOperator::Shl => Ok(BinaryOpType::Arithmetic(BinaryOp::Shl)), BinaryOperator::Equal => Ok(BinaryOpType::Comparison(CompareOp::Eq)), BinaryOperator::NotEqual => Ok(BinaryOpType::Comparison(CompareOp::Ne)), BinaryOperator::Less => Ok(BinaryOpType::Comparison(CompareOp::Lt)), diff --git a/src/parser/expressions.rs b/src/parser/expressions.rs index d288728d..5c6bf4a8 100644 --- a/src/parser/expressions.rs +++ b/src/parser/expressions.rs @@ -231,13 +231,13 @@ impl NyashParser { /// 項をパース: + - >> fn parse_term(&mut self) -> Result { - let mut expr = self.parse_factor()?; + let mut expr = self.parse_shift()?; while self.match_token(&TokenType::PLUS) || self.match_token(&TokenType::MINUS) || self.match_token(&TokenType::ARROW) { if self.match_token(&TokenType::ARROW) { // >> Arrow演算子 self.advance(); - let right = self.parse_factor()?; + let right = self.parse_shift()?; expr = ASTNode::Arrow { sender: Box::new(expr), receiver: Box::new(right), @@ -250,7 +250,7 @@ impl NyashParser { _ => unreachable!(), }; self.advance(); - let right = self.parse_factor()?; + let right = self.parse_shift()?; if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") { let name = match operator { BinaryOperator::Add=>"add", BinaryOperator::Subtract=>"sub", _=>"term" }; let ok = crate::grammar::engine::get().syntax_is_allowed_binop(name); @@ -268,6 +268,22 @@ impl NyashParser { Ok(expr) } + /// シフトをパース: << のみ(Phase 1) + fn parse_shift(&mut self) -> Result { + let mut expr = self.parse_factor()?; + while self.match_token(&TokenType::SHIFT_LEFT) { + self.advance(); // consume '<<' + let rhs = self.parse_factor()?; + expr = ASTNode::BinaryOp { + operator: BinaryOperator::Shl, + left: Box::new(expr), + right: Box::new(rhs), + span: Span::unknown(), + }; + } + Ok(expr) + } + /// 因子をパース: * / fn parse_factor(&mut self) -> Result { let mut expr = self.parse_unary()?; diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 51c311ef..21f78584 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -59,6 +59,7 @@ pub enum TokenType { // 演算子 (長いものから先に定義) ARROW, // >> (legacy arrow) + SHIFT_LEFT, // << (bitwise shift-left) FAT_ARROW, // => (peek arms) EQUALS, // == NotEquals, // != @@ -269,6 +270,12 @@ impl NyashTokenizer { self.advance(); Ok(Token::new(TokenType::NotEquals, start_line, start_column)) } + // Shift-left must be detected before <= and < + Some('<') if self.peek_char() == Some('<') => { + self.advance(); + self.advance(); + Ok(Token::new(TokenType::SHIFT_LEFT, start_line, start_column)) + } Some('<') if self.peek_char() == Some('=') => { self.advance(); self.advance();