Add new analyzer and vm tests
This commit is contained in:
parent
f2c0786bfb
commit
78228ce8d6
@ -220,7 +220,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
} else {
|
} else {
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
return Err(AnalyzerError::ExpectedBoolean {
|
||||||
actual: condition.as_ref().clone(),
|
actual: condition.as_ref().clone(),
|
||||||
position: condition.position,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +237,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
} else {
|
} else {
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
return Err(AnalyzerError::ExpectedBoolean {
|
||||||
actual: condition.as_ref().clone(),
|
actual: condition.as_ref().clone(),
|
||||||
position: condition.position,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +255,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
} else {
|
} else {
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
return Err(AnalyzerError::ExpectedBoolean {
|
||||||
actual: condition.as_ref().clone(),
|
actual: condition.as_ref().clone(),
|
||||||
position: condition.position,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +268,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
} else {
|
} else {
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
return Err(AnalyzerError::ExpectedBoolean {
|
||||||
actual: condition.clone(),
|
actual: condition.clone(),
|
||||||
position: condition.position,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +287,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
} else {
|
} else {
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
return Err(AnalyzerError::ExpectedBoolean {
|
||||||
actual: condition.as_ref().clone(),
|
actual: condition.as_ref().clone(),
|
||||||
position: condition.position,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +300,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
} else {
|
} else {
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
return Err(AnalyzerError::ExpectedBoolean {
|
||||||
actual: condition.clone(),
|
actual: condition.clone(),
|
||||||
position: condition.position,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,13 +322,20 @@ impl<'a> Analyzer<'a> {
|
|||||||
self.analyze_node(node)?;
|
self.analyze_node(node)?;
|
||||||
}
|
}
|
||||||
Statement::PropertyAccess(left, right) => {
|
Statement::PropertyAccess(left, right) => {
|
||||||
|
self.analyze_node(left)?;
|
||||||
|
|
||||||
|
if let Statement::Identifier(_) = right.inner {
|
||||||
|
// Do not expect a value for property accessors
|
||||||
|
} else {
|
||||||
|
self.analyze_node(right)?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(Type::List { .. }) = left.inner.expected_type(self.context) {
|
if let Some(Type::List { .. }) = left.inner.expected_type(self.context) {
|
||||||
if let Some(Type::Integer) = right.inner.expected_type(self.context) {
|
if let Some(Type::Integer) = right.inner.expected_type(self.context) {
|
||||||
// Allow indexing lists with integers
|
// Allow indexing lists with integers
|
||||||
} else {
|
} else {
|
||||||
return Err(AnalyzerError::ExpectedInteger {
|
return Err(AnalyzerError::ExpectedInteger {
|
||||||
actual: right.as_ref().clone(),
|
actual: right.as_ref().clone(),
|
||||||
position: right.position,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,9 +351,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.analyze_node(left)?;
|
|
||||||
self.analyze_node(right)?;
|
|
||||||
}
|
}
|
||||||
Statement::While { condition, body } => {
|
Statement::While { condition, body } => {
|
||||||
self.analyze_node(condition)?;
|
self.analyze_node(condition)?;
|
||||||
@ -362,7 +360,6 @@ impl<'a> Analyzer<'a> {
|
|||||||
} else {
|
} else {
|
||||||
return Err(AnalyzerError::ExpectedBoolean {
|
return Err(AnalyzerError::ExpectedBoolean {
|
||||||
actual: condition.as_ref().clone(),
|
actual: condition.as_ref().clone(),
|
||||||
position: condition.position,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,18 +373,15 @@ impl<'a> Analyzer<'a> {
|
|||||||
pub enum AnalyzerError {
|
pub enum AnalyzerError {
|
||||||
ExpectedBoolean {
|
ExpectedBoolean {
|
||||||
actual: Node<Statement>,
|
actual: Node<Statement>,
|
||||||
position: Span,
|
|
||||||
},
|
},
|
||||||
ExpectedIdentifier {
|
ExpectedIdentifier {
|
||||||
actual: Node<Statement>,
|
actual: Node<Statement>,
|
||||||
position: Span,
|
|
||||||
},
|
},
|
||||||
ExpectedIdentifierOrString {
|
ExpectedIdentifierOrString {
|
||||||
actual: Node<Statement>,
|
actual: Node<Statement>,
|
||||||
},
|
},
|
||||||
ExpectedInteger {
|
ExpectedInteger {
|
||||||
actual: Node<Statement>,
|
actual: Node<Statement>,
|
||||||
position: Span,
|
|
||||||
},
|
},
|
||||||
ExpectedValue {
|
ExpectedValue {
|
||||||
actual: Node<Statement>,
|
actual: Node<Statement>,
|
||||||
@ -416,10 +410,10 @@ pub enum AnalyzerError {
|
|||||||
impl AnalyzerError {
|
impl AnalyzerError {
|
||||||
pub fn position(&self) -> Span {
|
pub fn position(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
AnalyzerError::ExpectedBoolean { position, .. } => *position,
|
AnalyzerError::ExpectedBoolean { actual, .. } => actual.position,
|
||||||
AnalyzerError::ExpectedIdentifier { position, .. } => *position,
|
AnalyzerError::ExpectedIdentifier { actual, .. } => actual.position,
|
||||||
AnalyzerError::ExpectedIdentifierOrString { actual } => actual.position,
|
AnalyzerError::ExpectedIdentifierOrString { actual } => actual.position,
|
||||||
AnalyzerError::ExpectedInteger { position, .. } => *position,
|
AnalyzerError::ExpectedInteger { 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 {
|
||||||
@ -485,6 +479,36 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_list_index() {
|
||||||
|
let source = "[1, 2, 3].foo";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
analyze(source),
|
||||||
|
Err(DustError::AnalyzerError {
|
||||||
|
analyzer_error: AnalyzerError::ExpectedInteger {
|
||||||
|
actual: Node::new(Statement::Identifier(Identifier::new("foo")), (10, 13)),
|
||||||
|
},
|
||||||
|
source
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_property_access() {
|
||||||
|
let source = "{ x = 1 }.0";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
analyze(source),
|
||||||
|
Err(DustError::AnalyzerError {
|
||||||
|
analyzer_error: AnalyzerError::ExpectedIdentifierOrString {
|
||||||
|
actual: Node::new(Statement::Constant(Value::integer(0)), (10, 11)),
|
||||||
|
},
|
||||||
|
source
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn length_no_arguments() {
|
fn length_no_arguments() {
|
||||||
let source = "length()";
|
let source = "length()";
|
||||||
|
@ -121,6 +121,14 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_map(&self) -> Option<&BTreeMap<Identifier, Value>> {
|
||||||
|
if let ValueInner::Map(map) = self.inner().as_ref() {
|
||||||
|
Some(map)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_integer(&self) -> Option<i64> {
|
pub fn as_integer(&self) -> Option<i64> {
|
||||||
if let ValueInner::Integer(integer) = self.inner().as_ref() {
|
if let ValueInner::Integer(integer) = self.inner().as_ref() {
|
||||||
Some(*integer)
|
Some(*integer)
|
||||||
|
@ -463,6 +463,14 @@ impl Vm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let (Some(map), Statement::Identifier(identifier)) =
|
||||||
|
(left_value.as_map(), &right.inner)
|
||||||
|
{
|
||||||
|
let value = map.get(identifier).cloned();
|
||||||
|
|
||||||
|
return Ok(value);
|
||||||
|
}
|
||||||
|
|
||||||
Err(VmError::ExpectedIdentifierOrInteger {
|
Err(VmError::ExpectedIdentifierOrInteger {
|
||||||
position: right_span,
|
position: right_span,
|
||||||
})
|
})
|
||||||
@ -623,6 +631,27 @@ impl Display for VmError {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_index() {
|
||||||
|
let input = "[1, 42, 3].1";
|
||||||
|
|
||||||
|
assert_eq!(run(input), Ok(Some(Value::integer(42))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn map_property_access() {
|
||||||
|
let input = "{ a = 42 }.a";
|
||||||
|
|
||||||
|
assert_eq!(run(input), Ok(Some(Value::integer(42))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn built_in_function_dot_notation() {
|
||||||
|
let input = "42.to_string()";
|
||||||
|
|
||||||
|
assert_eq!(run(input), Ok(Some(Value::string("42"))));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_string() {
|
fn to_string() {
|
||||||
let input = "to_string(42)";
|
let input = "to_string(42)";
|
||||||
|
Loading…
Reference in New Issue
Block a user