From 2864bee057ab8c39ee0d72b1cff3839d2bc78134 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 12 Oct 2024 20:19:21 -0400 Subject: [PATCH] Add types to functions and improve calls --- dust-lang/src/chunk.rs | 37 +++++++++++++++----- dust-lang/src/lexer.rs | 4 +++ dust-lang/src/parser.rs | 77 ++++++++++++++++++++++++++--------------- dust-lang/src/type.rs | 18 ++++------ dust-lang/src/value.rs | 61 ++++++++++++++++++++++++++++---- dust-lang/src/vm.rs | 8 +++-- dust-shell/src/main.rs | 5 +-- 7 files changed, 153 insertions(+), 57 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 0ad186c..55d99ed 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -132,6 +132,10 @@ impl Chunk { operations } + pub fn constants(&self) -> &[Option] { + &self.constants + } + pub fn get_constant(&self, index: u8, position: Span) -> Result<&Value, ChunkError> { let index = index as usize; @@ -333,6 +337,7 @@ impl Local { pub struct ChunkDisassembler<'a> { name: &'a str, chunk: &'a Chunk, + source: Option<&'a str>, width: usize, styled: bool, indent: usize, @@ -375,12 +380,19 @@ impl<'a> ChunkDisassembler<'a> { Self { name, chunk, + source: None, width: Self::default_width(), styled: false, indent: 0, } } + pub fn source(&mut self, source: &'a str) -> &mut Self { + self.source = Some(source); + + self + } + pub fn width(&mut self, width: usize) -> &mut Self { self.width = width; @@ -439,6 +451,17 @@ impl<'a> ChunkDisassembler<'a> { push(self.name, self.styled); + if let Some(source) = self.source { + let length = if source.len() < self.width { + source.len() - 2 + } else { + self.width - 2 + }; + let source_line = format!("\"{}\"", &source[..length]).dimmed(); + + push(&source_line, false); + } + let info_line = format!( "{} instructions, {} constants, {} locals", self.chunk.instructions.len(), @@ -518,10 +541,7 @@ impl<'a> ChunkDisassembler<'a> { for (index, value_option) in self.chunk.constants.iter().enumerate() { let value_display = value_option .as_ref() - .map(|value| match value { - Value::Primitive(value_data) => value_data.to_string(), - Value::Object(_) => "object".to_string(), - }) + .map(|value| value.to_string()) .unwrap_or("empty".to_string()); let trucated_length = 8; let with_elipsis = trucated_length - 3; @@ -535,15 +555,16 @@ impl<'a> ChunkDisassembler<'a> { if let Some(function_disassembly) = value_option.as_ref().and_then(|value| match value { - Value::Primitive(value_data) => value_data.as_function().map(|function| { + Value::Function(function) => Some( function - .chunk + .chunk() .disassembler("function") .styled(self.styled) .indent(self.indent + 1) .width(self.width) - .disassemble() - }), + .disassemble(), + ), + Value::Primitive(_) => None, Value::Object(_) => None, }) { diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 3b4c086..1328acd 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -95,6 +95,10 @@ impl<'src> Lexer<'src> { } } + pub fn source(&self) -> &'src str { + self.source + } + pub fn skip_to(&mut self, position: usize) { self.position = position; } diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 251dffc..4af2625 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -1,6 +1,6 @@ use std::{ fmt::{self, Display, Formatter}, - mem::replace, + mem::{replace, take}, num::{ParseFloatError, ParseIntError}, }; @@ -8,8 +8,8 @@ use colored::Colorize; use serde::{Deserialize, Serialize}; use crate::{ - AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError, Lexer, - Operation, Span, Token, TokenKind, TokenOwned, Type, Value, + AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Identifier, Instruction, LexError, + Lexer, Operation, Span, Token, TokenKind, TokenOwned, Type, Value, }; pub fn parse(source: &str) -> Result { @@ -36,12 +36,17 @@ pub fn parse(source: &str) -> Result { pub struct Parser<'src> { chunk: Chunk, lexer: Lexer<'src>, + current_register: u8, + current_token: Token<'src>, current_position: Span, + previous_token: Token<'src>, previous_position: Span, + parsed_expression: bool, + latest_value_type: Option, } impl<'src> Parser<'src> { @@ -63,6 +68,7 @@ impl<'src> Parser<'src> { previous_token: Token::Eof, previous_position: Span(0, 0), parsed_expression: false, + latest_value_type: None, }) } @@ -142,6 +148,8 @@ impl<'src> Parser<'src> { } fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> { + self.latest_value_type = Some(value.r#type()); + let constant_index = self.chunk.push_constant(value, position)?; self.emit_instruction( @@ -165,6 +173,7 @@ impl<'src> Parser<'src> { position, ); + self.latest_value_type = Some(Type::Boolean); self.parsed_expression = true; Ok(()) @@ -954,34 +963,26 @@ impl<'src> Parser<'src> { } fn parse_return(&mut self, allowed: Allowed) -> Result<(), ParseError> { - let start = self.current_position.0; - if !allowed.explicit_return { return Err(ParseError::UnexpectedReturn { position: self.current_position, }); } + let start = self.current_position.0; + self.advance()?; - let has_return_value = if !matches!( + let has_return_value = if matches!( self.current_token, Token::Semicolon | Token::RightCurlyBrace ) { - self.parse_statement( - Allowed { - assignment: false, - explicit_return: false, - implicit_return: false, - }, - Context::None, - )?; + false + } else { + self.parse_expression()?; true - } else { - false }; - let end = self.current_position.1; self.emit_instruction(Instruction::r#return(has_return_value), Span(start, end)); @@ -1060,6 +1061,8 @@ impl<'src> Parser<'src> { function_parser.expect(Token::LeftParenthesis)?; + let mut value_parameters: Option> = None; + while function_parser.current_token != Token::RightParenthesis { let start = function_parser.current_position.0; let is_mutable = function_parser.allow(Token::Mut)?; @@ -1086,6 +1089,12 @@ impl<'src> Parser<'src> { let end = function_parser.current_position.1; + if let Some(value_parameters) = value_parameters.as_mut() { + value_parameters.push((parameter.clone(), r#type.clone())); + } else { + value_parameters = Some(vec![(parameter.clone(), r#type.clone())]); + }; + function_parser.chunk.declare_local( parameter, Some(r#type), @@ -1119,7 +1128,13 @@ impl<'src> Parser<'src> { self.current_token = function_parser.current_token; self.current_position = function_parser.current_position; - let function = Value::function(function_parser.chunk); + let return_type = take(&mut self.latest_value_type).map(Box::new); + let function_type = FunctionType { + type_parameters: None, + value_parameters, + return_type, + }; + let function = Value::function(function_parser.chunk, function_type); let function_end = self.current_position.1; self.lexer.skip_to(function_end); @@ -1131,6 +1146,17 @@ impl<'src> Parser<'src> { } fn parse_call(&mut self) -> Result<(), ParseError> { + let last_instruction = self.chunk.get_last_instruction()?.0; + + if !last_instruction.is_expression() { + return Err(ParseError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: self.previous_position, + }); + } + + let start = self.current_position.0; + self.advance()?; let function_register = self.current_register; @@ -1143,14 +1169,7 @@ impl<'src> Parser<'src> { let register = self.current_register; - self.parse( - Precedence::None, - Allowed { - assignment: false, - explicit_return: false, - implicit_return: false, - }, - )?; + self.parse_expression()?; if self.current_register == register { return Err(ParseError::ExpectedExpression { @@ -1162,11 +1181,15 @@ impl<'src> Parser<'src> { argument_count += 1; } + let end = self.current_position.1; + self.emit_instruction( Instruction::call(function_register, argument_count), - self.current_position, + Span(start, end), ); + self.parsed_expression = true; + Ok(()) } diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index 2e1a15f..c7e49b5 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -191,20 +191,17 @@ impl Type { } ( Type::Function(FunctionType { - name: left_name, type_parameters: left_type_parameters, value_parameters: left_value_parameters, return_type: left_return, }), Type::Function(FunctionType { - name: right_name, type_parameters: right_type_parameters, value_parameters: right_value_parameters, return_type: right_return, }), ) => { - if left_name != right_name - || left_return != right_return + if left_return != right_return || left_type_parameters != right_type_parameters || left_value_parameters != right_value_parameters { @@ -419,7 +416,6 @@ impl Ord for Type { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct FunctionType { - pub name: Identifier, pub type_parameters: Option>, pub value_parameters: Option>, pub return_type: Option>, @@ -433,11 +429,11 @@ impl Display for FunctionType { write!(f, "<")?; for (index, type_parameter) in type_parameters.iter().enumerate() { - write!(f, "{type_parameter}")?; - - if index != type_parameters.len() - 1 { + if index > 0 { write!(f, ", ")?; } + + write!(f, "{type_parameter}")?; } write!(f, ">")?; @@ -447,11 +443,11 @@ impl Display for FunctionType { if let Some(value_parameters) = &self.value_parameters { for (index, (identifier, r#type)) in value_parameters.iter().enumerate() { - write!(f, "{identifier}: {type}")?; - - if index != value_parameters.len() - 1 { + if index > 0 { write!(f, ", ")?; } + + write!(f, "{identifier}: {type}")?; } } diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 68bd5b6..94c0a89 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -31,7 +31,7 @@ use serde::{ Deserialize, Deserializer, Serialize, Serializer, }; -use crate::{Chunk, RangeableType, Span, Type, Vm, VmError}; +use crate::{Chunk, FunctionType, RangeableType, Span, Type, Vm, VmError}; /// Dust value representation /// @@ -39,6 +39,7 @@ use crate::{Chunk, RangeableType, Span, Type, Vm, VmError}; #[derive(Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Value { Primitive(Primitive), + Function(Function), Object(Object), } @@ -59,8 +60,11 @@ impl Value { Value::Primitive(Primitive::Float(value)) } - pub fn function(body: Chunk) -> Self { - Value::Primitive(Primitive::Function(Function { chunk: body })) + pub fn function(body: Chunk, r#type: FunctionType) -> Self { + Value::Primitive(Primitive::Function(Function { + chunk: body, + r#type: Type::Function(r#type), + })) } pub fn integer>(into_i64: T) -> Self { @@ -79,9 +83,14 @@ impl Value { Value::Primitive(Primitive::String(to_string.to_string())) } + pub fn is_function(&self) -> bool { + matches!(self, Value::Function(_)) + } + pub fn r#type(&self) -> Type { match self { Value::Primitive(data) => data.r#type(), + Value::Function(function) => function.r#type().clone(), Value::Object(Object::List { start, end, @@ -231,6 +240,7 @@ impl Value { pub fn display(&self, vm: &Vm, position: Span) -> Result { match self { Value::Primitive(primitive) => Ok(primitive.to_string()), + Value::Function(function) => Ok(function.to_string()), Value::Object(object) => object.display(vm, position), } } @@ -290,6 +300,7 @@ impl Clone for Value { match self { Value::Primitive(data) => Value::Primitive(data.clone()), + Value::Function(function) => Value::Function(function.clone()), Value::Object(object) => Value::Object(object.clone()), } } @@ -299,7 +310,8 @@ impl Display for Value { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Value::Primitive(primitive) => write!(f, "{primitive}"), - Value::Object(_) => write!(f, "object"), + Value::Function(function) => write!(f, "{function}"), + Value::Object(object) => write!(f, "{object}"), } } } @@ -308,6 +320,7 @@ impl Serialize for Value { fn serialize(&self, serializer: S) -> Result { match self { Value::Primitive(data) => data.serialize(serializer), + Value::Function(function) => function.serialize(serializer), Value::Object(object) => object.serialize(serializer), } } @@ -371,7 +384,7 @@ impl Primitive { Primitive::Boolean(_) => Type::Boolean, Primitive::Byte(_) => Type::Byte, Primitive::Character(_) => Type::Character, - Primitive::Function(Function { .. }) => todo!(), + Primitive::Function(Function { r#type, .. }) => r#type.clone(), Primitive::Float(_) => Type::Float, Primitive::Integer(_) => Type::Integer, Primitive::Range(range) => range.r#type(), @@ -643,7 +656,32 @@ impl Ord for Primitive { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Function { - pub chunk: Chunk, + chunk: Chunk, + r#type: Type, +} + +impl Function { + pub fn new(chunk: Chunk, r#type: Type) -> Self { + Self { chunk, r#type } + } + + pub fn chunk(&self) -> &Chunk { + &self.chunk + } + + pub fn take_chunk(self) -> Chunk { + self.chunk + } + + pub fn r#type(&self) -> &Type { + &self.r#type + } +} + +impl Display for Function { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.r#type) + } } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -894,6 +932,16 @@ impl Object { } } +impl Display for Object { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Object::List { start, end, .. } => { + write!(f, "List [R{}..=R{}]", start, end) + } + } + } +} + #[derive(Clone, Debug, PartialEq)] pub enum ValueError { CannotAdd(Value, Value), @@ -918,6 +966,7 @@ impl Display for ValueError { let get_value_display = |value: &Value| -> String { match value { Value::Primitive(primitive) => primitive.to_string(), + Value::Function(function) => function.to_string(), Value::Object(_) => "Object".to_string(), } }; diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index fb16dd9..16c3e1e 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -378,11 +378,11 @@ impl Vm { } else { todo!() }; - let mut function_vm = Vm::new(function.chunk); + let mut function_vm = Vm::new(function.take_chunk()); let first_argument_index = function_index + 1; let last_argument_index = first_argument_index + argument_count; - for argument_index in first_argument_index..=last_argument_index { + for argument_index in first_argument_index..last_argument_index { let argument = self.empty(argument_index, position)?; function_vm.stack.push(Register::Value(argument)); @@ -392,13 +392,15 @@ impl Vm { if let Some(value) = return_value { self.set(function_index, value, position)?; + } else { + self.empty(function_index, position)?; } } Operation::Return => { let should_return_value = instruction.b_as_boolean(); - let top_of_stack = (self.stack.len() - 1) as u8; return if should_return_value { + let top_of_stack = (self.stack.len() - 1) as u8; let value = self.empty(top_of_stack, position)?; Ok(Some(value)) diff --git a/dust-shell/src/main.rs b/dust-shell/src/main.rs index 161c4d8..49d774d 100644 --- a/dust-shell/src/main.rs +++ b/dust-shell/src/main.rs @@ -60,13 +60,14 @@ fn main() { } } -fn parse_and_display(source: &str, pretty_print: bool) { +fn parse_and_display(source: &str, styled: bool) { match parse(source) { Ok(chunk) => println!( "{}", chunk .disassembler("Dust CLI Input") - .styled(pretty_print) + .source(source) + .styled(styled) .disassemble() ), Err(error) => {