Compare commits

...

3 Commits

Author SHA1 Message Date
a048577143 Improve assignment parsing 2024-08-09 06:32:44 -04:00
929468338d Add map parsing 2024-08-09 06:09:59 -04:00
55a8661618 Begin adding maps 2024-08-09 05:18:39 -04:00
7 changed files with 239 additions and 123 deletions

View File

@ -1,6 +1,6 @@
//! In-memory representation of a Dust program. //! In-memory representation of a Dust program.
use std::{ use std::{
collections::{HashMap, VecDeque}, collections::{BTreeMap, HashMap, VecDeque},
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
}; };
@ -36,7 +36,7 @@ impl<T: Display> Display for Node<T> {
pub enum Statement { pub enum Statement {
// Top-level statements // Top-level statements
Assignment { Assignment {
identifier: Identifier, identifier: Node<Identifier>,
value_node: Box<Node<Statement>>, value_node: Box<Node<Statement>>,
}, },
@ -67,6 +67,7 @@ pub enum Statement {
// Value collection expressions // Value collection expressions
List(Vec<Node<Statement>>), List(Vec<Node<Statement>>),
Map(Vec<(Node<Identifier>, Node<Statement>)>),
// Hard-coded values // Hard-coded values
Constant(Value), Constant(Value),
@ -83,9 +84,26 @@ impl Statement {
Statement::Identifier(identifier) => variables Statement::Identifier(identifier) => variables
.get(identifier) .get(identifier)
.map(|value| value.r#type(variables)), .map(|value| value.r#type(variables)),
Statement::List(nodes) => nodes Statement::List(nodes) => {
.first() let item_type = nodes.first().unwrap().inner.expected_type(variables)?;
.and_then(|node| node.inner.expected_type(variables)),
Some(Type::List {
length: nodes.len(),
item_type: Box::new(item_type),
})
}
Statement::Map(nodes) => {
let mut types = BTreeMap::new();
for (identifier, item) in nodes {
types.insert(
identifier.inner.clone(),
item.inner.expected_type(variables)?,
);
}
Some(Type::Map(types))
}
Statement::PropertyAccess(_, _) => None, Statement::PropertyAccess(_, _) => None,
} }
} }
@ -181,14 +199,30 @@ impl Display for Statement {
Statement::Identifier(identifier) => write!(f, "{identifier}"), Statement::Identifier(identifier) => write!(f, "{identifier}"),
Statement::List(nodes) => { Statement::List(nodes) => {
write!(f, "[")?; write!(f, "[")?;
for (i, node) in nodes.iter().enumerate() { for (i, node) in nodes.iter().enumerate() {
if i > 0 { if i > 0 {
write!(f, ", ")?; write!(f, ", ")?;
} }
write!(f, "{node}")?; write!(f, "{node}")?;
} }
write!(f, "]") write!(f, "]")
} }
Statement::Map(nodes) => {
write!(f, "{{")?;
for (i, (identifier, node)) in nodes.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{identifier} = {node}")?;
}
write!(f, "}}")
}
Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"), Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
} }
} }

View File

