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::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 { pub struct Chunk {
name: String, name: String,
@ -34,7 +38,7 @@ impl Chunk {
while offset < self.code.len() { while offset < self.code.len() {
offset = self.disassemble_inst(offset); offset = self.disassemble_inst(offset);
} }
println!("== {} ==", self.name); println!();
} }
fn disassemble_inst(&self, offset: usize) -> usize { fn disassemble_inst(&self, offset: usize) -> usize {
@ -48,9 +52,12 @@ impl Chunk {
match instruction { match instruction {
OP_CONSTANT => self.constant_inst("OP_CONSTANT", offset), OP_CONSTANT => self.constant_inst("OP_CONSTANT", offset),
OP_ADD => self.simple_inst("OP_ADD", 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_SUBTRACT => self.simple_inst("OP_SUBTRACT", offset),
OP_MULTIPLY => self.simple_inst("OP_MULTIPLY", offset), OP_MULTIPLY => self.simple_inst("OP_MULTIPLY", offset),
OP_DIVIDE => self.simple_inst("OP_DIVIDE", 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_NEGATE => self.simple_inst("OP_NEGATE", offset),
OP_RETURN => self.simple_inst("OP_RETURN", offset), OP_RETURN => self.simple_inst("OP_RETURN", offset),
_ => { _ => {
@ -67,7 +74,7 @@ impl Chunk {
fn constant_inst(&self, name: &str, offset: usize) -> usize { fn constant_inst(&self, name: &str, offset: usize) -> usize {
let constant = self.code[offset + 1]; let constant = self.code[offset + 1];
print!("{} {} ", name, constant); debug!("{} {} ", name, constant);
self.print_value(&self.constants[constant as usize]); self.print_value(&self.constants[constant as usize]);
offset + 2 offset + 2
} }

View file

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

View file

@ -1,8 +1,6 @@
use anyhow::anyhow;
use tracing::debug;
use crate::chunk::Chunk; 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; use crate::value::Value;
pub mod chunk; pub mod chunk;
@ -31,11 +29,11 @@ pub struct Vm {
impl Vm { impl Vm {
fn run(&mut self) -> Result { fn run(&mut self) -> Result {
loop { loop {
print!("["); debug!("[");
for value in self.stack.iter() { for value in self.stack.iter() {
print!("{:?} ", value); debug!("{:?} ", value);
} }
println!("]"); debug!("]");
let opcode = self.chunk.code[self.ip]; let opcode = self.chunk.code[self.ip];
self.ip += 1; self.ip += 1;
match opcode { match opcode {
@ -46,21 +44,32 @@ impl Vm {
} }
OP_FALSE => self.push(Value::Bool(false)), OP_FALSE => self.push(Value::Bool(false)),
OP_TRUE => self.push(Value::Bool(true)), OP_TRUE => self.push(Value::Bool(true)),
OP_ADD => binary_op(self, add), OP_ADD => binary_op(self, |a, b| a + b),
OP_SUBTRACT => binary_op(self, sub), OP_SUBTRACT => binary_op(self, |a, b| a - b),
OP_MULTIPLY => binary_op(self, mul), OP_MULTIPLY => binary_op(self, |a, b| a * b),
OP_DIVIDE => binary_op(self, div), OP_DIVIDE => binary_op(self, |a, b| a / b),
OP_NEGATE => { OP_AND => binary_op(self, |a, b| {
let value = &self.pop(); if let (Value::Bool(a), Value::Bool(b)) = (a, b) {
let result = -value; Ok(Value::Bool(*a && *b))
match result { } else {
Ok(result) => self.push(result), Err(anyhow!("Cannot and"))
Err(e) => panic!("Error: {:?} {:?}", e, value),
} }
}),
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 => { OP_RETURN => {
println!("return {:?}", self.pop()); // println!("{:?}", self.pop());
return Result::Ok; return Result::Ok(self.pop());
} }
_ => {} _ => {}
} }
@ -76,36 +85,61 @@ impl Vm {
} }
fn pop(&mut self) -> Value { 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) { fn binary_op(vm: &mut Vm, op: impl Fn(&Value, &Value) -> anyhow::Result<Value> + Copy) {
let a = stack.pop(); let a = vm.pop();
let b = stack.pop(); let b = vm.pop();
let result = op(&a, &b); let result = op(&a, &b);
match result { match result {
Ok(result) => stack.push(result), Ok(result) => vm.push(result),
Err(e) => panic!("Error: {:?} {:?} and {:?}", e, a, b), Err(e) => println!("Error: {} {:?} and {:?}", e.to_string(), a, b),
} }
} }
fn add(a: &Value, b: &Value) -> anyhow::Result<Value> { fn unary_op(stack: &mut Vm, op: impl Fn(&Value) -> anyhow::Result<Value> + Copy) {
a + b let a = stack.pop();
} let result = op(&a);
fn sub(a: &Value, b: &Value) -> anyhow::Result<Value> { match result {
a - b Ok(result) => stack.push(result),
} Err(e) => panic!("Error: {:?} {:?}", e, a),
fn mul(a: &Value, b: &Value) -> anyhow::Result<Value> { }
a * b
}
fn div(a: &Value, b: &Value) -> anyhow::Result<Value> {
a / b
} }
#[derive(Debug, PartialEq)]
fn bitand(a: &Value, b: &Value) -> anyhow::Result<Value> {
a& b
}
#[derive(Debug)]
pub enum Result { pub enum Result {
Ok, Ok(Value),
CompileError, CompileError,
Error, 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_ADD, 123);
// //
// chunk.add(crudlang::opcode::OP_RETURN, 123); // chunk.add(crudlang::opcode::OP_RETURN, 123);
let chunk = compile("-true")?; let chunk = compile("1&3")?;
chunk.disassemble(); chunk.disassemble();
let result = interpret(chunk); let result = interpret(chunk);
println!("{:?}",result);
Ok(()) 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}, TokenType::{self},
}, },
}; };
use crate::tokens::TokenType::BitXor;
pub fn scan(source: &str) -> Vec<Token> { pub fn scan(source: &str) -> Vec<Token> {
let scanner = Scanner { let scanner = Scanner {
@ -98,6 +99,23 @@ impl Scanner {
self.line += 1; self.line += 1;
self.new_line = true; 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) { if is_digit(c) {
self.number(); self.number();

View file

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

View file

@ -1,7 +1,7 @@
use anyhow::anyhow; use anyhow::anyhow;
use chrono::Utc; use chrono::Utc;
use std::collections::HashMap; 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)] #[derive(Debug, Clone)]
pub enum Value { pub enum Value {
@ -19,6 +19,7 @@ pub enum Value {
Struct, Struct,
List(Vec<Value>), List(Vec<Value>),
Map(HashMap<Value, Value>), Map(HashMap<Value, Value>),
Error(String),
} }
impl Into<Value> for i32 { 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::U64(a), Value::U64(b)) => Ok(Value::U64(a / b)),
(Value::F32(a), Value::F32(b)) => Ok(Value::F32(a / b)), (Value::F32(a), Value::F32(b)) => Ok(Value::F32(a / b)),
(Value::F64(a), Value::F64(b)) => Ok(Value::F64(a / b)), (Value::F64(a), Value::F64(b)) => Ok(Value::F64(a / b)),
//enum?
_ => Err(anyhow!("Cannot divide")), _ => 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")),
}
}
}