Fix tests; Implement type generics
This commit is contained in:
parent
d53ddd07eb
commit
cddf199156
@ -44,6 +44,9 @@ impl AbstractNode for Expression {
|
||||
fn validate(&self, context: &mut Context, manage_memory: bool) -> Result<(), ValidationError> {
|
||||
match self {
|
||||
Expression::As(r#as) => r#as.node.validate(context, manage_memory),
|
||||
Expression::BuiltInFunctionCall(built_in_function_call) => {
|
||||
built_in_function_call.node.validate(context, manage_memory)
|
||||
}
|
||||
Expression::FunctionCall(function_call) => {
|
||||
function_call.node.validate(context, manage_memory)
|
||||
}
|
||||
@ -68,9 +71,6 @@ impl AbstractNode for Expression {
|
||||
Expression::Logic(logic) => logic.node.validate(context, manage_memory),
|
||||
Expression::Math(math) => math.node.validate(context, manage_memory),
|
||||
Expression::Value(value_node) => value_node.node.validate(context, manage_memory),
|
||||
Expression::BuiltInFunctionCall(built_in_function_call) => {
|
||||
built_in_function_call.node.validate(context, manage_memory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ pub mod assignment;
|
||||
pub mod async_block;
|
||||
pub mod block;
|
||||
pub mod built_in_function_call;
|
||||
pub mod expression;
|
||||
pub mod function_call;
|
||||
pub mod if_else;
|
||||
pub mod list_index;
|
||||
@ -15,7 +16,6 @@ pub mod structure_definition;
|
||||
pub mod r#type;
|
||||
pub mod type_alias;
|
||||
pub mod type_constructor;
|
||||
pub mod value_expression;
|
||||
pub mod value_node;
|
||||
pub mod r#while;
|
||||
|
||||
@ -29,6 +29,7 @@ pub use self::{
|
||||
async_block::AsyncBlock,
|
||||
block::Block,
|
||||
built_in_function_call::BuiltInFunctionCall,
|
||||
expression::Expression,
|
||||
function_call::FunctionCall,
|
||||
if_else::IfElse,
|
||||
list_index::ListIndex,
|
||||
@ -43,7 +44,6 @@ pub use self::{
|
||||
structure_definition::StructureDefinition,
|
||||
type_alias::TypeAssignment,
|
||||
type_constructor::{FunctionTypeConstructor, ListTypeConstructor, TypeConstructor},
|
||||
value_expression::Expression,
|
||||
value_node::ValueNode,
|
||||
};
|
||||
|
||||
|
@ -21,6 +21,7 @@ pub enum Type {
|
||||
value_parameters: Vec<(Identifier, Type)>,
|
||||
return_type: Box<Type>,
|
||||
},
|
||||
Generic(Option<Box<Type>>),
|
||||
Integer,
|
||||
List {
|
||||
length: usize,
|
||||
@ -49,8 +50,19 @@ impl Type {
|
||||
| (Type::None, Type::None)
|
||||
| (Type::Range, Type::Range)
|
||||
| (Type::String, Type::String) => return Ok(()),
|
||||
(Type::Generic(left), Type::Generic(right)) => match (left, right) {
|
||||
(Some(left), Some(right)) => {
|
||||
if left.check(&right).is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
(None, None) => {
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
(Type::ListOf(left), Type::ListOf(right)) => {
|
||||
if let Ok(()) = left.check(&right) {
|
||||
if left.check(&right).is_ok() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@ -195,6 +207,13 @@ impl Display for Type {
|
||||
Type::Any => write!(f, "any"),
|
||||
Type::Boolean => write!(f, "bool"),
|
||||
Type::Float => write!(f, "float"),
|
||||
Type::Generic(type_option) => {
|
||||
if let Some(concrete_type) = type_option {
|
||||
write!(f, "implied to be {concrete_type}")
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Type::Integer => write!(f, "int"),
|
||||
Type::List { length, item_type } => write!(f, "[{length}; {}]", item_type),
|
||||
Type::ListOf(item_type) => write!(f, "list({})", item_type),
|
||||
|
@ -26,7 +26,7 @@ pub enum ValueNode {
|
||||
name: WithPosition<Identifier>,
|
||||
fields: Vec<(WithPosition<Identifier>, Expression)>,
|
||||
},
|
||||
Parsed {
|
||||
Function {
|
||||
type_parameters: Option<Vec<Identifier>>,
|
||||
value_parameters: Vec<(Identifier, TypeConstructor)>,
|
||||
return_type: TypeConstructor,
|
||||
@ -57,8 +57,8 @@ impl AbstractNode for ValueNode {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let ValueNode::Parsed {
|
||||
type_parameters: _,
|
||||
if let ValueNode::Function {
|
||||
type_parameters,
|
||||
value_parameters,
|
||||
return_type,
|
||||
body,
|
||||
@ -66,6 +66,12 @@ impl AbstractNode for ValueNode {
|
||||
{
|
||||
let mut function_context = Context::new(Some(&context));
|
||||
|
||||
if let Some(type_parameters) = type_parameters {
|
||||
for identifier in type_parameters {
|
||||
function_context.set_type(identifier.clone(), Type::Generic(None))?;
|
||||
}
|
||||
}
|
||||
|
||||
for (identifier, type_constructor) in value_parameters {
|
||||
let r#type = type_constructor.clone().construct(&function_context)?;
|
||||
|
||||
@ -175,18 +181,14 @@ impl AbstractNode for ValueNode {
|
||||
}
|
||||
ValueNode::Range(range) => Value::range(range),
|
||||
ValueNode::String(string) => Value::string(string),
|
||||
ValueNode::Parsed {
|
||||
ValueNode::Function {
|
||||
type_parameters,
|
||||
value_parameters: constructors,
|
||||
return_type,
|
||||
body,
|
||||
} => {
|
||||
let type_parameters = type_parameters.map(|parameter_list| {
|
||||
parameter_list
|
||||
.into_iter()
|
||||
.map(|parameter| parameter)
|
||||
.collect()
|
||||
});
|
||||
let type_parameters =
|
||||
type_parameters.map(|parameter_list| parameter_list.into_iter().collect());
|
||||
let mut value_parameters = Vec::with_capacity(constructors.len());
|
||||
|
||||
for (identifier, constructor) in constructors {
|
||||
@ -263,13 +265,13 @@ impl Ord for ValueNode {
|
||||
(String(left), String(right)) => left.cmp(right),
|
||||
(String(_), _) => Ordering::Greater,
|
||||
(
|
||||
Parsed {
|
||||
Function {
|
||||
type_parameters: left_type_arguments,
|
||||
value_parameters: left_parameters,
|
||||
return_type: left_return,
|
||||
body: left_body,
|
||||
},
|
||||
Parsed {
|
||||
Function {
|
||||
type_parameters: right_type_arguments,
|
||||
value_parameters: right_parameters,
|
||||
return_type: right_return,
|
||||
@ -296,7 +298,7 @@ impl Ord for ValueNode {
|
||||
parameter_cmp
|
||||
}
|
||||
}
|
||||
(Parsed { .. }, _) => Ordering::Greater,
|
||||
(Function { .. }, _) => Ordering::Greater,
|
||||
(
|
||||
Structure {
|
||||
name: left_name,
|
||||
@ -337,7 +339,7 @@ impl ExpectedType for ValueNode {
|
||||
ValueNode::Map(_) => Type::Map,
|
||||
ValueNode::Range(_) => Type::Range,
|
||||
ValueNode::String(_) => Type::String,
|
||||
ValueNode::Parsed {
|
||||
ValueNode::Function {
|
||||
type_parameters,
|
||||
value_parameters,
|
||||
return_type,
|
||||
|
@ -26,6 +26,10 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_child<'b>(&'b self) -> Context<'b> {
|
||||
Context::new(Some(self))
|
||||
}
|
||||
|
||||
pub fn inner(
|
||||
&self,
|
||||
) -> Result<RwLockReadGuard<BTreeMap<Identifier, (VariableData, UsageData)>>, RwLockPoisonError>
|
||||
|
@ -10,16 +10,16 @@ use crate::{
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
Parse {
|
||||
expected: String,
|
||||
span: (usize, usize),
|
||||
found: Option<String>,
|
||||
},
|
||||
Lex {
|
||||
expected: String,
|
||||
span: (usize, usize),
|
||||
reason: String,
|
||||
},
|
||||
Parse {
|
||||
expected: String,
|
||||
span: (usize, usize),
|
||||
found: Option<String>,
|
||||
},
|
||||
Runtime {
|
||||
error: RuntimeError,
|
||||
position: SourcePosition,
|
||||
|
@ -234,14 +234,10 @@ impl InterpreterError {
|
||||
} => {
|
||||
let description = if expected.is_empty() {
|
||||
"Invalid token.".to_string()
|
||||
} else {
|
||||
} else {
|
||||
format!("Expected {expected}.")
|
||||
};
|
||||
|
||||
let label_message = format!(
|
||||
"{} is not valid in this position.",
|
||||
found.unwrap_or_else(|| String::with_capacity(0))
|
||||
);
|
||||
let found = found.unwrap_or_else(|| "End of input".to_string());
|
||||
|
||||
(
|
||||
Report::build(
|
||||
@ -252,7 +248,7 @@ impl InterpreterError {
|
||||
.with_message(description)
|
||||
.with_label(
|
||||
Label::new((self.source_id.clone(), span.0..span.1))
|
||||
.with_message(label_message)
|
||||
.with_message(format!("{found} is not valid in this position."))
|
||||
.with_color(Color::Red),
|
||||
),
|
||||
None,
|
||||
|
@ -246,7 +246,7 @@ pub fn parser<'src>(
|
||||
.map_with(
|
||||
|(((type_parameters, value_parameters), return_type), body), state| {
|
||||
Expression::Value(
|
||||
ValueNode::Parsed {
|
||||
ValueNode::Function {
|
||||
type_parameters,
|
||||
value_parameters,
|
||||
return_type,
|
||||
@ -640,6 +640,73 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
// Reuse these tests when structures are reimplemented
|
||||
// #[test]
|
||||
// fn structure_instance() {
|
||||
// assert_eq!(
|
||||
// parse(
|
||||
// &lex("
|
||||
// Foo {
|
||||
// bar = 42,
|
||||
// baz = 'hiya',
|
||||
// }
|
||||
// ")
|
||||
// .unwrap()
|
||||
// )
|
||||
// .unwrap()[0],
|
||||
// Statement::Expression(Expression::Value(
|
||||
// ValueNode::Structure {
|
||||
// name: Identifier::new("Foo").with_position((21, 24)),
|
||||
// fields: vec![
|
||||
// (
|
||||
// Identifier::new("bar").with_position((0, 0)),
|
||||
// Expression::Value(ValueNode::Integer(42).with_position((57, 59)))
|
||||
// ),
|
||||
// (
|
||||
// Identifier::new("baz").with_position((0, 0)),
|
||||
// Expression::Value(
|
||||
// ValueNode::String("hiya".to_string()).with_position((91, 97))
|
||||
// )
|
||||
// ),
|
||||
// ]
|
||||
// }
|
||||
// .with_position((21, 120))
|
||||
// ))
|
||||
// )
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn structure_definition() {
|
||||
// assert_eq!(
|
||||
// parse(
|
||||
// &lex("
|
||||
// struct Foo {
|
||||
// bar : int,
|
||||
// baz : str,
|
||||
// }
|
||||
// ")
|
||||
// .unwrap()
|
||||
// )
|
||||
// .unwrap()[0],
|
||||
// Statement::StructureDefinition(
|
||||
// StructureDefinition::new(
|
||||
// Identifier::new("Foo"),
|
||||
// vec![
|
||||
// (
|
||||
// Identifier::new("bar"),
|
||||
// TypeConstructor::Type(Type::Integer.with_position((64, 67)))
|
||||
// ),
|
||||
// (
|
||||
// Identifier::new("baz"),
|
||||
// TypeConstructor::Type(Type::String.with_position((99, 102)))
|
||||
// ),
|
||||
// ]
|
||||
// )
|
||||
// .with_position((21, 125))
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn type_alias() {
|
||||
assert_eq!(
|
||||
@ -743,72 +810,6 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structure_instance() {
|
||||
assert_eq!(
|
||||
parse(
|
||||
&lex("
|
||||
Foo {
|
||||
bar = 42,
|
||||
baz = 'hiya',
|
||||
}
|
||||
")
|
||||
.unwrap()
|
||||
)
|
||||
.unwrap()[0],
|
||||
Statement::Expression(Expression::Value(
|
||||
ValueNode::Structure {
|
||||
name: Identifier::new("Foo").with_position((21, 24)),
|
||||
fields: vec![
|
||||
(
|
||||
Identifier::new("bar").with_position((0, 0)),
|
||||
Expression::Value(ValueNode::Integer(42).with_position((57, 59)))
|
||||
),
|
||||
(
|
||||
Identifier::new("baz").with_position((0, 0)),
|
||||
Expression::Value(
|
||||
ValueNode::String("hiya".to_string()).with_position((91, 97))
|
||||
)
|
||||
),
|
||||
]
|
||||
}
|
||||
.with_position((21, 120))
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structure_definition() {
|
||||
assert_eq!(
|
||||
parse(
|
||||
&lex("
|
||||
struct Foo {
|
||||
bar : int,
|
||||
baz : str,
|
||||
}
|
||||
")
|
||||
.unwrap()
|
||||
)
|
||||
.unwrap()[0],
|
||||
Statement::StructureDefinition(
|
||||
StructureDefinition::new(
|
||||
Identifier::new("Foo"),
|
||||
vec![
|
||||
(
|
||||
Identifier::new("bar"),
|
||||
TypeConstructor::Type(Type::Integer.with_position((64, 67)))
|
||||
),
|
||||
(
|
||||
Identifier::new("baz"),
|
||||
TypeConstructor::Type(Type::String.with_position((99, 102)))
|
||||
),
|
||||
]
|
||||
)
|
||||
.with_position((21, 125))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_index() {
|
||||
assert_eq!(
|
||||
@ -912,23 +913,23 @@ mod tests {
|
||||
#[test]
|
||||
fn list_of_type() {
|
||||
assert_eq!(
|
||||
parse(&lex("foobar : list(bool) = [true]").unwrap()).unwrap()[0],
|
||||
parse(&lex("foobar : [bool] = [true]").unwrap()).unwrap()[0],
|
||||
Statement::Assignment(
|
||||
Assignment::new(
|
||||
Identifier::new("foobar").with_position((0, 6)),
|
||||
Some(TypeConstructor::ListOf(
|
||||
Box::new(TypeConstructor::Type(Type::Boolean.with_position((9, 19))))
|
||||
.with_position((0, 0))
|
||||
Box::new(TypeConstructor::Type(Type::Boolean.with_position((10, 14))))
|
||||
.with_position((9, 15))
|
||||
)),
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Value(
|
||||
ValueNode::List(vec![Expression::Value(
|
||||
ValueNode::Boolean(true).with_position((23, 27))
|
||||
ValueNode::Boolean(true).with_position((19, 23))
|
||||
)])
|
||||
.with_position((22, 28))
|
||||
.with_position((18, 24))
|
||||
))
|
||||
)
|
||||
.with_position((0, 28))
|
||||
.with_position((0, 24))
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -1009,7 +1010,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(&lex("fn () -> int { 0 }").unwrap()).unwrap()[0],
|
||||
Statement::Expression(Expression::Value(
|
||||
ValueNode::Parsed {
|
||||
ValueNode::Function {
|
||||
type_parameters: None,
|
||||
value_parameters: vec![],
|
||||
return_type: TypeConstructor::Type(Type::Integer.with_position((9, 12))),
|
||||
@ -1025,7 +1026,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(&lex("fn (x: int) -> int { x }").unwrap()).unwrap()[0],
|
||||
Statement::Expression(Expression::Value(
|
||||
ValueNode::Parsed {
|
||||
ValueNode::Function {
|
||||
type_parameters: None,
|
||||
value_parameters: vec![(
|
||||
Identifier::new("x"),
|
||||
@ -1047,7 +1048,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
parse(&lex("fn T, U (x: T, y: U) -> T { x }").unwrap()).unwrap()[0],
|
||||
Statement::Expression(Expression::Value(
|
||||
ValueNode::Parsed {
|
||||
ValueNode::Function {
|
||||
type_parameters: Some(vec![Identifier::new("T"), Identifier::new("U"),]),
|
||||
value_parameters: vec![
|
||||
(
|
||||
|
@ -1,5 +1,5 @@
|
||||
json = {
|
||||
parse = fn (T)(input: str) -> T {
|
||||
parse = fn T (input: str) -> T {
|
||||
JSON_PARSE T input
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user