Fix dot notations precedence; Add some miscellaneous expansions
This commit is contained in:
parent
a61c1756f2
commit
0fb0b63a97
@ -62,10 +62,6 @@ pub enum Statement {
|
|||||||
value_arguments: Option<Vec<Node<Statement>>>,
|
value_arguments: Option<Vec<Node<Statement>>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Property access expression
|
|
||||||
// TODO: This should be a binary operation
|
|
||||||
PropertyAccess(Box<Node<Statement>>, Box<Node<Statement>>),
|
|
||||||
|
|
||||||
// Loops
|
// Loops
|
||||||
While {
|
While {
|
||||||
condition: Box<Node<Statement>>,
|
condition: Box<Node<Statement>>,
|
||||||
@ -113,7 +109,11 @@ impl Statement {
|
|||||||
pub fn expected_type(&self, context: &Context) -> Option<Type> {
|
pub fn expected_type(&self, context: &Context) -> Option<Type> {
|
||||||
match self {
|
match self {
|
||||||
Statement::Block(nodes) => nodes.last().unwrap().inner.expected_type(context),
|
Statement::Block(nodes) => nodes.last().unwrap().inner.expected_type(context),
|
||||||
Statement::BinaryOperation { left, operator, .. } => match operator.inner {
|
Statement::BinaryOperation {
|
||||||
|
left,
|
||||||
|
operator,
|
||||||
|
right,
|
||||||
|
} => match operator.inner {
|
||||||
BinaryOperator::Add
|
BinaryOperator::Add
|
||||||
| BinaryOperator::Divide
|
| BinaryOperator::Divide
|
||||||
| BinaryOperator::Modulo
|
| BinaryOperator::Modulo
|
||||||
@ -129,6 +129,21 @@ impl Statement {
|
|||||||
| BinaryOperator::Or => Some(Type::Boolean),
|
| BinaryOperator::Or => Some(Type::Boolean),
|
||||||
|
|
||||||
BinaryOperator::Assign | BinaryOperator::AddAssign => None,
|
BinaryOperator::Assign | BinaryOperator::AddAssign => None,
|
||||||
|
|
||||||
|
BinaryOperator::FieldAccess => {
|
||||||
|
let left_type = left.inner.expected_type(context)?;
|
||||||
|
|
||||||
|
if let Type::Map(properties) = left_type {
|
||||||
|
let key = match &right.inner {
|
||||||
|
Statement::Identifier(identifier) => identifier,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
properties.get(key).cloned()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
|
Statement::BuiltInFunctionCall { function, .. } => function.expected_return_type(),
|
||||||
Statement::Constant(value) => Some(value.r#type(context)),
|
Statement::Constant(value) => Some(value.r#type(context)),
|
||||||
@ -157,7 +172,6 @@ impl Statement {
|
|||||||
Some(Type::Map(types))
|
Some(Type::Map(types))
|
||||||
}
|
}
|
||||||
Statement::Nil(_) => None,
|
Statement::Nil(_) => None,
|
||||||
Statement::PropertyAccess(_, _) => None,
|
|
||||||
Statement::UnaryOperation { operator, operand } => match operator.inner {
|
Statement::UnaryOperation { operator, operand } => match operator.inner {
|
||||||
UnaryOperator::Negate => Some(operand.inner.expected_type(context)?),
|
UnaryOperator::Negate => Some(operand.inner.expected_type(context)?),
|
||||||
UnaryOperator::Not => Some(Type::Boolean),
|
UnaryOperator::Not => Some(Type::Boolean),
|
||||||
@ -202,7 +216,11 @@ impl Display for Statement {
|
|||||||
operator,
|
operator,
|
||||||
right,
|
right,
|
||||||
} => {
|
} => {
|
||||||
write!(f, "{left} {operator} {right}")
|
if let BinaryOperator::FieldAccess = operator.inner {
|
||||||
|
write!(f, "{left}{operator}{right}")
|
||||||
|
} else {
|
||||||
|
write!(f, "{left} {operator} {right}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Statement::BuiltInFunctionCall {
|
Statement::BuiltInFunctionCall {
|
||||||
function,
|
function,
|
||||||
@ -340,7 +358,6 @@ impl Display for Statement {
|
|||||||
write!(f, "}}")
|
write!(f, "}}")
|
||||||
}
|
}
|
||||||
Statement::Nil(node) => write!(f, "{node};"),
|
Statement::Nil(node) => write!(f, "{node};"),
|
||||||
Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
|
|
||||||
Statement::UnaryOperation { operator, operand } => {
|
Statement::UnaryOperation { operator, operand } => {
|
||||||
write!(f, "{operator}{operand}")
|
write!(f, "{operator}{operand}")
|
||||||
}
|
}
|
||||||
@ -353,6 +370,8 @@ impl Display for Statement {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub enum BinaryOperator {
|
pub enum BinaryOperator {
|
||||||
|
FieldAccess,
|
||||||
|
|
||||||
// Math
|
// Math
|
||||||
Add,
|
Add,
|
||||||
Divide,
|
Divide,
|
||||||
@ -385,6 +404,7 @@ impl Display for BinaryOperator {
|
|||||||
BinaryOperator::And => write!(f, "&&"),
|
BinaryOperator::And => write!(f, "&&"),
|
||||||
BinaryOperator::Divide => write!(f, "/"),
|
BinaryOperator::Divide => write!(f, "/"),
|
||||||
BinaryOperator::Equal => write!(f, "=="),
|
BinaryOperator::Equal => write!(f, "=="),
|
||||||
|
BinaryOperator::FieldAccess => write!(f, "."),
|
||||||
BinaryOperator::Greater => write!(f, ">"),
|
BinaryOperator::Greater => write!(f, ">"),
|
||||||
BinaryOperator::GreaterOrEqual => write!(f, ">="),
|
BinaryOperator::GreaterOrEqual => write!(f, ">="),
|
||||||
BinaryOperator::Less => write!(f, "<"),
|
BinaryOperator::Less => write!(f, "<"),
|
||||||
|
@ -96,6 +96,44 @@ impl<'a> Analyzer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let BinaryOperator::FieldAccess = operator.inner {
|
||||||
|
self.analyze_statement(left)?;
|
||||||
|
|
||||||
|
if let Statement::Identifier(_) = right.inner {
|
||||||
|
// Do not expect a value for property accessors
|
||||||
|
} else {
|
||||||
|
self.analyze_statement(right)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Type::List { .. }) = left.inner.expected_type(self.context) {
|
||||||
|
let right_type = right.inner.expected_type(self.context);
|
||||||
|
|
||||||
|
if let Some(Type::Integer) = right_type {
|
||||||
|
// Allow indexing lists with integers
|
||||||
|
} else if let Some(Type::Range) = right_type {
|
||||||
|
// Allow indexing lists with ranges
|
||||||
|
} else {
|
||||||
|
return Err(AnalyzerError::ExpectedIntegerOrRange {
|
||||||
|
actual: right.as_ref().clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Type::Map { .. }) = left.inner.expected_type(self.context) {
|
||||||
|
if let Some(Type::String) = right.inner.expected_type(self.context) {
|
||||||
|
// Allow indexing maps with strings
|
||||||
|
} else if let Statement::Identifier(_) = right.inner {
|
||||||
|
// Allow indexing maps with identifiers
|
||||||
|
} else {
|
||||||
|
return Err(AnalyzerError::ExpectedIdentifierOrString {
|
||||||
|
actual: right.as_ref().clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
self.analyze_statement(left)?;
|
self.analyze_statement(left)?;
|
||||||
self.analyze_statement(right)?;
|
self.analyze_statement(right)?;
|
||||||
|
|
||||||
@ -324,41 +362,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
Statement::Nil(node) => {
|
Statement::Nil(node) => {
|
||||||
self.analyze_statement(node)?;
|
self.analyze_statement(node)?;
|
||||||
}
|
}
|
||||||
Statement::PropertyAccess(left, right) => {
|
|
||||||
self.analyze_statement(left)?;
|
|
||||||
|
|
||||||
if let Statement::Identifier(_) = right.inner {
|
|
||||||
// Do not expect a value for property accessors
|
|
||||||
} else {
|
|
||||||
self.analyze_statement(right)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(Type::List { .. }) = left.inner.expected_type(self.context) {
|
|
||||||
let right_type = right.inner.expected_type(self.context);
|
|
||||||
|
|
||||||
if let Some(Type::Integer) = right_type {
|
|
||||||
// Allow indexing lists with integers
|
|
||||||
} else if let Some(Type::Range) = right_type {
|
|
||||||
// Allow indexing lists with ranges
|
|
||||||
} else {
|
|
||||||
return Err(AnalyzerError::ExpectedIntegerOrRange {
|
|
||||||
actual: right.as_ref().clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(Type::Map { .. }) = left.inner.expected_type(self.context) {
|
|
||||||
if let Some(Type::String) = right.inner.expected_type(self.context) {
|
|
||||||
// Allow indexing maps with strings
|
|
||||||
} else if let Statement::Identifier(_) = right.inner {
|
|
||||||
// Allow indexing maps with identifiers
|
|
||||||
} else {
|
|
||||||
return Err(AnalyzerError::ExpectedIdentifierOrString {
|
|
||||||
actual: right.as_ref().clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Statement::UnaryOperation { operator, operand } => {
|
Statement::UnaryOperation { operator, operand } => {
|
||||||
self.analyze_statement(operand)?;
|
self.analyze_statement(operand)?;
|
||||||
|
|
||||||
|
@ -264,6 +264,11 @@ impl Lexer {
|
|||||||
|
|
||||||
(Token::Bang, (self.position - 1, self.position))
|
(Token::Bang, (self.position - 1, self.position))
|
||||||
}
|
}
|
||||||
|
':' => {
|
||||||
|
self.position += 1;
|
||||||
|
|
||||||
|
(Token::Colon, (self.position - 1, self.position))
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.position += 1;
|
self.position += 1;
|
||||||
|
|
||||||
@ -408,13 +413,17 @@ impl Lexer {
|
|||||||
let token = match string {
|
let token = match string {
|
||||||
"Infinity" => Token::Float("Infinity"),
|
"Infinity" => Token::Float("Infinity"),
|
||||||
"NaN" => Token::Float("NaN"),
|
"NaN" => Token::Float("NaN"),
|
||||||
|
"bool" => Token::Bool,
|
||||||
"else" => Token::Else,
|
"else" => Token::Else,
|
||||||
"false" => Token::Boolean("false"),
|
"false" => Token::Boolean("false"),
|
||||||
|
"float" => Token::FloatKeyword,
|
||||||
"if" => Token::If,
|
"if" => Token::If,
|
||||||
|
"int" => Token::Int,
|
||||||
"is_even" => Token::IsEven,
|
"is_even" => Token::IsEven,
|
||||||
"is_odd" => Token::IsOdd,
|
"is_odd" => Token::IsOdd,
|
||||||
"length" => Token::Length,
|
"length" => Token::Length,
|
||||||
"read_line" => Token::ReadLine,
|
"read_line" => Token::ReadLine,
|
||||||
|
"struct" => Token::Struct,
|
||||||
"to_string" => Token::ToString,
|
"to_string" => Token::ToString,
|
||||||
"true" => Token::Boolean("true"),
|
"true" => Token::Boolean("true"),
|
||||||
"while" => Token::While,
|
"while" => Token::While,
|
||||||
|
@ -604,6 +604,8 @@ impl<'src> Parser<'src> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Token::Dot = &self.current.0 {
|
if let Token::Dot = &self.current.0 {
|
||||||
|
let operator_position = self.current.1;
|
||||||
|
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
let right = self.parse_statement(operator_precedence)?;
|
let right = self.parse_statement(operator_precedence)?;
|
||||||
@ -658,7 +660,11 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Node::new(
|
return Ok(Node::new(
|
||||||
Statement::PropertyAccess(Box::new(left), Box::new(right)),
|
Statement::BinaryOperation {
|
||||||
|
left: Box::new(left),
|
||||||
|
operator: Node::new(BinaryOperator::FieldAccess, operator_position),
|
||||||
|
right: Box::new(right),
|
||||||
|
},
|
||||||
(left_start, right_end),
|
(left_start, right_end),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -852,47 +858,48 @@ mod tests {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(input),
|
parse(input),
|
||||||
Ok(AbstractSyntaxTree {
|
Ok(AbstractSyntaxTree {
|
||||||
nodes: [Node {
|
nodes: [Node::new(
|
||||||
inner: Statement::PropertyAccess(
|
Statement::BinaryOperation {
|
||||||
Box::new(Node {
|
left: Box::new(Node::new(
|
||||||
inner: Statement::Map(vec![(
|
Statement::BinaryOperation {
|
||||||
Node {
|
left: Box::new(Node::new(
|
||||||
inner: Statement::Identifier(Identifier::new("x")),
|
Statement::Map(vec![(
|
||||||
position: (2, 3)
|
Node::new(
|
||||||
},
|
Statement::Identifier(Identifier::new("x")),
|
||||||
Node {
|
(2, 3)
|
||||||
inner: Statement::Map(vec![(
|
),
|
||||||
Node {
|
Node::new(
|
||||||
inner: Statement::Identifier(Identifier::new("y")),
|
Statement::Map(vec![(
|
||||||
position: (8, 9)
|
Node::new(
|
||||||
},
|
Statement::Identifier(Identifier::new("y")),
|
||||||
Node {
|
(8, 9)
|
||||||
inner: Statement::Constant(Value::integer(42)),
|
),
|
||||||
position: (12, 14)
|
Node::new(
|
||||||
}
|
Statement::Constant(Value::integer(42)),
|
||||||
|
(12, 14)
|
||||||
|
)
|
||||||
|
)]),
|
||||||
|
(6, 16)
|
||||||
|
)
|
||||||
)]),
|
)]),
|
||||||
position: (6, 16)
|
(0, 18)
|
||||||
}
|
)),
|
||||||
)]),
|
operator: Node::new(BinaryOperator::FieldAccess, (18, 19)),
|
||||||
position: (0, 18)
|
right: Box::new(Node::new(
|
||||||
}),
|
Statement::Identifier(Identifier::new("x")),
|
||||||
Box::new(Node {
|
(19, 20)
|
||||||
inner: Statement::PropertyAccess(
|
))
|
||||||
Box::new(Node {
|
},
|
||||||
inner: Statement::Identifier(Identifier::new("x")),
|
(0, 20)
|
||||||
position: (19, 20)
|
)),
|
||||||
}),
|
operator: Node::new(BinaryOperator::FieldAccess, (20, 21)),
|
||||||
Box::new(Node {
|
right: Box::new(Node::new(
|
||||||
inner: Statement::Identifier(Identifier::new("y")),
|
Statement::Identifier(Identifier::new("y")),
|
||||||
|
(21, 22)
|
||||||
position: (21, 22)
|
))
|
||||||
})
|
},
|
||||||
),
|
(0, 22)
|
||||||
position: (19, 22)
|
)]
|
||||||
})
|
|
||||||
),
|
|
||||||
position: (0, 22)
|
|
||||||
}]
|
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -1444,7 +1451,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn map_with_two_properties() {
|
fn map_with_two_fields() {
|
||||||
let input = "{ x = 42, y = 'foobar' }";
|
let input = "{ x = 42, y = 'foobar' }";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -1469,8 +1476,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn map_with_one_property() {
|
fn map_with_one_field() {
|
||||||
let input = "{ x = 42, }";
|
let input = "{ x = 42 }";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(input),
|
parse(input),
|
||||||
@ -1480,7 +1487,7 @@ mod tests {
|
|||||||
Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)),
|
Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)),
|
||||||
Node::new(Statement::Constant(Value::integer(42)), (6, 8))
|
Node::new(Statement::Constant(Value::integer(42)), (6, 8))
|
||||||
)]),
|
)]),
|
||||||
(0, 11)
|
(0, 10)
|
||||||
)]
|
)]
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
@ -1732,8 +1739,8 @@ mod tests {
|
|||||||
parse(input),
|
parse(input),
|
||||||
Ok(AbstractSyntaxTree {
|
Ok(AbstractSyntaxTree {
|
||||||
nodes: [Node::new(
|
nodes: [Node::new(
|
||||||
Statement::PropertyAccess(
|
Statement::BinaryOperation {
|
||||||
Box::new(Node::new(
|
left: Box::new(Node::new(
|
||||||
Statement::List(vec![
|
Statement::List(vec![
|
||||||
Node::new(Statement::Constant(Value::integer(1)), (1, 2)),
|
Node::new(Statement::Constant(Value::integer(1)), (1, 2)),
|
||||||
Node::new(Statement::Constant(Value::integer(2)), (4, 5)),
|
Node::new(Statement::Constant(Value::integer(2)), (4, 5)),
|
||||||
@ -1741,8 +1748,12 @@ mod tests {
|
|||||||
]),
|
]),
|
||||||
(0, 9)
|
(0, 9)
|
||||||
)),
|
)),
|
||||||
Box::new(Node::new(Statement::Constant(Value::integer(0)), (10, 11))),
|
operator: Node::new(BinaryOperator::FieldAccess, (9, 10)),
|
||||||
),
|
right: Box::new(Node::new(
|
||||||
|
Statement::Constant(Value::integer(0)),
|
||||||
|
(10, 11)
|
||||||
|
)),
|
||||||
|
},
|
||||||
(0, 11),
|
(0, 11),
|
||||||
)]
|
)]
|
||||||
.into()
|
.into()
|
||||||
@ -1758,16 +1769,17 @@ mod tests {
|
|||||||
parse(input),
|
parse(input),
|
||||||
Ok(AbstractSyntaxTree {
|
Ok(AbstractSyntaxTree {
|
||||||
nodes: [Node::new(
|
nodes: [Node::new(
|
||||||
Statement::PropertyAccess(
|
Statement::BinaryOperation {
|
||||||
Box::new(Node::new(
|
left: Box::new(Node::new(
|
||||||
Statement::Identifier(Identifier::new("a")),
|
Statement::Identifier(Identifier::new("a")),
|
||||||
(0, 1)
|
(0, 1)
|
||||||
)),
|
)),
|
||||||
Box::new(Node::new(
|
operator: Node::new(BinaryOperator::FieldAccess, (1, 2)),
|
||||||
|
right: Box::new(Node::new(
|
||||||
Statement::Identifier(Identifier::new("b")),
|
Statement::Identifier(Identifier::new("b")),
|
||||||
(2, 3)
|
(2, 3)
|
||||||
)),
|
)),
|
||||||
),
|
},
|
||||||
(0, 3),
|
(0, 3),
|
||||||
)]
|
)]
|
||||||
.into()
|
.into()
|
||||||
|
@ -17,18 +17,24 @@ pub enum Token<'src> {
|
|||||||
String(&'src str),
|
String(&'src str),
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
|
Bool,
|
||||||
Else,
|
Else,
|
||||||
|
FloatKeyword,
|
||||||
If,
|
If,
|
||||||
|
Int,
|
||||||
IsEven,
|
IsEven,
|
||||||
IsOdd,
|
IsOdd,
|
||||||
Length,
|
Length,
|
||||||
ReadLine,
|
ReadLine,
|
||||||
|
Str,
|
||||||
|
Struct,
|
||||||
ToString,
|
ToString,
|
||||||
While,
|
While,
|
||||||
WriteLine,
|
WriteLine,
|
||||||
|
|
||||||
// Symbols
|
// Symbols
|
||||||
Bang,
|
Bang,
|
||||||
|
Colon,
|
||||||
Comma,
|
Comma,
|
||||||
Dot,
|
Dot,
|
||||||
DoubleAmpersand,
|
DoubleAmpersand,
|
||||||
@ -59,7 +65,9 @@ impl<'src> Token<'src> {
|
|||||||
pub fn to_owned(&self) -> TokenOwned {
|
pub fn to_owned(&self) -> TokenOwned {
|
||||||
match self {
|
match self {
|
||||||
Token::Bang => TokenOwned::Bang,
|
Token::Bang => TokenOwned::Bang,
|
||||||
|
Token::Bool => TokenOwned::Bool,
|
||||||
Token::Boolean(boolean) => TokenOwned::Boolean(boolean.to_string()),
|
Token::Boolean(boolean) => TokenOwned::Boolean(boolean.to_string()),
|
||||||
|
Token::Colon => TokenOwned::Colon,
|
||||||
Token::Comma => TokenOwned::Comma,
|
Token::Comma => TokenOwned::Comma,
|
||||||
Token::Dot => TokenOwned::Dot,
|
Token::Dot => TokenOwned::Dot,
|
||||||
Token::DoubleAmpersand => TokenOwned::DoubleAmpersand,
|
Token::DoubleAmpersand => TokenOwned::DoubleAmpersand,
|
||||||
@ -70,10 +78,12 @@ impl<'src> Token<'src> {
|
|||||||
Token::Eof => TokenOwned::Eof,
|
Token::Eof => TokenOwned::Eof,
|
||||||
Token::Equal => TokenOwned::Equal,
|
Token::Equal => TokenOwned::Equal,
|
||||||
Token::Float(float) => TokenOwned::Float(float.to_string()),
|
Token::Float(float) => TokenOwned::Float(float.to_string()),
|
||||||
|
Token::FloatKeyword => TokenOwned::FloatKeyword,
|
||||||
Token::Greater => TokenOwned::Greater,
|
Token::Greater => TokenOwned::Greater,
|
||||||
Token::GreaterEqual => TokenOwned::GreaterOrEqual,
|
Token::GreaterEqual => TokenOwned::GreaterOrEqual,
|
||||||
Token::Identifier(text) => TokenOwned::Identifier(text.to_string()),
|
Token::Identifier(text) => TokenOwned::Identifier(text.to_string()),
|
||||||
Token::If => TokenOwned::If,
|
Token::If => TokenOwned::If,
|
||||||
|
Token::Int => TokenOwned::Int,
|
||||||
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
|
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
|
||||||
Token::IsEven => TokenOwned::IsEven,
|
Token::IsEven => TokenOwned::IsEven,
|
||||||
Token::IsOdd => TokenOwned::IsOdd,
|
Token::IsOdd => TokenOwned::IsOdd,
|
||||||
@ -95,6 +105,8 @@ impl<'src> Token<'src> {
|
|||||||
Token::Star => TokenOwned::Star,
|
Token::Star => TokenOwned::Star,
|
||||||
Token::Slash => TokenOwned::Slash,
|
Token::Slash => TokenOwned::Slash,
|
||||||
Token::String(text) => TokenOwned::String(text.to_string()),
|
Token::String(text) => TokenOwned::String(text.to_string()),
|
||||||
|
Token::Str => TokenOwned::Str,
|
||||||
|
Token::Struct => TokenOwned::Struct,
|
||||||
Token::ToString => TokenOwned::ToString,
|
Token::ToString => TokenOwned::ToString,
|
||||||
Token::While => TokenOwned::While,
|
Token::While => TokenOwned::While,
|
||||||
Token::WriteLine => TokenOwned::WriteLine,
|
Token::WriteLine => TokenOwned::WriteLine,
|
||||||
@ -104,11 +116,14 @@ impl<'src> Token<'src> {
|
|||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
Token::Boolean(boolean_text) => boolean_text,
|
Token::Boolean(boolean_text) => boolean_text,
|
||||||
|
Token::Float(float_text) => float_text,
|
||||||
Token::Identifier(text) => text,
|
Token::Identifier(text) => text,
|
||||||
Token::Integer(integer_text) => integer_text,
|
Token::Integer(integer_text) => integer_text,
|
||||||
Token::String(text) => text,
|
Token::String(text) => text,
|
||||||
|
|
||||||
Token::Bang => "!",
|
Token::Bang => "!",
|
||||||
|
Token::Bool => "bool",
|
||||||
|
Token::Colon => ":",
|
||||||
Token::Comma => ",",
|
Token::Comma => ",",
|
||||||
Token::Dot => ".",
|
Token::Dot => ".",
|
||||||
Token::DoubleAmpersand => "&&",
|
Token::DoubleAmpersand => "&&",
|
||||||
@ -118,10 +133,11 @@ impl<'src> Token<'src> {
|
|||||||
Token::Else => "else",
|
Token::Else => "else",
|
||||||
Token::Eof => "EOF",
|
Token::Eof => "EOF",
|
||||||
Token::Equal => "=",
|
Token::Equal => "=",
|
||||||
Token::Float(_) => "float",
|
Token::FloatKeyword => "float",
|
||||||
Token::Greater => ">",
|
Token::Greater => ">",
|
||||||
Token::GreaterEqual => ">=",
|
Token::GreaterEqual => ">=",
|
||||||
Token::If => "if",
|
Token::If => "if",
|
||||||
|
Token::Int => "int",
|
||||||
Token::IsEven => "is_even",
|
Token::IsEven => "is_even",
|
||||||
Token::IsOdd => "is_odd",
|
Token::IsOdd => "is_odd",
|
||||||
Token::LeftCurlyBrace => "{",
|
Token::LeftCurlyBrace => "{",
|
||||||
@ -141,6 +157,8 @@ impl<'src> Token<'src> {
|
|||||||
Token::Semicolon => ";",
|
Token::Semicolon => ";",
|
||||||
Token::Star => "*",
|
Token::Star => "*",
|
||||||
Token::Slash => "/",
|
Token::Slash => "/",
|
||||||
|
Token::Str => "str",
|
||||||
|
Token::Struct => "struct",
|
||||||
Token::ToString => "to_string",
|
Token::ToString => "to_string",
|
||||||
Token::While => "while",
|
Token::While => "while",
|
||||||
Token::WriteLine => "write_line",
|
Token::WriteLine => "write_line",
|
||||||
@ -150,7 +168,9 @@ impl<'src> Token<'src> {
|
|||||||
pub fn kind(&self) -> TokenKind {
|
pub fn kind(&self) -> TokenKind {
|
||||||
match self {
|
match self {
|
||||||
Token::Bang => TokenKind::Bang,
|
Token::Bang => TokenKind::Bang,
|
||||||
|
Token::Bool => TokenKind::Bool,
|
||||||
Token::Boolean(_) => TokenKind::Boolean,
|
Token::Boolean(_) => TokenKind::Boolean,
|
||||||
|
Token::Colon => TokenKind::Colon,
|
||||||
Token::Comma => TokenKind::Comma,
|
Token::Comma => TokenKind::Comma,
|
||||||
Token::Dot => TokenKind::Dot,
|
Token::Dot => TokenKind::Dot,
|
||||||
Token::DoubleAmpersand => TokenKind::DoubleAmpersand,
|
Token::DoubleAmpersand => TokenKind::DoubleAmpersand,
|
||||||
@ -161,10 +181,12 @@ impl<'src> Token<'src> {
|
|||||||
Token::Eof => TokenKind::Eof,
|
Token::Eof => TokenKind::Eof,
|
||||||
Token::Equal => TokenKind::Equal,
|
Token::Equal => TokenKind::Equal,
|
||||||
Token::Float(_) => TokenKind::Float,
|
Token::Float(_) => TokenKind::Float,
|
||||||
|
Token::FloatKeyword => TokenKind::FloatKeyword,
|
||||||
Token::Greater => TokenKind::Greater,
|
Token::Greater => TokenKind::Greater,
|
||||||
Token::GreaterEqual => TokenKind::GreaterOrEqual,
|
Token::GreaterEqual => TokenKind::GreaterOrEqual,
|
||||||
Token::Identifier(_) => TokenKind::Identifier,
|
Token::Identifier(_) => TokenKind::Identifier,
|
||||||
Token::If => TokenKind::If,
|
Token::If => TokenKind::If,
|
||||||
|
Token::Int => TokenKind::Int,
|
||||||
Token::Integer(_) => TokenKind::Integer,
|
Token::Integer(_) => TokenKind::Integer,
|
||||||
Token::IsEven => TokenKind::IsEven,
|
Token::IsEven => TokenKind::IsEven,
|
||||||
Token::IsOdd => TokenKind::IsOdd,
|
Token::IsOdd => TokenKind::IsOdd,
|
||||||
@ -185,7 +207,9 @@ impl<'src> Token<'src> {
|
|||||||
Token::Semicolon => TokenKind::Semicolon,
|
Token::Semicolon => TokenKind::Semicolon,
|
||||||
Token::Star => TokenKind::Star,
|
Token::Star => TokenKind::Star,
|
||||||
Token::Slash => TokenKind::Slash,
|
Token::Slash => TokenKind::Slash,
|
||||||
|
Token::Str => TokenKind::Str,
|
||||||
Token::String(_) => TokenKind::String,
|
Token::String(_) => TokenKind::String,
|
||||||
|
Token::Struct => TokenKind::Struct,
|
||||||
Token::ToString => TokenKind::ToString,
|
Token::ToString => TokenKind::ToString,
|
||||||
Token::While => TokenKind::While,
|
Token::While => TokenKind::While,
|
||||||
Token::WriteLine => TokenKind::WriteLine,
|
Token::WriteLine => TokenKind::WriteLine,
|
||||||
@ -301,18 +325,23 @@ pub enum TokenOwned {
|
|||||||
String(String),
|
String(String),
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
|
Bool,
|
||||||
Else,
|
Else,
|
||||||
|
FloatKeyword,
|
||||||
If,
|
If,
|
||||||
|
Int,
|
||||||
IsEven,
|
IsEven,
|
||||||
IsOdd,
|
IsOdd,
|
||||||
Length,
|
Length,
|
||||||
ReadLine,
|
ReadLine,
|
||||||
|
Str,
|
||||||
ToString,
|
ToString,
|
||||||
While,
|
While,
|
||||||
WriteLine,
|
WriteLine,
|
||||||
|
|
||||||
// Symbols
|
// Symbols
|
||||||
Bang,
|
Bang,
|
||||||
|
Colon,
|
||||||
Comma,
|
Comma,
|
||||||
Dot,
|
Dot,
|
||||||
DoubleAmpersand,
|
DoubleAmpersand,
|
||||||
@ -336,6 +365,7 @@ pub enum TokenOwned {
|
|||||||
RightSquareBrace,
|
RightSquareBrace,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Star,
|
Star,
|
||||||
|
Struct,
|
||||||
Slash,
|
Slash,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,7 +373,9 @@ impl Display for TokenOwned {
|
|||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
TokenOwned::Bang => Token::Bang.fmt(f),
|
TokenOwned::Bang => Token::Bang.fmt(f),
|
||||||
|
TokenOwned::Bool => write!(f, "bool"),
|
||||||
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
|
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
|
||||||
|
TokenOwned::Colon => Token::Colon.fmt(f),
|
||||||
TokenOwned::Comma => Token::Comma.fmt(f),
|
TokenOwned::Comma => Token::Comma.fmt(f),
|
||||||
TokenOwned::Dot => Token::Dot.fmt(f),
|
TokenOwned::Dot => Token::Dot.fmt(f),
|
||||||
TokenOwned::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
TokenOwned::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
||||||
@ -354,10 +386,12 @@ impl Display for TokenOwned {
|
|||||||
TokenOwned::Eof => Token::Eof.fmt(f),
|
TokenOwned::Eof => Token::Eof.fmt(f),
|
||||||
TokenOwned::Equal => Token::Equal.fmt(f),
|
TokenOwned::Equal => Token::Equal.fmt(f),
|
||||||
TokenOwned::Float(float) => write!(f, "{float}"),
|
TokenOwned::Float(float) => write!(f, "{float}"),
|
||||||
|
TokenOwned::FloatKeyword => write!(f, "float"),
|
||||||
TokenOwned::Greater => Token::Greater.fmt(f),
|
TokenOwned::Greater => Token::Greater.fmt(f),
|
||||||
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
||||||
TokenOwned::Identifier(text) => write!(f, "{text}"),
|
TokenOwned::Identifier(text) => write!(f, "{text}"),
|
||||||
TokenOwned::If => Token::If.fmt(f),
|
TokenOwned::If => Token::If.fmt(f),
|
||||||
|
TokenOwned::Int => write!(f, "int"),
|
||||||
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),
|
||||||
@ -378,7 +412,9 @@ impl Display for TokenOwned {
|
|||||||
TokenOwned::Semicolon => Token::Semicolon.fmt(f),
|
TokenOwned::Semicolon => Token::Semicolon.fmt(f),
|
||||||
TokenOwned::Star => Token::Star.fmt(f),
|
TokenOwned::Star => Token::Star.fmt(f),
|
||||||
TokenOwned::Slash => Token::Slash.fmt(f),
|
TokenOwned::Slash => Token::Slash.fmt(f),
|
||||||
|
TokenOwned::Str => write!(f, "str"),
|
||||||
TokenOwned::String(string) => write!(f, "{string}"),
|
TokenOwned::String(string) => write!(f, "{string}"),
|
||||||
|
TokenOwned::Struct => Token::Struct.fmt(f),
|
||||||
TokenOwned::ToString => Token::ToString.fmt(f),
|
TokenOwned::ToString => Token::ToString.fmt(f),
|
||||||
TokenOwned::While => Token::While.fmt(f),
|
TokenOwned::While => Token::While.fmt(f),
|
||||||
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
|
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
|
||||||
@ -400,18 +436,23 @@ pub enum TokenKind {
|
|||||||
String,
|
String,
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
|
Bool,
|
||||||
Else,
|
Else,
|
||||||
|
FloatKeyword,
|
||||||
If,
|
If,
|
||||||
|
Int,
|
||||||
IsEven,
|
IsEven,
|
||||||
IsOdd,
|
IsOdd,
|
||||||
Length,
|
Length,
|
||||||
ReadLine,
|
ReadLine,
|
||||||
|
Str,
|
||||||
ToString,
|
ToString,
|
||||||
While,
|
While,
|
||||||
WriteLine,
|
WriteLine,
|
||||||
|
|
||||||
// Symbols
|
// Symbols
|
||||||
Bang,
|
Bang,
|
||||||
|
Colon,
|
||||||
Comma,
|
Comma,
|
||||||
Dot,
|
Dot,
|
||||||
DoubleAmpersand,
|
DoubleAmpersand,
|
||||||
@ -435,6 +476,7 @@ pub enum TokenKind {
|
|||||||
RightSquareBrace,
|
RightSquareBrace,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
Star,
|
Star,
|
||||||
|
Struct,
|
||||||
Slash,
|
Slash,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,7 +484,9 @@ impl Display for TokenKind {
|
|||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
TokenKind::Bang => Token::Bang.fmt(f),
|
TokenKind::Bang => Token::Bang.fmt(f),
|
||||||
TokenKind::Boolean => write!(f, "boolean"),
|
TokenKind::Bool => Token::Bool.fmt(f),
|
||||||
|
TokenKind::Boolean => write!(f, "boolean value"),
|
||||||
|
TokenKind::Colon => Token::Colon.fmt(f),
|
||||||
TokenKind::Comma => Token::Comma.fmt(f),
|
TokenKind::Comma => Token::Comma.fmt(f),
|
||||||
TokenKind::Dot => Token::Dot.fmt(f),
|
TokenKind::Dot => Token::Dot.fmt(f),
|
||||||
TokenKind::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
TokenKind::DoubleAmpersand => Token::DoubleAmpersand.fmt(f),
|
||||||
@ -452,12 +496,14 @@ impl Display for TokenKind {
|
|||||||
TokenKind::Else => Token::Else.fmt(f),
|
TokenKind::Else => Token::Else.fmt(f),
|
||||||
TokenKind::Eof => Token::Eof.fmt(f),
|
TokenKind::Eof => Token::Eof.fmt(f),
|
||||||
TokenKind::Equal => Token::Equal.fmt(f),
|
TokenKind::Equal => Token::Equal.fmt(f),
|
||||||
TokenKind::Float => write!(f, "float"),
|
TokenKind::Float => write!(f, "float value"),
|
||||||
|
TokenKind::FloatKeyword => Token::FloatKeyword.fmt(f),
|
||||||
TokenKind::Greater => Token::Greater.fmt(f),
|
TokenKind::Greater => Token::Greater.fmt(f),
|
||||||
TokenKind::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
TokenKind::GreaterOrEqual => Token::GreaterEqual.fmt(f),
|
||||||
TokenKind::Identifier => write!(f, "identifier"),
|
TokenKind::Identifier => write!(f, "identifier"),
|
||||||
TokenKind::If => Token::If.fmt(f),
|
TokenKind::If => Token::If.fmt(f),
|
||||||
TokenKind::Integer => write!(f, "integer"),
|
TokenKind::Int => Token::Int.fmt(f),
|
||||||
|
TokenKind::Integer => write!(f, "integer value"),
|
||||||
TokenKind::IsEven => Token::IsEven.fmt(f),
|
TokenKind::IsEven => Token::IsEven.fmt(f),
|
||||||
TokenKind::IsOdd => Token::IsOdd.fmt(f),
|
TokenKind::IsOdd => Token::IsOdd.fmt(f),
|
||||||
TokenKind::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
|
TokenKind::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
|
||||||
@ -476,11 +522,85 @@ impl Display for TokenKind {
|
|||||||
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
|
||||||
TokenKind::Semicolon => Token::Semicolon.fmt(f),
|
TokenKind::Semicolon => Token::Semicolon.fmt(f),
|
||||||
TokenKind::Star => Token::Star.fmt(f),
|
TokenKind::Star => Token::Star.fmt(f),
|
||||||
|
TokenKind::Str => write!(f, "str"),
|
||||||
TokenKind::Slash => Token::Slash.fmt(f),
|
TokenKind::Slash => Token::Slash.fmt(f),
|
||||||
TokenKind::String => write!(f, "string"),
|
TokenKind::String => write!(f, "string value"),
|
||||||
|
TokenKind::Struct => Token::Struct.fmt(f),
|
||||||
TokenKind::ToString => Token::ToString.fmt(f),
|
TokenKind::ToString => Token::ToString.fmt(f),
|
||||||
TokenKind::While => Token::While.fmt(f),
|
TokenKind::While => Token::While.fmt(f),
|
||||||
TokenKind::WriteLine => Token::WriteLine.fmt(f),
|
TokenKind::WriteLine => Token::WriteLine.fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn all_tokens<'src>() -> [Token<'src>; 42] {
|
||||||
|
[
|
||||||
|
Token::Bang,
|
||||||
|
Token::Boolean("true"),
|
||||||
|
Token::Colon,
|
||||||
|
Token::Comma,
|
||||||
|
Token::Dot,
|
||||||
|
Token::DoubleAmpersand,
|
||||||
|
Token::DoubleDot,
|
||||||
|
Token::DoubleEqual,
|
||||||
|
Token::DoublePipe,
|
||||||
|
Token::Else,
|
||||||
|
Token::Eof,
|
||||||
|
Token::Equal,
|
||||||
|
Token::Float("42.0"),
|
||||||
|
Token::Greater,
|
||||||
|
Token::GreaterEqual,
|
||||||
|
Token::Identifier("foobar"),
|
||||||
|
Token::If,
|
||||||
|
Token::Integer("42"),
|
||||||
|
Token::IsEven,
|
||||||
|
Token::IsOdd,
|
||||||
|
Token::LeftCurlyBrace,
|
||||||
|
Token::LeftParenthesis,
|
||||||
|
Token::LeftSquareBrace,
|
||||||
|
Token::Length,
|
||||||
|
Token::Less,
|
||||||
|
Token::LessEqual,
|
||||||
|
Token::Minus,
|
||||||
|
Token::Percent,
|
||||||
|
Token::Plus,
|
||||||
|
Token::PlusEqual,
|
||||||
|
Token::ReadLine,
|
||||||
|
Token::RightCurlyBrace,
|
||||||
|
Token::RightParenthesis,
|
||||||
|
Token::RightSquareBrace,
|
||||||
|
Token::Semicolon,
|
||||||
|
Token::Star,
|
||||||
|
Token::Slash,
|
||||||
|
Token::String("foobar"),
|
||||||
|
Token::Struct,
|
||||||
|
Token::ToString,
|
||||||
|
Token::While,
|
||||||
|
Token::WriteLine,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn token_displays() {
|
||||||
|
for token in all_tokens().iter() {
|
||||||
|
let display = token.to_string();
|
||||||
|
|
||||||
|
assert_eq!(display, token.to_owned().to_string());
|
||||||
|
|
||||||
|
if let Token::Boolean(_)
|
||||||
|
| Token::Float(_)
|
||||||
|
| Token::Identifier(_)
|
||||||
|
| Token::Integer(_)
|
||||||
|
| Token::String(_) = token
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
assert_eq!(display, token.kind().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -31,6 +31,7 @@ pub struct TypeConflict {
|
|||||||
pub enum Type {
|
pub enum Type {
|
||||||
Any,
|
Any,
|
||||||
Boolean,
|
Boolean,
|
||||||
|
Defined(Identifier),
|
||||||
Enum {
|
Enum {
|
||||||
name: Identifier,
|
name: Identifier,
|
||||||
type_parameters: Option<Vec<Type>>,
|
type_parameters: Option<Vec<Type>>,
|
||||||
@ -216,6 +217,7 @@ impl Display for Type {
|
|||||||
match self {
|
match self {
|
||||||
Type::Any => write!(f, "any"),
|
Type::Any => write!(f, "any"),
|
||||||
Type::Boolean => write!(f, "bool"),
|
Type::Boolean => write!(f, "bool"),
|
||||||
|
Type::Defined(identifier) => write!(f, "{identifier}"),
|
||||||
Type::Enum { variants, .. } => {
|
Type::Enum { variants, .. } => {
|
||||||
write!(f, "enum ")?;
|
write!(f, "enum ")?;
|
||||||
|
|
||||||
|
@ -348,6 +348,7 @@ impl Display for Value {
|
|||||||
}
|
}
|
||||||
ValueInner::Range(range) => write!(f, "{}..{}", range.start, range.end),
|
ValueInner::Range(range) => write!(f, "{}..{}", range.start, range.end),
|
||||||
ValueInner::String(string) => write!(f, "{string}"),
|
ValueInner::String(string) => write!(f, "{string}"),
|
||||||
|
ValueInner::Struct(r#struct) => write!(f, "{struct}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -403,6 +404,7 @@ impl Serialize for Value {
|
|||||||
tuple_ser.end()
|
tuple_ser.end()
|
||||||
}
|
}
|
||||||
ValueInner::String(string) => serializer.serialize_str(string),
|
ValueInner::String(string) => serializer.serialize_str(string),
|
||||||
|
ValueInner::Struct(r#struct) => r#struct.serialize(serializer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -659,6 +661,7 @@ pub enum ValueInner {
|
|||||||
Map(BTreeMap<Identifier, Value>),
|
Map(BTreeMap<Identifier, Value>),
|
||||||
Range(Range<i64>),
|
Range(Range<i64>),
|
||||||
String(String),
|
String(String),
|
||||||
|
Struct(Struct),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValueInner {
|
impl ValueInner {
|
||||||
@ -692,6 +695,7 @@ impl ValueInner {
|
|||||||
}
|
}
|
||||||
ValueInner::Range(_) => Type::Range,
|
ValueInner::Range(_) => Type::Range,
|
||||||
ValueInner::String(_) => Type::String,
|
ValueInner::String(_) => Type::String,
|
||||||
|
ValueInner::Struct(r#struct) => Type::Defined(r#struct.name().clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,6 +751,8 @@ impl Ord for ValueInner {
|
|||||||
(Range(_), _) => Ordering::Greater,
|
(Range(_), _) => Ordering::Greater,
|
||||||
(String(left), String(right)) => left.cmp(right),
|
(String(left), String(right)) => left.cmp(right),
|
||||||
(String(_), _) => Ordering::Greater,
|
(String(_), _) => Ordering::Greater,
|
||||||
|
(Struct(left), Struct(right)) => left.cmp(right),
|
||||||
|
(Struct(_), _) => Ordering::Greater,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -832,6 +838,65 @@ impl Display for Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
|
pub enum Struct {
|
||||||
|
Unit {
|
||||||
|
name: Identifier,
|
||||||
|
},
|
||||||
|
Tuple {
|
||||||
|
name: Identifier,
|
||||||
|
fields: Vec<Value>,
|
||||||
|
},
|
||||||
|
Fields {
|
||||||
|
name: Identifier,
|
||||||
|
fields: Vec<(Identifier, Value)>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Struct {
|
||||||
|
pub fn name(&self) -> &Identifier {
|
||||||
|
match self {
|
||||||
|
Struct::Unit { name } => name,
|
||||||
|
Struct::Tuple { name, .. } => name,
|
||||||
|
Struct::Fields { name, .. } => name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Struct {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Struct::Unit { name } => write!(f, "{}", name),
|
||||||
|
Struct::Tuple { name, fields } => {
|
||||||
|
write!(f, "{}(", name)?;
|
||||||
|
|
||||||
|
for (index, field) in fields.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}", field)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, ")")
|
||||||
|
}
|
||||||
|
Struct::Fields { name, fields } => {
|
||||||
|
write!(f, "{} {{", name)?;
|
||||||
|
|
||||||
|
for (index, (identifier, r#type)) in fields.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "{}: {}", identifier, r#type)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, "}}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum ValueError {
|
pub enum ValueError {
|
||||||
CannotAdd(Value, Value),
|
CannotAdd(Value, Value),
|
||||||
|
@ -137,6 +137,58 @@ impl Vm {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let BinaryOperator::FieldAccess = operator.inner {
|
||||||
|
let left_span = left.position;
|
||||||
|
let left_value = if let Some(value) = self.run_statement(*left)? {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(VmError::ExpectedValue {
|
||||||
|
position: left_span,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let right_span = right.position;
|
||||||
|
|
||||||
|
if let (Some(list), Statement::Constant(value)) =
|
||||||
|
(left_value.as_list(), &right.inner)
|
||||||
|
{
|
||||||
|
if let Some(index) = value.as_integer() {
|
||||||
|
let value = list.get(index as usize).cloned();
|
||||||
|
|
||||||
|
return Ok(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(range) = value.as_range() {
|
||||||
|
let range = range.start as usize..range.end as usize;
|
||||||
|
|
||||||
|
if let Some(items) = list.get(range) {
|
||||||
|
return Ok(Some(Value::list(items.to_vec())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(map) = left_value.as_map() {
|
||||||
|
if let Statement::Identifier(identifier) = right.inner {
|
||||||
|
let value = map.get(&identifier).cloned();
|
||||||
|
|
||||||
|
return Ok(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(value) = self.run_statement(*right)? {
|
||||||
|
if let Some(string) = value.as_string() {
|
||||||
|
let identifier = Identifier::new(string);
|
||||||
|
|
||||||
|
let value = map.get(&identifier).cloned();
|
||||||
|
|
||||||
|
return Ok(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(VmError::ExpectedIdentifierIntegerOrRange {
|
||||||
|
position: right_span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let left_position = left.position;
|
let left_position = left.position;
|
||||||
let left_value = if let Some(value) = self.run_statement(*left)? {
|
let left_value = if let Some(value) = self.run_statement(*left)? {
|
||||||
value
|
value
|
||||||
@ -466,57 +518,6 @@ impl Vm {
|
|||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
Statement::PropertyAccess(left, right) => {
|
|
||||||
let left_span = left.position;
|
|
||||||
let left_value = if let Some(value) = self.run_statement(*left)? {
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
return Err(VmError::ExpectedValue {
|
|
||||||
position: left_span,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
let right_span = right.position;
|
|
||||||
|
|
||||||
if let (Some(list), Statement::Constant(value)) =
|
|
||||||
(left_value.as_list(), &right.inner)
|
|
||||||
{
|
|
||||||
if let Some(index) = value.as_integer() {
|
|
||||||
let value = list.get(index as usize).cloned();
|
|
||||||
|
|
||||||
return Ok(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(range) = value.as_range() {
|
|
||||||
let range = range.start as usize..range.end as usize;
|
|
||||||
|
|
||||||
if let Some(items) = list.get(range) {
|
|
||||||
return Ok(Some(Value::list(items.to_vec())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(map) = left_value.as_map() {
|
|
||||||
if let Statement::Identifier(identifier) = right.inner {
|
|
||||||
let value = map.get(&identifier).cloned();
|
|
||||||
|
|
||||||
return Ok(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(value) = self.run_statement(*right)? {
|
|
||||||
if let Some(string) = value.as_string() {
|
|
||||||
let identifier = Identifier::new(string);
|
|
||||||
|
|
||||||
let value = map.get(&identifier).cloned();
|
|
||||||
|
|
||||||
return Ok(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(VmError::ExpectedIdentifierIntegerOrRange {
|
|
||||||
position: right_span,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Statement::UnaryOperation { operator, operand } => {
|
Statement::UnaryOperation { operator, operand } => {
|
||||||
let position = operand.position;
|
let position = operand.position;
|
||||||
let value = if let Some(value) = self.run_statement(*operand)? {
|
let value = if let Some(value) = self.run_statement(*operand)? {
|
||||||
|
Loading…
Reference in New Issue
Block a user