Begin adding type evaluation to the parser
This commit is contained in:
parent
9294b8f7ed
commit
314913dbf5
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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() {
|
||||
|
@ -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";
|
||||
|
Loading…
Reference in New Issue
Block a user