Pass analyzer test
This commit is contained in:
parent
08a9b265ec
commit
e22d0254f5
@ -13,8 +13,9 @@ use crate::{
|
|||||||
ast::{
|
ast::{
|
||||||
AbstractSyntaxTree, AstError, BlockExpression, CallExpression, ElseExpression,
|
AbstractSyntaxTree, AstError, BlockExpression, CallExpression, ElseExpression,
|
||||||
FieldAccessExpression, IfExpression, LetStatement, ListExpression, ListIndexExpression,
|
FieldAccessExpression, IfExpression, LetStatement, ListExpression, ListIndexExpression,
|
||||||
LoopExpression, MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement,
|
LiteralExpression, LoopExpression, MapExpression, Node, OperatorExpression,
|
||||||
StructDefinition, StructExpression, TupleAccessExpression,
|
PrimitiveValueExpression, RangeExpression, Span, Statement, StructDefinition,
|
||||||
|
StructExpression, TupleAccessExpression,
|
||||||
},
|
},
|
||||||
core_library, parse, Context, ContextError, DustError, Expression, Identifier, StructType,
|
core_library, parse, Context, ContextError, DustError, Expression, Identifier, StructType,
|
||||||
Type,
|
Type,
|
||||||
@ -224,6 +225,56 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
|||||||
Expression::ListIndex(list_index_expression) => {
|
Expression::ListIndex(list_index_expression) => {
|
||||||
let ListIndexExpression { list, index } = list_index_expression.inner.as_ref();
|
let ListIndexExpression { list, index } = list_index_expression.inner.as_ref();
|
||||||
|
|
||||||
|
let list_type = list.return_type(&self.context)?;
|
||||||
|
|
||||||
|
let literal_type = if let Expression::Literal(Node { inner, .. }) = index {
|
||||||
|
Some(inner.as_ref().clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(Type::List { length, .. }) = list_type {
|
||||||
|
if let Some(LiteralExpression::Primitive(PrimitiveValueExpression::Integer(
|
||||||
|
integer,
|
||||||
|
))) = literal_type
|
||||||
|
{
|
||||||
|
if integer < 0 || integer >= length as i64 {
|
||||||
|
return Err(AnalysisError::IndexOutOfBounds {
|
||||||
|
index: index.clone(),
|
||||||
|
length,
|
||||||
|
list: list.clone(),
|
||||||
|
index_value: integer,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(Type::String {
|
||||||
|
length: Some(length),
|
||||||
|
}) = list_type
|
||||||
|
{
|
||||||
|
if let Some(LiteralExpression::Primitive(PrimitiveValueExpression::Integer(
|
||||||
|
integer,
|
||||||
|
))) = literal_type
|
||||||
|
{
|
||||||
|
if integer < 0 || integer >= length as i64 {
|
||||||
|
return Err(AnalysisError::IndexOutOfBounds {
|
||||||
|
index: index.clone(),
|
||||||
|
length,
|
||||||
|
list: list.clone(),
|
||||||
|
index_value: integer,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if list_type.is_none() {
|
||||||
|
return Err(AnalysisError::ExpectedValueFromExpression {
|
||||||
|
expression: list.clone(),
|
||||||
|
found_type: list_type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
self.analyze_expression(list)?;
|
self.analyze_expression(list)?;
|
||||||
self.analyze_expression(index)?;
|
self.analyze_expression(index)?;
|
||||||
}
|
}
|
||||||
@ -302,10 +353,60 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
|||||||
OperatorExpression::Math { left, right, .. } => {
|
OperatorExpression::Math { left, right, .. } => {
|
||||||
self.analyze_expression(left)?;
|
self.analyze_expression(left)?;
|
||||||
self.analyze_expression(right)?;
|
self.analyze_expression(right)?;
|
||||||
|
|
||||||
|
let left_type = left.return_type(&self.context)?;
|
||||||
|
let right_type = right.return_type(&self.context)?;
|
||||||
|
|
||||||
|
if left_type.is_none() {
|
||||||
|
return Err(AnalysisError::ExpectedValueFromExpression {
|
||||||
|
expression: left.clone(),
|
||||||
|
found_type: left_type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if right_type.is_none() {
|
||||||
|
return Err(AnalysisError::ExpectedValueFromExpression {
|
||||||
|
expression: right.clone(),
|
||||||
|
found_type: right_type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if left_type != right_type {
|
||||||
|
return Err(AnalysisError::ExpectedType {
|
||||||
|
expected: left_type.unwrap(),
|
||||||
|
actual: right_type.unwrap(),
|
||||||
|
actual_expression: right.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OperatorExpression::Logic { left, right, .. } => {
|
OperatorExpression::Logic { left, right, .. } => {
|
||||||
self.analyze_expression(left)?;
|
self.analyze_expression(left)?;
|
||||||
self.analyze_expression(right)?;
|
self.analyze_expression(right)?;
|
||||||
|
|
||||||
|
let left_type = left.return_type(&self.context)?;
|
||||||
|
let right_type = right.return_type(&self.context)?;
|
||||||
|
|
||||||
|
if left_type.is_none() {
|
||||||
|
return Err(AnalysisError::ExpectedValueFromExpression {
|
||||||
|
expression: left.clone(),
|
||||||
|
found_type: left_type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if right_type.is_none() {
|
||||||
|
return Err(AnalysisError::ExpectedValueFromExpression {
|
||||||
|
expression: right.clone(),
|
||||||
|
found_type: right_type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if left_type != right_type {
|
||||||
|
return Err(AnalysisError::ExpectedType {
|
||||||
|
expected: left_type.unwrap(),
|
||||||
|
actual: right_type.unwrap(),
|
||||||
|
actual_expression: right.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Expression::Range(range_expression) => match range_expression.inner.as_ref() {
|
Expression::Range(range_expression) => match range_expression.inner.as_ref() {
|
||||||
@ -325,7 +426,7 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
|||||||
.map_err(|error| AnalysisError::ContextError {
|
.map_err(|error| AnalysisError::ContextError {
|
||||||
error,
|
error,
|
||||||
position: name.position,
|
position: name.position,
|
||||||
});
|
})?;
|
||||||
|
|
||||||
for (_, expression) in fields {
|
for (_, expression) in fields {
|
||||||
self.analyze_expression(expression)?;
|
self.analyze_expression(expression)?;
|
||||||
@ -401,23 +502,21 @@ pub enum AnalysisError {
|
|||||||
error: ContextError,
|
error: ContextError,
|
||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
ExpectedBoolean {
|
ExpectedType {
|
||||||
actual: Statement,
|
expected: Type,
|
||||||
|
actual: Type,
|
||||||
|
actual_expression: Expression,
|
||||||
|
},
|
||||||
|
ExpectedTypeMultiple {
|
||||||
|
expected: Vec<Type>,
|
||||||
|
actual: Type,
|
||||||
|
actual_expression: Expression,
|
||||||
},
|
},
|
||||||
ExpectedIdentifier {
|
ExpectedIdentifier {
|
||||||
actual: Statement,
|
actual: Expression,
|
||||||
},
|
},
|
||||||
ExpectedIdentifierOrString {
|
ExpectedIdentifierOrString {
|
||||||
actual: Statement,
|
actual: Expression,
|
||||||
},
|
|
||||||
ExpectedIntegerOrRange {
|
|
||||||
actual: Statement,
|
|
||||||
},
|
|
||||||
ExpectedList {
|
|
||||||
actual: Statement,
|
|
||||||
},
|
|
||||||
ExpectedMap {
|
|
||||||
actual: Statement,
|
|
||||||
},
|
},
|
||||||
ExpectedValueFromStatement {
|
ExpectedValueFromStatement {
|
||||||
actual: Statement,
|
actual: Statement,
|
||||||
@ -432,9 +531,9 @@ pub enum AnalysisError {
|
|||||||
position: Span,
|
position: Span,
|
||||||
},
|
},
|
||||||
IndexOutOfBounds {
|
IndexOutOfBounds {
|
||||||
list: Statement,
|
list: Expression,
|
||||||
index: Statement,
|
index: Expression,
|
||||||
index_value: usize,
|
index_value: i64,
|
||||||
length: usize,
|
length: usize,
|
||||||
},
|
},
|
||||||
TypeConflict {
|
TypeConflict {
|
||||||
@ -443,8 +542,8 @@ pub enum AnalysisError {
|
|||||||
expected: Type,
|
expected: Type,
|
||||||
},
|
},
|
||||||
UndefinedField {
|
UndefinedField {
|
||||||
identifier: Statement,
|
identifier: Expression,
|
||||||
statement: Statement,
|
statement: Expression,
|
||||||
},
|
},
|
||||||
UndefinedType {
|
UndefinedType {
|
||||||
identifier: Node<Identifier>,
|
identifier: Node<Identifier>,
|
||||||
@ -453,7 +552,7 @@ pub enum AnalysisError {
|
|||||||
identifier: Node<Identifier>,
|
identifier: Node<Identifier>,
|
||||||
},
|
},
|
||||||
UnexectedString {
|
UnexectedString {
|
||||||
actual: Statement,
|
actual: Expression,
|
||||||
},
|
},
|
||||||
UndefinedVariable {
|
UndefinedVariable {
|
||||||
identifier: Node<Identifier>,
|
identifier: Node<Identifier>,
|
||||||
@ -471,12 +570,14 @@ impl AnalysisError {
|
|||||||
match self {
|
match self {
|
||||||
AnalysisError::AstError(ast_error) => ast_error.position(),
|
AnalysisError::AstError(ast_error) => ast_error.position(),
|
||||||
AnalysisError::ContextError { position, .. } => *position,
|
AnalysisError::ContextError { position, .. } => *position,
|
||||||
AnalysisError::ExpectedBoolean { actual } => actual.position(),
|
AnalysisError::ExpectedType {
|
||||||
|
actual_expression, ..
|
||||||
|
} => actual_expression.position(),
|
||||||
|
AnalysisError::ExpectedTypeMultiple {
|
||||||
|
actual_expression, ..
|
||||||
|
} => actual_expression.position(),
|
||||||
AnalysisError::ExpectedIdentifier { actual } => actual.position(),
|
AnalysisError::ExpectedIdentifier { actual } => actual.position(),
|
||||||
AnalysisError::ExpectedIdentifierOrString { actual } => actual.position(),
|
AnalysisError::ExpectedIdentifierOrString { actual } => actual.position(),
|
||||||
AnalysisError::ExpectedIntegerOrRange { actual } => actual.position(),
|
|
||||||
AnalysisError::ExpectedList { actual } => actual.position(),
|
|
||||||
AnalysisError::ExpectedMap { actual } => actual.position(),
|
|
||||||
AnalysisError::ExpectedValueFromExpression { expression, .. } => expression.position(),
|
AnalysisError::ExpectedValueFromExpression { expression, .. } => expression.position(),
|
||||||
AnalysisError::ExpectedValueFromStatement { actual } => actual.position(),
|
AnalysisError::ExpectedValueFromStatement { actual } => actual.position(),
|
||||||
AnalysisError::ExpectedValueArgumentCount { position, .. } => *position,
|
AnalysisError::ExpectedValueArgumentCount { position, .. } => *position,
|
||||||
@ -499,23 +600,37 @@ impl Display for AnalysisError {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
AnalysisError::AstError(ast_error) => write!(f, "{}", ast_error),
|
AnalysisError::AstError(ast_error) => write!(f, "{}", ast_error),
|
||||||
Self::ContextError { error, position } => {
|
AnalysisError::ContextError { error, .. } => write!(f, "{}", error),
|
||||||
write!(f, "Context error at {:?}: {}", position, error)
|
|
||||||
|
AnalysisError::ExpectedType {
|
||||||
|
expected,
|
||||||
|
actual,
|
||||||
|
actual_expression,
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Expected type {:?}, found {:?} in {}",
|
||||||
|
expected, actual, actual_expression
|
||||||
|
)
|
||||||
}
|
}
|
||||||
AnalysisError::ExpectedBoolean { actual, .. } => {
|
AnalysisError::ExpectedTypeMultiple {
|
||||||
write!(f, "Expected boolean, found {}", actual)
|
expected,
|
||||||
|
actual,
|
||||||
|
actual_expression,
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Expected one of {:?}, found {:?} in {}",
|
||||||
|
expected, actual, actual_expression
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
AnalysisError::ExpectedIdentifier { actual, .. } => {
|
AnalysisError::ExpectedIdentifier { actual, .. } => {
|
||||||
write!(f, "Expected identifier, found {}", actual)
|
write!(f, "Expected identifier, found {}", actual)
|
||||||
}
|
}
|
||||||
AnalysisError::ExpectedIdentifierOrString { actual } => {
|
AnalysisError::ExpectedIdentifierOrString { actual } => {
|
||||||
write!(f, "Expected identifier or string, found {}", actual)
|
write!(f, "Expected identifier or string, found {}", actual)
|
||||||
}
|
}
|
||||||
AnalysisError::ExpectedIntegerOrRange { actual, .. } => {
|
|
||||||
write!(f, "Expected integer or range, found {}", actual)
|
|
||||||
}
|
|
||||||
AnalysisError::ExpectedList { actual } => write!(f, "Expected list, found {}", actual),
|
|
||||||
AnalysisError::ExpectedMap { actual } => write!(f, "Expected map, found {}", actual),
|
|
||||||
AnalysisError::ExpectedValueFromExpression {
|
AnalysisError::ExpectedValueFromExpression {
|
||||||
expression,
|
expression,
|
||||||
found_type,
|
found_type,
|
||||||
@ -623,18 +738,46 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn tuple_struct_with_wrong_field_types() {
|
fn tuple_struct_with_wrong_field_types() {
|
||||||
let source = "
|
let source = "
|
||||||
struct Foo(int, float)
|
struct Foo(int, float);
|
||||||
Foo(1, 2)
|
Foo(1, 2)
|
||||||
";
|
";
|
||||||
|
|
||||||
assert_eq!(analyze(source), todo!());
|
assert_eq!(
|
||||||
|
analyze(source),
|
||||||
|
Err(DustError::Analysis {
|
||||||
|
analysis_error: AnalysisError::TypeConflict {
|
||||||
|
actual_expression: Expression::literal(2, (52, 53)),
|
||||||
|
actual_type: Type::Integer,
|
||||||
|
expected: Type::Float,
|
||||||
|
},
|
||||||
|
source,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn constant_list_index_out_of_bounds() {
|
fn constant_list_index_out_of_bounds() {
|
||||||
let source = "[1, 2, 3][3]";
|
let source = "[1, 2, 3][3]";
|
||||||
|
|
||||||
assert_eq!(analyze(source), todo!());
|
assert_eq!(
|
||||||
|
analyze(source),
|
||||||
|
Err(DustError::Analysis {
|
||||||
|
analysis_error: AnalysisError::IndexOutOfBounds {
|
||||||
|
list: Expression::list(
|
||||||
|
vec![
|
||||||
|
Expression::literal(1, (1, 2)),
|
||||||
|
Expression::literal(2, (4, 5)),
|
||||||
|
Expression::literal(3, (7, 8)),
|
||||||
|
],
|
||||||
|
(0, 9)
|
||||||
|
),
|
||||||
|
index: Expression::literal(3, (10, 11)),
|
||||||
|
index_value: 3,
|
||||||
|
length: 3,
|
||||||
|
},
|
||||||
|
source,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -665,18 +808,21 @@ mod tests {
|
|||||||
assert_eq!(analyze(source), todo!());
|
assert_eq!(analyze(source), todo!());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn length_no_arguments() {
|
|
||||||
let source = "length()";
|
|
||||||
|
|
||||||
assert_eq!(analyze(source), todo!());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn float_plus_integer() {
|
fn float_plus_integer() {
|
||||||
let source = "42.0 + 2";
|
let source = "42.0 + 2";
|
||||||
|
|
||||||
assert_eq!(analyze(source), todo!());
|
assert_eq!(
|
||||||
|
analyze(source),
|
||||||
|
Err(DustError::Analysis {
|
||||||
|
analysis_error: AnalysisError::ExpectedType {
|
||||||
|
expected: Type::Float,
|
||||||
|
actual: Type::Integer,
|
||||||
|
actual_expression: Expression::literal(2, (7, 8)),
|
||||||
|
},
|
||||||
|
source,
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -381,7 +381,9 @@ impl Expression {
|
|||||||
PrimitiveValueExpression::Integer(_) => Some(Type::Integer),
|
PrimitiveValueExpression::Integer(_) => Some(Type::Integer),
|
||||||
PrimitiveValueExpression::Float(_) => Some(Type::Float),
|
PrimitiveValueExpression::Float(_) => Some(Type::Float),
|
||||||
},
|
},
|
||||||
LiteralExpression::String(_) => Some(Type::String),
|
LiteralExpression::String(string) => Some(Type::String {
|
||||||
|
length: Some(string.len()),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
Expression::Loop(loop_expression) => match loop_expression.inner.as_ref() {
|
Expression::Loop(loop_expression) => match loop_expression.inner.as_ref() {
|
||||||
LoopExpression::For { block, .. } => block.inner.return_type(context)?,
|
LoopExpression::For { block, .. } => block.inner.return_type(context)?,
|
||||||
|
@ -14,15 +14,11 @@ use crate::{Identifier, Type, Value};
|
|||||||
pub enum BuiltInFunction {
|
pub enum BuiltInFunction {
|
||||||
// String tools
|
// String tools
|
||||||
ToString,
|
ToString,
|
||||||
LengthString,
|
|
||||||
|
|
||||||
// Integer and float tools
|
// Integer and float tools
|
||||||
IsEven,
|
IsEven,
|
||||||
IsOdd,
|
IsOdd,
|
||||||
|
|
||||||
// List tools
|
|
||||||
LengthList,
|
|
||||||
|
|
||||||
// I/O
|
// I/O
|
||||||
ReadLine,
|
ReadLine,
|
||||||
WriteLine,
|
WriteLine,
|
||||||
@ -33,8 +29,6 @@ impl BuiltInFunction {
|
|||||||
match self {
|
match self {
|
||||||
BuiltInFunction::IsEven => "is_even",
|
BuiltInFunction::IsEven => "is_even",
|
||||||
BuiltInFunction::IsOdd => "is_odd",
|
BuiltInFunction::IsOdd => "is_odd",
|
||||||
BuiltInFunction::LengthList => "length",
|
|
||||||
BuiltInFunction::LengthString => "length",
|
|
||||||
BuiltInFunction::ReadLine => "read_line",
|
BuiltInFunction::ReadLine => "read_line",
|
||||||
BuiltInFunction::ToString { .. } => "to_string",
|
BuiltInFunction::ToString { .. } => "to_string",
|
||||||
BuiltInFunction::WriteLine => "write_line",
|
BuiltInFunction::WriteLine => "write_line",
|
||||||
@ -46,8 +40,6 @@ impl BuiltInFunction {
|
|||||||
BuiltInFunction::ToString { .. } => None,
|
BuiltInFunction::ToString { .. } => None,
|
||||||
BuiltInFunction::IsEven => None,
|
BuiltInFunction::IsEven => None,
|
||||||
BuiltInFunction::IsOdd => None,
|
BuiltInFunction::IsOdd => None,
|
||||||
BuiltInFunction::LengthList => None,
|
|
||||||
BuiltInFunction::LengthString => None,
|
|
||||||
BuiltInFunction::ReadLine => None,
|
BuiltInFunction::ReadLine => None,
|
||||||
BuiltInFunction::WriteLine => None,
|
BuiltInFunction::WriteLine => None,
|
||||||
}
|
}
|
||||||
@ -58,13 +50,6 @@ impl BuiltInFunction {
|
|||||||
BuiltInFunction::ToString { .. } => Some(vec![("value".into(), Type::Any)]),
|
BuiltInFunction::ToString { .. } => Some(vec![("value".into(), Type::Any)]),
|
||||||
BuiltInFunction::IsEven => Some(vec![("value".into(), Type::Number)]),
|
BuiltInFunction::IsEven => Some(vec![("value".into(), Type::Number)]),
|
||||||
BuiltInFunction::IsOdd => Some(vec![("value".into(), Type::Number)]),
|
BuiltInFunction::IsOdd => Some(vec![("value".into(), Type::Number)]),
|
||||||
BuiltInFunction::LengthList => Some(vec![(
|
|
||||||
"value".into(),
|
|
||||||
Type::ListOf {
|
|
||||||
item_type: Box::new(Type::Any),
|
|
||||||
},
|
|
||||||
)]),
|
|
||||||
BuiltInFunction::LengthString => Some(vec![("value".into(), Type::String)]),
|
|
||||||
BuiltInFunction::ReadLine => None,
|
BuiltInFunction::ReadLine => None,
|
||||||
BuiltInFunction::WriteLine => Some(vec![("value".into(), Type::Any)]),
|
BuiltInFunction::WriteLine => Some(vec![("value".into(), Type::Any)]),
|
||||||
}
|
}
|
||||||
@ -72,12 +57,10 @@ impl BuiltInFunction {
|
|||||||
|
|
||||||
pub fn return_type(&self) -> Option<Type> {
|
pub fn return_type(&self) -> Option<Type> {
|
||||||
match self {
|
match self {
|
||||||
BuiltInFunction::ToString { .. } => Some(Type::String),
|
BuiltInFunction::ToString { .. } => Some(Type::String { length: None }),
|
||||||
BuiltInFunction::IsEven => Some(Type::Boolean),
|
BuiltInFunction::IsEven => Some(Type::Boolean),
|
||||||
BuiltInFunction::IsOdd => Some(Type::Boolean),
|
BuiltInFunction::IsOdd => Some(Type::Boolean),
|
||||||
BuiltInFunction::LengthList => Some(Type::Number),
|
BuiltInFunction::ReadLine => Some(Type::String { length: None }),
|
||||||
BuiltInFunction::LengthString => Some(Type::Number),
|
|
||||||
BuiltInFunction::ReadLine => Some(Type::String),
|
|
||||||
BuiltInFunction::WriteLine => None,
|
BuiltInFunction::WriteLine => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,20 +100,6 @@ impl BuiltInFunction {
|
|||||||
Err(BuiltInFunctionError::ExpectedInteger)
|
Err(BuiltInFunctionError::ExpectedInteger)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BuiltInFunction::LengthList => {
|
|
||||||
if let Value::List(list) = &value_arguments.unwrap()[0] {
|
|
||||||
Ok(Some(Value::Integer(list.len() as i64)))
|
|
||||||
} else {
|
|
||||||
Err(BuiltInFunctionError::ExpectedList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BuiltInFunction::LengthString => {
|
|
||||||
if let Value::String(string) = &value_arguments.unwrap()[0] {
|
|
||||||
Ok(Some(Value::Integer(string.len() as i64)))
|
|
||||||
} else {
|
|
||||||
Err(BuiltInFunctionError::ExpectedString)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BuiltInFunction::ReadLine => {
|
BuiltInFunction::ReadLine => {
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
|
|
||||||
|
@ -34,15 +34,6 @@ pub fn core_library<'a>() -> &'a Context {
|
|||||||
(0, 0),
|
(0, 0),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
|
||||||
Identifier::new("length"),
|
|
||||||
(
|
|
||||||
ContextData::VariableValue(Value::Function(Function::BuiltIn(
|
|
||||||
BuiltInFunction::LengthList,
|
|
||||||
))),
|
|
||||||
(0, 0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
Identifier::new("read_line"),
|
Identifier::new("read_line"),
|
||||||
(
|
(
|
||||||
|
@ -54,7 +54,9 @@ pub enum Type {
|
|||||||
Range {
|
Range {
|
||||||
r#type: RangeableType,
|
r#type: RangeableType,
|
||||||
},
|
},
|
||||||
String,
|
String {
|
||||||
|
length: Option<usize>,
|
||||||
|
},
|
||||||
Struct(StructType),
|
Struct(StructType),
|
||||||
Tuple(Vec<Type>),
|
Tuple(Vec<Type>),
|
||||||
}
|
}
|
||||||
@ -83,7 +85,7 @@ impl Type {
|
|||||||
| (Type::Character, Type::Character)
|
| (Type::Character, Type::Character)
|
||||||
| (Type::Float, Type::Float)
|
| (Type::Float, Type::Float)
|
||||||
| (Type::Integer, Type::Integer)
|
| (Type::Integer, Type::Integer)
|
||||||
| (Type::String, Type::String) => return Ok(()),
|
| (Type::String { .. }, Type::String { .. }) => return Ok(()),
|
||||||
(
|
(
|
||||||
Type::Generic {
|
Type::Generic {
|
||||||
concrete_type: left,
|
concrete_type: left,
|
||||||
@ -272,7 +274,7 @@ impl Display for Type {
|
|||||||
}
|
}
|
||||||
Type::Number => write!(f, "num"),
|
Type::Number => write!(f, "num"),
|
||||||
Type::Range { r#type } => write!(f, "{type} range"),
|
Type::Range { r#type } => write!(f, "{type} range"),
|
||||||
Type::String => write!(f, "str"),
|
Type::String { .. } => write!(f, "str"),
|
||||||
Type::Struct(struct_type) => write!(f, "{struct_type}"),
|
Type::Struct(struct_type) => write!(f, "{struct_type}"),
|
||||||
Type::Tuple(fields) => {
|
Type::Tuple(fields) => {
|
||||||
write!(f, "(")?;
|
write!(f, "(")?;
|
||||||
@ -358,8 +360,8 @@ impl Ord for Type {
|
|||||||
left_type.cmp(right_type)
|
left_type.cmp(right_type)
|
||||||
}
|
}
|
||||||
(Type::Range { .. }, _) => Ordering::Greater,
|
(Type::Range { .. }, _) => Ordering::Greater,
|
||||||
(Type::String, Type::String) => Ordering::Equal,
|
(Type::String { length: left }, Type::String { length: right }) => left.cmp(right),
|
||||||
(Type::String, _) => Ordering::Greater,
|
(Type::String { .. }, _) => Ordering::Greater,
|
||||||
(Type::Struct(left_struct), Type::Struct(right_struct)) => {
|
(Type::Struct(left_struct), Type::Struct(right_struct)) => {
|
||||||
left_struct.cmp(right_struct)
|
left_struct.cmp(right_struct)
|
||||||
}
|
}
|
||||||
@ -638,7 +640,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn errors() {
|
fn errors() {
|
||||||
let foo = Type::Integer;
|
let foo = Type::Integer;
|
||||||
let bar = Type::String;
|
let bar = Type::String { length: None };
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
foo.check(&bar),
|
foo.check(&bar),
|
||||||
@ -666,7 +668,7 @@ mod tests {
|
|||||||
Type::Range {
|
Type::Range {
|
||||||
r#type: RangeableType::Integer,
|
r#type: RangeableType::Integer,
|
||||||
},
|
},
|
||||||
Type::String,
|
Type::String { length: None },
|
||||||
];
|
];
|
||||||
|
|
||||||
for left in types.clone() {
|
for left in types.clone() {
|
||||||
|
@ -200,7 +200,9 @@ impl Value {
|
|||||||
r#type: rangeable_type,
|
r#type: rangeable_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::String(_) => Type::String,
|
Value::String(string) => Type::String {
|
||||||
|
length: Some(string.len()),
|
||||||
|
},
|
||||||
Value::Struct(r#struct) => match r#struct {
|
Value::Struct(r#struct) => match r#struct {
|
||||||
Struct::Unit { name } => Type::Struct(StructType::Unit { name: name.clone() }),
|
Struct::Unit { name } => Type::Struct(StructType::Unit { name: name.clone() }),
|
||||||
Struct::Tuple { name, fields } => {
|
Struct::Tuple { name, fields } => {
|
||||||
|
@ -1685,12 +1685,26 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn length() {
|
fn list_length() {
|
||||||
let input = "length([1, 2, 3])";
|
let input = "[1, 2, 3].length";
|
||||||
|
|
||||||
assert_eq!(run(input), Ok(Some(Value::Integer(3))));
|
assert_eq!(run(input), Ok(Some(Value::Integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_length() {
|
||||||
|
let input = "\"hello\".length";
|
||||||
|
|
||||||
|
assert_eq!(run(input), Ok(Some(Value::Integer(5))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn map_length() {
|
||||||
|
let input = "map { a = 42, b = 4.0 }.length";
|
||||||
|
|
||||||
|
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