@ -80,7 +80,9 @@ impl<'a> Analyzer<'a> {
Statement::Assignment { Statement::Assignment {
value_node: value, .. value_node: value, ..
} => { } => {
if let None = value.inner.expected_type(self.variables) { self.analyze_node(value)?;
if value.inner.expected_type(self.variables).is_none() {
return Err(AnalyzerError::ExpectedValue { return Err(AnalyzerError::ExpectedValue {
actual: value.as_ref().clone(), actual: value.as_ref().clone(),
position: value.position, position: value.position,
@ -161,6 +163,11 @@ impl<'a> Analyzer<'a> {
self.analyze_node(statement)?; self.analyze_node(statement)?;
} }
} }
Statement::Map(properties) => {
for (_key, value_node) in properties {
self.analyze_node(value_node)?;
}
}
Statement::PropertyAccess(left, right) => { Statement::PropertyAccess(left, right) => {
if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) = if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) =
&left.inner &left.inner

View File

@ -189,6 +189,16 @@ impl Lexer {
(Token::Less, (self.position - 1, self.position)) (Token::Less, (self.position - 1, self.position))
} }
} }
'{' => {
self.position += 1;
(Token::LeftCurlyBrace, (self.position - 1, self.position))
}
'}' => {
self.position += 1;
(Token::RightCurlyBrace, (self.position - 1, self.position))
}
_ => { _ => {
self.position += 1; self.position += 1;
@ -415,6 +425,27 @@ impl From<ParseIntError> for LexError {
mod tests { mod tests {
use super::*; use super::*;
#[test]
fn map() {
let input = "{ x = 42, y = 'foobar' }";
assert_eq!(
lex(input),
Ok(vec![
(Token::LeftCurlyBrace, (0, 1)),
(Token::Identifier("x"), (2, 3)),
(Token::Equal, (4, 5)),
(Token::Integer(42), (6, 8)),
(Token::Comma, (8, 9)),
(Token::Identifier("y"), (10, 11)),
(Token::Equal, (12, 13)),
(Token::String("foobar"), (14, 22)),
(Token::RightCurlyBrace, (23, 24)),
(Token::Eof, (24, 24)),
])
)
}
#[test] #[test]
fn greater_than() { fn greater_than() {
let input = ">"; let input = ">";

View File

@ -27,16 +27,16 @@ use crate::{
/// Ok(AbstractSyntaxTree { /// Ok(AbstractSyntaxTree {
/// nodes: [ /// nodes: [
/// Node { /// Node {
/// inner: Statement::Assign( /// inner: Statement::Assignment {
/// Box::new(Node { /// identifier: Node {
/// inner: Statement::Identifier("x".into()), /// inner: Identifier::new("x"),
/// position: (0, 1), /// position: (0, 1),
/// }), /// },
/// Box::new(Node { /// value_node: Box::new(Node {
/// inner: Statement::Constant(Value::integer(42)), /// inner: Statement::Constant(Value::integer(42)),
/// position: (4, 6), /// position: (4, 6),
/// }) /// })
/// ), /// },
/// position: (0, 6), /// position: (0, 6),
/// } /// }
/// ].into(), /// ].into(),
@ -86,16 +86,16 @@ pub fn parse(input: &str) -> Result<AbstractSyntaxTree, ParseError> {
/// nodes, /// nodes,
/// Into::<VecDeque<Node<Statement>>>::into([ /// Into::<VecDeque<Node<Statement>>>::into([
/// Node { /// Node {
/// inner: Statement::Assign( /// inner: Statement::Assignment {
/// Box::new(Node { /// identifier: Node {
/// inner: Statement::Identifier("x".into()), /// inner: Identifier::new("x"),
/// position: (0, 1), /// position: (0, 1),
/// }), /// },
/// Box::new(Node { /// value_node: Box::new(Node {
/// inner: Statement::Constant(Value::integer(42)), /// inner: Statement::Constant(Value::integer(42)),
/// position: (4, 6), /// position: (4, 6),
/// }) /// }),
/// ), /// },
/// position: (0, 6), /// position: (0, 6),
/// } /// }
/// ]), /// ]),
@ -166,28 +166,6 @@ impl<'src> Parser<'src> {
(left_start, right_end), (left_start, right_end),
)); ));
} }
(Token::Equal, _) => {
self.next_token()?;
let identifier = if let Statement::Identifier(identifier) = left_node.inner {
identifier
} else {
return Err(ParseError::ExpectedIdentifier {
actual: left_node.inner,
position: left_node.position,
});
};
let right_node = self.parse_node(self.current_precedence())?;
let right_end = right_node.position.1;
return Ok(Node::new(
Statement::Assignment {
identifier,
value_node: Box::new(right_node),
},
(left_start, right_end),
));
}
(Token::Greater, _) => { (Token::Greater, _) => {
let operator = Node::new(BinaryOperator::Greater, self.current.1); let operator = Node::new(BinaryOperator::Greater, self.current.1);
@ -337,16 +315,76 @@ impl<'src> Parser<'src> {
(Token::Identifier(text), span) => { (Token::Identifier(text), span) => {
self.next_token()?; self.next_token()?;
if let (Token::Equal, _) = self.current {
self.next_token()?;
let value = self.parse_node(0)?;
let right_end = value.position.1;
Ok(Node::new(
Statement::Assignment {
identifier: Node::new(Identifier::new(text), span),
value_node: Box::new(value),
},
(span.0, right_end),
))
} else {
Ok(Node::new( Ok(Node::new(
Statement::Identifier(Identifier::new(text)), Statement::Identifier(Identifier::new(text)),
span, span,
)) ))
} }
}
(Token::String(string), span) => { (Token::String(string), span) => {
self.next_token()?; self.next_token()?;
Ok(Node::new(Statement::Constant(Value::string(string)), span)) Ok(Node::new(Statement::Constant(Value::string(string)), span))
} }
(Token::LeftCurlyBrace, left_span) => {
self.next_token()?;
let mut nodes = Vec::new();
loop {
if let (Token::RightCurlyBrace, right_span) = self.current {
self.next_token()?;
return Ok(Node::new(
Statement::Map(nodes),
(left_span.0, right_span.1),
));
}
let identifier = if let (Token::Identifier(text), right_span) = self.current {
self.next_token()?;
Node::new(Identifier::new(text), right_span)
} else {
return Err(ParseError::ExpectedIdentifier {
actual: self.current.0.to_owned(),
position: self.current.1,
});
};
if let Token::Equal = self.current.0 {
self.next_token()?;
} else {
return Err(ParseError::ExpectedToken {
expected: TokenOwned::Equal,
actual: self.current.0.to_owned(),
position: self.current.1,
});
}
let current_value_node = self.parse_node(0)?;
nodes.push((identifier, current_value_node));
if let Token::Comma = self.current.0 {
self.next_token()?;
}
}
}
(Token::LeftParenthesis, left_span) => { (Token::LeftParenthesis, left_span) => {
self.next_token()?; self.next_token()?;
@ -357,7 +395,8 @@ impl<'src> Parser<'src> {
Ok(Node::new(node.inner, (left_span.0, right_span.1))) Ok(Node::new(node.inner, (left_span.0, right_span.1)))
} else { } else {
Err(ParseError::ExpectedClosingParenthesis { Err(ParseError::ExpectedToken {
expected: TokenOwned::RightParenthesis,
actual: self.current.0.to_owned(), actual: self.current.0.to_owned(),
position: self.current.1, position: self.current.1,
}) })
@ -387,7 +426,8 @@ impl<'src> Parser<'src> {
if let Ok(instruction) = self.parse_node(0) { if let Ok(instruction) = self.parse_node(0) {
nodes.push(instruction); nodes.push(instruction);
} else { } else {
return Err(ParseError::ExpectedClosingSquareBrace { return Err(ParseError::ExpectedToken {
expected: TokenOwned::RightSquareBrace,
actual: self.current.0.to_owned(), actual: self.current.0.to_owned(),
position: self.current.1, position: self.current.1,
}); });
@ -412,7 +452,8 @@ impl<'src> Parser<'src> {
if let (Token::LeftParenthesis, _) = self.current { if let (Token::LeftParenthesis, _) = self.current {
self.next_token()?; self.next_token()?;
} else { } else {
return Err(ParseError::ExpectedOpeningParenthesis { return Err(ParseError::ExpectedToken {
expected: TokenOwned::LeftParenthesis,
actual: self.current.0.to_owned(), actual: self.current.0.to_owned(),
position: self.current.1, position: self.current.1,
}); });
@ -438,7 +479,8 @@ impl<'src> Parser<'src> {
value_arguments = Some(vec![node]); value_arguments = Some(vec![node]);
} }
} else { } else {
return Err(ParseError::ExpectedClosingParenthesis { return Err(ParseError::ExpectedToken {
expected: TokenOwned::RightParenthesis,
actual: self.current.0.to_owned(), actual: self.current.0.to_owned(),
position: self.current.1, position: self.current.1,
}); });
@ -465,7 +507,6 @@ impl<'src> Parser<'src> {
match self.current.0 { match self.current.0 {
Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5, Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5,
Token::Dot => 4, Token::Dot => 4,
Token::Equal => 3,
Token::Star => 2, Token::Star => 2,
Token::Plus => 1, Token::Plus => 1,
Token::Minus => 1, Token::Minus => 1,
@ -480,20 +521,12 @@ pub enum ParseError {
error: LexError, error: LexError,
position: Span, position: Span,
}, },
ExpectedClosingParenthesis {
actual: TokenOwned,
position: Span,
},
ExpectedClosingSquareBrace {
actual: TokenOwned,
position: Span,
},
ExpectedIdentifier { ExpectedIdentifier {
actual: Statement, actual: TokenOwned,
position: (usize, usize), position: (usize, usize),
}, },
ExpectedOpeningParenthesis { ExpectedToken {
expected: TokenOwned,
actual: TokenOwned, actual: TokenOwned,
position: Span, position: Span,
}, },
@ -507,10 +540,8 @@ impl ParseError {
pub fn position(&self) -> Span { pub fn position(&self) -> Span {
match self { match self {
Self::LexError { position, .. } => *position, Self::LexError { position, .. } => *position,
Self::ExpectedClosingParenthesis { position, .. } => *position,
Self::ExpectedClosingSquareBrace { position, .. } => *position,
Self::ExpectedIdentifier { position, .. } => *position, Self::ExpectedIdentifier { position, .. } => *position,
Self::ExpectedOpeningParenthesis { position, .. } => *position, Self::ExpectedToken { position, .. } => *position,
Self::UnexpectedToken { position, .. } => *position, Self::UnexpectedToken { position, .. } => *position,
} }
} }
@ -529,18 +560,12 @@ impl Display for ParseError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::LexError { error, .. } => write!(f, "{}", error), Self::LexError { error, .. } => write!(f, "{}", error),
Self::ExpectedClosingParenthesis { actual, .. } => {
write!(f, "Expected closing parenthesis, found {actual}",)
}
Self::ExpectedClosingSquareBrace { actual, .. } => {
write!(f, "Expected closing square brace, found {actual}",)
}
Self::ExpectedIdentifier { actual, .. } => { Self::ExpectedIdentifier { actual, .. } => {
write!(f, "Expected identifier, found {actual}") write!(f, "Expected identifier, found {actual}")
} }
Self::ExpectedOpeningParenthesis { actual, .. } => { Self::ExpectedToken {
write!(f, "Expected opening parenthesis, found {actual}",) expected, actual, ..
} } => write!(f, "Expected token {expected}, found {actual}"),
Self::UnexpectedToken { actual, .. } => write!(f, "Unexpected token {actual}"), Self::UnexpectedToken { actual, .. } => write!(f, "Unexpected token {actual}"),
} }
} }
@ -552,6 +577,44 @@ mod tests {
use super::*; use super::*;
#[test]
fn malformed_assignment() {
let input = "false = 1";
assert_eq!(
parse(input),
Err(ParseError::UnexpectedToken {
actual: TokenOwned::Equal,
position: (6, 7)
})
);
}
#[test]
fn map() {
let input = "{ x = 42, y = 'foobar' }";
assert_eq!(
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node::new(
Statement::Map(vec![
(
Node::new(Identifier::new("x"), (2, 3)),
Node::new(Statement::Constant(Value::integer(42)), (6, 8))
),
(
Node::new(Identifier::new("y"), (10, 11)),
Node::new(Statement::Constant(Value::string("foobar")), (14, 22))
)
]),
(0, 24)
)]
.into()
})
);
}
#[test] #[test]
fn less_than() { fn less_than() {
let input = "1 < 2"; let input = "1 < 2";
@ -964,7 +1027,7 @@ mod tests {
Ok(AbstractSyntaxTree { Ok(AbstractSyntaxTree {
nodes: [Node::new( nodes: [Node::new(
Statement::Assignment { Statement::Assignment {
identifier: Identifier::new("a"), identifier: Node::new(Identifier::new("a"), (0, 1)),
value_node: Box::new(Node::new( value_node: Box::new(Node::new(
Statement::BinaryOperation { Statement::BinaryOperation {
left: Box::new(Node::new( left: Box::new(Node::new(

View File

@ -31,12 +31,14 @@ pub enum Token<'src> {
Equal, Equal,
Greater, Greater,
GreaterEqual, GreaterEqual,
LeftCurlyBrace,
LeftParenthesis, LeftParenthesis,
LeftSquareBrace, LeftSquareBrace,
Less, Less,
LessEqual, LessEqual,
Minus, Minus,
Plus, Plus,
RightCurlyBrace,
RightParenthesis, RightParenthesis,
RightSquareBrace, RightSquareBrace,
Star, Star,
@ -59,6 +61,7 @@ impl<'src> Token<'src> {
Token::Integer(integer) => TokenOwned::Integer(*integer), Token::Integer(integer) => TokenOwned::Integer(*integer),
Token::IsEven => TokenOwned::IsEven, Token::IsEven => TokenOwned::IsEven,
Token::IsOdd => TokenOwned::IsOdd, Token::IsOdd => TokenOwned::IsOdd,
Token::LeftCurlyBrace => TokenOwned::LeftCurlyBrace,
Token::LeftParenthesis => TokenOwned::LeftParenthesis, Token::LeftParenthesis => TokenOwned::LeftParenthesis,
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace, Token::LeftSquareBrace => TokenOwned::LeftSquareBrace,
Token::Length => TokenOwned::Length, Token::Length => TokenOwned::Length,
@ -67,6 +70,7 @@ impl<'src> Token<'src> {
Token::Minus => TokenOwned::Minus, Token::Minus => TokenOwned::Minus,
Token::Plus => TokenOwned::Plus, Token::Plus => TokenOwned::Plus,
Token::ReadLine => TokenOwned::ReadLine, Token::ReadLine => TokenOwned::ReadLine,
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
Token::RightParenthesis => TokenOwned::RightParenthesis, Token::RightParenthesis => TokenOwned::RightParenthesis,
Token::RightSquareBrace => TokenOwned::RightSquareBrace, Token::RightSquareBrace => TokenOwned::RightSquareBrace,
Token::Star => TokenOwned::Star, Token::Star => TokenOwned::Star,
@ -91,6 +95,7 @@ impl<'src> Token<'src> {
Token::Integer(_) => "integer", Token::Integer(_) => "integer",
Token::IsEven => "is_even", Token::IsEven => "is_even",
Token::IsOdd => "is_odd", Token::IsOdd => "is_odd",
Token::LeftCurlyBrace => "{",
Token::LeftParenthesis => "(", Token::LeftParenthesis => "(",
Token::LeftSquareBrace => "[", Token::LeftSquareBrace => "[",
Token::Length => "length", Token::Length => "length",
@ -99,6 +104,7 @@ impl<'src> Token<'src> {
Token::Minus => "-", Token::Minus => "-",
Token::Plus => "+", Token::Plus => "+",
Token::ReadLine => "read_line", Token::ReadLine => "read_line",
Token::RightCurlyBrace => "}",
Token::RightParenthesis => ")", Token::RightParenthesis => ")",
Token::RightSquareBrace => "]", Token::RightSquareBrace => "]",
Token::Star => "*", Token::Star => "*",
@ -132,6 +138,7 @@ impl<'src> PartialEq for Token<'src> {
(Token::Integer(left), Token::Integer(right)) => left == right, (Token::Integer(left), Token::Integer(right)) => left == right,
(Token::IsEven, Token::IsEven) => true, (Token::IsEven, Token::IsEven) => true,
(Token::IsOdd, Token::IsOdd) => true, (Token::IsOdd, Token::IsOdd) => true,
(Token::LeftCurlyBrace, Token::LeftCurlyBrace) => true,
(Token::LeftParenthesis, Token::LeftParenthesis) => true, (Token::LeftParenthesis, Token::LeftParenthesis) => true,
(Token::LeftSquareBrace, Token::LeftSquareBrace) => true, (Token::LeftSquareBrace, Token::LeftSquareBrace) => true,
(Token::Length, Token::Length) => true, (Token::Length, Token::Length) => true,
@ -140,6 +147,7 @@ impl<'src> PartialEq for Token<'src> {
(Token::Minus, Token::Minus) => true, (Token::Minus, Token::Minus) => true,
(Token::Plus, Token::Plus) => true, (Token::Plus, Token::Plus) => true,
(Token::ReadLine, Token::ReadLine) => true, (Token::ReadLine, Token::ReadLine) => true,
(Token::RightCurlyBrace, Token::RightCurlyBrace) => true,
(Token::RightParenthesis, Token::RightParenthesis) => true, (Token::RightParenthesis, Token::RightParenthesis) => true,
(Token::RightSquareBrace, Token::RightSquareBrace) => true, (Token::RightSquareBrace, Token::RightSquareBrace) => true,
(Token::Star, Token::Star) => true, (Token::Star, Token::Star) => true,
@ -180,12 +188,14 @@ pub enum TokenOwned {
Equal, Equal,
Greater, Greater,
GreaterOrEqual, GreaterOrEqual,
LeftCurlyBrace,
LeftParenthesis, LeftParenthesis,
LeftSquareBrace, LeftSquareBrace,
Less, Less,
LessOrEqual, LessOrEqual,
Minus, Minus,
Plus, Plus,
RightCurlyBrace,
RightParenthesis, RightParenthesis,
RightSquareBrace, RightSquareBrace,
Star, Star,
@ -208,6 +218,7 @@ impl Display for TokenOwned {
TokenOwned::Integer(integer) => write!(f, "{integer}"), TokenOwned::Integer(integer) => write!(f, "{integer}"),
TokenOwned::IsEven => Token::IsEven.fmt(f), TokenOwned::IsEven => Token::IsEven.fmt(f),
TokenOwned::IsOdd => Token::IsOdd.fmt(f), TokenOwned::IsOdd => Token::IsOdd.fmt(f),
TokenOwned::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f), TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f),
TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f), TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
TokenOwned::Length => Token::Length.fmt(f), TokenOwned::Length => Token::Length.fmt(f),
@ -216,6 +227,7 @@ impl Display for TokenOwned {
TokenOwned::Minus => Token::Minus.fmt(f), TokenOwned::Minus => Token::Minus.fmt(f),
TokenOwned::Plus => Token::Plus.fmt(f), TokenOwned::Plus => Token::Plus.fmt(f),
TokenOwned::ReadLine => Token::ReadLine.fmt(f), TokenOwned::ReadLine => Token::ReadLine.fmt(f),
TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f), TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),
TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f), TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f),
TokenOwned::Star => Token::Star.fmt(f), TokenOwned::Star => Token::Star.fmt(f),

View File

@ -51,7 +51,6 @@ pub enum Type {
length: usize, length: usize,
item_type: Box<Type>, item_type: Box<Type>,
}, },
ListOf(Box<Type>),
Map(BTreeMap<Identifier, Type>), Map(BTreeMap<Identifier, Type>),
Range, Range,
String, String,
@ -111,11 +110,6 @@ impl Type {
} }
} }
} }
(Type::ListOf(left), Type::ListOf(right)) => {
if left.check(right).is_ok() {
return Ok(());
}
}
( (
Type::Structure { Type::Structure {
name: left_name, name: left_name,
@ -160,29 +154,6 @@ impl Type {
return Ok(()); return Ok(());
} }
(
Type::ListOf(left_type),
Type::List {
item_type: right_type,
..
},
)
| (
Type::List {
item_type: right_type,
..
},
Type::ListOf(left_type),
) => {
if right_type.check(left_type).is_err() {
return Err(TypeConflict {
actual: other.clone(),
expected: self.clone(),
});
} else {
return Ok(());
}
}
( (
Type::Function { Type::Function {
type_parameters: left_type_parameters, type_parameters: left_type_parameters,
@ -274,7 +245,6 @@ impl Display for Type {
} }
Type::Integer => write!(f, "int"), Type::Integer => write!(f, "int"),
Type::List { length, item_type } => write!(f, "[{length}; {}]", item_type), Type::List { length, item_type } => write!(f, "[{length}; {}]", item_type),
Type::ListOf(item_type) => write!(f, "[{}]", item_type),
Type::Map(map) => { Type::Map(map) => {
write!(f, "{{ ")?; write!(f, "{{ ")?;
@ -349,10 +319,6 @@ mod tests {
}), }),
Ok(()) Ok(())
); );
assert_eq!(
Type::ListOf(Box::new(Type::Integer)).check(&Type::ListOf(Box::new(Type::Integer))),
Ok(())
);
let mut map = BTreeMap::new(); let mut map = BTreeMap::new();
@ -393,7 +359,6 @@ mod tests {
length: 10, length: 10,
item_type: Box::new(Type::Integer), item_type: Box::new(Type::Integer),
}, },
Type::ListOf(Box::new(Type::Boolean)),
Type::Map(BTreeMap::new()), Type::Map(BTreeMap::new()),
Type::Range, Type::Range,
Type::String, Type::String,
@ -415,16 +380,4 @@ mod tests {
} }
} }
} }
#[test]
fn check_list_types() {
let list = Type::List {
length: 42,
item_type: Box::new(Type::Integer),
};
let list_of = Type::ListOf(Box::new(Type::Integer));
assert_eq!(list.check(&list_of), Ok(()));
assert_eq!(list_of.check(&list), Ok(()));
}
} }

View File

@ -1,6 +1,6 @@
//! Virtual machine for running the abstract syntax tree. //! Virtual machine for running the abstract syntax tree.
use std::{ use std::{
collections::HashMap, collections::{BTreeMap, HashMap},
error::Error, error::Error,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
}; };
@ -65,7 +65,7 @@ impl Vm {
}); });
}; };
variables.insert(identifier, value); variables.insert(identifier.inner, value);
Ok(None) Ok(None)
} }
@ -216,6 +216,22 @@ impl Vm {
Ok(Some(Value::list(values))) Ok(Some(Value::list(values)))
} }
Statement::Map(nodes) => {
let mut values = BTreeMap::new();
for (identifier, value_node) in nodes {
let position = value_node.position;
let value = if let Some(value) = self.run_node(value_node, variables)? {
value
} else {
return Err(VmError::ExpectedValue { position });
};
values.insert(identifier.inner, value);
}
Ok(Some(Value::map(values)))
}
Statement::PropertyAccess(left, right) => { Statement::PropertyAccess(left, right) => {
let left_span = left.position; let left_span = left.position;
let left_value = if let Some(value) = self.run_node(*left, variables)? { let left_value = if let Some(value) = self.run_node(*left, variables)? {