diff --git a/dust-lang/src/abstract_tree/assignment.rs b/dust-lang/src/abstract_tree/assignment.rs index 33c01b2..2789b22 100644 --- a/dust-lang/src/abstract_tree/assignment.rs +++ b/dust-lang/src/abstract_tree/assignment.rs @@ -65,11 +65,7 @@ impl AbstractNode for Assignment { .. } = function_type { - if let Type::Generic { - identifier, - concrete_type, - } = *return_type - { + if let Type::Generic { identifier, .. } = *return_type { let returned_parameter = type_parameters .into_iter() .find(|parameter| parameter == &identifier); diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 79bfab1..d9bdb80 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -24,7 +24,7 @@ impl<'src> Display for Token<'src> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Token::Boolean(boolean) => write!(f, "{boolean}"), - Token::Comment(comment) => write!(f, "# {comment}"), + Token::Comment(comment) => write!(f, "// {comment}"), Token::Integer(integer) => write!(f, "{integer}"), Token::Float(float) => write!(f, "{float}"), Token::String(string) => write!(f, "{string}"), diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index c4ca668..a6c29b5 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -204,96 +204,109 @@ impl InterpreterError { for error in self.errors { let (mut builder, validation_error) = match error { - DustError::Lex { - expected, - span, - reason, - } => { - let description = if expected.is_empty() { - "Invalid character.".to_string() - } else { - format!("Expected {expected}.") - }; + DustError::Lex { + expected, + span, + reason, + } => { + let description = if expected.is_empty() { + "Invalid character.".to_string() + } else { + format!("Expected {expected}.") + }; - ( - Report::build( - ReportKind::Custom("Lexing Error", Color::Yellow), - self.source_id.clone(), - span.1, + ( + Report::build( + ReportKind::Custom("Lexing Error", Color::Yellow), + self.source_id.clone(), + span.1, + ) + .with_message(description) + .with_label( + Label::new((self.source_id.clone(), span.0..span.1)) + .with_message(reason) + .with_color(Color::Red), + ), + None, ) - .with_message(description) - .with_label( - Label::new((self.source_id.clone(), span.0..span.1)) - .with_message(reason) - .with_color(Color::Red), - ), - None, - ) - } - DustError::Parse { - expected, - span, - found, - } => { - let description = if expected.is_empty() { - "Invalid token.".to_string() - } else { - format!("Expected {expected}.") - }; - let found = found.unwrap_or_else(|| "End of input".to_string()).fg(token_color); + } + DustError::Parse { + expected, + span, + found, + } => { + let description = if expected.is_empty() { + "Invalid token.".to_string() + } else { + format!("Expected {expected}.") + }; + let found = found + .unwrap_or_else(|| "End of input".to_string()) + .fg(token_color); - ( - Report::build( - ReportKind::Custom("Parsing Error", Color::Yellow), - self.source_id.clone(), - span.1, + ( + Report::build( + ReportKind::Custom("Parsing Error", Color::Yellow), + self.source_id.clone(), + span.1, + ) + .with_message(description) + .with_label( + Label::new((self.source_id.clone(), span.0..span.1)) + .with_message(format!("{found} is not valid in this position.")) + .with_color(Color::Red), + ), + None, ) - .with_message(description) - .with_label( - Label::new((self.source_id.clone(), span.0..span.1)) - .with_message(format!("{found} is not valid in this position.")) - .with_color(Color::Red), + } + DustError::Validation { error, position } => ( + Report::build( + ReportKind::Custom("Validation Error", Color::Magenta), + self.source_id.clone(), + position.1, + ) + .with_message("The syntax is valid but this code would cause an error.") + .with_note( + "This error was detected by the interpreter before running the code.", ), - None, - ) - } - DustError::Validation { error, position } => ( - Report::build( - ReportKind::Custom("Validation Error", Color::Magenta), - self.source_id.clone(), - position.1, - ) - .with_message("The syntax is valid but this code would cause an error.") - .with_note("This error was detected by the interpreter before running the code."), - Some(error), - ), - DustError::Runtime { error, position } => ( - Report::build( - ReportKind::Custom("Runtime Error", Color::Red), - self.source_id.clone(), - position.1, - ) - .with_message("An error occured that forced the program to exit.") - .with_note( - "There may be unexpected side-effects because the program could not finish.", - ) - .with_help( - "This is the interpreter's fault. Please submit a bug with this error message.", - ) - .with_label( - Label::new((self.source_id.clone(), position.0..position.1)).with_message("Runtime error occured here.") + Some(error), ), - if let RuntimeError::ValidationFailure(validation_error) = error { - Some(validation_error) - } else { - None - }, - ), - }; + DustError::Runtime { error, position } => { + let error_message = match &error { + RuntimeError::Io(_) => todo!(), + RuntimeError::RwLockPoison(_) => todo!(), + RuntimeError::ValidationFailure(_) => todo!(), + RuntimeError::SerdeJson(serde_json_error) => serde_json_error.to_string(), + }; + + ( + Report::build( + ReportKind::Custom("Runtime Error", Color::Red), + self.source_id.clone(), + position.1, + ) + .with_message("An error occured that forced the program to exit.") + .with_note( + "There may be unexpected side-effects because the program could not finish.", + ) + .with_help( + "This is the interpreter's fault. Please submit a bug with this error message.", + ) + .with_label( + Label::new((self.source_id.clone(), position.0..position.1)).with_message(error_message) + ), + + if let RuntimeError::ValidationFailure(validation_error) = error { + Some(validation_error) + } else { + None + }, + ) + } + }; if let Some(validation_error) = validation_error { match validation_error { - ValidationError::CannotAssignToNone(postion) => { builder.add_label( Label::new((self.source_id.clone(), postion.0..postion.1)) diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index e91f8ad..967a6af 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -33,6 +33,9 @@ pub fn parse<'src>( pub fn parser<'src>( allow_built_ins: bool, ) -> impl Parser<'src, ParserInput<'src>, Vec, ParserExtra<'src>> { + let comment = select_ref! { + Token::Comment(_) => {} + }; let identifiers: RefCell> = RefCell::new(HashMap::new()); let identifier = select! { Token::Identifier(text) => { @@ -527,6 +530,7 @@ pub fn parser<'src>( basic_value, identifier_expression, )) + // .delimited_by(comment.clone().or_not(), comment.or_not()) }); let expression_statement = expression @@ -621,27 +625,24 @@ pub fn parser<'src>( ) }); - choice(( - async_block, - if_else, - assignment, - expression_statement, - r#break, - block_statement, - r#loop, - r#while, - type_assignment, - )) - .then_ignore(just(Token::Control(Control::Semicolon)).or_not()) + comment + .repeated() + .or_not() + .ignore_then(choice(( + async_block, + if_else, + assignment, + expression_statement, + r#break, + block_statement, + r#loop, + r#while, + type_assignment, + ))) + .then_ignore(just(Token::Control(Control::Semicolon)).or_not()) }); - select_ref! { - Token::Comment(_) => {} - } - .or_not() - .ignore_then(statement) - .repeated() - .collect() + statement.repeated().collect() } #[cfg(test)] diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index ceb5ec0..1139d40 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -6,7 +6,11 @@ use std::{ sync::Arc, }; -use serde::{Deserialize, Serialize}; +use serde::{ + de::Visitor, + ser::{SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple}, + Deserialize, Deserializer, Serialize, +}; use crate::{ abstract_tree::{AbstractNode, Block, Evaluation, Type, WithPosition}, @@ -184,20 +188,312 @@ impl Ord for Value { } impl Serialize for Value { - fn serialize(&self, _serializer: S) -> Result + fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, + { + match self.0.as_ref() { + ValueInner::Boolean(boolean) => serializer.serialize_bool(*boolean), + ValueInner::Float(float) => serializer.serialize_f64(*float), + ValueInner::Function(Function { + type_parameters, + value_parameters, + return_type, + body, + }) => { + let mut struct_ser = serializer.serialize_struct("Function", 4)?; + + struct_ser.serialize_field("type_parameters", type_parameters)?; + struct_ser.serialize_field("value_parameters", value_parameters)?; + struct_ser.serialize_field("return_type", return_type)?; + struct_ser.serialize_field("body", body)?; + + struct_ser.end() + } + ValueInner::Integer(integer) => serializer.serialize_i64(*integer), + ValueInner::List(list) => { + let mut list_ser = serializer.serialize_seq(Some(list.len()))?; + + for item in list { + list_ser.serialize_element(&item.node)?; + } + + list_ser.end() + } + ValueInner::Map(map) => { + let mut map_ser = serializer.serialize_map(Some(map.len()))?; + + for (identifier, value) in map { + map_ser.serialize_entry(identifier, value)?; + } + + map_ser.end() + } + ValueInner::Range(range) => { + let mut tuple_ser = serializer.serialize_tuple(2)?; + + tuple_ser.serialize_element(&range.start)?; + tuple_ser.serialize_element(&range.end)?; + + tuple_ser.end() + } + ValueInner::String(string) => serializer.serialize_str(string), + ValueInner::Structure { name, fields } => { + let mut struct_ser = serializer.serialize_struct("Structure", 2)?; + + struct_ser.serialize_field("name", name)?; + struct_ser.serialize_field("fields", fields)?; + + struct_ser.end() + } + } + } +} + +struct ValueVisitor; + +impl<'de> Visitor<'de> for ValueVisitor { + type Value = Value; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter + .write_str("a boolean, float, function, integer, list, map, range, string or structure") + } + + fn visit_bool(self, v: bool) -> Result + where + E: serde::de::Error, + { + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::Bool(v), + &self, + )) + } + + fn visit_i8(self, v: i8) -> Result + where + E: serde::de::Error, + { + self.visit_i64(v as i64) + } + + fn visit_i16(self, v: i16) -> Result + where + E: serde::de::Error, + { + self.visit_i64(v as i64) + } + + fn visit_i32(self, v: i32) -> Result + where + E: serde::de::Error, + { + self.visit_i64(v as i64) + } + + fn visit_i64(self, v: i64) -> Result + where + E: serde::de::Error, + { + Ok(Value::integer(v)) + } + + fn visit_i128(self, v: i128) -> Result + where + E: serde::de::Error, { todo!() } + + fn visit_u8(self, v: u8) -> Result + where + E: serde::de::Error, + { + self.visit_u64(v as u64) + } + + fn visit_u16(self, v: u16) -> Result + where + E: serde::de::Error, + { + self.visit_u64(v as u64) + } + + fn visit_u32(self, v: u32) -> Result + where + E: serde::de::Error, + { + self.visit_u64(v as u64) + } + + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error, + { + Ok(Value::integer(v as i64)) + } + + fn visit_u128(self, v: u128) -> Result + where + E: serde::de::Error, + { + todo!() + } + + fn visit_f32(self, v: f32) -> Result + where + E: serde::de::Error, + { + self.visit_f64(v as f64) + } + + fn visit_f64(self, v: f64) -> Result + where + E: serde::de::Error, + { + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::Float(v), + &self, + )) + } + + fn visit_char(self, v: char) -> Result + where + E: serde::de::Error, + { + self.visit_str(v.encode_utf8(&mut [0u8; 4])) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::Str(v), + &self, + )) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: serde::de::Error, + { + self.visit_str(v) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + self.visit_str(&v) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: serde::de::Error, + { + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::Bytes(v), + &self, + )) + } + + fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result + where + E: serde::de::Error, + { + self.visit_bytes(v) + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: serde::de::Error, + { + self.visit_bytes(&v) + } + + fn visit_none(self) -> Result + where + E: serde::de::Error, + { + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::Option, + &self, + )) + } + + fn visit_some(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let _ = deserializer; + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::Option, + &self, + )) + } + + fn visit_unit(self) -> Result + where + E: serde::de::Error, + { + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::Unit, + &self, + )) + } + + fn visit_newtype_struct(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let _ = deserializer; + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::NewtypeStruct, + &self, + )) + } + + fn visit_seq(self, seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let _ = seq; + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::Seq, + &self, + )) + } + + fn visit_map(self, map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let _ = map; + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::Map, + &self, + )) + } + + fn visit_enum(self, data: A) -> Result + where + A: serde::de::EnumAccess<'de>, + { + let _ = data; + Err(serde::de::Error::invalid_type( + serde::de::Unexpected::Enum, + &self, + )) + } } impl<'de> Deserialize<'de> for Value { - fn deserialize(_deserializer: D) -> Result + fn deserialize(deserializer: D) -> Result where - D: serde::Deserializer<'de>, + D: Deserializer<'de>, { - todo!() + deserializer.deserialize_any(ValueVisitor) } } diff --git a/examples/type_inference.ds b/examples/type_inference.ds index 1cce8b1..fdc907a 100644 --- a/examples/type_inference.ds +++ b/examples/type_inference.ds @@ -1,5 +1,16 @@ +// This function returns its argument. foo = fn |T| (x: T) -> T { x } -bar: str = foo::(str)::("hi") + +// Use turbofish to supply type information. +bar = foo::(str)::("hi") + +// Use type annotation baz: str = foo("hi") + +// The `json.parse` function takes a string and returns the specified type + +// Use turbofish x = json.parse::(int)::("1") + +// Use type annotation x: int = json.parse("1")