1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//! Procedural macros of the `cortex-m-rtfm` crate
#![deny(warnings)]
#![feature(proc_macro)]
#![recursion_limit = "128"]

#[macro_use]
extern crate error_chain;
extern crate proc_macro;
#[macro_use]
extern crate quote;
extern crate rtfm_syntax as syntax;
extern crate syn;

use proc_macro::TokenStream;
use syntax::App;
use syntax::error::*;

mod analyze;
mod check;
mod trans;

/// The `app!` macro, a macro used to specify the tasks and resources of a
/// RTFM application.
///
/// The contents of this macro uses a `key: value` syntax. All the possible keys
/// are shown below:
///
/// ``` text
/// app! {
///     device: ..,
///
///     resources: { .. },
///
///     init: { .. },
///
///     idle: { .. },
///
///     tasks: { .. },
/// }
/// ```
///
/// # `device`
///
/// The value of this key is a Rust path, like `foo::bar::baz`, that must point to
/// a *device crate*, a crate generated using `svd2rust`.
///
/// # `resources`
///
/// This key is optional. Its value is a list of `static` variables. These
/// variables are the data that can be safely accessed, modified and shared by
/// tasks.
///
/// ``` text
/// resources: {
///     static A: bool = false;
///     static B: i32 = 0;
///     static C: [u8; 16] = [0; 16];
///     static D: Thing = Thing::new(..);
/// }
/// ```
///
/// If this key is omitted its value defaults to an empty list.
///
/// # `init`
///
/// This key is optional. Its value is a set of key values. All the possible
/// keys are shown below:
///
/// ``` text
/// init: {
///     path: ..,
/// }
/// ```
///
/// ## `init.path`
///
/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that
/// points to the initialization function.
///
/// If the key is omitted its value defaults to `init`.
///
/// # `idle`
///
/// This key is optional. Its value is a set of key values. All the possible
/// keys are shown below:
///
/// ``` text
/// idle: {
///     path: ..,
///     resources: [..],
/// }
/// ```
///
/// ## `idle.path`
///
/// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that
/// points to the idle loop function.
///
/// If the key is omitted its value defaults to `idle`.
///
/// ## `idle.resources`
///
/// This key is optional. Its value is a list of resources the `idle` loop has
/// access to. The resources in this list can refer to the resources listed in
/// the top `resources` key. If the name doesn't match one of the resources
/// listed in the top `resources` key the resource is assumed to be a
/// peripheral.
///
/// If omitted its value defaults to an empty list.
///
/// # `tasks`
///
/// This key is optional. Its value is a list of tasks. Each task itself is a
/// set of key value pair. The full syntax is shown below:
///
/// ``` text
/// tasks: {
///     $TASK: {
///         enabled: ..,
///         path: ..,
///         priority: ..,
///         resources: [..],
///     },
/// }
/// ```
///
/// If this key is omitted its value is assumed to be an empty list.
///
/// ## `tasks.$TASK`
///
/// The key must be either a Cortex-M exception or a device specific interrupt.
/// `PENDSV`, `SVCALL`, `SYS_TICK` are considered as exceptions. All other names
/// are assumed to be interrupts.
///
/// ## `tasks.$TASK.enabled`
///
/// This key is optional for interrupts and forbidden for exceptions. Its value
/// must be a boolean and indicates whether the interrupt will be enabled
/// (`true`) or disabled (`false`) after `init` ends and before `idle` starts.
///
/// If this key is omitted its value defaults to `true`.
///
/// ## `tasks.$TASK.path`
///
/// The value of this key is a Rust path, like `foo::bar::baz`, that points to
/// the handler of this task.
///
/// ## `tasks.$TASK.priority`
///
/// This key is optional. Its value is an integer with type `u8` that specifies
/// the priority of this task. The minimum valid priority is 1. The maximum
/// valid priority depends on the number of the NVIC priority bits the device
/// has; if the device has 4 priority bits the maximum allowed value would be
/// 16.
///
/// If this key is omitted its value defaults to `1`.
///
/// ## `tasks.$TASK.resources`
///
/// This key is optional. Its value is a list of resources this task has access
/// to. The resources in this list can refer to the resources listed in the top
/// `resources` key. If the name doesn't match one of the resources listed in
/// the top `resources` key the resource is assumed to be a peripheral.
///
/// If omitted its value defaults to an empty list.
#[proc_macro]
pub fn app(ts: TokenStream) -> TokenStream {
    match run(ts) {
        Err(e) => panic!("{}", error_chain::ChainedError::display(&e)),
        Ok(ts) => ts,
    }
}

fn run(ts: TokenStream) -> Result<TokenStream> {
    let input = format!("{}", ts);

    let app = App::parse(&input).chain_err(|| "parsing")?;
    let app = syntax::check::app(app).chain_err(|| "checking the AST")?;
    let app = check::app(app)?;

    let ownerships = analyze::app(&app);
    let tokens = trans::app(&app, &ownerships);

    Ok(format!("{}", tokens)
        .parse()
        .map_err(|_| "BUG: error parsing the generated code")?)
}