1
0

Fix compiling of comparison expressions; Implement LoadEncoded in the VM

This commit is contained in:
Jeff 2025-02-07 13:29:14 -05:00
parent 1155b5fff8
commit 72421bf510
4 changed files with 129 additions and 85 deletions

View File

@ -282,9 +282,13 @@ impl<'src> Compiler<'src> {
self.instructions
.iter()
.rev()
.find_map(|(instruction, r#type, _)| {
if r#type == &Type::Byte && instruction.yields_value() {
Some(instruction.a_field() + 1)
.find_map(|(instruction, _, _)| {
if instruction.operation() == Operation::LOAD_ENCODED {
if instruction.b_type() == TypeCode::BYTE {
Some(instruction.a_field() + 1)
} else {
None
}
} else {
None
}
@ -530,46 +534,6 @@ impl<'src> Compiler<'src> {
.unwrap_or(Type::None)
}
fn get_register_type(&self, register_index: u16) -> Result<Type, CompileError> {
if let Some(r#type) = self.locals.iter().find_map(|local| {
if local.register_index == register_index {
Some(local.r#type.clone())
} else {
None
}
}) {
return Ok(r#type);
}
for (instruction, r#type, _) in &self.instructions {
if !instruction.yields_value() {
continue;
}
let operation = instruction.operation();
if let Operation::LOAD_LIST = operation {
let LoadList { start_register, .. } = LoadList::from(*instruction);
let item_type = self.get_register_type(start_register)?;
return Ok(Type::List(Box::new(item_type)));
}
if let Operation::LOAD_SELF = operation {
return Ok(Type::SelfFunction);
}
if instruction.yields_value() {
return Ok(r#type.clone());
}
}
Err(CompileError::CannotResolveRegisterType {
register_index: register_index as usize,
position: self.current_position,
})
}
/// Updates [`Self::type`] with the given [Type] as `return_type`.
///
/// If [`Self::type`] is already set, it will check if the given [Type] is compatible.
@ -831,9 +795,13 @@ impl<'src> Compiler<'src> {
}
fn handle_binary_argument(&mut self, instruction: &Instruction) -> (Operand, bool) {
let (argument, push_back) = (instruction.as_operand(), !instruction.yields_value());
let operand = instruction.as_operand();
let push_back = match instruction.operation() {
Operation::LOAD_ENCODED | Operation::LOAD_SELF => true,
_ => !instruction.yields_value(),
};
(argument, push_back)
(operand, push_back)
}
fn parse_math_binary(&mut self) -> Result<(), CompileError> {
@ -875,14 +843,18 @@ impl<'src> Compiler<'src> {
check_math_type(&left_type, operator, &left_position)?;
let destination = match left_type {
Type::Boolean => self.next_boolean_register(),
Type::Byte => self.next_byte_register(),
Type::Character => self.next_character_register(),
Type::Float => self.next_float_register(),
Type::Integer => self.next_integer_register(),
Type::String => self.next_string_register(),
_ => unreachable!(),
let destination = if is_assignment {
left.index()
} else {
match left_type {
Type::Boolean => self.next_boolean_register(),
Type::Byte => self.next_byte_register(),
Type::Character => self.next_character_register(),
Type::Float => self.next_float_register(),
Type::Integer => self.next_integer_register(),
Type::String => self.next_string_register(),
_ => unreachable!(),
}
};
if is_assignment && !left_is_mutable_local {
@ -964,28 +936,15 @@ impl<'src> Compiler<'src> {
});
}
let (left_instruction, left_type, left_position) =
self.instructions
.pop()
.ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let (left, push_back_left) = self.handle_binary_argument(&left_instruction);
let operator = self.current_token;
let operator_position = self.current_position;
let rule = ParseRule::from(&operator);
// TODO: Check if the left type is a valid type for comparison
if push_back_left {
self.instructions
.push((left_instruction, left_type, left_position));
}
self.advance()?;
self.parse_sub_expression(&rule.precedence)?;
println!("{:?}", self.instructions);
let (right_instruction, right_type, right_position) =
self.instructions
.pop()
@ -993,17 +952,32 @@ impl<'src> Compiler<'src> {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let (left_instruction, left_type, left_position) =
self.instructions
.pop()
.ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})?;
let (left, push_back_left) = self.handle_binary_argument(&left_instruction);
let (right, push_back_right) = self.handle_binary_argument(&right_instruction);
println!("{left_instruction} {right_instruction}");
// TODO: Check if the left type is a valid type for comparison
// TODO: Check if the right type is a valid type for comparison
// TODO: Check if the left and right types are compatible
if push_back_left {
self.instructions
.push((left_instruction, left_type, left_position));
}
if push_back_right {
self.instructions
.push((right_instruction, right_type, right_position));
}
let destination = self.next_boolean_register();
let comparison = match operator {
Token::DoubleEqual => Instruction::equal(true, left, right),
Token::BangEqual => Instruction::equal(false, left, right),
@ -1027,6 +1001,7 @@ impl<'src> Compiler<'src> {
}
};
let jump = Instruction::jump(1, true);
let destination = self.next_boolean_register();
let load_true =
Instruction::load_encoded(destination, true as u16, TypeCode::BOOLEAN, true);
let load_false =
@ -1471,7 +1446,7 @@ impl<'src> Compiler<'src> {
self.instructions
.insert(block_start, (jump, Type::None, self.current_position));
let jump_back_distance = (block_end - expression_start + 1) as u16;
let jump_back_distance = (block_end - expression_start) as u16;
let jump_back = Instruction::from(Jump {
offset: jump_back_distance,
is_positive: false,
@ -1611,10 +1586,10 @@ impl<'src> Compiler<'src> {
fn parse_implicit_return(&mut self) -> Result<(), CompileError> {
if matches!(self.get_last_operation(), Some(Operation::POINT)) {
let Point { destination, to } = Point::from(self.instructions.last().unwrap().0);
let Point { to, .. } = Point::from(self.instructions.last().unwrap().0);
let (_, r#type, _) = self.instructions.pop().unwrap();
let r#return = Instruction::r#return(true, destination, to.as_type());
let r#return = Instruction::r#return(true, to.index(), to.as_type());
self.emit_instruction(r#return, r#type, self.current_position);
} else if matches!(self.get_last_operation(), Some(Operation::RETURN))
@ -1715,7 +1690,7 @@ impl<'src> Compiler<'src> {
let r#type = if let Some(r#type) = explicit_type {
r#type
} else {
self.get_register_type(register_index)?
self.get_last_instruction_type()
};
self.declare_local(

View File

@ -281,9 +281,9 @@ impl Instruction {
pub fn as_operand(&self) -> Operand {
match self.operation() {
Operation::POINT => {
let Point { destination, to } = Point::from(*self);
let Point { to, .. } = Point::from(*self);
Operand::Register(destination, to.as_type())
Operand::Register(to.index(), to.as_type())
}
Operation::LOAD_ENCODED => {
let LoadEncoded {
@ -664,24 +664,24 @@ impl Display for Operand {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Operand::Constant(index, r#type) => match *r#type {
TypeCode::BOOLEAN => write!(f, "C_{}", index),
TypeCode::CHARACTER => write!(f, "C_{}", index),
TypeCode::INTEGER => write!(f, "C_{}", index),
TypeCode::FLOAT => write!(f, "C_{}", index),
TypeCode::STRING => write!(f, "C_{}", index),
TypeCode::LIST => write!(f, "C_{}", index),
TypeCode::FUNCTION => write!(f, "C_{}", index),
_ => panic!("Unknown type code: {}", r#type.0),
_ => unreachable!(),
},
Operand::Register(index, r#type) => match *r#type {
TypeCode::BOOLEAN => write!(f, "R_BOOL_{}", index),
TypeCode::BYTE => write!(f, "R_BYTE_{}", index),
TypeCode::CHARACTER => write!(f, "R_CHAR_{}", index),
TypeCode::INTEGER => write!(f, "R_INT_{}", index),
TypeCode::FLOAT => write!(f, "R_FLOAT_{}", index),
TypeCode::STRING => write!(f, "R_STR_{}", index),
TypeCode::LIST => write!(f, "R_LIST_{}", index),
TypeCode::FUNCTION => write!(f, "R_FN_{}", index),
_ => panic!("Unknown type code: {}", r#type.0),
_ => unreachable!(),
},
}
}

View File

@ -45,7 +45,7 @@ pub type RunnerLogic = fn(InstructionFields, &mut Thread);
pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 23] = [
point,
close,
load_boolean,
load_encoded,
load_constant,
load_function,
load_list,
@ -72,7 +72,30 @@ pub fn point(_: InstructionFields, thread: &mut Thread) {}
pub fn close(_: InstructionFields, thread: &mut Thread) {}
pub fn load_boolean(_: InstructionFields, _: &mut Thread) {}
pub fn load_encoded(instruction: InstructionFields, thread: &mut Thread) {
let destination = instruction.a_field;
let value = instruction.b_field;
let value_type = instruction.b_type;
let jump_next = instruction.c_field != 0;
match value_type {
TypeCode::BOOLEAN => {
let register = Register::Value(value != 0);
thread.set_boolean_register(destination as usize, register);
}
TypeCode::BYTE => {
let register = Register::Value(value as u8);
thread.set_byte_register(destination as usize, register);
}
_ => unreachable!(),
}
if jump_next {
thread.current_frame_mut().ip += 1;
}
}
pub fn load_constant(instruction: InstructionFields, thread: &mut Thread) {
let destination = instruction.a_field as usize;
@ -152,6 +175,30 @@ pub fn add(instruction: InstructionFields, thread: &mut Thread) {
thread.set_integer_register(destination, register);
}
(TypeCode::BYTE, TypeCode::BYTE) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_byte().unwrap()
} else {
unsafe { thread.get_constant(left).as_byte().unwrap_unchecked() }
}
} else {
thread.get_byte_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_byte().unwrap()
} else {
unsafe { thread.get_constant(right).as_byte().unwrap_unchecked() }
}
} else {
thread.get_byte_register(right)
};
let sum = left_value + right_value;
let register = Register::Value(sum);
thread.set_byte_register(destination, register);
}
(TypeCode::STRING, TypeCode::STRING) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
@ -241,6 +288,31 @@ pub fn less(instruction: InstructionFields, thread: &mut Thread) {
thread.current_frame_mut().ip += 1;
}
}
(TypeCode::BYTE, TypeCode::BYTE) => {
let left_value = if left_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(left).as_byte().unwrap()
} else {
unsafe { thread.get_constant(left).as_byte().unwrap_unchecked() }
}
} else {
thread.get_byte_register(left)
};
let right_value = if right_is_constant {
if cfg!(debug_assertions) {
thread.get_constant(right).as_byte().unwrap()
} else {
unsafe { thread.get_constant(right).as_byte().unwrap_unchecked() }
}
} else {
thread.get_byte_register(right)
};
let result = left_value < right_value;
if result == comparator {
thread.current_frame_mut().ip += 1;
}
}
_ => unimplemented!(),
}
}
@ -314,7 +386,7 @@ mod tests {
const ALL_OPERATIONS: [(Operation, RunnerLogic); 23] = [
(Operation::POINT, point),
(Operation::CLOSE, close),
(Operation::LOAD_ENCODED, load_boolean),
(Operation::LOAD_ENCODED, load_encoded),
(Operation::LOAD_CONSTANT, load_constant),
(Operation::LOAD_FUNCTION, load_function),
(Operation::LOAD_LIST, load_list),

View File

@ -53,10 +53,7 @@ impl Thread {
unsafe { current_frame.action_sequence.actions.get_unchecked_mut(ip) }
};
trace!(
"Instruction operation: {}",
current_action.instruction.operation
);
trace!("Instruction: {}", current_action.instruction.operation);
(current_action.logic)(current_action.instruction, &mut self);