1
0

Fix some aspects of the compiler for logic and semicolons

This commit is contained in:
Jeff 2024-12-17 18:11:06 -05:00
parent 72365cd399
commit d7289414f4
5 changed files with 69 additions and 115 deletions

View File

@ -27,7 +27,7 @@ use std::io::Write;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::vm::{Record, Register, RunAction}; use crate::vm::{Record, RunAction};
use crate::{DustString, Function, FunctionType, Instruction, Span, Value}; use crate::{DustString, Function, FunctionType, Instruction, Span, Value};
/// Representation of a Dust program or function. /// Representation of a Dust program or function.

View File

@ -7,6 +7,17 @@
//! - [`Compiler`] is created with a [`Lexer`] and protentially emits a [`CompileError`] or //! - [`Compiler`] is created with a [`Lexer`] and protentially emits a [`CompileError`] or
//! [`LexError`] if the input is invalid. Allows passing a name for the main chunk when //! [`LexError`] if the input is invalid. Allows passing a name for the main chunk when
//! [`Compiler::finish`] is called. //! [`Compiler::finish`] is called.
//!
//! # Errors
//!
//! The compiler can return errors due to:
//! - Lexing errors
//! - Parsing errors
//! - Type conflicts
//!
//! It is a logic error to call [`Compiler::finish`] on a compiler that has emitted an error and
//! pass that chunk to the VM. Otherwise, if the compiler gives no errors and the VM encounters a
//! runtime error, it is the compiler's fault and the error should be fixed here.
mod error; mod error;
mod optimize; mod optimize;
@ -22,9 +33,7 @@ use optimize::control_flow_register_consolidation;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::{ use crate::{
instruction::{ instruction::{CallNative, Close, GetLocal, Jump, LoadList, Negate, Not, Return, SetLocal},
CallNative, Close, GetLocal, Jump, LoadList, Negate, Not, Return, SetLocal, Test,
},
Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local, Argument, Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local,
NativeFunction, Operation, Scope, Span, Token, TokenKind, Type, Value, NativeFunction, Operation, Scope, Span, Token, TokenKind, Type, Value,
}; };
@ -192,9 +201,9 @@ impl<'src> Compiler<'src> {
) )
} }
/// Compiles the source while checking for errors and returning a [`CompileError`] if any are /// Compiles the source (which is in the lexer) while checking for errors and returning a
/// found. After calling this function, check its return value for an error, then call /// [`CompileError`] if any are found. After calling this function, check its return value for
/// [`Compiler::finish`] to get the compiled chunk. /// an error, then call [`Compiler::finish`] to get the compiled chunk.
pub fn compile(&mut self) -> Result<(), CompileError> { pub fn compile(&mut self) -> Result<(), CompileError> {
loop { loop {
self.parse(Precedence::None)?; self.parse(Precedence::None)?;
@ -351,15 +360,6 @@ impl<'src> Compiler<'src> {
} }
} }
fn pop_last_instruction(&mut self) -> Result<(Instruction, Type, Span), CompileError> {
self.instructions
.pop()
.ok_or_else(|| CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
})
}
fn get_last_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> { fn get_last_operations<const COUNT: usize>(&self) -> Option<[Operation; COUNT]> {
let mut n_operations = [Operation::RETURN; COUNT]; let mut n_operations = [Operation::RETURN; COUNT];
@ -628,7 +628,7 @@ impl<'src> Compiler<'src> {
self.parse_expression()?; self.parse_expression()?;
let (previous_instruction, previous_type, previous_position) = let (previous_instruction, previous_type, previous_position) =
self.pop_last_instruction()?; self.instructions.pop().unwrap();
let (argument, push_back) = self.handle_binary_argument(&previous_instruction)?; let (argument, push_back) = self.handle_binary_argument(&previous_instruction)?;
if push_back { if push_back {
@ -759,7 +759,7 @@ impl<'src> Compiler<'src> {
self.advance()?; self.advance()?;
self.parse_sub_expression(&rule.precedence)?; self.parse_sub_expression(&rule.precedence)?;
let (right_instruction, right_type, right_position) = self.pop_last_instruction()?; let (right_instruction, right_type, right_position) = self.instructions.pop().unwrap();
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?; let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
match operator { match operator {
@ -948,44 +948,17 @@ impl<'src> Compiler<'src> {
fn parse_logical_binary(&mut self) -> Result<(), CompileError> { fn parse_logical_binary(&mut self) -> Result<(), CompileError> {
let is_logic_chain = matches!( let is_logic_chain = matches!(
self.get_last_operations(), self.get_last_operations(),
Some([Operation::TEST, Operation::JUMP, _]) Some([_, Operation::TEST, Operation::JUMP, _])
); );
let jump_over_index = self.instructions.len() - 2;
let jump_over = if is_logic_chain {
let (instruction, _, _) = self.instructions.remove(jump_over_index);
let (left_instruction, left_type, left_position) = self.pop_last_instruction()?; Some(instruction)
let (left, _) = self.handle_binary_argument(&left_instruction)?; } else {
None
// if is_logic_chain { };
// let destination = self let operand_register = self.next_register() - 1;
// .instructions
// .iter()
// .rev()
// .nth(2)
// .map_or(0, |(instruction, _, _)| instruction.a_field());
// left_instruction.set_a_field(destination);
// }
if !left_instruction.yields_value() {
return Err(CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
});
}
// self.instructions
// .push((left_instruction, left_type.clone(), left_position));
// let short_circuit_jump_index = self.instructions.len();
// let mut short_circuit_jump_distance = if is_logic_chain {
// self.instructions.pop().map_or(0, |(jump, _, _)| {
// let Jump { offset, .. } = Jump::from(&jump);
// offset
// })
// } else {
// 1
// };
let operator = self.current_token; let operator = self.current_token;
let operator_position = self.current_position; let operator_position = self.current_position;
let rule = ParseRule::from(&operator); let rule = ParseRule::from(&operator);
@ -1000,7 +973,7 @@ impl<'src> Compiler<'src> {
}) })
} }
}; };
let test = Instruction::test(left, test_boolean); let test = Instruction::test(operand_register, test_boolean);
self.advance()?; self.advance()?;
self.emit_instruction(test, Type::None, operator_position); self.emit_instruction(test, Type::None, operator_position);
@ -1015,9 +988,21 @@ impl<'src> Compiler<'src> {
self.instructions self.instructions
.insert(jump_index, (jump, Type::None, operator_position)); .insert(jump_index, (jump, Type::None, operator_position));
let (mut right_instruction, _, _) = self.instructions.last_mut().unwrap(); if let Some(jump_over) = jump_over {
let is_end_of_context = matches!(self.current_token, Token::RightBrace | Token::Eof);
let jump_over_distance =
jump_over.b_field() + (self.instructions.len() - jump_over_index) as u8 - {
if is_end_of_context {
1
} else {
0
}
};
let jump_over = Instruction::jump(jump_over_distance, true);
right_instruction.set_a_field(left_instruction.a_field()); self.instructions
.insert(jump_over_index, (jump_over, Type::None, operator_position));
}
// short_circuit_jump_distance += (self.instructions.len() - short_circuit_jump_index) as u8; // short_circuit_jump_distance += (self.instructions.len() - short_circuit_jump_index) as u8;
// let jump = Instruction::jump(short_circuit_jump_distance, true); // let jump = Instruction::jump(short_circuit_jump_distance, true);
@ -1201,20 +1186,8 @@ impl<'src> Compiler<'src> {
self.advance()?; self.advance()?;
self.parse_expression()?; self.parse_expression()?;
let (last_instruction, _, _) = self.instructions.last().unwrap(); let operand_register = self.next_register() - 1;
let argument = match last_instruction.as_argument() { let test = Instruction::test(operand_register, true);
Some(argument) => argument,
None => {
return Err(CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: self.previous_position,
});
}
};
let test = Instruction::from(Test {
argument,
test_value: true,
});
self.emit_instruction(test, Type::None, self.current_position); self.emit_instruction(test, Type::None, self.current_position);
@ -1334,27 +1307,8 @@ impl<'src> Compiler<'src> {
self.instructions.pop(); self.instructions.pop();
self.instructions.pop(); self.instructions.pop();
} else { } else {
let (expression_instruction, expression_type, expression_position) = let operand_register = self.next_register() - 1;
self.instructions.last().unwrap(); let test = Instruction::test(operand_register, true);
if expression_type != &Type::Boolean {
return Err(CompileError::ExpectedFunction {
found: self.previous_token.to_owned(),
actual_type: expression_type.clone(),
position: *expression_position,
});
}
let test_argument = match expression_instruction.as_argument() {
Some(argument) => argument,
None => {
return Err(CompileError::ExpectedExpression {
found: self.previous_token.to_owned(),
position: *expression_position,
})
}
};
let test = Instruction::test(test_argument, true);
self.emit_instruction(test, Type::None, self.current_position); self.emit_instruction(test, Type::None, self.current_position);
} }
@ -1427,8 +1381,7 @@ impl<'src> Compiler<'src> {
fn parse_semicolon(&mut self) -> Result<(), CompileError> { fn parse_semicolon(&mut self) -> Result<(), CompileError> {
self.advance()?; self.advance()?;
self.parse(Precedence::None)
Ok(())
} }
fn parse_expression(&mut self) -> Result<(), CompileError> { fn parse_expression(&mut self) -> Result<(), CompileError> {

View File

@ -327,9 +327,9 @@ impl Instruction {
}) })
} }
pub fn test(argument: Argument, value: bool) -> Instruction { pub fn test(operand_register: u8, value: bool) -> Instruction {
Instruction::from(Test { Instruction::from(Test {
argument, operand_register,
test_value: value, test_value: value,
}) })
} }
@ -622,12 +622,12 @@ impl Instruction {
} }
Operation::TEST => { Operation::TEST => {
let Test { let Test {
argument, operand_register,
test_value: value, test_value,
} = Test::from(self); } = Test::from(self);
let bang = if value { "" } else { "!" }; let bang = if test_value { "" } else { "!" };
format!("if {bang}{argument} {{ JUMP +1 }}",) format!("if {bang}R{operand_register} {{ JUMP +1 }}",)
} }
Operation::TEST_SET => { Operation::TEST_SET => {
let TestSet { let TestSet {

View File

@ -1,17 +1,17 @@
use crate::{Argument, Instruction, Operation}; use crate::{Instruction, Operation};
pub struct Test { pub struct Test {
pub argument: Argument, pub operand_register: u8,
pub test_value: bool, pub test_value: bool,
} }
impl From<&Instruction> for Test { impl From<&Instruction> for Test {
fn from(instruction: &Instruction) -> Self { fn from(instruction: &Instruction) -> Self {
let argument = instruction.b_as_argument(); let operand_register = instruction.b_field();
let test_value = instruction.c_field() != 0; let test_value = instruction.c_field() != 0;
Test { Test {
argument, operand_register,
test_value, test_value,
} }
} }
@ -19,10 +19,14 @@ impl From<&Instruction> for Test {
impl From<Test> for Instruction { impl From<Test> for Instruction {
fn from(test: Test) -> Self { fn from(test: Test) -> Self {
let operation = Operation::TEST; Instruction::new(
let (b, b_is_constant) = test.argument.as_index_and_constant_flag(); Operation::TEST,
let c = test.test_value as u8; 0,
test.operand_register,
Instruction::new(operation, 0, b, c, b_is_constant, false, false) test.test_value as u8,
false,
false,
false,
)
} }
} }

View File

@ -356,18 +356,15 @@ pub fn modulo(instruction_data: InstructionData, record: &mut Record) -> ThreadS
pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal { pub fn test(instruction_data: InstructionData, record: &mut Record) -> ThreadSignal {
let InstructionData { let InstructionData {
b_field: b, b_field, c_field, ..
b_is_constant,
c_field: c,
..
} = instruction_data; } = instruction_data;
let value = record.get_argument(b, b_is_constant); let value = record.open_register(b_field);
let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value {
*boolean *boolean
} else { } else {
panic!("VM Error: Expected boolean value for TEST operation",); panic!("VM Error: Expected boolean value for TEST operation",);
}; };
let test_value = c != 0; let test_value = c_field != 0;
if boolean == test_value { if boolean == test_value {
record.ip += 1; record.ip += 1;