2018-11-03 17:02:41 +01:00
|
|
|
use std::{collections::HashSet, iter};
|
2017-07-15 01:54:54 +02:00
|
|
|
|
2018-11-03 17:02:41 +01:00
|
|
|
use proc_macro2::Span;
|
2019-05-21 14:43:05 +02:00
|
|
|
use syn::parse;
|
2017-07-15 01:54:54 +02:00
|
|
|
|
2018-12-16 19:13:22 +01:00
|
|
|
use crate::syntax::App;
|
2017-07-15 01:54:54 +02:00
|
|
|
|
2018-11-03 17:02:41 +01:00
|
|
|
pub fn app(app: &App) -> parse::Result<()> {
|
|
|
|
// Check that all referenced resources have been declared
|
|
|
|
for res in app
|
|
|
|
.idle
|
|
|
|
.as_ref()
|
2018-12-16 19:13:22 +01:00
|
|
|
.map(|idle| -> Box<dyn Iterator<Item = _>> { Box::new(idle.args.resources.iter()) })
|
2018-11-03 17:02:41 +01:00
|
|
|
.unwrap_or_else(|| Box::new(iter::empty()))
|
|
|
|
.chain(&app.init.args.resources)
|
|
|
|
.chain(app.exceptions.values().flat_map(|e| &e.args.resources))
|
|
|
|
.chain(app.interrupts.values().flat_map(|i| &i.args.resources))
|
|
|
|
.chain(app.tasks.values().flat_map(|t| &t.args.resources))
|
|
|
|
{
|
|
|
|
if !app.resources.contains_key(res) {
|
|
|
|
return Err(parse::Error::new(
|
|
|
|
res.span(),
|
|
|
|
"this resource has NOT been declared",
|
|
|
|
));
|
|
|
|
}
|
2017-07-28 00:08:42 +02:00
|
|
|
}
|
|
|
|
|
2018-11-03 17:02:41 +01:00
|
|
|
// Check that late resources have not been assigned to `init`
|
|
|
|
for res in &app.init.args.resources {
|
|
|
|
if app.resources.get(res).unwrap().expr.is_none() {
|
|
|
|
return Err(parse::Error::new(
|
|
|
|
res.span(),
|
|
|
|
"late resources can NOT be assigned to `init`",
|
|
|
|
));
|
2017-07-28 00:08:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-21 14:43:05 +02:00
|
|
|
if app.resources.iter().any(|(_, res)| res.expr.is_none()) {
|
|
|
|
// Check that `init` returns `LateResources` if there's any declared late resource
|
|
|
|
if !app.init.returns_late_resources {
|
|
|
|
return Err(parse::Error::new(
|
|
|
|
app.init.span,
|
|
|
|
"late resources have been specified so `init` must return `init::LateResources`",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
} else if app.init.returns_late_resources {
|
|
|
|
// If there are no late resources the signature should be `fn(init::Context)`
|
|
|
|
if app.init.returns_late_resources {
|
|
|
|
return Err(parse::Error::new(
|
|
|
|
app.init.span,
|
|
|
|
"`init` signature must be `fn(init::Context)` if there are no late resources",
|
|
|
|
));
|
|
|
|
}
|
2018-11-03 17:02:41 +01:00
|
|
|
}
|
2017-07-15 01:54:54 +02:00
|
|
|
|
2018-11-03 17:02:41 +01:00
|
|
|
// Check that all referenced tasks have been declared
|
|
|
|
for task in app
|
|
|
|
.idle
|
|
|
|
.as_ref()
|
2018-12-16 19:13:22 +01:00
|
|
|
.map(|idle| -> Box<dyn Iterator<Item = _>> {
|
2018-11-03 17:02:41 +01:00
|
|
|
Box::new(idle.args.schedule.iter().chain(&idle.args.spawn))
|
|
|
|
})
|
|
|
|
.unwrap_or_else(|| Box::new(iter::empty()))
|
|
|
|
.chain(&app.init.args.schedule)
|
|
|
|
.chain(&app.init.args.spawn)
|
|
|
|
.chain(
|
|
|
|
app.exceptions
|
|
|
|
.values()
|
|
|
|
.flat_map(|e| e.args.schedule.iter().chain(&e.args.spawn)),
|
|
|
|
)
|
|
|
|
.chain(
|
|
|
|
app.interrupts
|
|
|
|
.values()
|
|
|
|
.flat_map(|i| i.args.schedule.iter().chain(&i.args.spawn)),
|
|
|
|
)
|
|
|
|
.chain(
|
|
|
|
app.tasks
|
|
|
|
.values()
|
|
|
|
.flat_map(|t| t.args.schedule.iter().chain(&t.args.spawn)),
|
2018-12-16 19:10:36 +01:00
|
|
|
)
|
|
|
|
{
|
2018-11-03 17:02:41 +01:00
|
|
|
if !app.tasks.contains_key(task) {
|
|
|
|
return Err(parse::Error::new(
|
|
|
|
task.span(),
|
|
|
|
"this task has NOT been declared",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
2017-07-15 01:54:54 +02:00
|
|
|
|
2018-11-03 17:02:41 +01:00
|
|
|
// Check that there are enough free interrupts to dispatch all tasks
|
|
|
|
let ndispatchers = app
|
|
|
|
.tasks
|
|
|
|
.values()
|
|
|
|
.map(|t| t.args.priority)
|
|
|
|
.collect::<HashSet<_>>()
|
|
|
|
.len();
|
|
|
|
if ndispatchers > app.free_interrupts.len() {
|
|
|
|
return Err(parse::Error::new(
|
|
|
|
Span::call_site(),
|
|
|
|
&*format!(
|
|
|
|
"{} free interrupt{} (`extern {{ .. }}`) {} required to dispatch all soft tasks",
|
|
|
|
ndispatchers,
|
|
|
|
if ndispatchers > 1 { "s" } else { "" },
|
|
|
|
if ndispatchers > 1 { "are" } else { "is" },
|
|
|
|
),
|
|
|
|
));
|
|
|
|
}
|
2017-07-28 00:08:42 +02:00
|
|
|
|
2018-11-03 17:02:41 +01:00
|
|
|
// Check that free interrupts are not being used
|
2019-02-26 23:25:16 +01:00
|
|
|
for (handler, interrupt) in &app.interrupts {
|
|
|
|
let name = interrupt.args.binds(handler);
|
2019-02-23 21:54:56 +01:00
|
|
|
|
|
|
|
if app.free_interrupts.contains_key(name) {
|
2018-11-03 17:02:41 +01:00
|
|
|
return Err(parse::Error::new(
|
2019-02-23 21:54:56 +01:00
|
|
|
name.span(),
|
2018-11-03 17:02:41 +01:00
|
|
|
"free interrupts (`extern { .. }`) can't be used as interrupt handlers",
|
|
|
|
));
|
2017-07-28 00:08:42 +02:00
|
|
|
}
|
2018-11-03 17:02:41 +01:00
|
|
|
}
|
2017-07-28 00:08:42 +02:00
|
|
|
|
2018-11-03 17:02:41 +01:00
|
|
|
Ok(())
|
2017-07-04 18:26:11 +02:00
|
|
|
}
|