From 0b64afccb18c7b7c426c839c80573b5514be759d Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 17 Aug 2024 10:07:38 -0400 Subject: [PATCH] Extend VM, abstract tree and parser --- dust-lang/src/analyzer.rs | 48 ++++- dust-lang/src/ast/expression.rs | 69 ++++++- dust-lang/src/lexer.rs | 27 +++ dust-lang/src/parser.rs | 218 +++++++++++++++----- dust-lang/src/token.rs | 49 +++-- dust-lang/src/type.rs | 277 +++++++++++++++++++------ dust-lang/src/value.rs | 141 +++++++++++-- dust-lang/src/vm.rs | 350 +++++++++++++++++++++++--------- 8 files changed, 923 insertions(+), 256 deletions(-) diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index 00d2d4e..4be3653 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -12,11 +12,12 @@ use std::{ use crate::{ ast::{ AbstractSyntaxTree, BlockExpression, CallExpression, ElseExpression, FieldAccessExpression, - IfExpression, LetStatement, ListExpression, ListIndexExpression, LoopExpression, Node, - OperatorExpression, RangeExpression, Span, Statement, StructExpression, - TupleAccessExpression, + IfExpression, LetStatement, ListExpression, ListIndexExpression, LoopExpression, + MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement, + StructDefinition, StructExpression, TupleAccessExpression, }, - parse, Context, DustError, Expression, Identifier, Type, + parse, Context, DustError, Expression, FieldsStructType, Identifier, StructType, TupleType, + Type, }; /// Analyzes the abstract syntax tree for errors. @@ -129,7 +130,37 @@ impl<'a> Analyzer<'a> { value, } => todo!(), }, - Statement::StructDefinition(_) => {} + Statement::StructDefinition(struct_definition) => { + let (name, struct_type) = match &struct_definition.inner { + StructDefinition::Unit { name } => { + (name.inner.clone(), Type::Struct(StructType::Unit)) + } + StructDefinition::Tuple { name, items } => { + let fields = items.iter().map(|item| item.inner.clone()).collect(); + + ( + name.inner.clone(), + Type::Struct(StructType::Tuple(TupleType { fields })), + ) + } + StructDefinition::Fields { name, fields } => { + let fields = fields + .iter() + .map(|(identifier, r#type)| { + (identifier.inner.clone(), r#type.inner.clone()) + }) + .collect(); + + ( + name.inner.clone(), + Type::Struct(StructType::Fields(FieldsStructType { fields })), + ) + } + }; + + self.context + .set_type(name, struct_type, struct_definition.position); + } } Ok(()) @@ -204,6 +235,13 @@ impl<'a> Analyzer<'a> { self.analyze_block(&block.inner)?; } }, + Expression::Map(map_expression) => { + let MapExpression { pairs } = map_expression.inner.as_ref(); + + for (_, expression) in pairs { + self.analyze_expression(expression)?; + } + } Expression::Operator(operator_expression) => match operator_expression.inner.as_ref() { OperatorExpression::Assignment { assignee, value } => { self.analyze_expression(assignee)?; diff --git a/dust-lang/src/ast/expression.rs b/dust-lang/src/ast/expression.rs index f5b352a..bf41b3e 100644 --- a/dust-lang/src/ast/expression.rs +++ b/dust-lang/src/ast/expression.rs @@ -6,7 +6,9 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::{Context, FieldsStructType, FunctionType, Identifier, StructType, TupleType, Type}; +use crate::{ + Context, FieldsStructType, FunctionType, Identifier, RangeableType, StructType, TupleType, Type, +}; use super::{Node, Span, Statement}; @@ -22,6 +24,7 @@ pub enum Expression { ListIndex(Node>), Literal(Node>), Loop(Node>), + Map(Node>), Operator(Node>), Range(Node>), Struct(Node>), @@ -29,6 +32,15 @@ pub enum Expression { } impl Expression { + pub fn map(pairs: Vec<(Node, Expression)>, position: Span) -> Self { + Self::Map(Node::new( + Box::new(MapExpression { + pairs: pairs.into_iter().collect(), + }), + position, + )) + } + pub fn operator(operator_expression: OperatorExpression, position: Span) -> Self { Self::Operator(Node::new(Box::new(operator_expression), position)) } @@ -323,6 +335,19 @@ impl Expression { LoopExpression::Infinite { .. } => None, LoopExpression::While { block, .. } => block.inner.return_type(context), }, + Expression::Map(map_expression) => { + let MapExpression { pairs } = map_expression.inner.as_ref(); + + let mut types = HashMap::with_capacity(pairs.len()); + + for (key, value) in pairs { + let value_type = value.return_type(context)?; + + types.insert(key.inner.clone(), value_type); + } + + Some(Type::Map { pairs: types }) + } Expression::Operator(operator_expression) => match operator_expression.inner.as_ref() { OperatorExpression::Assignment { .. } => None, OperatorExpression::Comparison { .. } => Some(Type::Boolean), @@ -333,7 +358,24 @@ impl Expression { OperatorExpression::Math { left, .. } => left.return_type(context), OperatorExpression::Logic { .. } => Some(Type::Boolean), }, - Expression::Range(_) => Some(Type::Range), + Expression::Range(range_expression) => { + let start = match range_expression.inner.as_ref() { + RangeExpression::Exclusive { start, .. } => start, + RangeExpression::Inclusive { start, end } => start, + }; + let start_type = start.return_type(context)?; + let rangeable_type = match start_type { + Type::Byte => RangeableType::Byte, + Type::Character => RangeableType::Character, + Type::Float => RangeableType::Float, + Type::Integer => RangeableType::Integer, + _ => return None, + }; + + Some(Type::Range { + r#type: rangeable_type, + }) + } Expression::Struct(struct_expression) => match struct_expression.inner.as_ref() { StructExpression::Fields { fields, .. } => { let mut types = HashMap::with_capacity(fields.len()); @@ -375,6 +417,7 @@ impl Expression { Expression::ListIndex(list_index) => list_index.position, Expression::Literal(literal) => literal.position, Expression::Loop(r#loop) => r#loop.position, + Expression::Map(map) => map.position, Expression::Operator(operator) => operator.position, Expression::Range(range) => range.position, Expression::Struct(r#struct) => r#struct.position, @@ -396,6 +439,7 @@ impl Display for Expression { Expression::ListIndex(list_index) => write!(f, "{}", list_index.inner), Expression::Literal(literal) => write!(f, "{}", literal.inner), Expression::Loop(r#loop) => write!(f, "{}", r#loop.inner), + Expression::Map(map) => write!(f, "{}", map.inner), Expression::Operator(operator) => write!(f, "{}", operator.inner), Expression::Range(range) => write!(f, "{}", range), Expression::Struct(r#struct) => write!(f, "{}", r#struct.inner), @@ -404,6 +448,27 @@ impl Display for Expression { } } +#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct MapExpression { + pub pairs: Vec<(Node, Expression)>, +} + +impl Display for MapExpression { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{{")?; + + for (index, (key, value)) in self.pairs.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + + write!(f, "{} = {}", key.inner, value)?; + } + + write!(f, "}}") + } +} + #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct TupleAccessExpression { pub tuple: Expression, diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 5052dea..8021c36 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -433,6 +433,7 @@ impl<'src> Lexer<'src> { "is_odd" => Token::IsOdd, "length" => Token::Length, "let" => Token::Let, + "map" => Token::Map, "mut" => Token::Mut, "read_line" => Token::ReadLine, "struct" => Token::Struct, @@ -501,6 +502,32 @@ impl Display for LexError { mod tests { use super::*; + #[test] + fn map_expression() { + let input = "map { x = '1', y = 2, z = 3.0 }"; + + assert_eq!( + lex(input), + Ok(vec![ + (Token::Map, (0, 3)), + (Token::LeftCurlyBrace, (4, 5)), + (Token::Identifier("x"), (6, 7)), + (Token::Equal, (8, 9)), + (Token::String("1"), (10, 13)), + (Token::Comma, (13, 14)), + (Token::Identifier("y"), (15, 16)), + (Token::Equal, (17, 18)), + (Token::Integer("2"), (19, 20)), + (Token::Comma, (20, 21)), + (Token::Identifier("z"), (22, 23)), + (Token::Equal, (24, 25)), + (Token::Float("3.0"), (26, 29)), + (Token::RightCurlyBrace, (30, 31)), + (Token::Eof, (31, 31)), + ]) + ); + } + #[test] fn let_statement() { let input = "let x = 42"; diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 4ebcc79..9359ef5 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -4,7 +4,6 @@ //! - `parse` convenience function //! - `Parser` struct, which parses the input a statement at a time use std::{ - collections::VecDeque, error::Error, fmt::{self, Display, Formatter}, num::{ParseFloatError, ParseIntError}, @@ -180,10 +179,8 @@ impl<'src> Parser<'src> { self.next_token()?; - let (name, name_end) = if let Token::Identifier(_) = self.current_token { - let end = self.current_position.1; - - (self.parse_identifier()?, end) + let name = if let Token::Identifier(_) = self.current_token { + self.parse_identifier()? } else { return Err(ParseError::ExpectedToken { expected: TokenKind::Identifier, @@ -192,6 +189,17 @@ impl<'src> Parser<'src> { }); }; + if let Token::Semicolon = self.current_token { + let end = self.current_position.1; + + self.next_token()?; + + return Ok(Statement::struct_definition( + StructDefinition::Unit { name }, + (start_position.0, end), + )); + } + if let Token::LeftParenthesis = self.current_token { self.next_token()?; @@ -199,26 +207,45 @@ impl<'src> Parser<'src> { loop { if let Token::RightParenthesis = self.current_token { - let position = (start_position.0, self.current_position.1); - self.next_token()?; - return Ok(Statement::struct_definition( - StructDefinition::Tuple { name, items: types }, - position, - )); + if let Token::Semicolon = self.current_token { + break; + } else { + return Err(ParseError::ExpectedToken { + expected: TokenKind::Semicolon, + actual: self.current_token.to_owned(), + position: self.current_position, + }); + } } + let type_node = self.parse_type()?; + + types.push(type_node); + if let Token::Comma = self.current_token { self.next_token()?; continue; } - - let type_node = self.parse_type()?; - - types.push(type_node); } + + let position = (start_position.0, self.current_position.1); + + self.next_token()?; + + return if types.is_empty() { + Ok(Statement::struct_definition( + StructDefinition::Unit { name }, + position, + )) + } else { + Ok(Statement::struct_definition( + StructDefinition::Tuple { name, items: types }, + position, + )) + }; } if let Token::LeftCurlyBrace = self.current_token { @@ -228,20 +255,11 @@ impl<'src> Parser<'src> { loop { if let Token::RightCurlyBrace = self.current_token { - let position = (start_position.0, self.current_position.1); + if let Token::Semicolon = self.current_token { + self.next_token()?; + } - self.next_token()?; - - return Ok(Statement::struct_definition( - StructDefinition::Fields { name, fields }, - position, - )); - } - - if let Token::Comma = self.current_token { - self.next_token()?; - - continue; + break; } let field_name = self.parse_identifier()?; @@ -259,13 +277,40 @@ impl<'src> Parser<'src> { let field_type = self.parse_type()?; fields.push((field_name, field_type)); + + if let Token::Comma = self.current_token { + self.next_token()?; + + continue; + } } + + let position = (start_position.0, self.current_position.1); + + self.next_token()?; + + return if fields.is_empty() { + Ok(Statement::struct_definition( + StructDefinition::Unit { name }, + position, + )) + } else { + Ok(Statement::struct_definition( + StructDefinition::Fields { name, fields }, + position, + )) + }; } - return Ok(Statement::struct_definition( - StructDefinition::Unit { name }, - (start_position.0, name_end), - )); + return Err(ParseError::ExpectedTokenMultiple { + expected: vec![ + TokenKind::LeftParenthesis, + TokenKind::LeftCurlyBrace, + TokenKind::Semicolon, + ], + actual: self.current_token.to_owned(), + position: self.current_position, + }); } let expression = self.parse_expression(0)?; @@ -569,6 +614,51 @@ impl<'src> Parser<'src> { expressions.push(expression); } } + Token::Map => { + self.next_token()?; + + if let Token::LeftCurlyBrace = self.current_token { + self.next_token()?; + } else { + return Err(ParseError::ExpectedToken { + expected: TokenKind::LeftCurlyBrace, + actual: self.current_token.to_owned(), + position: self.current_position, + }); + } + + let mut fields = Vec::new(); + + loop { + if let Token::RightCurlyBrace = self.current_token { + let position = (start_position.0, self.current_position.1); + + self.next_token()?; + + return Ok(Expression::map(fields, position)); + } + + let field_name = self.parse_identifier()?; + + if let Token::Equal = self.current_token { + self.next_token()?; + } else { + return Err(ParseError::ExpectedToken { + expected: TokenKind::Equal, + actual: self.current_token.to_owned(), + position: self.current_position, + }); + } + + let field_value = self.parse_expression(0)?; + + fields.push((field_name, field_value)); + + if let Token::Comma = self.current_token { + self.next_token()?; + } + } + } Token::While => { self.next_token()?; @@ -1084,9 +1174,39 @@ mod tests { use super::*; #[test] - fn let_mut_while_loop() { - env_logger::builder().is_test(true).try_init().ok(); + fn map_expression() { + let source = "map { x = '1', y = 2, z = 3.0 }"; + assert_eq!( + parse(source), + Ok(AbstractSyntaxTree { + statements: [Statement::Expression(Expression::map( + vec![ + ( + Node::new(Identifier::new("x"), (6, 7)), + Expression::literal( + LiteralExpression::String("1".to_string()), + (10, 13) + ), + ), + ( + Node::new(Identifier::new("y"), (15, 16)), + Expression::literal(LiteralExpression::Integer(2), (19, 20)), + ), + ( + Node::new(Identifier::new("z"), (22, 23)), + Expression::literal(LiteralExpression::Float(3.0), (26, 29)), + ), + ], + (0, 31), + ))] + .into(), + }) + ); + } + + #[test] + fn let_mut_while_loop() { let source = "let mut x = 0; while x < 10 { x += 1 }; x"; assert_eq!( @@ -1307,36 +1427,26 @@ mod tests { #[test] fn tuple_struct_instantiation() { - let source = "struct Foo(int, float) Foo(1, 2.0)"; + let source = "Foo(1, 2.0)"; assert_eq!( parse(source), Ok(AbstractSyntaxTree::with_statements([ - Statement::struct_definition( - StructDefinition::Tuple { - name: Node::new(Identifier::new("Foo"), (7, 10)), - items: vec![ - Node::new(Type::Integer, (11, 14)), - Node::new(Type::Float, (16, 21)), - ] - }, - (0, 22) - ), Statement::Expression(Expression::call( - Expression::identifier(Identifier::new("Foo"), (23, 26)), + Expression::identifier(Identifier::new("Foo"), (0, 3)), vec![ - Expression::literal(LiteralExpression::Integer(1), (27, 28)), - Expression::literal(LiteralExpression::Float(2.0), (30, 33)) + Expression::literal(LiteralExpression::Integer(1), (4, 5)), + Expression::literal(LiteralExpression::Float(2.0), (7, 10)) ], - (23, 34) + (0, 11) )) ])) ); } #[test] - fn tuple_struct() { - let source = "struct Foo(int, float)"; + fn tuple_struct_definition() { + let source = "struct Foo(int, float);"; assert_eq!( parse(source), @@ -1349,7 +1459,7 @@ mod tests { Node::new(Type::Float, (16, 21)), ], }, - (0, 22) + (0, 23) )) ])) ); @@ -1357,7 +1467,7 @@ mod tests { #[test] fn unit_struct() { - let source = "struct Foo"; + let source = "struct Foo;"; assert_eq!( parse(source), @@ -1366,7 +1476,7 @@ mod tests { StructDefinition::Unit { name: Node::new(Identifier::new("Foo"), (7, 10)), }, - (0, 10) + (0, 11) )) ])) ); diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index 7624d27..060ce57 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -27,6 +27,7 @@ pub enum Token<'src> { IsOdd, Length, Let, + Map, Mut, ReadLine, Str, @@ -101,6 +102,7 @@ impl<'src> Token<'src> { Token::Let => TokenOwned::Let, Token::Less => TokenOwned::Less, Token::LessEqual => TokenOwned::LessOrEqual, + Token::Map => TokenOwned::Map, Token::Minus => TokenOwned::Minus, Token::MinusEqual => TokenOwned::MinusEqual, Token::Mut => TokenOwned::Mut, @@ -159,6 +161,7 @@ impl<'src> Token<'src> { Token::Length => "length", Token::Less => "<", Token::LessEqual => "<=", + Token::Map => "map", Token::Minus => "-", Token::MinusEqual => "-=", Token::Mut => "mut", @@ -214,6 +217,7 @@ impl<'src> Token<'src> { Token::Length => TokenKind::Length, Token::Less => TokenKind::Less, Token::LessEqual => TokenKind::LessOrEqual, + Token::Map => TokenKind::Map, Token::Minus => TokenKind::Minus, Token::MinusEqual => TokenKind::MinusEqual, Token::Mut => TokenKind::Mut, @@ -320,6 +324,7 @@ pub enum TokenOwned { IsOdd, Let, Length, + Map, Mut, ReadLine, Str, @@ -395,6 +400,7 @@ impl Display for TokenOwned { TokenOwned::Let => Token::Let.fmt(f), TokenOwned::Less => Token::Less.fmt(f), TokenOwned::LessOrEqual => Token::LessEqual.fmt(f), + TokenOwned::Map => Token::Map.fmt(f), TokenOwned::Minus => Token::Minus.fmt(f), TokenOwned::MinusEqual => Token::MinusEqual.fmt(f), TokenOwned::Mut => Token::Mut.fmt(f), @@ -442,6 +448,7 @@ pub enum TokenKind { IsOdd, Length, Let, + Map, ReadLine, Str, ToString, @@ -516,6 +523,7 @@ impl Display for TokenKind { TokenKind::Let => Token::Let.fmt(f), TokenKind::Less => Token::Less.fmt(f), TokenKind::LessOrEqual => Token::LessEqual.fmt(f), + TokenKind::Map => Token::Map.fmt(f), TokenKind::Minus => Token::Minus.fmt(f), TokenKind::MinusEqual => Token::MinusEqual.fmt(f), TokenKind::Mut => Token::Mut.fmt(f), @@ -543,31 +551,17 @@ impl Display for TokenKind { pub(crate) mod tests { use super::*; - pub fn all_tokens<'src>() -> [Token<'src>; 51] { + pub fn all_tokens<'src>() -> [Token<'src>; 52] { [ - Token::Eof, - Token::Identifier("identifier"), + Token::Identifier("foobar"), Token::Boolean("true"), Token::Float("1.0"), Token::Integer("1"), Token::String("string"), Token::Async, - Token::Bool, - Token::Else, - Token::FloatKeyword, - Token::If, - Token::Int, - Token::IsEven, - Token::IsOdd, - Token::Length, - Token::Let, - Token::ReadLine, - Token::Str, - Token::ToString, - Token::While, - Token::WriteLine, - Token::BangEqual, Token::Bang, + Token::BangEqual, + Token::Bool, Token::Colon, Token::Comma, Token::Dot, @@ -575,27 +569,42 @@ pub(crate) mod tests { Token::DoubleDot, Token::DoubleEqual, Token::DoublePipe, + Token::Else, + Token::Eof, Token::Equal, + Token::FloatKeyword, Token::Greater, Token::GreaterEqual, + Token::If, + Token::Int, + Token::IsEven, + Token::IsOdd, Token::LeftCurlyBrace, Token::LeftParenthesis, Token::LeftSquareBrace, + Token::Length, Token::Less, Token::LessEqual, + Token::Let, + Token::Map, Token::Minus, Token::MinusEqual, Token::Mut, Token::Percent, Token::Plus, Token::PlusEqual, + Token::ReadLine, Token::RightCurlyBrace, Token::RightParenthesis, Token::RightSquareBrace, Token::Semicolon, - Token::Star, - Token::Struct, Token::Slash, + Token::Star, + Token::Str, + Token::Struct, + Token::ToString, + Token::While, + Token::WriteLine, ] } diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index 6e2bf5b..17fc188 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -13,17 +13,16 @@ use std::{ cmp::Ordering, collections::HashMap, fmt::{self, Display, Formatter}, - sync::Arc, }; use serde::{Deserialize, Serialize}; -use crate::{value::Function, Identifier}; +use crate::Identifier; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] /// Description of a kind of value. /// /// See the [module documentation](index.html) for more information. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum Type { Any, Boolean, @@ -45,8 +44,13 @@ pub enum Type { ListOf { item_type: Box, }, + Map { + pairs: HashMap, + }, Number, - Range, + Range { + r#type: RangeableType, + }, String, Struct(StructType), Tuple(TupleType), @@ -72,7 +76,6 @@ impl Type { | (Type::Boolean, Type::Boolean) | (Type::Float, Type::Float) | (Type::Integer, Type::Integer) - | (Type::Range, Type::Range) | (Type::String, Type::String) => return Ok(()), ( Type::Generic { @@ -192,38 +195,21 @@ impl Type { return_type: right_return, }), ) => { - if left_name != right_name { + if left_name != right_name + || left_return != right_return + || left_type_parameters != right_type_parameters + || left_value_parameters != right_value_parameters + { return Err(TypeConflict { actual: other.clone(), expected: self.clone(), }); } - if left_return == right_return { - for (left_parameter, right_parameter) in left_type_parameters - .iter() - .zip(right_type_parameters.iter()) - { - if left_parameter != right_parameter { - return Err(TypeConflict { - actual: other.clone(), - expected: self.clone(), - }); - } - } - - for (left_parameter, right_parameter) in left_value_parameters - .iter() - .zip(right_value_parameters.iter()) - { - if left_parameter != right_parameter { - return Err(TypeConflict { - actual: other.clone(), - expected: self.clone(), - }); - } - } - + return Ok(()); + } + (Type::Range { r#type: left_type }, Type::Range { r#type: right_type }) => { + if left_type == right_type { return Ok(()); } } @@ -248,7 +234,7 @@ impl Display for Type { Type::Boolean => write!(f, "bool"), Type::Byte => write!(f, "byte"), Type::Character => write!(f, "char"), - Type::Enum(enum_type) => write!(f, "{enum_type}"), + Type::Enum(EnumType { name, .. }) => write!(f, "{name}"), Type::Float => write!(f, "float"), Type::Function(function_type) => write!(f, "{function_type}"), Type::Generic { concrete_type, .. } => { @@ -262,10 +248,53 @@ impl Display for Type { Type::List { item_type, length } => write!(f, "[{item_type}; {length}]"), Type::ListEmpty => write!(f, "[]"), Type::ListOf { item_type } => write!(f, "[{item_type}]"), + Type::Map { pairs } => { + write!(f, "map ")?; + + write!(f, "{{")?; + + for (index, (key, value)) in pairs.iter().enumerate() { + write!(f, "{key}: {value}")?; + + if index != pairs.len() - 1 { + write!(f, ", ")?; + } + } + + write!(f, "}}") + } Type::Number => write!(f, "num"), - Type::Range => write!(f, "range"), + Type::Range { r#type } => write!(f, "{type} range"), Type::String => write!(f, "str"), - Type::Struct(struct_type) => write!(f, "{struct_type}"), + Type::Struct(struct_type) => match struct_type { + StructType::Unit => write!(f, "()"), + StructType::Tuple(TupleType { fields }) => { + write!(f, "(")?; + + for (index, r#type) in fields.iter().enumerate() { + write!(f, "{type}")?; + + if index != fields.len() - 1 { + write!(f, ", ")?; + } + } + + write!(f, ")") + } + StructType::Fields(FieldsStructType { fields }) => { + write!(f, "{{")?; + + for (index, (name, r#type)) in fields.iter().enumerate() { + write!(f, "{name}: {type}")?; + + if index != fields.len() - 1 { + write!(f, ", ")?; + } + } + + write!(f, "}}") + } + }, Type::Tuple(TupleType { fields }) => { write!(f, "(")?; @@ -283,6 +312,85 @@ impl Display for Type { } } +impl PartialOrd for Type { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Type { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Type::Any, Type::Any) => Ordering::Equal, + (Type::Any, _) => Ordering::Greater, + (Type::Boolean, Type::Boolean) => Ordering::Equal, + (Type::Boolean, _) => Ordering::Greater, + (Type::Byte, Type::Byte) => Ordering::Equal, + (Type::Byte, _) => Ordering::Greater, + (Type::Character, Type::Character) => Ordering::Equal, + (Type::Character, _) => Ordering::Greater, + (Type::Enum(left_enum), Type::Enum(right_enum)) => left_enum.cmp(right_enum), + (Type::Enum(_), _) => Ordering::Greater, + (Type::Float, Type::Float) => Ordering::Equal, + (Type::Float, _) => Ordering::Greater, + (Type::Function(left_function), Type::Function(right_function)) => { + left_function.cmp(right_function) + } + (Type::Function(_), _) => Ordering::Greater, + (Type::Generic { .. }, Type::Generic { .. }) => Ordering::Equal, + (Type::Generic { .. }, _) => Ordering::Greater, + (Type::Integer, Type::Integer) => Ordering::Equal, + (Type::Integer, _) => Ordering::Greater, + ( + Type::List { + item_type: left_item_type, + length: left_length, + }, + Type::List { + item_type: right_item_type, + length: right_length, + }, + ) => { + if left_length == right_length { + left_item_type.cmp(right_item_type) + } else { + left_length.cmp(right_length) + } + } + (Type::List { .. }, _) => Ordering::Greater, + (Type::ListEmpty, Type::ListEmpty) => Ordering::Equal, + (Type::ListEmpty, _) => Ordering::Greater, + ( + Type::ListOf { + item_type: left_item_type, + }, + Type::ListOf { + item_type: right_item_type, + }, + ) => left_item_type.cmp(right_item_type), + (Type::ListOf { .. }, _) => Ordering::Greater, + (Type::Map { pairs: left_pairs }, Type::Map { pairs: right_pairs }) => { + left_pairs.iter().cmp(right_pairs.iter()) + } + (Type::Map { .. }, _) => Ordering::Greater, + (Type::Number, Type::Number) => Ordering::Equal, + (Type::Number, _) => Ordering::Greater, + (Type::Range { r#type: left_type }, Type::Range { r#type: right_type }) => { + left_type.cmp(right_type) + } + (Type::Range { .. }, _) => Ordering::Greater, + (Type::String, Type::String) => Ordering::Equal, + (Type::String, _) => Ordering::Greater, + (Type::Struct(left_struct), Type::Struct(right_struct)) => { + left_struct.cmp(right_struct) + } + (Type::Struct(_), _) => Ordering::Greater, + (Type::Tuple(left_tuple), Type::Tuple(right_tuple)) => left_tuple.cmp(right_tuple), + (Type::Tuple(_), _) => Ordering::Greater, + } + } +} + #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct FunctionType { pub name: Identifier, @@ -339,35 +447,11 @@ pub enum StructType { } impl Display for StructType { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { StructType::Unit => write!(f, "()"), - StructType::Tuple(TupleType { fields, .. }) => { - write!(f, "(")?; - - for (index, r#type) in fields.iter().enumerate() { - write!(f, "{type}")?; - - if index != fields.len() - 1 { - write!(f, ", ")?; - } - } - - write!(f, ")") - } - StructType::Fields(FieldsStructType { fields, .. }) => { - write!(f, "{{ ")?; - - for (index, (identifier, r#type)) in fields.iter().enumerate() { - write!(f, "{identifier}: {type}")?; - - if index != fields.len() - 1 { - write!(f, ", ")?; - } - } - - write!(f, " }}") - } + StructType::Tuple(tuple) => write!(f, "{tuple}"), + StructType::Fields(fields) => write!(f, "{fields}"), } } } @@ -377,11 +461,43 @@ pub struct TupleType { pub fields: Vec, } +impl Display for TupleType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "(")?; + + for (index, field) in self.fields.iter().enumerate() { + write!(f, "{field}")?; + + if index != self.fields.len() - 1 { + write!(f, ", ")?; + } + } + + write!(f, ")") + } +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct FieldsStructType { pub fields: HashMap, } +impl Display for FieldsStructType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{{ ")?; + + for (index, (identifier, r#type)) in self.fields.iter().enumerate() { + write!(f, "{identifier}: {type}")?; + + if index != self.fields.len() - 1 { + write!(f, ", ")?; + } + } + + write!(f, " }}") + } +} + impl PartialOrd for FieldsStructType { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -401,8 +517,39 @@ pub struct EnumType { } impl Display for EnumType { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.name) + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let EnumType { name, variants } = self; + + write!(f, "enum {name} {{")?; + + for (index, variant) in variants.iter().enumerate() { + write!(f, "{variant}")?; + + if index != self.variants.len() - 1 { + write!(f, ", ")?; + } + } + + write!(f, "}}") + } +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum RangeableType { + Byte, + Character, + Float, + Integer, +} + +impl Display for RangeableType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + RangeableType::Byte => Type::Byte.fmt(f), + RangeableType::Character => Type::Character.fmt(f), + RangeableType::Float => Type::Float.fmt(f), + RangeableType::Integer => Type::Integer.fmt(f), + } } } @@ -476,7 +623,9 @@ mod tests { item_type: Box::new(Type::Integer), length: 42, }, - Type::Range, + Type::Range { + r#type: RangeableType::Integer, + }, Type::String, ]; diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index a3184fc..b9041fa 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -4,19 +4,20 @@ use std::{ collections::HashMap, error::Error, fmt::{self, Display, Formatter}, - ops::{Range, RangeInclusive}, + ops::{Index, Range, RangeInclusive}, + rc::Weak, sync::{Arc, RwLock}, }; use serde::{ de::{self, MapAccess, Visitor}, - ser::{SerializeStruct, SerializeStructVariant}, + ser::{SerializeMap, SerializeStruct, SerializeStructVariant}, Deserialize, Deserializer, Serialize, Serializer, }; use crate::{ AbstractSyntaxTree, Context, EnumType, FieldsStructType, FunctionType, Identifier, - RuntimeError, StructType, TupleType, Type, Vm, + RangeableType, RuntimeError, StructType, TupleType, Type, Vm, }; /// Dust value representation @@ -61,6 +62,7 @@ pub enum Value { Function(Function), Integer(i64), List(Vec), + Map(HashMap), Mutable(Arc>), Range(Range), RangeInclusive(RangeInclusive), @@ -70,6 +72,10 @@ pub enum Value { } impl Value { + pub fn map>>(pairs: T) -> Value { + Value::Map(pairs.into()) + } + pub fn mutable(value: Value) -> Value { Value::Mutable(Arc::new(RwLock::new(value))) } @@ -194,9 +200,25 @@ impl Value { length: values.len(), } } + Value::Map(map) => { + let pairs = map + .iter() + .map(|(key, value)| (key.clone(), value.r#type())) + .collect(); + + Type::Map { pairs } + } Value::Mutable(locked) => locked.read().unwrap().r#type(), - Value::Range(_) => Type::Range, - Value::RangeInclusive(_) => Type::Range, + Value::Range(range) => Type::Range { + r#type: range.start.r#type(), + }, + Value::RangeInclusive(range_inclusive) => { + let rangeable_type = range_inclusive.start().r#type(); + + Type::Range { + r#type: rangeable_type, + } + } Value::String(_) => Type::String, Value::Struct(r#struct) => match r#struct { Struct::Unit { .. } => Type::Struct(StructType::Unit), @@ -230,12 +252,69 @@ impl Value { } } - pub fn get_index(&self, index: usize) -> Option { - match self { - Value::List(values) => values.get(index).cloned(), - Value::Mutable(inner) => inner.read().unwrap().get_index(index), - Value::Struct(Struct::Tuple { fields, .. }) => fields.get(index).cloned(), - _ => None, + pub fn get_index(&self, index: Value) -> Result, ValueError> { + match (self, index) { + (Value::Mutable(left), Value::Mutable(right)) => { + return left + .read() + .unwrap() + .get_index(right.read().unwrap().clone()); + } + (Value::Mutable(locked), index) => { + return locked.read().unwrap().get_index(index); + } + (left, Value::Mutable(locked)) => { + return left.get_index(locked.read().unwrap().clone()); + } + (Value::List(values), Value::Integer(integer)) => { + let index = integer as usize; + + return Ok(values.get(index).cloned()); + } + (Value::List(values), Value::Range(range)) => match (range.start, range.end) { + (Rangeable::Integer(start), Rangeable::Integer(end)) => { + let start = start as usize; + let end = end as usize; + + return Ok(values + .get(start..end) + .map(|values| Value::List(values.to_vec()))); + } + (start, end) => Err(ValueError::CannotIndex { + value: self.clone(), + index: Value::Range(start..end), + }), + }, + (Value::String(string), Value::Range(range)) => match (range.start, range.end) { + (Rangeable::Integer(start), Rangeable::Integer(end)) => { + let start = start as usize; + let end = end as usize; + + return Ok(string.get(start..end).map(Value::string)); + } + (start, end) => Err(ValueError::CannotIndex { + value: self.clone(), + index: Value::Range(start..end), + }), + }, + (Value::Range(range), Value::Integer(index)) => match (range.start, range.end) { + (Rangeable::Integer(start), Rangeable::Integer(end)) => { + Ok((start..end).nth(index as usize).map(Value::Integer)) + } + (start, end) => Err(ValueError::CannotIndex { + value: self.clone(), + index: Value::Range(start..end), + }), + }, + (Value::String(string), Value::Integer(integer)) => { + let index = integer as usize; + + return Ok(string.chars().nth(index).map(Value::Character)); + } + (value, index) => Err(ValueError::CannotIndex { + value: value.clone(), + index, + }), } } @@ -295,7 +374,7 @@ impl Value { _ => {} } - Err(ValueError::CannotAdd(self.clone(), other.clone())) + Err(ValueError::CannotMutate(self.clone())) } pub fn subtract(&self, other: &Value) -> Result { @@ -698,6 +777,19 @@ impl Display for Value { Value::Float(float) => write!(f, "{float}"), Value::Function(function) => write!(f, "{function}"), Value::Integer(integer) => write!(f, "{integer}"), + Value::Map(pairs) => { + write!(f, "{{ ")?; + + for (index, (key, value)) in pairs.iter().enumerate() { + write!(f, "{key}: {value}")?; + + if index < pairs.len() - 1 { + write!(f, ", ")?; + } + } + + write!(f, " }}") + } Value::List(list) => { write!(f, "[")?; @@ -751,6 +843,7 @@ impl PartialEq for Value { (Value::Function(left), Value::Function(right)) => left == right, (Value::Integer(left), Value::Integer(right)) => left == right, (Value::List(left), Value::List(right)) => left == right, + (Value::Map(left), Value::Map(right)) => left == right, (Value::Mutable(left), Value::Mutable(right)) => { let left = &*left.read().unwrap(); let right = &*right.read().unwrap(); @@ -853,6 +946,15 @@ impl Serialize for Value { Value::Function(function) => function.serialize(serializer), Value::Integer(integer) => serializer.serialize_i64(*integer), Value::List(list) => list.serialize(serializer), + Value::Map(pairs) => { + let mut ser = serializer.serialize_map(Some(pairs.len()))?; + + for (key, value) in pairs { + ser.serialize_entry(key, value)?; + } + + ser.end() + } Value::Range(range) => range.serialize(serializer), Value::RangeInclusive(inclusive) => inclusive.serialize(serializer), Value::String(string) => serializer.serialize_str(string), @@ -1033,7 +1135,7 @@ impl<'de> Deserialize<'de> for Function { return Err(de::Error::duplicate_field("body")); } - body = Some(map.next_value().map(|ast| Arc::new(ast))?); + body = Some(map.next_value().map(Arc::new)?); } _ => { return Err(de::Error::unknown_field(key, &["name", "type", "body"])); @@ -1156,7 +1258,7 @@ impl Display for Struct { } } -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub enum Rangeable { Byte(u8), Character(char), @@ -1164,6 +1266,17 @@ pub enum Rangeable { Integer(i64), } +impl Rangeable { + fn r#type(&self) -> RangeableType { + match self { + Rangeable::Byte(_) => RangeableType::Byte, + Rangeable::Character(_) => RangeableType::Character, + Rangeable::Float(_) => RangeableType::Float, + Rangeable::Integer(_) => RangeableType::Integer, + } + } +} + impl Display for Rangeable { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 4993445..d69e3aa 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -5,6 +5,7 @@ //! - `run_with_context` convenience function that takes a source code string and a context //! - `Vm` struct that can be used to run an abstract syntax tree use std::{ + collections::HashMap, fmt::{self, Display, Formatter}, sync::{Arc, Mutex}, }; @@ -15,11 +16,11 @@ use crate::{ ast::{ AbstractSyntaxTree, BlockExpression, CallExpression, ComparisonOperator, ElseExpression, FieldAccessExpression, IfExpression, LetStatement, ListExpression, ListIndexExpression, - LiteralExpression, LogicOperator, LoopExpression, MathOperator, Node, OperatorExpression, - RangeExpression, Span, Statement, StructDefinition, + LiteralExpression, LogicOperator, LoopExpression, MapExpression, MathOperator, Node, + OperatorExpression, RangeExpression, Span, Statement, StructDefinition, StructExpression, }, - parse, Analyzer, BuiltInFunctionError, Context, DustError, Expression, Identifier, ParseError, - StructType, TupleType, Type, Value, ValueError, + parse, Analyzer, BuiltInFunctionError, Context, DustError, Expression, FieldsStructType, + Identifier, ParseError, Struct, StructType, TupleType, Type, Value, ValueError, }; /// Run the source code and return the result. @@ -129,7 +130,17 @@ impl Vm { (name.inner.clone(), StructType::Tuple(TupleType { fields })) } - StructDefinition::Fields { name, fields } => todo!(), + StructDefinition::Fields { name, fields } => { + let fields = fields + .into_iter() + .map(|(identifier, r#type)| (identifier.inner, r#type.inner)) + .collect(); + + ( + name.inner.clone(), + StructType::Fields(FieldsStructType { fields }), + ) + } }; self.context @@ -202,7 +213,7 @@ impl Vm { if let Some(value) = get_value { Ok(Evaluation::Return(Some(value))) } else { - Err(RuntimeError::UndefinedVariable { + Err(RuntimeError::UndefinedValue { identifier: identifier.inner, position: identifier.position, }) @@ -217,70 +228,16 @@ impl Vm { } Expression::Literal(literal) => self.run_literal(*literal.inner), Expression::Loop(loop_expression) => self.run_loop(*loop_expression.inner), + Expression::Map(map_expression) => self.run_map(*map_expression.inner, collect_garbage), Expression::Operator(operator_expression) => { self.run_operator(*operator_expression.inner, collect_garbage) } - Expression::Range(range_expression) => match *range_expression.inner { - RangeExpression::Exclusive { start, end } => { - let start_position = start.position(); - let start = self - .run_expression(start, collect_garbage)? - .expect_value(start_position)?; - let end_position = end.position(); - let end = self - .run_expression(end, collect_garbage)? - .expect_value(end_position)?; - - match (start, end) { - (Value::Byte(start), Value::Byte(end)) => { - Ok(Evaluation::Return(Some(Value::byte_range(start, end)))) - } - (Value::Character(start), Value::Character(end)) => { - Ok(Evaluation::Return(Some(Value::character_range(start, end)))) - } - (Value::Float(start), Value::Float(end)) => { - Ok(Evaluation::Return(Some(Value::float_range(start, end)))) - } - (Value::Integer(start), Value::Integer(end)) => { - Ok(Evaluation::Return(Some(Value::integer_range(start, end)))) - } - _ => Err(RuntimeError::InvalidRange { - start_position, - end_position, - }), - } - } - RangeExpression::Inclusive { start, end } => { - let start_position = start.position(); - let start = self - .run_expression(start, collect_garbage)? - .expect_value(start_position)?; - let end_position = end.position(); - let end = self - .run_expression(end, collect_garbage)? - .expect_value(end_position)?; - - match (start, end) { - (Value::Byte(start), Value::Byte(end)) => Ok(Evaluation::Return(Some( - Value::byte_range_inclusive(start, end), - ))), - (Value::Character(start), Value::Character(end)) => Ok(Evaluation::Return( - Some(Value::character_range_inclusive(start, end)), - )), - (Value::Float(start), Value::Float(end)) => Ok(Evaluation::Return(Some( - Value::float_range_inclusive(start, end), - ))), - (Value::Integer(start), Value::Integer(end)) => Ok(Evaluation::Return( - Some(Value::integer_range_inclusive(start, end)), - )), - _ => Err(RuntimeError::InvalidRange { - start_position, - end_position, - }), - } - } - }, - Expression::Struct(_) => todo!(), + Expression::Range(range_expression) => { + self.run_range(*range_expression.inner, collect_garbage) + } + Expression::Struct(struct_expression) => { + self.run_struct(*struct_expression.inner, collect_garbage) + } Expression::TupleAccess(_) => todo!(), }; @@ -290,6 +247,162 @@ impl Vm { }) } + fn run_struct( + &self, + struct_expression: StructExpression, + collect_garbage: bool, + ) -> Result { + match struct_expression { + StructExpression::Unit { name } => { + let position = name.position; + let r#type = self.context.get_type(&name.inner).ok_or_else(|| { + RuntimeError::UndefinedType { + identifier: name.inner.clone(), + position, + } + })?; + + if let Type::Struct(StructType::Unit) = r#type { + Ok(Evaluation::Return(Some(Value::Struct(Struct::Unit { + name: name.inner, + })))) + } else { + Err(RuntimeError::ExpectedType { + expected: Type::Struct(StructType::Unit), + actual: r#type, + position, + }) + } + } + StructExpression::Fields { + name, + fields: expressions, + } => { + let position = name.position; + let r#type = self.context.get_type(&name.inner).ok_or_else(|| { + RuntimeError::UndefinedType { + identifier: name.inner.clone(), + position, + } + })?; + + if let Type::Struct(StructType::Fields(FieldsStructType { .. })) = r#type { + let mut values = HashMap::with_capacity(expressions.len()); + + for (identifier, expression) in expressions { + let expression_position = expression.position(); + let value = self + .run_expression(expression, collect_garbage)? + .expect_value(expression_position)?; + + values.insert(identifier.inner, value); + } + + Ok(Evaluation::Return(Some(Value::Struct(Struct::Fields { + name: name.inner, + fields: values, + })))) + } else { + Err(RuntimeError::ExpectedType { + expected: Type::Struct(StructType::Fields(FieldsStructType { + fields: HashMap::new(), + })), + actual: r#type, + position, + }) + } + } + } + } + + fn run_range( + &self, + range: RangeExpression, + collect_garbage: bool, + ) -> Result { + match range { + RangeExpression::Exclusive { start, end } => { + let start_position = start.position(); + let start = self + .run_expression(start, collect_garbage)? + .expect_value(start_position)?; + let end_position = end.position(); + let end = self + .run_expression(end, collect_garbage)? + .expect_value(end_position)?; + + match (start, end) { + (Value::Byte(start), Value::Byte(end)) => { + Ok(Evaluation::Return(Some(Value::byte_range(start, end)))) + } + (Value::Character(start), Value::Character(end)) => { + Ok(Evaluation::Return(Some(Value::character_range(start, end)))) + } + (Value::Float(start), Value::Float(end)) => { + Ok(Evaluation::Return(Some(Value::float_range(start, end)))) + } + (Value::Integer(start), Value::Integer(end)) => { + Ok(Evaluation::Return(Some(Value::integer_range(start, end)))) + } + _ => Err(RuntimeError::InvalidRange { + start_position, + end_position, + }), + } + } + RangeExpression::Inclusive { start, end } => { + let start_position = start.position(); + let start = self + .run_expression(start, collect_garbage)? + .expect_value(start_position)?; + let end_position = end.position(); + let end = self + .run_expression(end, collect_garbage)? + .expect_value(end_position)?; + + match (start, end) { + (Value::Byte(start), Value::Byte(end)) => Ok(Evaluation::Return(Some( + Value::byte_range_inclusive(start, end), + ))), + (Value::Character(start), Value::Character(end)) => Ok(Evaluation::Return( + Some(Value::character_range_inclusive(start, end)), + )), + (Value::Float(start), Value::Float(end)) => Ok(Evaluation::Return(Some( + Value::float_range_inclusive(start, end), + ))), + (Value::Integer(start), Value::Integer(end)) => Ok(Evaluation::Return(Some( + Value::integer_range_inclusive(start, end), + ))), + _ => Err(RuntimeError::InvalidRange { + start_position, + end_position, + }), + } + } + } + } + + fn run_map( + &self, + map: MapExpression, + collect_garbage: bool, + ) -> Result { + let MapExpression { pairs } = map; + + let mut map = HashMap::new(); + + for (identifier, expression) in pairs { + let expression_position = expression.position(); + let value = self + .run_expression(expression, collect_garbage)? + .expect_value(expression_position)?; + + map.insert(identifier.inner, value); + } + + Ok(Evaluation::Return(Some(Value::Map(map)))) + } + fn run_operator( &self, operator: OperatorExpression, @@ -530,17 +643,16 @@ impl Vm { .run_expression(index, collect_garbage)? .expect_value(index_position)?; - let index = if let Some(index) = index_value.as_integer() { - index as usize - } else { - return Err(RuntimeError::ExpectedInteger { - position: index_position, - }); - }; + let get_index = + list_value + .get_index(index_value) + .map_err(|error| RuntimeError::ValueError { + error, + left_position: list_position, + right_position: index_position, + })?; - let value_option = list_value.get_index(index); - - Ok(Evaluation::Return(value_option)) + Ok(Evaluation::Return(get_index)) } fn run_call( @@ -797,10 +909,6 @@ pub enum RuntimeError { error: BuiltInFunctionError, position: Span, }, - CannotMutate { - value: Value, - position: Span, - }, ExpectedBoolean { position: Span, }, @@ -822,6 +930,11 @@ pub enum RuntimeError { ExpectedMap { position: Span, }, + ExpectedType { + expected: Type, + actual: Type, + position: Span, + }, ExpectedFunction { actual: Value, position: Span, @@ -836,7 +949,11 @@ pub enum RuntimeError { start_position: Span, end_position: Span, }, - UndefinedVariable { + UndefinedType { + identifier: Identifier, + position: Span, + }, + UndefinedValue { identifier: Identifier, position: Span, }, @@ -859,23 +976,24 @@ impl RuntimeError { .. } => (left_position.0, right_position.1), Self::BuiltInFunctionError { position, .. } => *position, - Self::CannotMutate { position, .. } => *position, Self::ExpectedBoolean { position } => *position, + Self::ExpectedFunction { position, .. } => *position, Self::ExpectedIdentifier { position } => *position, - Self::ExpectedIntegerOrRange { position } => *position, Self::ExpectedIdentifierOrString { position } => *position, Self::ExpectedInteger { position } => *position, - Self::ExpectedNumber { position } => *position, - Self::ExpectedMap { position } => *position, - Self::ExpectedFunction { position, .. } => *position, + Self::ExpectedIntegerOrRange { position } => *position, Self::ExpectedList { position } => *position, + Self::ExpectedMap { position } => *position, + Self::ExpectedNumber { position } => *position, + Self::ExpectedType { position, .. } => *position, Self::ExpectedValue { position } => *position, Self::InvalidRange { start_position, end_position, .. } => (start_position.0, end_position.1), - Self::UndefinedVariable { position, .. } => *position, + Self::UndefinedType { position, .. } => *position, + Self::UndefinedValue { position, .. } => *position, Self::UndefinedProperty { property_position, .. } => *property_position, @@ -911,9 +1029,6 @@ impl Display for RuntimeError { left_position, right_position, error ) } - Self::CannotMutate { value, .. } => { - write!(f, "Cannot mutate immutable value {}", value) - } Self::BuiltInFunctionError { error, .. } => { write!(f, "{}", error) } @@ -950,6 +1065,17 @@ impl Display for RuntimeError { Self::ExpectedList { position } => { write!(f, "Expected a list at position: {:?}", position) } + Self::ExpectedType { + expected, + actual, + position, + } => { + write!( + f, + "Expected type {}, but got {} at position: {:?}", + expected, actual, position + ) + } Self::ExpectedMap { position } => { write!(f, "Expected a map at position: {:?}", position) } @@ -973,7 +1099,7 @@ impl Display for RuntimeError { start_position, end_position ) } - Self::UndefinedVariable { + Self::UndefinedValue { identifier, position, } => { @@ -988,6 +1114,16 @@ impl Display for RuntimeError { } => { write!(f, "Value {} does not have the property {}", value, property) } + Self::UndefinedType { + identifier, + position, + } => { + write!( + f, + "Undefined type {} at position: {:?}", + identifier, position + ) + } } } } @@ -1001,10 +1137,30 @@ mod tests { use super::*; #[test] - fn async_block() { - let input = "let x = 1; async { x += 1; x -= 1; } x"; + fn string_index() { + let input = "'foo'[0]"; - assert_eq!(run(input), Ok(Some(Value::Integer(1)))); + assert_eq!(run(input), Ok(Some(Value::Character('f')))); + } + + #[test] + fn map_expression() { + let input = "let x = map { foo = 42, bar = 4.2 }; x"; + + assert_eq!( + run(input), + Ok(Some(Value::map([ + (Identifier::new("foo"), Value::Integer(42)), + (Identifier::new("bar"), Value::Float(4.2)) + ]))) + ); + } + + #[test] + fn async_block() { + let input = "let mut x = 1; async { x += 1; x -= 1; } x"; + + assert_eq!(run(input), Ok(Some(Value::mutable(Value::Integer(1))))); } #[test] @@ -1056,8 +1212,8 @@ mod tests { #[test] fn assign_unit_struct_variable() { let input = " - struct Foo - x = Foo + struct Foo; + let x = Foo; x ";