Extend VM, abstract tree and parser

This commit is contained in:
Jeff 2024-08-17 10:07:38 -04:00
parent fa67a568d9
commit 0b64afccb1
8 changed files with 923 additions and 256 deletions

View File

@ -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)?;

View File

@ -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<Box<ListIndexExpression>>),
Literal(Node<Box<LiteralExpression>>),
Loop(Node<Box<LoopExpression>>),
Map(Node<Box<MapExpression>>),
Operator(Node<Box<OperatorExpression>>),
Range(Node<Box<RangeExpression>>),
Struct(Node<Box<StructExpression>>),
@ -29,6 +32,15 @@ pub enum Expression {
}
impl Expression {
pub fn map(pairs: Vec<(Node<Identifier>, 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<Identifier>, 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,

View File

@ -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";

View File

@ -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)
))
]))
);

View File

@ -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,
]
}

View File

@ -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<Type>,
},
Map {
pairs: HashMap<Identifier, Type>,
},
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<Ordering> {
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<Type>,
}
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<Identifier, Type>,
}
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<Ordering> {
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,
];

View File

@ -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<Value>),
Map(HashMap<Identifier, Value>),
Mutable(Arc<RwLock<Value>>),
Range(Range<Rangeable>),
RangeInclusive(RangeInclusive<Rangeable>),
@ -70,6 +72,10 @@ pub enum Value {
}
impl Value {
pub fn map<T: Into<HashMap<Identifier, Value>>>(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<Value> {
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<Option<Value>, 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<Value, ValueError> {
@ -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 {

View File

@ -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<Evaluation, RuntimeError> {
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<Evaluation, RuntimeError> {
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<Evaluation, RuntimeError> {
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
";