Refine parsing and lexing
This commit is contained in:
parent
0fb0b63a97
commit
5ad6012021
@ -144,6 +144,15 @@ impl Statement {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BinaryOperator::ListIndex => {
|
||||||
|
let left_type = left.inner.expected_type(context)?;
|
||||||
|
|
||||||
|
if let Type::List { item_type } = left_type {
|
||||||
|
Some(*item_type)
|
||||||
|
} 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)),
|
||||||
@ -216,11 +225,26 @@ impl Display for Statement {
|
|||||||
operator,
|
operator,
|
||||||
right,
|
right,
|
||||||
} => {
|
} => {
|
||||||
if let BinaryOperator::FieldAccess = operator.inner {
|
let operator = match operator.inner {
|
||||||
|
BinaryOperator::FieldAccess => return write!(f, "{left}.{right}"),
|
||||||
|
BinaryOperator::ListIndex => return write!(f, "{left}[{right}]"),
|
||||||
|
BinaryOperator::Add => "+",
|
||||||
|
BinaryOperator::AddAssign => "+=",
|
||||||
|
BinaryOperator::Assign => "=",
|
||||||
|
BinaryOperator::Divide => "/",
|
||||||
|
BinaryOperator::Equal => "==",
|
||||||
|
BinaryOperator::Greater => ">",
|
||||||
|
BinaryOperator::GreaterOrEqual => ">=",
|
||||||
|
BinaryOperator::Less => "<",
|
||||||
|
BinaryOperator::LessOrEqual => "<=",
|
||||||
|
BinaryOperator::Modulo => "%",
|
||||||
|
BinaryOperator::Multiply => "*",
|
||||||
|
BinaryOperator::Subtract => "-",
|
||||||
|
BinaryOperator::And => "&&",
|
||||||
|
BinaryOperator::Or => "||",
|
||||||
|
};
|
||||||
|
|
||||||
write!(f, "{left} {operator} {right}")
|
write!(f, "{left} {operator} {right}")
|
||||||
} else {
|
|
||||||
write!(f, "{left} {operator} {right}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Statement::BuiltInFunctionCall {
|
Statement::BuiltInFunctionCall {
|
||||||
function,
|
function,
|
||||||
@ -359,6 +383,11 @@ impl Display for Statement {
|
|||||||
}
|
}
|
||||||
Statement::Nil(node) => write!(f, "{node};"),
|
Statement::Nil(node) => write!(f, "{node};"),
|
||||||
Statement::UnaryOperation { operator, operand } => {
|
Statement::UnaryOperation { operator, operand } => {
|
||||||
|
let operator = match operator.inner {
|
||||||
|
UnaryOperator::Negate => "-",
|
||||||
|
UnaryOperator::Not => "!",
|
||||||
|
};
|
||||||
|
|
||||||
write!(f, "{operator}{operand}")
|
write!(f, "{operator}{operand}")
|
||||||
}
|
}
|
||||||
Statement::While { condition, body } => {
|
Statement::While { condition, body } => {
|
||||||
@ -370,7 +399,9 @@ 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 {
|
||||||
|
// Accessors
|
||||||
FieldAccess,
|
FieldAccess,
|
||||||
|
ListIndex,
|
||||||
|
|
||||||
// Math
|
// Math
|
||||||
Add,
|
Add,
|
||||||
@ -395,39 +426,8 @@ pub enum BinaryOperator {
|
|||||||
AddAssign,
|
AddAssign,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for BinaryOperator {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
BinaryOperator::Add => write!(f, "+"),
|
|
||||||
BinaryOperator::AddAssign => write!(f, "+="),
|
|
||||||
BinaryOperator::Assign => write!(f, "="),
|
|
||||||
BinaryOperator::And => write!(f, "&&"),
|
|
||||||
BinaryOperator::Divide => write!(f, "/"),
|
|
||||||
BinaryOperator::Equal => write!(f, "=="),
|
|
||||||
BinaryOperator::FieldAccess => write!(f, "."),
|
|
||||||
BinaryOperator::Greater => write!(f, ">"),
|
|
||||||
BinaryOperator::GreaterOrEqual => write!(f, ">="),
|
|
||||||
BinaryOperator::Less => write!(f, "<"),
|
|
||||||
BinaryOperator::LessOrEqual => write!(f, "<="),
|
|
||||||
BinaryOperator::Modulo => write!(f, "%"),
|
|
||||||
BinaryOperator::Multiply => write!(f, "*"),
|
|
||||||
BinaryOperator::Or => write!(f, "||"),
|
|
||||||
BinaryOperator::Subtract => write!(f, "-"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub enum UnaryOperator {
|
pub enum UnaryOperator {
|
||||||
Negate,
|
Negate,
|
||||||
Not,
|
Not,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for UnaryOperator {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
UnaryOperator::Negate => write!(f, "-"),
|
|
||||||
UnaryOperator::Not => write!(f, "!"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -105,20 +105,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
self.analyze_statement(right)?;
|
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::Map { .. }) = left.inner.expected_type(self.context) {
|
||||||
if let Some(Type::String) = right.inner.expected_type(self.context) {
|
if let Some(Type::String) = right.inner.expected_type(self.context) {
|
||||||
// Allow indexing maps with strings
|
// Allow indexing maps with strings
|
||||||
@ -129,6 +115,33 @@ impl<'a> Analyzer<'a> {
|
|||||||
actual: right.as_ref().clone(),
|
actual: right.as_ref().clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Err(AnalyzerError::ExpectedMap {
|
||||||
|
actual: left.as_ref().clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let BinaryOperator::ListIndex = operator.inner {
|
||||||
|
self.analyze_statement(left)?;
|
||||||
|
self.analyze_statement(right)?;
|
||||||
|
|
||||||
|
if let Some(Type::List { .. }) = left.inner.expected_type(self.context) {
|
||||||
|
let index_type = right.inner.expected_type(self.context);
|
||||||
|
|
||||||
|
if let Some(Type::Integer | Type::Range) = index_type {
|
||||||
|
// List and index are valid
|
||||||
|
} else {
|
||||||
|
return Err(AnalyzerError::ExpectedIntegerOrRange {
|
||||||
|
actual: right.as_ref().clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(AnalyzerError::ExpectedList {
|
||||||
|
actual: left.as_ref().clone(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -418,6 +431,12 @@ pub enum AnalyzerError {
|
|||||||
ExpectedIntegerOrRange {
|
ExpectedIntegerOrRange {
|
||||||
actual: Node<Statement>,
|
actual: Node<Statement>,
|
||||||
},
|
},
|
||||||
|
ExpectedList {
|
||||||
|
actual: Node<Statement>,
|
||||||
|
},
|
||||||
|
ExpectedMap {
|
||||||
|
actual: Node<Statement>,
|
||||||
|
},
|
||||||
ExpectedValue {
|
ExpectedValue {
|
||||||
actual: Node<Statement>,
|
actual: Node<Statement>,
|
||||||
},
|
},
|
||||||
@ -449,6 +468,8 @@ impl AnalyzerError {
|
|||||||
AnalyzerError::ExpectedIdentifier { actual, .. } => actual.position,
|
AnalyzerError::ExpectedIdentifier { actual, .. } => actual.position,
|
||||||
AnalyzerError::ExpectedIdentifierOrString { actual } => actual.position,
|
AnalyzerError::ExpectedIdentifierOrString { actual } => actual.position,
|
||||||
AnalyzerError::ExpectedIntegerOrRange { actual, .. } => actual.position,
|
AnalyzerError::ExpectedIntegerOrRange { actual, .. } => actual.position,
|
||||||
|
AnalyzerError::ExpectedList { actual } => actual.position,
|
||||||
|
AnalyzerError::ExpectedMap { actual } => actual.position,
|
||||||
AnalyzerError::ExpectedValue { actual } => actual.position,
|
AnalyzerError::ExpectedValue { actual } => actual.position,
|
||||||
AnalyzerError::ExpectedValueArgumentCount { position, .. } => *position,
|
AnalyzerError::ExpectedValueArgumentCount { position, .. } => *position,
|
||||||
AnalyzerError::TypeConflict {
|
AnalyzerError::TypeConflict {
|
||||||
@ -478,6 +499,8 @@ impl Display for AnalyzerError {
|
|||||||
AnalyzerError::ExpectedIntegerOrRange { actual, .. } => {
|
AnalyzerError::ExpectedIntegerOrRange { actual, .. } => {
|
||||||
write!(f, "Expected integer or range, found {}", actual)
|
write!(f, "Expected integer or range, found {}", actual)
|
||||||
}
|
}
|
||||||
|
AnalyzerError::ExpectedList { actual } => write!(f, "Expected list, found {}", actual),
|
||||||
|
AnalyzerError::ExpectedMap { actual } => write!(f, "Expected map, found {}", actual),
|
||||||
AnalyzerError::ExpectedValue { actual, .. } => {
|
AnalyzerError::ExpectedValue { actual, .. } => {
|
||||||
write!(f, "Expected value, found {}", actual)
|
write!(f, "Expected value, found {}", actual)
|
||||||
}
|
}
|
||||||
@ -516,13 +539,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn malformed_list_index() {
|
fn malformed_list_index() {
|
||||||
let source = "[1, 2, 3].foo";
|
let source = "[1, 2, 3]['foo']";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
analyze(source),
|
analyze(source),
|
||||||
Err(DustError::AnalyzerError {
|
Err(DustError::AnalyzerError {
|
||||||
analyzer_error: AnalyzerError::ExpectedIntegerOrRange {
|
analyzer_error: AnalyzerError::ExpectedIntegerOrRange {
|
||||||
actual: Node::new(Statement::Identifier(Identifier::new("foo")), (10, 13)),
|
actual: Node::new(Statement::Constant(Value::string("foo")), (10, 15)),
|
||||||
},
|
},
|
||||||
source
|
source
|
||||||
})
|
})
|
||||||
|
@ -499,6 +499,73 @@ impl Display for LexError {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_index() {
|
||||||
|
let input = "[1, 2, 3][1]";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lex(input),
|
||||||
|
Ok(vec![
|
||||||
|
(Token::LeftSquareBrace, (0, 1)),
|
||||||
|
(Token::Integer("1"), (1, 2)),
|
||||||
|
(Token::Comma, (2, 3)),
|
||||||
|
(Token::Integer("2"), (4, 5)),
|
||||||
|
(Token::Comma, (5, 6)),
|
||||||
|
(Token::Integer("3"), (7, 8)),
|
||||||
|
(Token::RightSquareBrace, (8, 9)),
|
||||||
|
(Token::LeftSquareBrace, (9, 10)),
|
||||||
|
(Token::Integer("1"), (10, 11)),
|
||||||
|
(Token::RightSquareBrace, (11, 12)),
|
||||||
|
(Token::Eof, (12, 12)),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list() {
|
||||||
|
let input = "[1, 2, 3]";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lex(input),
|
||||||
|
Ok(vec![
|
||||||
|
(Token::LeftSquareBrace, (0, 1)),
|
||||||
|
(Token::Integer("1"), (1, 2)),
|
||||||
|
(Token::Comma, (2, 3)),
|
||||||
|
(Token::Integer("2"), (4, 5)),
|
||||||
|
(Token::Comma, (5, 6)),
|
||||||
|
(Token::Integer("3"), (7, 8)),
|
||||||
|
(Token::RightSquareBrace, (8, 9)),
|
||||||
|
(Token::Eof, (9, 9)),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn map_field_access() {
|
||||||
|
let input = "{a = 1, b = 2, c = 3}.c";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lex(input),
|
||||||
|
Ok(vec![
|
||||||
|
(Token::LeftCurlyBrace, (0, 1)),
|
||||||
|
(Token::Identifier("a"), (1, 2)),
|
||||||
|
(Token::Equal, (3, 4)),
|
||||||
|
(Token::Integer("1"), (5, 6)),
|
||||||
|
(Token::Comma, (6, 7)),
|
||||||
|
(Token::Identifier("b"), (8, 9)),
|
||||||
|
(Token::Equal, (10, 11)),
|
||||||
|
(Token::Integer("2"), (12, 13)),
|
||||||
|
(Token::Comma, (13, 14)),
|
||||||
|
(Token::Identifier("c"), (15, 16)),
|
||||||
|
(Token::Equal, (17, 18)),
|
||||||
|
(Token::Integer("3"), (19, 20)),
|
||||||
|
(Token::RightCurlyBrace, (20, 21)),
|
||||||
|
(Token::Dot, (21, 22)),
|
||||||
|
(Token::Identifier("c"), (22, 23)),
|
||||||
|
(Token::Eof, (23, 23)),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn range() {
|
fn range() {
|
||||||
let input = "0..42";
|
let input = "0..42";
|
||||||
|
@ -483,15 +483,9 @@ impl<'src> Parser<'src> {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(instruction) = self.parse_statement(0) {
|
let statement = self.parse_statement(0)?;
|
||||||
nodes.push(instruction);
|
|
||||||
} else {
|
nodes.push(statement);
|
||||||
return Err(ParseError::ExpectedToken {
|
|
||||||
expected: TokenKind::RightSquareBrace,
|
|
||||||
actual: self.current.0.to_owned(),
|
|
||||||
position: self.current.1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
@ -712,18 +706,48 @@ impl<'src> Parser<'src> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
left: Node<Statement>,
|
left: Node<Statement>,
|
||||||
) -> Result<(Node<Statement>, u8), ParseError> {
|
) -> Result<(Node<Statement>, u8), ParseError> {
|
||||||
let node = if let Token::Semicolon = &self.current.0 {
|
let node = match &self.current.0 {
|
||||||
|
Token::LeftSquareBrace => {
|
||||||
self.next_token()?;
|
self.next_token()?;
|
||||||
|
|
||||||
|
let index = self.parse_statement(0)?;
|
||||||
|
|
||||||
|
if let Token::RightSquareBrace = self.current.0 {
|
||||||
|
self.next_token()?;
|
||||||
|
} else {
|
||||||
|
return Err(ParseError::ExpectedToken {
|
||||||
|
expected: TokenKind::RightSquareBrace,
|
||||||
|
actual: self.current.0.to_owned(),
|
||||||
|
position: self.current.1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let left_start = left.position.0;
|
||||||
|
let right_end = self.current.1 .1;
|
||||||
|
|
||||||
|
Node::new(
|
||||||
|
Statement::BinaryOperation {
|
||||||
|
left: Box::new(left),
|
||||||
|
operator: Node::new(BinaryOperator::ListIndex, self.current.1),
|
||||||
|
right: Box::new(index),
|
||||||
|
},
|
||||||
|
(left_start, right_end),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Token::Semicolon => {
|
||||||
let left_start = left.position.0;
|
let left_start = left.position.0;
|
||||||
let operator_end = self.current.1 .1;
|
let operator_end = self.current.1 .1;
|
||||||
|
|
||||||
|
self.next_token()?;
|
||||||
|
|
||||||
Node::new(Statement::Nil(Box::new(left)), (left_start, operator_end))
|
Node::new(Statement::Nil(Box::new(left)), (left_start, operator_end))
|
||||||
} else {
|
}
|
||||||
|
_ => {
|
||||||
return Err(ParseError::UnexpectedToken {
|
return Err(ParseError::UnexpectedToken {
|
||||||
actual: self.current.0.to_owned(),
|
actual: self.current.0.to_owned(),
|
||||||
position: self.current.1,
|
position: self.current.1,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((node, self.current.0.precedence()))
|
Ok((node, self.current.0.precedence()))
|
||||||
@ -851,6 +875,52 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_index_nested() {
|
||||||
|
let input = "[1, [2], 3][1][0]";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse(input),
|
||||||
|
Ok(AbstractSyntaxTree {
|
||||||
|
nodes: [Node::new(
|
||||||
|
Statement::BinaryOperation {
|
||||||
|
left: Box::new(Node::new(
|
||||||
|
Statement::BinaryOperation {
|
||||||
|
left: Box::new(Node::new(
|
||||||
|
Statement::List(vec![
|
||||||
|
Node::new(Statement::Constant(Value::integer(1)), (1, 2)),
|
||||||
|
Node::new(
|
||||||
|
Statement::List(vec![Node::new(
|
||||||
|
Statement::Constant(Value::integer(2)),
|
||||||
|
(5, 6)
|
||||||
|
)]),
|
||||||
|
(4, 7)
|
||||||
|
),
|
||||||
|
Node::new(Statement::Constant(Value::integer(3)), (9, 10))
|
||||||
|
]),
|
||||||
|
(0, 11)
|
||||||
|
)),
|
||||||
|
operator: Node::new(BinaryOperator::ListIndex, (0, 0)),
|
||||||
|
right: Box::new(Node::new(
|
||||||
|
Statement::Constant(Value::integer(1)),
|
||||||
|
(12, 13)
|
||||||
|
))
|
||||||
|
},
|
||||||
|
(0, 0)
|
||||||
|
)),
|
||||||
|
operator: Node::new(BinaryOperator::ListIndex, (0, 0)),
|
||||||
|
right: Box::new(Node::new(
|
||||||
|
Statement::Constant(Value::integer(0)),
|
||||||
|
(15, 16)
|
||||||
|
))
|
||||||
|
},
|
||||||
|
(0, 0)
|
||||||
|
),]
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn map_property_nested() {
|
fn map_property_nested() {
|
||||||
let input = "{ x = { y = 42 } }.x.y";
|
let input = "{ x = { y = 42 } }.x.y";
|
||||||
@ -940,7 +1010,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
(0, 5)
|
(0, 5)
|
||||||
))),
|
))),
|
||||||
(0, 8)
|
(0, 6)
|
||||||
),
|
),
|
||||||
Node::new(
|
Node::new(
|
||||||
Statement::UnaryOperation {
|
Statement::UnaryOperation {
|
||||||
@ -1045,7 +1115,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
(0, 9)
|
(0, 9)
|
||||||
))),
|
))),
|
||||||
(0, 12)
|
(0, 10)
|
||||||
),
|
),
|
||||||
Node::new(
|
Node::new(
|
||||||
Statement::UnaryOperation {
|
Statement::UnaryOperation {
|
||||||
@ -1195,7 +1265,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
(16, 21)
|
(16, 21)
|
||||||
))),
|
))),
|
||||||
(16, 24)
|
(16, 22)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1368,7 +1438,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
(2, 10)
|
(2, 10)
|
||||||
),)),
|
),)),
|
||||||
(2, 15)
|
(2, 11)
|
||||||
),
|
),
|
||||||
Node::new(
|
Node::new(
|
||||||
Statement::Nil(Box::new(Node::new(
|
Statement::Nil(Box::new(Node::new(
|
||||||
@ -1385,7 +1455,7 @@ mod tests {
|
|||||||
},
|
},
|
||||||
(12, 20)
|
(12, 20)
|
||||||
),)),
|
),)),
|
||||||
(12, 25)
|
(12, 21)
|
||||||
),
|
),
|
||||||
Node::new(
|
Node::new(
|
||||||
Statement::BinaryOperation {
|
Statement::BinaryOperation {
|
||||||
@ -1732,8 +1802,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_access() {
|
fn list_index() {
|
||||||
let input = "[1, 2, 3].0";
|
let input = "[1, 2, 3][0]";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse(input),
|
parse(input),
|
||||||
@ -1748,13 +1818,13 @@ mod tests {
|
|||||||
]),
|
]),
|
||||||
(0, 9)
|
(0, 9)
|
||||||
)),
|
)),
|
||||||
operator: Node::new(BinaryOperator::FieldAccess, (9, 10)),
|
operator: Node::new(BinaryOperator::ListIndex, (12, 12)),
|
||||||
right: Box::new(Node::new(
|
right: Box::new(Node::new(
|
||||||
Statement::Constant(Value::integer(0)),
|
Statement::Constant(Value::integer(0)),
|
||||||
(10, 11)
|
(10, 11)
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
(0, 11),
|
(0, 12),
|
||||||
)]
|
)]
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
|
@ -222,7 +222,8 @@ impl<'src> Token<'src> {
|
|||||||
|
|
||||||
pub fn precedence(&self) -> u8 {
|
pub fn precedence(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
Token::Dot => 9,
|
Token::Dot => 10,
|
||||||
|
Token::LeftSquareBrace => 9,
|
||||||
Token::Star | Token::Slash | Token::Percent => 8,
|
Token::Star | Token::Slash | Token::Percent => 8,
|
||||||
Token::Minus => 7,
|
Token::Minus => 7,
|
||||||
Token::Plus => 6,
|
Token::Plus => 6,
|
||||||
@ -240,7 +241,16 @@ impl<'src> Token<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_left_associative(&self) -> bool {
|
pub fn is_left_associative(&self) -> bool {
|
||||||
!self.is_right_associative()
|
matches!(
|
||||||
|
self,
|
||||||
|
Token::DoubleAmpersand
|
||||||
|
| Token::DoublePipe
|
||||||
|
| Token::Plus
|
||||||
|
| Token::Minus
|
||||||
|
| Token::Star
|
||||||
|
| Token::Slash
|
||||||
|
| Token::Percent
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_right_associative(&self) -> bool {
|
pub fn is_right_associative(&self) -> bool {
|
||||||
@ -252,7 +262,7 @@ impl<'src> Token<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_postfix(&self) -> bool {
|
pub fn is_postfix(&self) -> bool {
|
||||||
matches!(self, Token::Semicolon)
|
matches!(self, Token::LeftSquareBrace | Token::Semicolon)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,24 +148,6 @@ impl Vm {
|
|||||||
};
|
};
|
||||||
let right_span = right.position;
|
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 Some(map) = left_value.as_map() {
|
||||||
if let Statement::Identifier(identifier) = right.inner {
|
if let Statement::Identifier(identifier) = right.inner {
|
||||||
let value = map.get(&identifier).cloned();
|
let value = map.get(&identifier).cloned();
|
||||||
@ -182,10 +164,62 @@ impl Vm {
|
|||||||
return Ok(value);
|
return Ok(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Err(VmError::ExpectedIdentifierOrString {
|
||||||
|
position: right_span,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return Err(VmError::ExpectedMap {
|
||||||
|
position: left_span,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Err(VmError::ExpectedIdentifierIntegerOrRange {
|
if let BinaryOperator::ListIndex = operator.inner {
|
||||||
position: right_span,
|
let list_position = left.position;
|
||||||
|
let list_value = if let Some(value) = self.run_statement(*left)? {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(VmError::ExpectedValue {
|
||||||
|
position: list_position,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let list = if let Some(list) = list_value.as_list() {
|
||||||
|
list
|
||||||
|
} else {
|
||||||
|
return Err(VmError::ExpectedList {
|
||||||
|
position: list_position,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let index_position = right.position;
|
||||||
|
let index_value = if let Some(value) = self.run_statement(*right)? {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(VmError::ExpectedValue {
|
||||||
|
position: index_position,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(index) = index_value.as_integer() {
|
||||||
|
return if let Some(value) = list.get(index as usize) {
|
||||||
|
Ok(Some(value.clone()))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(range) = index_value.as_range() {
|
||||||
|
let range = range.start as usize..range.end as usize;
|
||||||
|
|
||||||
|
return if let Some(list) = list.get(range) {
|
||||||
|
Ok(Some(Value::list(list.to_vec())))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(VmError::ExpectedIntegerOrRange {
|
||||||
|
position: index_position,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -594,7 +628,10 @@ pub enum VmError {
|
|||||||
ExpectedIdentifier {
|
ExpectedIdentifier {
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
ExpectedIdentifierIntegerOrRange {
|
ExpectedIntegerOrRange {
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
|
ExpectedIdentifierOrString {
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
ExpectedInteger {
|
ExpectedInteger {
|
||||||
@ -603,6 +640,9 @@ pub enum VmError {
|
|||||||
ExpectedNumber {
|
ExpectedNumber {
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
|
ExpectedMap {
|
||||||
|
position: Span,
|
||||||
|
},
|
||||||
ExpectedFunction {
|
ExpectedFunction {
|
||||||
actual: Value,
|
actual: Value,
|
||||||
position: Span,
|
position: Span,
|
||||||
@ -632,10 +672,12 @@ impl VmError {
|
|||||||
Self::BuiltInFunctionError { position, .. } => *position,
|
Self::BuiltInFunctionError { position, .. } => *position,
|
||||||
Self::ExpectedBoolean { position } => *position,
|
Self::ExpectedBoolean { position } => *position,
|
||||||
Self::ExpectedIdentifier { position } => *position,
|
Self::ExpectedIdentifier { position } => *position,
|
||||||
Self::ExpectedIdentifierIntegerOrRange { position } => *position,
|
Self::ExpectedIdentifierOrString { position } => *position,
|
||||||
|
Self::ExpectedIntegerOrRange { position } => *position,
|
||||||
Self::ExpectedInteger { position } => *position,
|
Self::ExpectedInteger { position } => *position,
|
||||||
Self::ExpectedFunction { position, .. } => *position,
|
Self::ExpectedFunction { position, .. } => *position,
|
||||||
Self::ExpectedList { position } => *position,
|
Self::ExpectedList { position } => *position,
|
||||||
|
Self::ExpectedMap { position } => *position,
|
||||||
Self::ExpectedNumber { position } => *position,
|
Self::ExpectedNumber { position } => *position,
|
||||||
Self::ExpectedValue { position } => *position,
|
Self::ExpectedValue { position } => *position,
|
||||||
Self::UndefinedVariable { identifier } => identifier.position,
|
Self::UndefinedVariable { identifier } => identifier.position,
|
||||||
@ -673,7 +715,14 @@ impl Display for VmError {
|
|||||||
Self::ExpectedIdentifier { position } => {
|
Self::ExpectedIdentifier { position } => {
|
||||||
write!(f, "Expected an identifier at position: {:?}", position)
|
write!(f, "Expected an identifier at position: {:?}", position)
|
||||||
}
|
}
|
||||||
Self::ExpectedIdentifierIntegerOrRange { position } => {
|
Self::ExpectedIdentifierOrString { position } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Expected an identifier or string at position: {:?}",
|
||||||
|
position
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Self::ExpectedIntegerOrRange { position } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Expected an identifier, integer, or range at position: {:?}",
|
"Expected an identifier, integer, or range at position: {:?}",
|
||||||
@ -686,6 +735,9 @@ impl Display for VmError {
|
|||||||
Self::ExpectedList { position } => {
|
Self::ExpectedList { position } => {
|
||||||
write!(f, "Expected a list at position: {:?}", position)
|
write!(f, "Expected a list at position: {:?}", position)
|
||||||
}
|
}
|
||||||
|
Self::ExpectedMap { position } => {
|
||||||
|
write!(f, "Expected a map at position: {:?}", position)
|
||||||
|
}
|
||||||
Self::ExpectedNumber { position } => {
|
Self::ExpectedNumber { position } => {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
@ -712,6 +764,13 @@ impl Display for VmError {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_index_nested() {
|
||||||
|
let input = "[[1, 2], [42, 4], [5, 6]][1][0]";
|
||||||
|
|
||||||
|
assert_eq!(run(input), Ok(Some(Value::integer(42))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn map_property() {
|
fn map_property() {
|
||||||
let input = "{ x = 42 }.x";
|
let input = "{ x = 42 }.x";
|
||||||
@ -735,7 +794,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_index_range() {
|
fn list_index_range() {
|
||||||
let input = "[1, 2, 3, 4, 5].1..3";
|
let input = "[1, 2, 3, 4, 5][1..3]";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run(input),
|
run(input),
|
||||||
@ -769,7 +828,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_index() {
|
fn list_index() {
|
||||||
let input = "[1, 42, 3].1";
|
let input = "[1, 42, 3][1]";
|
||||||
|
|
||||||
assert_eq!(run(input), Ok(Some(Value::integer(42))));
|
assert_eq!(run(input), Ok(Some(Value::integer(42))));
|
||||||
}
|
}
|
||||||
@ -949,13 +1008,6 @@ mod tests {
|
|||||||
assert_eq!(run(input), Ok(Some(Value::integer(3))));
|
assert_eq!(run(input), Ok(Some(Value::integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn list_access() {
|
|
||||||
let input = "[1, 2, 3].1";
|
|
||||||
|
|
||||||
assert_eq!(run(input), Ok(Some(Value::integer(2))));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add() {
|
fn add() {
|
||||||
let input = "1 + 2";
|
let input = "1 + 2";
|
||||||
|
Loading…
Reference in New Issue
Block a user