CHECKPOINT: Everything builds again.
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
use crate::{
|
||||
eval::PrimitiveType,
|
||||
syntax::{Expression, Location, Program, TopLevel},
|
||||
util::scoped_map::ScopedMap,
|
||||
};
|
||||
use crate::eval::PrimitiveType;
|
||||
use crate::syntax::{Expression, Location, Program, StructureDefinition, TopLevel};
|
||||
use crate::util::scoped_map::ScopedMap;
|
||||
use crate::util::warning_result::WarningResult;
|
||||
use codespan_reporting::diagnostic::Diagnostic;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::{FunctionDefinition, Name, Type};
|
||||
|
||||
/// An error we found while validating the input program.
|
||||
///
|
||||
/// These errors indicate that we should stop trying to compile
|
||||
@@ -66,9 +68,9 @@ impl Program {
|
||||
/// This checks for things like references to variables that don't exist, for
|
||||
/// example, and generates warnings for things that are inadvisable but not
|
||||
/// actually a problem.
|
||||
pub fn validate(&self) -> (Vec<Error>, Vec<Warning>) {
|
||||
pub fn validate(raw_syntax: Vec<TopLevel>) -> WarningResult<Program, Warning, Error> {
|
||||
let mut bound_variables = ScopedMap::new();
|
||||
self.validate_with_bindings(&mut bound_variables)
|
||||
Self::validate_with_bindings(raw_syntax, &mut bound_variables)
|
||||
}
|
||||
|
||||
/// Validate that the program makes semantic sense, not just syntactic sense.
|
||||
@@ -77,118 +79,137 @@ impl Program {
|
||||
/// example, and generates warnings for things that are inadvisable but not
|
||||
/// actually a problem.
|
||||
pub fn validate_with_bindings(
|
||||
&self,
|
||||
raw_syntax: Vec<TopLevel>,
|
||||
bound_variables: &mut ScopedMap<String, Location>,
|
||||
) -> (Vec<Error>, Vec<Warning>) {
|
||||
let mut errors = vec![];
|
||||
let mut warnings = vec![];
|
||||
) -> WarningResult<Program, Warning, Error> {
|
||||
let mut functions = HashMap::new();
|
||||
let mut structures = HashMap::new();
|
||||
let mut result = WarningResult::ok(vec![]);
|
||||
let location = Location::infer_from(&raw_syntax);
|
||||
|
||||
for stmt in self.items.iter() {
|
||||
let (mut new_errors, mut new_warnings) = stmt.validate_with_bindings(bound_variables);
|
||||
errors.append(&mut new_errors);
|
||||
warnings.append(&mut new_warnings);
|
||||
for stmt in raw_syntax.into_iter() {
|
||||
match stmt {
|
||||
TopLevel::Expression(expr) => {
|
||||
let expr_result =
|
||||
expr.validate(bound_variables, &mut structures, &mut functions);
|
||||
result = result.merge_with(expr_result, |mut previous, current| {
|
||||
previous.push(current);
|
||||
Ok(previous)
|
||||
});
|
||||
}
|
||||
|
||||
TopLevel::Structure(loc, name, fields) => {
|
||||
let definition = StructureDefinition::new(
|
||||
loc,
|
||||
name.clone(),
|
||||
fields.into_iter().map(|(n, t)| (n, Some(t))).collect(),
|
||||
);
|
||||
|
||||
structures.insert(name, definition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(errors, warnings)
|
||||
}
|
||||
}
|
||||
|
||||
impl TopLevel {
|
||||
/// Validate that the top level item makes semantic sense, not just syntactic
|
||||
/// sense.
|
||||
///
|
||||
/// This checks for things like references to variables that don't exist, for
|
||||
/// example, and generates warnings for thins that are inadvisable but not
|
||||
/// actually a problem.
|
||||
pub fn validate(&self) -> (Vec<Error>, Vec<Warning>) {
|
||||
let mut bound_variables = ScopedMap::new();
|
||||
self.validate_with_bindings(&mut bound_variables)
|
||||
}
|
||||
|
||||
/// Validate that the top level item makes semantic sense, not just syntactic
|
||||
/// sense.
|
||||
///
|
||||
/// This checks for things like references to variables that don't exist, for
|
||||
/// example, and generates warnings for thins that are inadvisable but not
|
||||
/// actually a problem.
|
||||
pub fn validate_with_bindings(
|
||||
&self,
|
||||
bound_variables: &mut ScopedMap<String, Location>,
|
||||
) -> (Vec<Error>, Vec<Warning>) {
|
||||
match self {
|
||||
TopLevel::Expression(expr) => expr.validate(bound_variables),
|
||||
TopLevel::Structure(_, _, _) => (vec![], vec![]),
|
||||
}
|
||||
result.map(move |exprs| Program {
|
||||
functions,
|
||||
structures,
|
||||
body: Expression::Block(location, exprs),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
fn validate(
|
||||
&self,
|
||||
self,
|
||||
variable_map: &mut ScopedMap<String, Location>,
|
||||
) -> (Vec<Error>, Vec<Warning>) {
|
||||
structure_map: &mut HashMap<Name, StructureDefinition>,
|
||||
function_map: &mut HashMap<Name, FunctionDefinition>,
|
||||
) -> WarningResult<Expression, Warning, Error> {
|
||||
match self {
|
||||
Expression::Value(_, _) => (vec![], vec![]),
|
||||
Expression::Constructor(_, _, fields) => {
|
||||
let mut errors = vec![];
|
||||
let mut warnings = vec![];
|
||||
Expression::Value(_, _) => WarningResult::ok(self),
|
||||
|
||||
for (_, expr) in fields.iter() {
|
||||
let (mut e, mut w) = expr.validate(variable_map);
|
||||
errors.append(&mut e);
|
||||
warnings.append(&mut w);
|
||||
Expression::Constructor(location, name, fields) => {
|
||||
let mut result = WarningResult::ok(vec![]);
|
||||
|
||||
for (name, expr) in fields.into_iter() {
|
||||
let expr_result = expr.validate(variable_map, structure_map, function_map);
|
||||
result = result.merge_with(expr_result, move |mut fields, new_expr| {
|
||||
fields.push((name, new_expr));
|
||||
Ok(fields)
|
||||
});
|
||||
}
|
||||
|
||||
(errors, warnings)
|
||||
result.map(move |fields| Expression::Constructor(location, name, fields))
|
||||
}
|
||||
Expression::Reference(var) if variable_map.contains_key(&var.original_name().to_string()) => (vec![], vec![]),
|
||||
Expression::Reference(var) => (
|
||||
vec![Error::UnboundVariable(var.location().clone(), var.original_name().to_string())],
|
||||
vec![],
|
||||
),
|
||||
Expression::FieldRef(_, exp, _) => exp.validate(variable_map),
|
||||
|
||||
Expression::Reference(ref var)
|
||||
if variable_map.contains_key(&var.original_name().to_string()) =>
|
||||
{
|
||||
WarningResult::ok(self)
|
||||
}
|
||||
Expression::Reference(var) => WarningResult::err(Error::UnboundVariable(
|
||||
var.location().clone(),
|
||||
var.original_name().to_string(),
|
||||
)),
|
||||
|
||||
Expression::FieldRef(location, exp, field) => exp
|
||||
.validate(variable_map, structure_map, function_map)
|
||||
.map(|x| Expression::FieldRef(location, Box::new(x), field)),
|
||||
|
||||
Expression::Cast(location, t, expr) => {
|
||||
let (mut errs, warns) = expr.validate(variable_map);
|
||||
let mut expr_result = expr.validate(variable_map, structure_map, function_map);
|
||||
|
||||
if PrimitiveType::from_str(t).is_err() {
|
||||
errs.push(Error::UnknownType(location.clone(), t.clone()))
|
||||
if PrimitiveType::from_str(&t).is_err() {
|
||||
expr_result.add_error(Error::UnknownType(location.clone(), t.clone()));
|
||||
}
|
||||
|
||||
(errs, warns)
|
||||
expr_result.map(|e| Expression::Cast(location, t, Box::new(e)))
|
||||
}
|
||||
Expression::Primitive(_, _) => (vec![], vec![]),
|
||||
Expression::Call(_, func, args) => {
|
||||
let (mut errors, mut warnings) = func.validate(variable_map);
|
||||
|
||||
for arg in args.iter() {
|
||||
let (mut e, mut w) = arg.validate(variable_map);
|
||||
errors.append(&mut e);
|
||||
warnings.append(&mut w);
|
||||
// FIXME: Check for valid primitives here!!
|
||||
Expression::Primitive(_, _) => WarningResult::ok(self),
|
||||
|
||||
Expression::Call(loc, func, args) => {
|
||||
let mut result = func
|
||||
.validate(variable_map, structure_map, function_map)
|
||||
.map(|x| (x, vec![]));
|
||||
|
||||
for arg in args.into_iter() {
|
||||
let expr_result = arg.validate(variable_map, structure_map, function_map);
|
||||
result =
|
||||
result.merge_with(expr_result, |(func, mut previous_args), new_arg| {
|
||||
previous_args.push(new_arg);
|
||||
Ok((func, previous_args))
|
||||
});
|
||||
}
|
||||
|
||||
(errors, warnings)
|
||||
result.map(|(func, args)| Expression::Call(loc, Box::new(func), args))
|
||||
}
|
||||
Expression::Block(_, stmts) => {
|
||||
let mut errors = vec![];
|
||||
let mut warnings = vec![];
|
||||
|
||||
for stmt in stmts.iter() {
|
||||
let (mut local_errors, mut local_warnings) = stmt.validate(variable_map);
|
||||
Expression::Block(loc, stmts) => {
|
||||
let mut result = WarningResult::ok(vec![]);
|
||||
|
||||
errors.append(&mut local_errors);
|
||||
warnings.append(&mut local_warnings);
|
||||
for stmt in stmts.into_iter() {
|
||||
let stmt_result = stmt.validate(variable_map, structure_map, function_map);
|
||||
result = result.merge_with(stmt_result, |mut stmts, stmt| {
|
||||
stmts.push(stmt);
|
||||
Ok(stmts)
|
||||
});
|
||||
}
|
||||
|
||||
(errors, warnings)
|
||||
result.map(|stmts| Expression::Block(loc, stmts))
|
||||
}
|
||||
|
||||
Expression::Binding(loc, var, val) => {
|
||||
// we're going to make the decision that a variable is not bound in the right
|
||||
// hand side of its binding, which makes a lot of things easier. So we'll just
|
||||
// immediately check the expression, and go from there.
|
||||
let (errors, mut warnings) = val.validate(variable_map);
|
||||
let mut result = val.validate(variable_map, structure_map, function_map);
|
||||
|
||||
if let Some(original_binding_site) = variable_map.get(&var.original_name().to_string()) {
|
||||
warnings.push(Warning::ShadowedVariable(
|
||||
if let Some(original_binding_site) =
|
||||
variable_map.get(&var.original_name().to_string())
|
||||
{
|
||||
result.add_warning(Warning::ShadowedVariable(
|
||||
original_binding_site.clone(),
|
||||
loc.clone(),
|
||||
var.to_string(),
|
||||
@@ -197,19 +218,118 @@ impl Expression {
|
||||
variable_map.insert(var.to_string(), loc.clone());
|
||||
}
|
||||
|
||||
(errors, warnings)
|
||||
result.map(|val| Expression::Binding(loc, var, Box::new(val)))
|
||||
}
|
||||
Expression::Function(_, name, arguments, _, body) => {
|
||||
if let Some(name) = name {
|
||||
|
||||
Expression::Function(loc, name, mut arguments, return_type, body) => {
|
||||
let mut result = WarningResult::ok(());
|
||||
|
||||
// first we should check for shadowing
|
||||
for new_name in name.iter().chain(arguments.iter().map(|x| &x.0)) {
|
||||
if let Some(original_site) = variable_map.get(new_name.original_name()) {
|
||||
result.add_warning(Warning::ShadowedVariable(
|
||||
original_site.clone(),
|
||||
loc.clone(),
|
||||
new_name.original_name().to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// the function name is now available in our current scope, if the function was given one
|
||||
if let Some(name) = &name {
|
||||
variable_map.insert(name.original_name().to_string(), name.location().clone());
|
||||
}
|
||||
|
||||
// the arguments are available in a new scope, which we will use to validate the function
|
||||
// body
|
||||
variable_map.new_scope();
|
||||
for (arg, _) in arguments.iter() {
|
||||
variable_map.insert(arg.original_name().to_string(), arg.location().clone());
|
||||
}
|
||||
let result = body.validate(variable_map);
|
||||
|
||||
let body_result = body.validate(variable_map, structure_map, function_map);
|
||||
variable_map.release_scope();
|
||||
result
|
||||
|
||||
body_result.merge_with(result, move |mut body, _| {
|
||||
// figure out what, if anything, needs to be in the closure for this function.
|
||||
let mut free_variables = body.free_variables();
|
||||
for (n, _) in arguments.iter() {
|
||||
free_variables.remove(n);
|
||||
}
|
||||
// generate a new name for the closure type we're about to create
|
||||
let closure_type_name = Name::located_gensym(
|
||||
loc.clone(),
|
||||
name.as_ref().map(Name::original_name).unwrap_or("closure_"),
|
||||
);
|
||||
// ... and then create a structure type that has all of the free variables
|
||||
// in it
|
||||
let closure_type = StructureDefinition::new(
|
||||
loc.clone(),
|
||||
closure_type_name.clone(),
|
||||
free_variables.iter().map(|x| (x.clone(), None)).collect(),
|
||||
);
|
||||
// this will become the first argument of the function, so name it and add
|
||||
// it to the argument list.
|
||||
let closure_arg = Name::gensym("__closure_arg");
|
||||
arguments.insert(
|
||||
0,
|
||||
(
|
||||
closure_arg.clone(),
|
||||
Some(Type::Named(closure_type_name.clone())),
|
||||
),
|
||||
);
|
||||
// Now make a map from the old free variable names to references into
|
||||
// our closure argument
|
||||
let rebinds = free_variables
|
||||
.into_iter()
|
||||
.map(|n| {
|
||||
(
|
||||
n.clone(),
|
||||
Expression::FieldRef(
|
||||
n.location().clone(),
|
||||
Box::new(Expression::Reference(closure_arg.clone())),
|
||||
n,
|
||||
),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(Name, Expression)>>();
|
||||
let mut rebind_map = rebinds.iter().cloned().collect();
|
||||
// and replace all the references in the function with this map
|
||||
body.replace_references(&mut rebind_map);
|
||||
// OK! This function definitely needs a name; if the user didn't give
|
||||
// it one, we'll do so.
|
||||
let function_name =
|
||||
name.unwrap_or_else(|| Name::located_gensym(loc.clone(), "function"));
|
||||
// And finally, we can make the function definition and insert it into our global
|
||||
// list along with the new closure type.
|
||||
let function = FunctionDefinition::new(
|
||||
function_name.clone(),
|
||||
arguments.clone(),
|
||||
return_type.clone(),
|
||||
body,
|
||||
);
|
||||
|
||||
structure_map.insert(closure_type_name.clone(), closure_type);
|
||||
function_map.insert(function_name.clone(), function);
|
||||
|
||||
// And the result of this function is a call to a primitive that generates
|
||||
// the closure value in some sort of reasonable way.
|
||||
Ok(Expression::Call(
|
||||
Location::manufactured(),
|
||||
Box::new(Expression::Primitive(
|
||||
Location::manufactured(),
|
||||
Name::new("<closure>", Location::manufactured()),
|
||||
)),
|
||||
vec![
|
||||
Expression::Reference(function_name),
|
||||
Expression::Constructor(
|
||||
Location::manufactured(),
|
||||
closure_type_name,
|
||||
rebinds,
|
||||
),
|
||||
],
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,16 +337,19 @@ impl Expression {
|
||||
|
||||
#[test]
|
||||
fn cast_checks_are_reasonable() {
|
||||
let good_stmt = TopLevel::parse(0, "x = <u16>4u8;").expect("valid test case");
|
||||
let (good_errs, good_warns) = good_stmt.validate();
|
||||
let mut variable_map = ScopedMap::new();
|
||||
let mut structure_map = HashMap::new();
|
||||
let mut function_map = HashMap::new();
|
||||
|
||||
assert!(good_errs.is_empty());
|
||||
assert!(good_warns.is_empty());
|
||||
let good_stmt = Expression::parse(0, "x = <u16>4u8;").expect("valid test case");
|
||||
let result_good = good_stmt.validate(&mut variable_map, &mut structure_map, &mut function_map);
|
||||
|
||||
let bad_stmt = TopLevel::parse(0, "x = <apple>4u8;").expect("valid test case");
|
||||
let (bad_errs, bad_warns) = bad_stmt.validate();
|
||||
assert!(result_good.is_ok());
|
||||
assert!(result_good.warnings().is_empty());
|
||||
|
||||
assert!(bad_warns.is_empty());
|
||||
assert_eq!(bad_errs.len(), 1);
|
||||
assert!(matches!(bad_errs[0], Error::UnknownType(_, ref x) if x == "apple"));
|
||||
let bad_stmt = Expression::parse(0, "x = <apple>4u8;").expect("valid test case");
|
||||
let result_err = bad_stmt.validate(&mut variable_map, &mut structure_map, &mut function_map);
|
||||
|
||||
assert!(result_err.is_err());
|
||||
assert!(result_err.warnings().is_empty());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user