Extend VM, abstract tree and parser
This commit is contained in:
parent
fa67a568d9
commit
0b64afccb1
@ -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)?;
|
||||
|
@ -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,
|
||||
|
@ -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";
|
||||
|
@ -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()?;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Statement::struct_definition(
|
||||
let position = (start_position.0, self.current_position.1);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
return if fields.is_empty() {
|
||||
Ok(Statement::struct_definition(
|
||||
StructDefinition::Unit { name },
|
||||
(start_position.0, name_end),
|
||||
));
|
||||
position,
|
||||
))
|
||||
} else {
|
||||
Ok(Statement::struct_definition(
|
||||
StructDefinition::Fields { name, fields },
|
||||
position,
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
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)
|
||||
))
|
||||
]))
|
||||
);
|
||||
|
@ -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,
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
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_name != right_name
|
||||
|| left_return != right_return
|
||||
|| left_type_parameters != right_type_parameters
|
||||
|| left_value_parameters != right_value_parameters
|
||||
{
|
||||
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,
|
||||
];
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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,10 +228,99 @@ 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 {
|
||||
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!(),
|
||||
};
|
||||
|
||||
evaluation_result.map_err(|error| RuntimeError::Trace {
|
||||
error: Box::new(error),
|
||||
position,
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
@ -270,24 +370,37 @@ impl Vm {
|
||||
(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)),
|
||||
)),
|
||||
(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::TupleAccess(_) => todo!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
evaluation_result.map_err(|error| RuntimeError::Trace {
|
||||
error: Box::new(error),
|
||||
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(
|
||||
@ -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
|
||||
";
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user