rtfm_syntax/
lib.rs

1//! The Real Time For the Masses (RTFM) meta language
2
3#![deny(missing_docs)]
4#![deny(rust_2018_compatibility)]
5#![deny(rust_2018_idioms)]
6#![deny(warnings)]
7
8#[allow(unused_extern_crates)]
9extern crate proc_macro;
10
11use core::ops;
12use proc_macro::TokenStream;
13
14use indexmap::{IndexMap, IndexSet};
15use proc_macro2::TokenStream as TokenStream2;
16use syn::Ident;
17
18use crate::ast::App;
19
20mod accessors;
21pub mod analyze;
22pub mod ast;
23mod check;
24mod optimize;
25mod parse;
26#[cfg(test)]
27mod tests;
28
29/// Core identifier
30pub type Core = u8;
31
32/// An ordered map keyed by identifier
33pub type Map<T> = IndexMap<Ident, T>;
34
35/// An order set
36pub type Set<T> = IndexSet<T>;
37
38/// Immutable pointer
39pub struct P<T> {
40    ptr: Box<T>,
41}
42
43impl<T> P<T> {
44    /// Boxes `x` making the value immutable
45    pub fn new(x: T) -> P<T> {
46        P { ptr: Box::new(x) }
47    }
48}
49
50impl<T> ops::Deref for P<T> {
51    type Target = T;
52
53    fn deref(&self) -> &T {
54        &self.ptr
55    }
56}
57
58/// Execution context
59#[derive(Clone, Copy)]
60pub enum Context<'a> {
61    /// A hardware task: `#[exception]` or `#[interrupt]`
62    HardwareTask(&'a Ident),
63
64    /// The `idle` context
65    Idle(Core),
66
67    /// The `init`-ialization function
68    Init(Core),
69
70    /// A software task: `#[task]`
71    SoftwareTask(&'a Ident),
72}
73
74impl<'a> Context<'a> {
75    /// The core this context belongs to
76    pub fn core(&self, app: &App) -> u8 {
77        match self {
78            Context::HardwareTask(name) => app.hardware_tasks[*name].args.core,
79            Context::Idle(core) => app.idles[core].args.core,
80            Context::Init(core) => app.inits[core].args.core,
81            Context::SoftwareTask(name) => app.software_tasks[*name].args.core,
82        }
83    }
84
85    /// The identifier of this context
86    pub fn ident(&self, app: &'a App) -> &'a Ident {
87        match self {
88            Context::HardwareTask(ident) => ident,
89            Context::Idle(core) => &app.idles[core].name,
90            Context::Init(core) => &app.inits[core].name,
91            Context::SoftwareTask(ident) => ident,
92        }
93    }
94
95    /// Is this the `idle` context?
96    pub fn is_idle(&self) -> bool {
97        if let Context::Idle(_) = self {
98            true
99        } else {
100            false
101        }
102    }
103
104    /// Is this the `init`-ialization context?
105    pub fn is_init(&self) -> bool {
106        if let Context::Init(_) = self {
107            true
108        } else {
109            false
110        }
111    }
112
113    /// Whether this context runs only once
114    pub fn runs_once(&self) -> bool {
115        self.is_init() || self.is_idle()
116    }
117
118    /// Whether this context has local `static` variables
119    pub fn has_locals(&self, app: &App) -> bool {
120        match *self {
121            Context::HardwareTask(name) => !app.hardware_tasks[name].locals.is_empty(),
122            Context::Idle(core) => !app.idles[&core].locals.is_empty(),
123            Context::Init(core) => !app.inits[&core].locals.is_empty(),
124            Context::SoftwareTask(name) => !app.software_tasks[name].locals.is_empty(),
125        }
126    }
127
128    /// Whether this context has resources
129    pub fn has_resources(&self, app: &App) -> bool {
130        match *self {
131            Context::HardwareTask(name) => !app.hardware_tasks[name].args.resources.is_empty(),
132            Context::Idle(core) => !app.idles[&core].args.resources.is_empty(),
133            Context::Init(core) => !app.inits[&core].args.resources.is_empty(),
134            Context::SoftwareTask(name) => !app.software_tasks[name].args.resources.is_empty(),
135        }
136    }
137
138    /// Whether this context may use the `schedule` API
139    pub fn uses_schedule(&self, app: &App) -> bool {
140        match *self {
141            Context::HardwareTask(name) => !app.hardware_tasks[name].args.schedule.is_empty(),
142            Context::Idle(core) => !app.idles[&core].args.schedule.is_empty(),
143            Context::Init(core) => !app.inits[&core].args.schedule.is_empty(),
144            Context::SoftwareTask(name) => !app.software_tasks[name].args.schedule.is_empty(),
145        }
146    }
147
148    /// Whether this context may use the `spawn` API
149    pub fn uses_spawn(&self, app: &App) -> bool {
150        match *self {
151            Context::HardwareTask(name) => !app.hardware_tasks[name].args.spawn.is_empty(),
152            Context::Idle(core) => !app.idles[&core].args.spawn.is_empty(),
153            Context::Init(core) => !app.inits[&core].args.spawn.is_empty(),
154            Context::SoftwareTask(name) => !app.software_tasks[name].args.spawn.is_empty(),
155        }
156    }
157}
158
159/// Parser and optimizer configuration
160#[derive(Default)]
161pub struct Settings {
162    /// Whether to accept the `binds` argument in `#[task]` or not
163    pub parse_binds: bool,
164    /// Whether to accept the `cores`, `core` and `late` arguments or not
165    pub parse_cores: bool,
166    /// Whether to parse `extern` interrupts (functions) or not
167    pub parse_extern_interrupt: bool,
168    /// Whether to accept the `schedule` argument or not
169    pub parse_schedule: bool,
170
171    /// Whether to "compress" priorities or not
172    pub optimize_priorities: bool,
173
174    _extensible: (),
175}
176
177/// Parses the input of the `#[app]` attribute
178pub fn parse(
179    args: TokenStream,
180    input: TokenStream,
181    settings: Settings,
182) -> Result<(P<ast::App>, P<analyze::Analysis>), syn::parse::Error> {
183    parse2(args.into(), input.into(), settings)
184}
185
186/// `proc_macro2::TokenStream` version of `parse`
187pub fn parse2(
188    args: TokenStream2,
189    input: TokenStream2,
190    ref settings: Settings,
191) -> Result<(P<ast::App>, P<analyze::Analysis>), syn::parse::Error> {
192    let mut app = parse::app(args, input, settings)?;
193    check::app(&app)?;
194    optimize::app(&mut app, settings);
195    let analysis = analyze::app(&app);
196
197    Ok((P::new(app), P::new(analysis)))
198}
199
200enum Either<A, B> {
201    Left(A),
202    Right(B),
203}