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

@ -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()) }) }
},
}
}