grammar: introduce bitwise ops & shifts; retire legacy >> ARROW

- Tokenizer: add tokens for << >> & | ^ (SHIFT_LEFT/RIGHT, BIT_AND/OR/XOR); keep ||, &&, |>
- Parser: precedence layers for bit ops (| ^ &) and shift (<< >>); remove >> Arrow production
- AST: add BitAnd/BitOr/BitXor/Shr with Display
- MIR builder: map bit ops and Shr to MIR BinaryOp
- Interpreter: implement integer-only bit ops and shifts, mask shift count to 0..63

Compatibility: legacy >> Arrow removed from parser; use |> for pipeline.
This commit is contained in:
Tomoaki
2025-09-08 03:54:34 +09:00
parent 7c2b09c647
commit 08d9b71297
6 changed files with 169 additions and 46 deletions

View File

@ -333,7 +333,11 @@ pub enum BinaryOperator {
Multiply,
Divide,
Modulo,
BitAnd,
BitOr,
BitXor,
Shl, // << shift-left (Phase 1)
Shr,
Equal,
NotEqual,
Less,
@ -362,7 +366,11 @@ impl fmt::Display for BinaryOperator {
BinaryOperator::Multiply => "*",
BinaryOperator::Divide => "/",
BinaryOperator::Modulo => "%",
BinaryOperator::BitAnd => "&",
BinaryOperator::BitOr => "|",
BinaryOperator::BitXor => "^",
BinaryOperator::Shl => "<<",
BinaryOperator::Shr => ">>",
BinaryOperator::Equal => "==",
BinaryOperator::NotEqual => "!=",
BinaryOperator::Less => "<",

View File

@ -329,12 +329,46 @@ impl NyashInterpreter {
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))));
let sh = (ri as u32) & 63;
return Ok(Box::new(IntegerBox::new(li.wrapping_shl(sh))));
}
Err(RuntimeError::TypeError {
message: format!("Shift-left '<<' requires integers (got {} and {})", left_val.type_name(), right_val.type_name())
})
}
BinaryOperator::Shr => {
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()),
) {
let sh = (ri as u32) & 63;
return Ok(Box::new(IntegerBox::new(((li as u64) >> sh) as i64)));
}
Err(RuntimeError::TypeError {
message: format!("Shift-right '>>' requires integers (got {} and {})", left_val.type_name(), right_val.type_name())
})
}
BinaryOperator::BitAnd => {
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 & ri))); }
Err(RuntimeError::TypeError { message: format!("Bitwise '&' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) })
}
BinaryOperator::BitOr => {
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 | ri))); }
Err(RuntimeError::TypeError { message: format!("Bitwise '|' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) })
}
BinaryOperator::BitXor => {
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 ^ ri))); }
Err(RuntimeError::TypeError { message: format!("Bitwise '^' 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());

View File

@ -256,17 +256,44 @@ 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::<IntegerBox>(),
right_val.as_any().downcast_ref::<IntegerBox>(),
) {
Ok(Box::new(IntegerBox::new(li.value.wrapping_shl(ri.value as u32))))
Ok(Box::new(IntegerBox::new(li.value.wrapping_shl((ri.value as u32) & 63))))
} else {
Err(RuntimeError::InvalidOperation { message: format!("Shift-left '<<' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) })
}
},
BinaryOperator::Shr => {
if let (Some(li), Some(ri)) = (
left_val.as_any().downcast_ref::<IntegerBox>(),
right_val.as_any().downcast_ref::<IntegerBox>(),
) {
Ok(Box::new(IntegerBox::new(((li.value as u64) >> ((ri.value as u32) & 63)) as i64)))
} else {
Err(RuntimeError::InvalidOperation { message: format!("Shift-right '>>' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) })
}
},
BinaryOperator::BitAnd => {
if let (Some(li), Some(ri)) = (
left_val.as_any().downcast_ref::<IntegerBox>(),
right_val.as_any().downcast_ref::<IntegerBox>(),
) { Ok(Box::new(IntegerBox::new(li.value & ri.value))) } else { Err(RuntimeError::InvalidOperation { message: format!("Bitwise '&' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) }) }
},
BinaryOperator::BitOr => {
if let (Some(li), Some(ri)) = (
left_val.as_any().downcast_ref::<IntegerBox>(),
right_val.as_any().downcast_ref::<IntegerBox>(),
) { Ok(Box::new(IntegerBox::new(li.value | ri.value))) } else { Err(RuntimeError::InvalidOperation { message: format!("Bitwise '|' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) }) }
},
BinaryOperator::BitXor => {
if let (Some(li), Some(ri)) = (
left_val.as_any().downcast_ref::<IntegerBox>(),
right_val.as_any().downcast_ref::<IntegerBox>(),
) { Ok(Box::new(IntegerBox::new(li.value ^ ri.value))) } else { Err(RuntimeError::InvalidOperation { message: format!("Bitwise '^' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) }) }
},
}
}

View File

@ -115,6 +115,10 @@ impl super::MirBuilder {
BinaryOperator::Divide => Ok(BinaryOpType::Arithmetic(BinaryOp::Div)),
BinaryOperator::Modulo => Ok(BinaryOpType::Arithmetic(BinaryOp::Mod)),
BinaryOperator::Shl => Ok(BinaryOpType::Arithmetic(BinaryOp::Shl)),
BinaryOperator::Shr => Ok(BinaryOpType::Arithmetic(BinaryOp::Shr)),
BinaryOperator::BitAnd => Ok(BinaryOpType::Arithmetic(BinaryOp::BitAnd)),
BinaryOperator::BitOr => Ok(BinaryOpType::Arithmetic(BinaryOp::BitOr)),
BinaryOperator::BitXor => Ok(BinaryOpType::Arithmetic(BinaryOp::BitXor)),
BinaryOperator::Equal => Ok(BinaryOpType::Comparison(CompareOp::Eq)),
BinaryOperator::NotEqual => Ok(BinaryOpType::Comparison(CompareOp::Ne)),
BinaryOperator::Less => Ok(BinaryOpType::Comparison(CompareOp::Lt)),

View File

@ -133,7 +133,7 @@ impl NyashParser {
/// AND演算子をパース: &&
fn parse_and(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_equality()?;
let mut expr = self.parse_bit_or()?;
while self.match_token(&TokenType::AND) {
let operator = BinaryOperator::And;
@ -154,6 +154,42 @@ impl NyashParser {
Ok(expr)
}
/// ビットOR: |
fn parse_bit_or(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_bit_xor()?;
while self.match_token(&TokenType::BIT_OR) {
let operator = BinaryOperator::BitOr;
self.advance();
let right = self.parse_bit_xor()?;
expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() };
}
Ok(expr)
}
/// ビットXOR: ^
fn parse_bit_xor(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_bit_and()?;
while self.match_token(&TokenType::BIT_XOR) {
let operator = BinaryOperator::BitXor;
self.advance();
let right = self.parse_bit_and()?;
expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() };
}
Ok(expr)
}
/// ビットAND: &
fn parse_bit_and(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_equality()?;
while self.match_token(&TokenType::BIT_AND) {
let operator = BinaryOperator::BitAnd;
self.advance();
let right = self.parse_equality()?;
expr = ASTNode::BinaryOp { operator, left: Box::new(expr), right: Box::new(right), span: Span::unknown() };
}
Ok(expr)
}
/// 等値演算子をパース: == !=
fn parse_equality(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_comparison()?;
@ -229,21 +265,11 @@ impl NyashParser {
Ok(expr)
}
/// 項をパース: + - >>
/// 項をパース: + -
fn parse_term(&mut self) -> Result<ASTNode, ParseError> {
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_shift()?;
expr = ASTNode::Arrow {
sender: Box::new(expr),
receiver: Box::new(right),
span: Span::unknown(),
};
} else {
while self.match_token(&TokenType::PLUS) || self.match_token(&TokenType::MINUS) {
let operator = match &self.current_token().token_type {
TokenType::PLUS => BinaryOperator::Add,
TokenType::MINUS => BinaryOperator::Subtract,
@ -263,23 +289,27 @@ impl NyashParser {
span: Span::unknown(),
};
}
}
Ok(expr)
}
/// シフトをパース: << のみPhase 1
/// シフトをパース: << >>
fn parse_shift(&mut self) -> Result<ASTNode, ParseError> {
let mut expr = self.parse_factor()?;
while self.match_token(&TokenType::SHIFT_LEFT) {
self.advance(); // consume '<<'
loop {
if self.match_token(&TokenType::SHIFT_LEFT) {
self.advance();
let rhs = self.parse_factor()?;
expr = ASTNode::BinaryOp {
operator: BinaryOperator::Shl,
left: Box::new(expr),
right: Box::new(rhs),
span: Span::unknown(),
};
expr = ASTNode::BinaryOp { operator: BinaryOperator::Shl, left: Box::new(expr), right: Box::new(rhs), span: Span::unknown() };
continue;
}
if self.match_token(&TokenType::SHIFT_RIGHT) {
self.advance();
let rhs = self.parse_factor()?;
expr = ASTNode::BinaryOp { operator: BinaryOperator::Shr, left: Box::new(expr), right: Box::new(rhs), span: Span::unknown() };
continue;
}
break;
}
Ok(expr)
}

View File

@ -58,8 +58,11 @@ pub enum TokenType {
USING, // using (名前空間インポート)
// 演算子 (長いものから先に定義)
ARROW, // >> (legacy arrow)
SHIFT_LEFT, // << (bitwise shift-left)
SHIFT_RIGHT, // >> (bitwise shift-right)
BIT_AND, // & (bitwise and)
BIT_OR, // | (bitwise or)
BIT_XOR, // ^ (bitwise xor)
FAT_ARROW, // => (peek arms)
EQUALS, // ==
NotEquals, // !=
@ -248,7 +251,7 @@ impl NyashTokenizer {
Some('>') if self.peek_char() == Some('>') => {
self.advance();
self.advance();
Ok(Token::new(TokenType::ARROW, start_line, start_column))
Ok(Token::new(TokenType::SHIFT_RIGHT, start_line, start_column))
}
Some(':') if self.peek_char() == Some(':') => {
self.advance();
@ -296,6 +299,11 @@ impl NyashTokenizer {
self.advance();
Ok(Token::new(TokenType::OR, start_line, start_column))
}
Some('|') if self.peek_char() == Some('>') => {
self.advance();
self.advance();
return Ok(Token::new(TokenType::PIPE_FORWARD, start_line, start_column));
}
Some('<') => {
self.advance();
Ok(Token::new(TokenType::LESS, start_line, start_column))
@ -304,6 +312,18 @@ impl NyashTokenizer {
self.advance();
Ok(Token::new(TokenType::GREATER, start_line, start_column))
}
Some('&') => {
self.advance();
Ok(Token::new(TokenType::BIT_AND, start_line, start_column))
}
Some('|') => {
self.advance();
Ok(Token::new(TokenType::BIT_OR, start_line, start_column))
}
Some('^') => {
self.advance();
Ok(Token::new(TokenType::BIT_XOR, start_line, start_column))
}
Some('=') => {
self.advance();
Ok(Token::new(TokenType::ASSIGN, start_line, start_column))