Fix compiling of comparison expressions; Implement LoadEncoded in the VM
This commit is contained in:
parent
1155b5fff8
commit
72421bf510
@ -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(
|
||||
|
@ -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!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user