1
0

Begin adding type evaluation to the parser

This commit is contained in:
Jeff 2024-11-05 23:20:58 -05:00
parent 9294b8f7ed
commit 314913dbf5
4 changed files with 135 additions and 57 deletions

View File

@ -153,6 +153,46 @@ impl Chunk {
}
}
pub fn get_constant_type(&self, constant_index: u8) -> Option<Type> {
self.constants
.get(constant_index as usize)
.map(|value| value.r#type())
}
pub fn get_local_type(&self, local_index: u8) -> Option<Type> {
self.locals.get(local_index as usize)?.r#type.clone()
}
pub fn get_register_type(&self, register_index: u8) -> Option<Type> {
let local_type_option = self
.locals
.iter()
.find(|local| local.register_index == register_index)
.map(|local| local.r#type.clone());
if let Some(local_type) = local_type_option {
return local_type;
}
self.instructions.iter().find_map(|(instruction, _)| {
if instruction.yields_value() && instruction.a() == register_index {
instruction.yielded_type(self)
} else {
None
}
})
}
pub fn return_type(&self) -> Option<Type> {
self.instructions.iter().rev().find_map(|(instruction, _)| {
if instruction.yields_value() {
instruction.yielded_type(self)
} else {
None
}
})
}
pub fn disassembler(&self) -> ChunkDisassembler {
ChunkDisassembler::new(self)
}
@ -259,8 +299,8 @@ impl<'a> ChunkDisassembler<'a> {
const INSTRUCTION_HEADER: [&'static str; 4] = [
"Instructions",
"------------",
"INDEX BYTECODE OPERATION INFO POSITION ",
"----- -------- --------------- ------------------------- -------------",
"INDEX BYTECODE OPERATION INFO TYPE POSITION ",
"----- -------- ------------- ------------------------- --------- -----------",
];
const CONSTANT_HEADER: [&'static str; 4] =
@ -427,10 +467,14 @@ impl<'a> ChunkDisassembler<'a> {
self.push_header(&name_display);
let info_line = format!(
"{} instructions, {} constants, {} locals",
"{} instructions, {} constants, {} locals, returns {}",
self.chunk.instructions.len(),
self.chunk.constants.len(),
self.chunk.locals.len()
self.chunk.locals.len(),
self.chunk
.return_type()
.map(|r#type| r#type.to_string())
.unwrap_or("none".to_string())
);
self.push(&info_line, true, false, false, true);
@ -440,12 +484,18 @@ impl<'a> ChunkDisassembler<'a> {
}
for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
let position = position.to_string();
let operation = instruction.operation().to_string();
let info = instruction.disassembly_info(Some(self.chunk));
let bytecode = u32::from(instruction);
let instruction_display =
format!("{index:<5} {bytecode:<08X} {operation:15} {info:25} {position:13}");
let operation = instruction.operation().to_string();
let info = instruction.disassembly_info(self.chunk);
let type_display = instruction
.yielded_type(self.chunk)
.map(|r#type| r#type.to_string())
.unwrap_or(String::with_capacity(0));
let position = position.to_string();
let instruction_display = format!(
"{index:<5} {bytecode:08X} {operation:13} {info:25} {type_display:9} {position:11}"
);
self.push_details(&instruction_display);
}

View File

@ -14,7 +14,7 @@
use serde::{Deserialize, Serialize};
use crate::{Chunk, NativeFunction, Operation};
use crate::{Chunk, NativeFunction, Operation, Type};
/// An operation and its arguments for the Dust virtual machine.
///
@ -364,35 +364,59 @@ impl Instruction {
}
pub fn yields_value(&self) -> bool {
if matches!(
self.operation(),
match self.operation() {
Operation::Add
| Operation::Call
| Operation::Divide
| Operation::GetLocal
| Operation::LoadBoolean
| Operation::LoadConstant
| Operation::LoadList
| Operation::LoadSelf
| Operation::Modulo
| Operation::Multiply
| Operation::Negate
| Operation::Not
| Operation::Subtract
) {
return true;
| Operation::Call
| Operation::Divide
| Operation::GetLocal
| Operation::LoadBoolean
| Operation::LoadConstant
| Operation::LoadList
| Operation::LoadSelf
| Operation::Modulo
| Operation::Multiply
| Operation::Negate
| Operation::Not
| Operation::Subtract => true,
Operation::CallNative => {
let native_function = NativeFunction::from(self.b());
native_function.r#type().return_type.is_some()
}
_ => false,
}
if matches!(self.operation(), Operation::CallNative) {
let native_function = NativeFunction::from(self.b());
return native_function.r#type().return_type.is_some();
}
false
}
pub fn disassembly_info(&self, chunk: Option<&Chunk>) -> String {
pub fn yielded_type(&self, chunk: &Chunk) -> Option<Type> {
use Operation::*;
match self.operation() {
Add | Divide | Modulo | Multiply | Subtract => {
if self.b_is_constant() {
chunk.get_constant_type(self.b())
} else {
chunk.get_register_type(self.b())
}
}
Equal | Less | LessEqual | Test | Not | LoadBoolean => Some(Type::Boolean),
Negate => {
if self.b_is_constant() {
chunk.get_constant_type(self.b())
} else {
chunk.get_register_type(self.b())
}
}
GetLocal => chunk.get_local_type(self.b()),
CallNative => {
let native_function = NativeFunction::from(self.b());
native_function.r#type().return_type.map(|boxed| *boxed)
}
_ => None,
}
}
pub fn disassembly_info(&self, chunk: &Chunk) -> String {
let format_arguments = || {
let first_argument = if self.b_is_constant() {
format!("C{}", self.b())
@ -448,26 +472,18 @@ impl Instruction {
Operation::LoadSelf => {
let to_register = self.a();
let name = chunk
.map(|chunk| {
chunk
.name()
.map(|idenifier| idenifier.as_str())
.unwrap_or("self")
})
.unwrap();
.name()
.map(|idenifier| idenifier.as_str())
.unwrap_or("self");
format!("R{to_register} = {name}")
}
Operation::DefineLocal => {
let to_register = self.a();
let local_index = self.b();
let identifier_display = if let Some(chunk) = chunk {
match chunk.get_identifier(local_index) {
Some(identifier) => identifier.to_string(),
None => "???".to_string(),
}
} else {
"???".to_string()
let identifier_display = match chunk.get_identifier(local_index) {
Some(identifier) => identifier.to_string(),
None => "???".to_string(),
};
let mutable_display = if self.c_as_boolean() { "mut" } else { "" };
@ -480,13 +496,9 @@ impl Instruction {
}
Operation::SetLocal => {
let local_index = self.b();
let identifier_display = if let Some(chunk) = chunk {
match chunk.get_identifier(local_index) {
Some(identifier) => identifier.to_string(),
None => "???".to_string(),
}
} else {
"???".to_string()
let identifier_display = match chunk.get_identifier(local_index) {
Some(identifier) => identifier.to_string(),
None => "???".to_string(),
};
format!("L{} = R{} {}", local_index, self.a(), identifier_display)

View File

@ -74,7 +74,7 @@ impl Vm {
self.ip - 1,
position,
instruction.operation(),
instruction.disassembly_info(Some(&self.chunk))
instruction.disassembly_info(&self.chunk)
);
match instruction.operation() {

View File

@ -64,6 +64,22 @@ fn add_assign_expects_mutable_variable() {
);
}
#[test]
fn add_expects_integer_float_or_string() {
let source = "true + false";
assert_eq!(
parse(source),
Err(DustError::Parse {
error: ParseError::ExpectedIntegerFloatOrString {
found: Token::True,
position: Span(0, 3)
},
source
})
);
}
#[test]
fn divide() {
let source = "2 / 2";