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:
@ -333,7 +333,11 @@ pub enum BinaryOperator {
|
|||||||
Multiply,
|
Multiply,
|
||||||
Divide,
|
Divide,
|
||||||
Modulo,
|
Modulo,
|
||||||
|
BitAnd,
|
||||||
|
BitOr,
|
||||||
|
BitXor,
|
||||||
Shl, // << shift-left (Phase 1)
|
Shl, // << shift-left (Phase 1)
|
||||||
|
Shr,
|
||||||
Equal,
|
Equal,
|
||||||
NotEqual,
|
NotEqual,
|
||||||
Less,
|
Less,
|
||||||
@ -362,7 +366,11 @@ impl fmt::Display for BinaryOperator {
|
|||||||
BinaryOperator::Multiply => "*",
|
BinaryOperator::Multiply => "*",
|
||||||
BinaryOperator::Divide => "/",
|
BinaryOperator::Divide => "/",
|
||||||
BinaryOperator::Modulo => "%",
|
BinaryOperator::Modulo => "%",
|
||||||
|
BinaryOperator::BitAnd => "&",
|
||||||
|
BinaryOperator::BitOr => "|",
|
||||||
|
BinaryOperator::BitXor => "^",
|
||||||
BinaryOperator::Shl => "<<",
|
BinaryOperator::Shl => "<<",
|
||||||
|
BinaryOperator::Shr => ">>",
|
||||||
BinaryOperator::Equal => "==",
|
BinaryOperator::Equal => "==",
|
||||||
BinaryOperator::NotEqual => "!=",
|
BinaryOperator::NotEqual => "!=",
|
||||||
BinaryOperator::Less => "<",
|
BinaryOperator::Less => "<",
|
||||||
|
|||||||
@ -329,12 +329,46 @@ impl NyashInterpreter {
|
|||||||
crate::runtime::semantics::coerce_to_i64(left_val.as_ref()),
|
crate::runtime::semantics::coerce_to_i64(left_val.as_ref()),
|
||||||
crate::runtime::semantics::coerce_to_i64(right_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 {
|
Err(RuntimeError::TypeError {
|
||||||
message: format!("Shift-left '<<' requires integers (got {} and {})", left_val.type_name(), right_val.type_name())
|
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 => {
|
BinaryOperator::Less => {
|
||||||
let result = CompareBox::less(left_val.as_ref(), right_val.as_ref());
|
let result = CompareBox::less(left_val.as_ref(), right_val.as_ref());
|
||||||
|
|||||||
@ -256,17 +256,44 @@ impl NyashInterpreter {
|
|||||||
Ok(Box::new(BoolBox::new(self.is_truthy(right_val))))
|
Ok(Box::new(BoolBox::new(self.is_truthy(right_val))))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
BinaryOperator::Shl => {
|
BinaryOperator::Shl => {
|
||||||
if let (Some(li), Some(ri)) = (
|
if let (Some(li), Some(ri)) = (
|
||||||
left_val.as_any().downcast_ref::<IntegerBox>(),
|
left_val.as_any().downcast_ref::<IntegerBox>(),
|
||||||
right_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 {
|
} else {
|
||||||
Err(RuntimeError::InvalidOperation { message: format!("Shift-left '<<' requires integers (got {} and {})", left_val.type_name(), right_val.type_name()) })
|
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()) }) }
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -115,6 +115,10 @@ impl super::MirBuilder {
|
|||||||
BinaryOperator::Divide => Ok(BinaryOpType::Arithmetic(BinaryOp::Div)),
|
BinaryOperator::Divide => Ok(BinaryOpType::Arithmetic(BinaryOp::Div)),
|
||||||
BinaryOperator::Modulo => Ok(BinaryOpType::Arithmetic(BinaryOp::Mod)),
|
BinaryOperator::Modulo => Ok(BinaryOpType::Arithmetic(BinaryOp::Mod)),
|
||||||
BinaryOperator::Shl => Ok(BinaryOpType::Arithmetic(BinaryOp::Shl)),
|
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::Equal => Ok(BinaryOpType::Comparison(CompareOp::Eq)),
|
||||||
BinaryOperator::NotEqual => Ok(BinaryOpType::Comparison(CompareOp::Ne)),
|
BinaryOperator::NotEqual => Ok(BinaryOpType::Comparison(CompareOp::Ne)),
|
||||||
BinaryOperator::Less => Ok(BinaryOpType::Comparison(CompareOp::Lt)),
|
BinaryOperator::Less => Ok(BinaryOpType::Comparison(CompareOp::Lt)),
|
||||||
|
|||||||
@ -133,7 +133,7 @@ impl NyashParser {
|
|||||||
|
|
||||||
/// AND演算子をパース: &&
|
/// AND演算子をパース: &&
|
||||||
fn parse_and(&mut self) -> Result<ASTNode, ParseError> {
|
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) {
|
while self.match_token(&TokenType::AND) {
|
||||||
let operator = BinaryOperator::And;
|
let operator = BinaryOperator::And;
|
||||||
@ -154,6 +154,42 @@ impl NyashParser {
|
|||||||
Ok(expr)
|
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> {
|
fn parse_equality(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
let mut expr = self.parse_comparison()?;
|
let mut expr = self.parse_comparison()?;
|
||||||
@ -229,21 +265,11 @@ impl NyashParser {
|
|||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 項をパース: + - >>
|
/// 項をパース: + -
|
||||||
fn parse_term(&mut self) -> Result<ASTNode, ParseError> {
|
fn parse_term(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
let mut expr = self.parse_shift()?;
|
let mut expr = self.parse_shift()?;
|
||||||
|
|
||||||
while self.match_token(&TokenType::PLUS) || self.match_token(&TokenType::MINUS) || self.match_token(&TokenType::ARROW) {
|
while self.match_token(&TokenType::PLUS) || self.match_token(&TokenType::MINUS) {
|
||||||
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 {
|
|
||||||
let operator = match &self.current_token().token_type {
|
let operator = match &self.current_token().token_type {
|
||||||
TokenType::PLUS => BinaryOperator::Add,
|
TokenType::PLUS => BinaryOperator::Add,
|
||||||
TokenType::MINUS => BinaryOperator::Subtract,
|
TokenType::MINUS => BinaryOperator::Subtract,
|
||||||
@ -263,23 +289,27 @@ impl NyashParser {
|
|||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// シフトをパース: << のみ(Phase 1)
|
/// シフトをパース: << >>
|
||||||
fn parse_shift(&mut self) -> Result<ASTNode, ParseError> {
|
fn parse_shift(&mut self) -> Result<ASTNode, ParseError> {
|
||||||
let mut expr = self.parse_factor()?;
|
let mut expr = self.parse_factor()?;
|
||||||
while self.match_token(&TokenType::SHIFT_LEFT) {
|
loop {
|
||||||
self.advance(); // consume '<<'
|
if self.match_token(&TokenType::SHIFT_LEFT) {
|
||||||
|
self.advance();
|
||||||
let rhs = self.parse_factor()?;
|
let rhs = self.parse_factor()?;
|
||||||
expr = ASTNode::BinaryOp {
|
expr = ASTNode::BinaryOp { operator: BinaryOperator::Shl, left: Box::new(expr), right: Box::new(rhs), span: Span::unknown() };
|
||||||
operator: BinaryOperator::Shl,
|
continue;
|
||||||
left: Box::new(expr),
|
}
|
||||||
right: Box::new(rhs),
|
if self.match_token(&TokenType::SHIFT_RIGHT) {
|
||||||
span: Span::unknown(),
|
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)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,8 +58,11 @@ pub enum TokenType {
|
|||||||
USING, // using (名前空間インポート)
|
USING, // using (名前空間インポート)
|
||||||
|
|
||||||
// 演算子 (長いものから先に定義)
|
// 演算子 (長いものから先に定義)
|
||||||
ARROW, // >> (legacy arrow)
|
|
||||||
SHIFT_LEFT, // << (bitwise shift-left)
|
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)
|
FAT_ARROW, // => (peek arms)
|
||||||
EQUALS, // ==
|
EQUALS, // ==
|
||||||
NotEquals, // !=
|
NotEquals, // !=
|
||||||
@ -248,7 +251,7 @@ impl NyashTokenizer {
|
|||||||
Some('>') if self.peek_char() == Some('>') => {
|
Some('>') if self.peek_char() == Some('>') => {
|
||||||
self.advance();
|
self.advance();
|
||||||
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(':') => {
|
Some(':') if self.peek_char() == Some(':') => {
|
||||||
self.advance();
|
self.advance();
|
||||||
@ -296,6 +299,11 @@ impl NyashTokenizer {
|
|||||||
self.advance();
|
self.advance();
|
||||||
Ok(Token::new(TokenType::OR, start_line, start_column))
|
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('<') => {
|
Some('<') => {
|
||||||
self.advance();
|
self.advance();
|
||||||
Ok(Token::new(TokenType::LESS, start_line, start_column))
|
Ok(Token::new(TokenType::LESS, start_line, start_column))
|
||||||
@ -304,6 +312,18 @@ impl NyashTokenizer {
|
|||||||
self.advance();
|
self.advance();
|
||||||
Ok(Token::new(TokenType::GREATER, start_line, start_column))
|
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('=') => {
|
Some('=') => {
|
||||||
self.advance();
|
self.advance();
|
||||||
Ok(Token::new(TokenType::ASSIGN, start_line, start_column))
|
Ok(Token::new(TokenType::ASSIGN, start_line, start_column))
|
||||||
|
|||||||
Reference in New Issue
Block a user