bitwise ops

This commit is contained in:
Shautvast 2025-10-20 10:38:46 +02:00
parent aa24a006ce
commit 515b3c1037
9 changed files with 182 additions and 74 deletions

View file

@ -1,5 +1,9 @@
use crate::opcode::{OP_CONSTANT, OP_RETURN, OP_NEGATE, OP_ADD, OP_SUBTRACT, OP_MULTIPLY, OP_DIVIDE};
use tracing::debug;
use crate::value::Value;
use crate::{
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DIVIDE, OP_FALSE, OP_MULTIPLY,
OP_NEGATE, OP_RETURN, OP_SUBTRACT, OP_TRUE,
};
pub struct Chunk {
name: String,
@ -34,7 +38,7 @@ impl Chunk {
while offset < self.code.len() {
offset = self.disassemble_inst(offset);
}
println!("== {} ==", self.name);
println!();
}
fn disassemble_inst(&self, offset: usize) -> usize {
@ -48,9 +52,12 @@ impl Chunk {
match instruction {
OP_CONSTANT => self.constant_inst("OP_CONSTANT", offset),
OP_ADD => self.simple_inst("OP_ADD", offset),
OP_FALSE => self.simple_inst("OP_FALSE", offset),
OP_TRUE => self.simple_inst("OP_TRUE", offset),
OP_SUBTRACT => self.simple_inst("OP_SUBTRACT", offset),
OP_MULTIPLY => self.simple_inst("OP_MULTIPLY", offset),
OP_DIVIDE => self.simple_inst("OP_DIVIDE", offset),
OP_BITAND => self.simple_inst("OP_BITAND", offset),
OP_NEGATE => self.simple_inst("OP_NEGATE", offset),
OP_RETURN => self.simple_inst("OP_RETURN", offset),
_ => {
@ -67,7 +74,7 @@ impl Chunk {
fn constant_inst(&self, name: &str, offset: usize) -> usize {
let constant = self.code[offset + 1];
print!("{} {} ", name, constant);
debug!("{} {} ", name, constant);
self.print_value(&self.constants[constant as usize]);
offset + 2
}

View file

@ -1,14 +1,16 @@
use crate::chunk::Chunk;
use crate::opcode::{OP_ADD, OP_CONSTANT, OP_DIVIDE, OP_FALSE, OP_MULTIPLY, OP_NEGATE, OP_RETURN, OP_SUBTRACT, OP_TRUE};
use crate::scanner::scan;
use crate::tokens::{Token, TokenType};
use crate::value::Value;
use crate::{OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DIVIDE, OP_FALSE, OP_MULTIPLY, OP_NEGATE, OP_RETURN, OP_SUBTRACT, OP_TRUE};
use anyhow::anyhow;
use std::collections::HashMap;
use std::sync::LazyLock;
use tracing::debug;
pub fn compile(source: &str) -> anyhow::Result<Chunk> {
let tokens = scan(source);
// println!("{:?}", tokens);
let mut compiler = Compiler {
chunk: Chunk::new("main"),
@ -74,8 +76,9 @@ impl<'a> Compiler<'a> {
fn parse_precedence(&mut self, precedence: usize) -> anyhow::Result<()> {
self.advance()?;
let prefix_rule = get_rule(&self.previous_token.tokentype).prefix;
if let Some(prefix) = prefix_rule {
let rule = get_rule(&self.previous_token.tokentype);
debug!("{:?}",rule);
if let Some(prefix) = rule.prefix {
prefix(self)?;
while precedence <= get_rule(&self.current_token.tokentype).precedence {
self.advance()?;
@ -107,6 +110,7 @@ impl<'a> Compiler<'a> {
type ParseFn = fn(&mut Compiler) -> anyhow::Result<()>;
#[derive(Debug)]
struct Rule {
prefix: Option<ParseFn>,
infix: Option<ParseFn>,
@ -163,6 +167,7 @@ fn unary(s: &mut Compiler) -> anyhow::Result<()> {
fn binary(s: &mut Compiler) -> anyhow::Result<()> {
let operator_type = &s.previous_token.tokentype;
debug!("{:?}",operator_type);
let rule = get_rule(operator_type);
s.parse_precedence(rule.precedence + 1)?;
match operator_type {
@ -170,12 +175,16 @@ fn binary(s: &mut Compiler) -> anyhow::Result<()> {
TokenType::Minus => s.emit_byte(OP_SUBTRACT),
TokenType::Star => s.emit_byte(OP_MULTIPLY),
TokenType::Slash => s.emit_byte(OP_DIVIDE),
TokenType::BitAnd => s.emit_byte(OP_BITAND),
TokenType::BitOr => s.emit_byte(OP_BITOR),
TokenType::BitXor => s.emit_byte(OP_BITXOR),
_ => unimplemented!("binary other than plus, minus, star, slash"),
}
Ok(())
}
fn get_rule(operator_type: &TokenType) -> &'static Rule {
debug!("{:?}", operator_type);
RULES.get(operator_type).unwrap()
}
@ -211,13 +220,16 @@ static RULES: LazyLock<HashMap<TokenType, Rule>> = LazyLock::new(|| {
rules.insert(TokenType::Identifier, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::String, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::Number, Rule::new(Some(number), None, PREC_NONE));
rules.insert(TokenType::And, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::LogicalAnd, Rule::new(None, Some(binary), PREC_AND));
rules.insert(TokenType::LogicalOr, Rule::new(None, Some(binary), PREC_OR));
rules.insert(TokenType::BitAnd, Rule::new(None, Some(binary), PREC_BITAND));
rules.insert(TokenType::BitOr, Rule::new(None, Some(binary), PREC_BITOR));
rules.insert(TokenType::BitXor, Rule::new(None, Some(binary), PREC_BITXOR));
rules.insert(TokenType::Fn, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::Struct, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::Else, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::False, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::True, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::Or, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::While, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::Print, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::Return, Rule::new(None, None, PREC_NONE));
@ -230,6 +242,7 @@ static RULES: LazyLock<HashMap<TokenType, Rule>> = LazyLock::new(|| {
rules.insert(TokenType::StringType, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::False, Rule::new(Some(literal), None, PREC_NONE));
rules.insert(TokenType::True, Rule::new(Some(literal), None, PREC_NONE));
rules.insert(TokenType::Indent, Rule::new(None, None, PREC_NONE));
rules
});
@ -238,10 +251,14 @@ const PREC_NONE: usize = 0;
const PREC_ASSIGNMENT: usize = 1;
const PREC_OR: usize = 2;
const PREC_AND: usize = 3;
const PREC_EQUALITY: usize = 4;
const PREC_COMPARISON: usize = 5;
const PREC_TERM: usize = 6;
const PREC_FACTOR: usize = 7;
const PREC_UNARY: usize = 8;
const PREC_CALL: usize = 9;
const PREC_PRIMARY: usize = 10;
const PREC_BITAND: usize = 4;
const PREC_BITOR: usize = 5;
const PREC_BITXOR: usize = 6;
const PREC_EQUALITY: usize = 7;
const PREC_COMPARISON: usize = 8;
const PREC_BITSHIFT: usize = 9;
const PREC_TERM: usize = 10;
const PREC_FACTOR: usize = 11;
const PREC_UNARY: usize = 12;
const PREC_CALL: usize = 13;
const PREC_PRIMARY: usize = 14;

View file

@ -8,13 +8,13 @@ pub(crate) fn get_keyword(lexeme: &str) -> Option<TokenType> {
"string" => Some(TokenType::StringType),
"date" => Some(TokenType::DateType),
"print" => Some(TokenType::Print),
"and" => Some(TokenType::And),
"and" => Some(TokenType::LogicalAnd),
"else" => Some(TokenType::Else),
"false" => Some(TokenType::False),
"true" => Some(TokenType::True),
"for" => Some(TokenType::For),
"if" => Some(TokenType::If),
"or" => Some(TokenType::Or),
"or" => Some(TokenType::LogicalOr),
"while" => Some(TokenType::While),
_ => None,

View file

@ -1,8 +1,6 @@
use anyhow::anyhow;
use tracing::debug;
use crate::chunk::Chunk;
use crate::opcode::{
OP_ADD, OP_CONSTANT, OP_DIVIDE, OP_FALSE, OP_MULTIPLY, OP_NEGATE, OP_RETURN, OP_SUBTRACT,
OP_TRUE,
};
use crate::value::Value;
pub mod chunk;
@ -31,11 +29,11 @@ pub struct Vm {
impl Vm {
fn run(&mut self) -> Result {
loop {
print!("[");
debug!("[");
for value in self.stack.iter() {
print!("{:?} ", value);
debug!("{:?} ", value);
}
println!("]");
debug!("]");
let opcode = self.chunk.code[self.ip];
self.ip += 1;
match opcode {
@ -46,21 +44,32 @@ impl Vm {
}
OP_FALSE => self.push(Value::Bool(false)),
OP_TRUE => self.push(Value::Bool(true)),
OP_ADD => binary_op(self, add),
OP_SUBTRACT => binary_op(self, sub),
OP_MULTIPLY => binary_op(self, mul),
OP_DIVIDE => binary_op(self, div),
OP_NEGATE => {
let value = &self.pop();
let result = -value;
match result {
Ok(result) => self.push(result),
Err(e) => panic!("Error: {:?} {:?}", e, value),
OP_ADD => binary_op(self, |a, b| a + b),
OP_SUBTRACT => binary_op(self, |a, b| a - b),
OP_MULTIPLY => binary_op(self, |a, b| a * b),
OP_DIVIDE => binary_op(self, |a, b| a / b),
OP_AND => binary_op(self, |a, b| {
if let (Value::Bool(a), Value::Bool(b)) = (a, b) {
Ok(Value::Bool(*a && *b))
} else {
Err(anyhow!("Cannot and"))
}
}),
OP_OR => binary_op(self, |a, b| {
if let (Value::Bool(a), Value::Bool(b)) = (a, b) {
Ok(Value::Bool(*a || *b))
} else {
Err(anyhow!("Cannot compare"))
}
}),
OP_NOT => {}
OP_BITAND => binary_op(self, bitand),
OP_BITOR => binary_op(self, |a, b| a | b),
OP_BITXOR => binary_op(self, |a, b| a ^ b),
OP_NEGATE => unary_op(self, |a| -a),
OP_RETURN => {
println!("return {:?}", self.pop());
return Result::Ok;
// println!("{:?}", self.pop());
return Result::Ok(self.pop());
}
_ => {}
}
@ -76,36 +85,61 @@ impl Vm {
}
fn pop(&mut self) -> Value {
self.stack.pop().unwrap() //?
self.stack.pop().unwrap_or_else(|| Value::Error("Error occurred".to_string()))
}
}
fn binary_op(stack: &mut Vm, op: impl Fn(&Value, &Value) -> anyhow::Result<Value> + Copy) {
let a = stack.pop();
let b = stack.pop();
fn binary_op(vm: &mut Vm, op: impl Fn(&Value, &Value) -> anyhow::Result<Value> + Copy) {
let a = vm.pop();
let b = vm.pop();
let result = op(&a, &b);
match result {
Ok(result) => vm.push(result),
Err(e) => println!("Error: {} {:?} and {:?}", e.to_string(), a, b),
}
}
fn unary_op(stack: &mut Vm, op: impl Fn(&Value) -> anyhow::Result<Value> + Copy) {
let a = stack.pop();
let result = op(&a);
match result {
Ok(result) => stack.push(result),
Err(e) => panic!("Error: {:?} {:?} and {:?}", e, a, b),
Err(e) => panic!("Error: {:?} {:?}", e, a),
}
}
fn add(a: &Value, b: &Value) -> anyhow::Result<Value> {
a + b
}
fn sub(a: &Value, b: &Value) -> anyhow::Result<Value> {
a - b
}
fn mul(a: &Value, b: &Value) -> anyhow::Result<Value> {
a * b
}
fn div(a: &Value, b: &Value) -> anyhow::Result<Value> {
a / b
fn bitand(a: &Value, b: &Value) -> anyhow::Result<Value> {
a& b
}
#[derive(Debug, PartialEq)]
#[derive(Debug)]
pub enum Result {
Ok,
Ok(Value),
CompileError,
Error,
}
pub const OP_CONSTANT: u16 = 1;
pub const OP_ADD: u16 = 2;
pub const OP_SUBTRACT: u16 = 3;
pub const OP_MULTIPLY: u16 = 4;
pub const OP_DIVIDE: u16 = 5;
pub const OP_NEGATE: u16 = 6;
pub const OP_PRINT: u16 = 7;
pub const OP_RETURN: u16 = 8;
pub const OP_TRUE: u16 = 9;
pub const OP_FALSE: u16 = 10;
pub const OP_AND: u16 = 11;
pub const OP_OR: u16 = 12;
pub const OP_NOT: u16 = 13;
pub const OP_EQUAL: u16 = 14;
pub const OP_GREATER: u16 = 15;
pub const OP_LESS: u16 = 16;
pub const OP_NOT_EQUAL: u16 = 17;
pub const OP_GREATER_EQUAL: u16 = 18;
pub const OP_LESS_EQUAL: u16 = 19;
pub const OP_BITAND: u16 = 20;
pub const OP_BITOR: u16 = 21;
pub const OP_BITXOR: u16 = 22;

View file

@ -16,11 +16,10 @@ fn main() -> anyhow::Result<()> {
// chunk.add(crudlang::opcode::OP_ADD, 123);
//
// chunk.add(crudlang::opcode::OP_RETURN, 123);
let chunk = compile("-true")?;
let chunk = compile("1&3")?;
chunk.disassemble();
let result = interpret(chunk);
println!("{:?}",result);
Ok(())
}

View file

@ -1,10 +1 @@
pub const OP_CONSTANT: u16 = 1;
pub const OP_ADD: u16 = 2;
pub const OP_SUBTRACT: u16 = 3;
pub const OP_MULTIPLY: u16 = 4;
pub const OP_DIVIDE: u16 = 5;
pub const OP_NEGATE: u16 = 6;
pub const OP_PRINT: u16 = 7;
pub const OP_RETURN: u16 = 8;
pub const OP_TRUE: u16 = 9;
pub const OP_FALSE: u16 = 10;

View file

@ -5,6 +5,7 @@ use crate::{
TokenType::{self},
},
};
use crate::tokens::TokenType::BitXor;
pub fn scan(source: &str) -> Vec<Token> {
let scanner = Scanner {
@ -98,6 +99,23 @@ impl Scanner {
self.line += 1;
self.new_line = true;
}
'&' => {
let t = if self.match_next('&') {
TokenType::LogicalAnd
} else {
TokenType::BitAnd
};
self.add_token(t);
}
'|' => {
let t = if self.match_next('|') {
TokenType::LogicalOr
} else {
TokenType::BitOr
};
self.add_token(t);
}
'^' => self.add_token(BitXor),
_ => {
if is_digit(c) {
self.number();

View file

@ -49,7 +49,11 @@ pub(crate) enum TokenType {
Identifier,
String,
Number,
And,
LogicalAnd,
LogicalOr,
BitAnd,
BitOr,
BitXor,
Fn,
Struct,
Else,
@ -57,7 +61,6 @@ pub(crate) enum TokenType {
True,
Null,
If,
Or,
While,
For,
Return,

View file

@ -1,7 +1,7 @@
use anyhow::anyhow;
use chrono::Utc;
use std::collections::HashMap;
use std::ops::{Add, Div, Mul, Neg, Sub};
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Sub};
#[derive(Debug, Clone)]
pub enum Value {
@ -19,6 +19,7 @@ pub enum Value {
Struct,
List(Vec<Value>),
Map(HashMap<Value, Value>),
Error(String),
}
impl Into<Value> for i32 {
@ -179,8 +180,46 @@ impl Div<&Value> for &Value {
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a / b)),
(Value::F32(a), Value::F32(b)) => Ok(Value::F32(a / b)),
(Value::F64(a), Value::F64(b)) => Ok(Value::F64(a / b)),
//enum?
_ => Err(anyhow!("Cannot divide")),
}
}
}
impl BitAnd<&Value> for &Value {
type Output = anyhow::Result<Value>;
fn bitand(self, rhs: &Value) -> Self::Output {
match (self, rhs) {
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(a & b)),
(Value::I64(a), Value::I64(b)) => Ok(Value::I64(a & b)),
(Value::U32(a), Value::U32(b)) => Ok(Value::U32(a & b)),
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a & b)),
_ => Err(anyhow!("Cannot do bitwise-and on")),
}
}
}
impl BitOr<&Value> for &Value {
type Output = anyhow::Result<Value>;
fn bitor(self, rhs: &Value) -> Self::Output {
match (self, rhs) {
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(a | b)),
(Value::I64(a), Value::I64(b)) => Ok(Value::I64(a | b)),
(Value::U32(a), Value::U32(b)) => Ok(Value::U32(a | b)),
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a | b)),
_ => Err(anyhow!("Cannot do bitwise-or on")),
}
}
}
impl BitXor<&Value> for &Value {
type Output = anyhow::Result<Value>;
fn bitxor(self, rhs: &Value) -> Self::Output {
match (self, rhs) {
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(a ^ b)),
(Value::I64(a), Value::I64(b)) => Ok(Value::I64(a ^ b)),
(Value::U32(a), Value::U32(b)) => Ok(Value::U32(a ^ b)),
(Value::U64(a), Value::U64(b)) => Ok(Value::U64(a ^ b)),
_ => Err(anyhow!("Cannot do bitwise-xor on")),
}
}
}