1
0

Continue fixing the compiler to work with the new instructions

This commit is contained in:
Jeff 2024-11-27 23:12:10 -05:00
parent a57268fcba
commit ef22fa18b6
3 changed files with 114 additions and 276 deletions

View File

@ -61,10 +61,7 @@ pub struct Compiler<'src> {
previous_position: Span,
return_type: Option<Type>,
optimization_count: usize,
previous_expression_type: Type,
minimum_register: u16,
block_index: u8,
current_scope: Scope,
}
@ -88,8 +85,6 @@ impl<'src> Compiler<'src> {
previous_token: Token::Eof,
previous_position: Span(0, 0),
return_type: None,
optimization_count: 0,
previous_expression_type: Type::None,
minimum_register: 0,
block_index: 0,
current_scope: Scope::default(),
@ -97,7 +92,7 @@ impl<'src> Compiler<'src> {
}
pub fn finish(mut self) -> Chunk {
log::info!("End chunk with {} optimizations", self.optimization_count);
log::info!("End chunk");
if let Type::None = *self.chunk.r#type().return_type {
self.chunk.set_type(FunctionType {
@ -120,14 +115,14 @@ impl<'src> Compiler<'src> {
self.chunk
.instructions()
.iter()
.filter_map(|(instruction, _, _)| {
.rev()
.find_map(|(instruction, _, _)| {
if instruction.yields_value() {
Some(instruction.a() + 1)
} else {
None
}
})
.max()
.unwrap_or(self.minimum_register)
}
@ -276,107 +271,12 @@ impl<'src> Compiler<'src> {
})
}
pub fn get_instruction_type(&self, instruction: &Instruction) -> Result<Type, CompileError> {
match instruction.operation() {
Operation::LoadBoolean => Ok(Type::Boolean),
Operation::LoadConstant => {
let LoadConstant { constant_index, .. } = LoadConstant::from(instruction);
self.chunk
.get_constant(constant_index)
.map(|value| value.r#type())
.map_err(|error| CompileError::Chunk {
error,
position: self.current_position,
})
}
Operation::LoadList => {
let LoadList { start_register, .. } = LoadList::from(instruction);
let first_instruction = self
.chunk
.get_instruction(start_register as usize)
.map_err(|error| CompileError::Chunk {
error,
position: self.current_position,
})?
.0; // TODO: Handle the case that the first instruction is Close
let item_type = self.get_instruction_type(&first_instruction)?;
Ok(Type::List(Box::new(item_type)))
}
Operation::LoadSelf => Ok(Type::SelfChunk),
Operation::GetLocal => {
let GetLocal { local_index, .. } = GetLocal::from(instruction);
self.get_local(local_index)
.map(|local| local.r#type.clone())
}
Operation::Add => {
let Add { left, .. } = Add::from(instruction);
self.get_argument_type(&left)
}
Operation::Subtract => {
let Subtract { left, .. } = Subtract::from(instruction);
self.get_argument_type(&left)
}
Operation::Multiply => {
let Multiply { left, .. } = Multiply::from(instruction);
self.get_argument_type(&left)
}
Operation::Divide => {
let Divide { left, .. } = Divide::from(instruction);
self.get_argument_type(&left)
}
Operation::Modulo => {
let Modulo { left, .. } = Modulo::from(instruction);
self.get_argument_type(&left)
}
Operation::Negate => {
let Negate { argument, .. } = Negate::from(instruction);
self.get_argument_type(&argument)
}
Operation::Not => {
let Not { argument, .. } = Not::from(instruction);
self.get_argument_type(&argument)
}
Operation::Call => {
let Call { function, .. } = Call::from(instruction);
let function_type = self.get_argument_type(&function)?;
if let Type::Function(FunctionType { return_type, .. }) = function_type {
Ok(*return_type)
} else {
Err(CompileError::ExpectedFunctionType {
found: function_type,
position: self.current_position,
})
}
}
Operation::CallNative => {
let CallNative { function, .. } = CallNative::from(instruction);
Ok(*function.r#type().return_type)
}
Operation::Move
| Operation::Close
| Operation::DefineLocal
| Operation::SetLocal
| Operation::Test
| Operation::TestSet
| Operation::Equal
| Operation::Less
| Operation::LessEqual
| Operation::Jump
| Operation::Return => Ok(Type::None),
}
fn get_last_instruction_type(&self) -> Type {
self.chunk
.instructions()
.last()
.map(|(_, r#type, _)| r#type.clone())
.unwrap_or(Type::None)
}
pub fn get_argument_type(&self, argument: &Argument) -> Result<Type, CompileError> {
@ -445,15 +345,12 @@ impl<'src> Compiler<'src> {
Ok(())
}
fn emit_instruction(&mut self, instruction: Instruction, position: Span) {
fn emit_instruction(&mut self, instruction: Instruction, r#type: Type, position: Span) {
log::debug!(
"Emitting {} at {}",
instruction.operation().to_string().bold(),
position.to_string()
);
let r#type = self
.get_instruction_type(&instruction)
.unwrap_or(Type::None);
self.chunk
.instructions_mut()
@ -465,6 +362,7 @@ impl<'src> Compiler<'src> {
constant: ConcreteValue,
position: Span,
) -> Result<(), CompileError> {
let r#type = constant.r#type();
let constant_index = self.chunk.push_or_get_constant(constant);
let destination = self.next_register();
let instruction = Instruction::from(LoadConstant {
@ -473,7 +371,7 @@ impl<'src> Compiler<'src> {
jump_next: false,
});
self.emit_instruction(instruction, position);
self.emit_instruction(instruction, r#type, position);
Ok(())
}
@ -492,9 +390,7 @@ impl<'src> Compiler<'src> {
jump_next: false,
});
self.emit_instruction(instruction, position);
self.previous_expression_type = Type::Boolean;
self.emit_instruction(instruction, Type::Boolean, position);
Ok(())
} else {
@ -518,8 +414,6 @@ impl<'src> Compiler<'src> {
self.emit_constant(value, position)?;
self.previous_expression_type = Type::Byte;
Ok(())
} else {
Err(CompileError::ExpectedToken {
@ -540,8 +434,6 @@ impl<'src> Compiler<'src> {
self.emit_constant(value, position)?;
self.previous_expression_type = Type::Character;
Ok(())
} else {
Err(CompileError::ExpectedToken {
@ -568,8 +460,6 @@ impl<'src> Compiler<'src> {
self.emit_constant(value, position)?;
self.previous_expression_type = Type::Float;
Ok(())
} else {
Err(CompileError::ExpectedToken {
@ -596,8 +486,6 @@ impl<'src> Compiler<'src> {
self.emit_constant(value, position)?;
self.previous_expression_type = Type::Integer;
Ok(())
} else {
Err(CompileError::ExpectedToken {
@ -618,8 +506,6 @@ impl<'src> Compiler<'src> {
self.emit_constant(value, position)?;
self.previous_expression_type = Type::String;
Ok(())
} else {
Err(CompileError::ExpectedToken {
@ -645,7 +531,8 @@ impl<'src> Compiler<'src> {
self.advance()?;
self.parse_expression()?;
let (previous_instruction, _, previous_position) = self.pop_last_instruction()?;
let (previous_instruction, previous_type, previous_position) =
self.pop_last_instruction()?;
let argument = if let Some(argument) = previous_instruction.destination_as_argument() {
argument
} else {
@ -674,11 +561,7 @@ impl<'src> Compiler<'src> {
}
};
self.emit_instruction(instruction, operator_position);
if let TokenKind::Bang = operator.kind() {
self.previous_expression_type = Type::Boolean;
}
self.emit_instruction(instruction, previous_type, operator_position);
Ok(())
}
@ -693,22 +576,14 @@ impl<'src> Compiler<'src> {
position: self.previous_position,
}
})?;
let push_back = matches!(
let push_back = !matches!(
instruction.operation(),
Operation::Add
| Operation::Subtract
| Operation::Multiply
| Operation::Divide
| Operation::Modulo
| Operation::Equal
| Operation::Less
| Operation::LessEqual
| Operation::Test
| Operation::TestSet
Operation::LoadConstant | Operation::GetLocal,
);
Ok((argument, push_back))
}
fn parse_math_binary(&mut self) -> Result<(), CompileError> {
let (left_instruction, left_type, left_position) =
self.chunk.instructions_mut().pop().ok_or_else(|| {
@ -726,43 +601,52 @@ impl<'src> Compiler<'src> {
let operator = self.current_token;
let operator_position = self.current_position;
let rule = ParseRule::from(&operator);
let is_assignment =
if let Token::PlusEqual | Token::MinusEqual | Token::StarEqual | Token::SlashEqual =
operator
{
if !left_is_mutable_local {
return Err(CompileError::ExpectedMutableVariable {
found: self.previous_token.to_owned(),
position: left_position,
});
}
let is_assignment = matches!(
operator,
Token::PlusEqual | Token::MinusEqual | Token::StarEqual | Token::SlashEqual
);
let r#type = if is_assignment {
Type::None
} else {
left_type.clone()
};
true
} else {
false
};
if is_assignment && !left_is_mutable_local {
return Err(CompileError::ExpectedMutableVariable {
found: self.previous_token.to_owned(),
position: left_position,
});
}
if push_back_left {
self.chunk
.instructions_mut()
.push((left_instruction, left_type, left_position));
}
self.advance()?;
self.parse_sub_expression(&rule.precedence)?;
let (right_instruction, right_type, right_position) = self.pop_last_instruction()?;
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
if push_back_right {
self.chunk
.instructions_mut()
.push((right_instruction, right_type, right_position));
}
let destination = if is_assignment {
left.index()
} else {
self.next_register()
};
let instruction = match operator {
Token::Plus => Instruction::add(destination, left, right),
Token::PlusEqual => Instruction::add(destination, left, right),
Token::Minus => Instruction::subtract(destination, left, right),
Token::MinusEqual => Instruction::subtract(destination, left, right),
Token::Star => Instruction::multiply(destination, left, right),
Token::StarEqual => Instruction::multiply(destination, left, right),
Token::Slash => Instruction::divide(destination, left, right),
Token::SlashEqual => Instruction::divide(destination, left, right),
Token::Percent => Instruction::modulo(destination, left, right),
Token::PercentEqual => Instruction::modulo(destination, left, right),
Token::Plus | Token::PlusEqual => Instruction::add(destination, left, right),
Token::Minus | Token::MinusEqual => Instruction::subtract(destination, left, right),
Token::Star | Token::StarEqual => Instruction::multiply(destination, left, right),
Token::Slash | Token::SlashEqual => Instruction::divide(destination, left, right),
Token::Percent | Token::PercentEqual => Instruction::modulo(destination, left, right),
_ => {
return Err(CompileError::ExpectedTokenMultiple {
expected: &[
@ -783,25 +667,7 @@ impl<'src> Compiler<'src> {
}
};
if push_back_left {
self.chunk
.instructions_mut()
.push((left_instruction, left_type, left_position));
}
if push_back_right {
self.chunk
.instructions_mut()
.push((right_instruction, right_type, right_position));
}
self.emit_instruction(instruction, operator_position);
if is_assignment {
self.previous_expression_type = Type::None;
} else {
self.previous_expression_type = self.get_instruction_type(&left_instruction)?;
}
self.emit_instruction(instruction, r#type, operator_position);
Ok(())
}
@ -848,7 +714,7 @@ impl<'src> Compiler<'src> {
position: right_position,
}
})?;
let instruction = match operator {
let comparison = match operator {
Token::DoubleEqual => Instruction::from(Equal {
value: true,
left,
@ -911,12 +777,10 @@ impl<'src> Compiler<'src> {
jump_next: false,
});
self.emit_instruction(instruction, operator_position);
self.emit_instruction(jump, operator_position);
self.emit_instruction(load_true, operator_position);
self.emit_instruction(load_false, operator_position);
self.previous_expression_type = Type::Boolean;
self.emit_instruction(comparison, Type::None, operator_position);
self.emit_instruction(jump, Type::None, operator_position);
self.emit_instruction(load_true, Type::Boolean, operator_position);
self.emit_instruction(load_false, Type::Boolean, operator_position);
Ok(())
}
@ -961,13 +825,11 @@ impl<'src> Compiler<'src> {
});
self.advance()?;
self.emit_instruction(left_instruction, left_position);
self.emit_instruction(test, operator_position);
self.emit_instruction(jump, operator_position);
self.emit_instruction(left_instruction, left_type, left_position);
self.emit_instruction(test, Type::None, operator_position);
self.emit_instruction(jump, Type::None, operator_position);
self.parse_sub_expression(&rule.precedence)?;
self.previous_expression_type = Type::Boolean;
Ok(())
}
@ -992,9 +854,7 @@ impl<'src> Compiler<'src> {
let destination = self.next_register();
let load_self = Instruction::from(LoadSelf { destination });
self.emit_instruction(load_self, start_position);
self.previous_expression_type = Type::SelfChunk;
self.emit_instruction(load_self, Type::SelfChunk, start_position);
return Ok(());
} else {
@ -1032,16 +892,11 @@ impl<'src> Compiler<'src> {
local_index,
});
self.emit_instruction(set_local, start_position);
self.previous_expression_type = Type::None;
self.emit_instruction(set_local, Type::None, start_position);
let mut optimizer = Optimizer::new(&mut self.chunk);
let optimized = optimizer.optimize_set_local();
if optimized {
self.optimization_count += 1;
}
optimizer.optimize_set_local();
return Ok(());
}
@ -1053,9 +908,7 @@ impl<'src> Compiler<'src> {
});
let r#type = self.get_local(local_index)?.r#type.clone();
self.emit_instruction(get_local, self.previous_position);
self.previous_expression_type = r#type;
self.emit_instruction(get_local, r#type, self.previous_position);
Ok(())
}
@ -1109,25 +962,19 @@ impl<'src> Compiler<'src> {
self.parse_expression()?;
if expected_register > start_register {
if let Err(conflict) = item_type.check(&self.previous_expression_type) {
return Err(CompileError::ListItemTypeConflict {
conflict,
position: self.previous_position,
});
}
}
item_type = self.previous_expression_type.clone();
let actual_register = self.next_register() - 1;
if item_type == Type::Any {
item_type = self.get_last_instruction_type();
}
if expected_register < actual_register {
let close = Instruction::from(Close {
from: expected_register,
to: actual_register,
});
self.emit_instruction(close, self.current_position);
self.emit_instruction(close, Type::None, self.current_position);
}
self.allow(Token::Comma)?;
@ -1140,9 +987,7 @@ impl<'src> Compiler<'src> {
start_register,
});
self.emit_instruction(load_list, Span(start, end));
self.previous_expression_type = Type::List(Box::new(item_type));
self.emit_instruction(load_list, Type::List(Box::new(item_type)), Span(start, end));
Ok(())
}
@ -1170,7 +1015,7 @@ impl<'src> Compiler<'src> {
value: true,
});
self.emit_instruction(test, self.current_position)
self.emit_instruction(test, Type::None, self.current_position)
}
let if_block_start = self.chunk.len();
@ -1188,7 +1033,7 @@ impl<'src> Compiler<'src> {
let if_block_end = self.chunk.len();
let mut if_block_distance = (if_block_end - if_block_start) as u16;
let if_block_type = self.previous_expression_type.clone();
let if_block_type = self.get_last_instruction_type();
let if_last_register = self.next_register().saturating_sub(1);
if let Token::Else = self.current_token {
@ -1205,7 +1050,7 @@ impl<'src> Compiler<'src> {
}
true
} else if self.previous_expression_type != Type::None {
} else if if_block_type != Type::None {
return Err(CompileError::IfMissingElse {
position: Span(if_block_start_position.0, self.current_position.1),
});
@ -1215,8 +1060,9 @@ impl<'src> Compiler<'src> {
let else_block_end = self.chunk.len();
let else_block_distance = (else_block_end - if_block_end) as u16;
let else_block_type = self.get_last_instruction_type();
if let Err(conflict) = if_block_type.check(&self.previous_expression_type) {
if let Err(conflict) = if_block_type.check(&else_block_type) {
return Err(CompileError::IfElseBranchMismatch {
conflict,
position: Span(if_block_start_position.0, self.current_position.1),
@ -1267,9 +1113,7 @@ impl<'src> Compiler<'src> {
if self.chunk.len() >= 4 {
let mut optimizer = Optimizer::new(&mut self.chunk);
if optimizer.optimize_control_flow() {
self.optimization_count += 1
}
optimizer.optimize_control_flow();
}
let else_last_register = self.next_register().saturating_sub(1);
@ -1279,7 +1123,7 @@ impl<'src> Compiler<'src> {
});
if if_last_register < else_last_register {
self.emit_instruction(r#move, self.current_position);
self.emit_instruction(r#move, Type::None, self.current_position);
}
Ok(())
@ -1327,9 +1171,7 @@ impl<'src> Compiler<'src> {
is_positive: false,
});
self.emit_instruction(jump_back, self.current_position);
self.previous_expression_type = Type::None;
self.emit_instruction(jump_back, Type::None, self.current_position);
Ok(())
}
@ -1353,23 +1195,23 @@ impl<'src> Compiler<'src> {
to: actual_register,
});
self.emit_instruction(close, self.current_position);
self.emit_instruction(close, Type::None, self.current_position);
}
self.allow(Token::Comma)?;
}
self.previous_expression_type = *function.r#type().return_type;
let end = self.previous_position.1;
let destination = self.next_register();
let argument_count = destination - start_register;
let return_type = *function.r#type().return_type;
let call_native = Instruction::from(CallNative {
destination,
function,
argument_count,
});
self.emit_instruction(call_native, Span(start, end));
self.emit_instruction(call_native, return_type, Span(start, end));
Ok(())
}
@ -1391,7 +1233,9 @@ impl<'src> Compiler<'src> {
fn parse_expression(&mut self) -> Result<(), CompileError> {
self.parse(Precedence::None)?;
if self.previous_expression_type == Type::None || self.chunk.is_empty() {
let expression_type = self.get_last_instruction_type();
if expression_type == Type::None || self.chunk.is_empty() {
return Err(CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.current_position,
@ -1417,7 +1261,10 @@ impl<'src> Compiler<'src> {
false
} else {
self.parse_expression()?;
self.update_return_type(self.previous_expression_type.clone())?;
let expression_type = self.get_last_instruction_type();
self.update_return_type(expression_type)?;
true
};
@ -1426,9 +1273,7 @@ impl<'src> Compiler<'src> {
should_return_value,
});
self.emit_instruction(r#return, Span(start, end));
self.previous_expression_type = Type::None;
self.emit_instruction(r#return, Type::None, Span(start, end));
Ok(())
}
@ -1439,14 +1284,15 @@ impl<'src> Compiler<'src> {
should_return_value: false,
});
self.emit_instruction(r#return, self.current_position);
self.emit_instruction(r#return, Type::None, self.current_position);
} else {
let should_return_value = self.previous_expression_type != Type::None;
let previous_expression_type = self.get_last_instruction_type();
let should_return_value = previous_expression_type != Type::None;
let r#return = Instruction::from(Return {
should_return_value,
});
self.emit_instruction(r#return, self.current_position);
self.emit_instruction(r#return, Type::None, self.current_position);
}
Ok(())
@ -1495,9 +1341,7 @@ impl<'src> Compiler<'src> {
is_mutable,
});
self.emit_instruction(define_local, position);
self.previous_expression_type = Type::None;
self.emit_instruction(define_local, Type::None, position);
Ok(())
}
@ -1601,7 +1445,7 @@ impl<'src> Compiler<'src> {
if let Some((identifier, position)) = identifier_info {
let (local_index, _) = self.declare_local(
identifier,
Type::Function(function_type),
Type::Function(function_type.clone()),
false,
self.current_scope,
);
@ -1616,10 +1460,12 @@ impl<'src> Compiler<'src> {
is_mutable: false,
});
self.emit_instruction(load_constant, Span(function_start, function_end));
self.emit_instruction(define_local, position);
self.previous_expression_type = Type::None;
self.emit_instruction(
load_constant,
Type::Function(function_type),
Span(function_start, function_end),
);
self.emit_instruction(define_local, Type::None, position);
} else {
let load_constant = Instruction::from(LoadConstant {
destination,
@ -1627,9 +1473,11 @@ impl<'src> Compiler<'src> {
jump_next: false,
});
self.emit_instruction(load_constant, Span(function_start, function_end));
self.previous_expression_type = Type::Function(function_type);
self.emit_instruction(
load_constant,
Type::Function(function_type),
Span(function_start, function_end),
);
}
Ok(())
@ -1687,7 +1535,7 @@ impl<'src> Compiler<'src> {
to: actual_register,
});
self.emit_instruction(close, self.current_position);
self.emit_instruction(close, Type::None, self.current_position);
}
self.allow(Token::Comma)?;
@ -1702,17 +1550,7 @@ impl<'src> Compiler<'src> {
argument_count,
});
self.emit_instruction(call, Span(start, end));
self.previous_expression_type = function_return_type;
Ok(())
}
fn parse_semicolon(&mut self) -> Result<(), CompileError> {
self.advance()?;
self.previous_expression_type = Type::None;
self.emit_instruction(call, function_return_type, Span(start, end));
Ok(())
}
@ -2037,7 +1875,7 @@ impl From<&Token<'_>> for ParseRule<'_> {
precedence: Precedence::None,
},
Token::Semicolon => ParseRule {
prefix: Some(Compiler::parse_semicolon),
prefix: Some(Compiler::expect_expression),
infix: None,
precedence: Precedence::None,
},

View File

@ -9,14 +9,14 @@ impl From<&Instruction> for Close {
fn from(instruction: &Instruction) -> Self {
Close {
from: instruction.b(),
to: instruction.a(),
to: instruction.c(),
}
}
}
impl From<Close> for Instruction {
fn from(r#move: Close) -> Self {
*Instruction::new(Operation::Move)
*Instruction::new(Operation::Close)
.set_b(r#move.from)
.set_c(r#move.to)
}

View File

@ -496,7 +496,7 @@ impl Instruction {
Operation::Close => {
let Close { from, to } = Close::from(self);
format!("R{from}..=R{to}")
format!("R{from}..R{to}")
}
Operation::LoadBoolean => {
let LoadBoolean {
@ -529,7 +529,7 @@ impl Instruction {
destination,
start_register,
} = LoadList::from(self);
let end_register = destination - 1;
let end_register = destination.saturating_sub(1);
format!("R{destination} = [R{start_register}..=R{end_register}]",)
}