Compare commits
3 Commits
d0dba35285
...
a048577143
Author | SHA1 | Date | |
---|---|---|---|
a048577143 | |||
929468338d | |||
55a8661618 |
@ -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}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 = ">";
|
||||||
|
@ -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()?;
|
||||||
|
|
||||||
Ok(Node::new(
|
if let (Token::Equal, _) = self.current {
|
||||||
Statement::Identifier(Identifier::new(text)),
|
self.next_token()?;
|
||||||
span,
|
|
||||||
))
|
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(
|
||||||
|
Statement::Identifier(Identifier::new(text)),
|
||||||
|
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(
|
||||||
|
@ -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),
|
||||||
|
@ -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(()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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)? {
|
||||||
|
Loading…
Reference in New Issue
Block a user