aboutsummaryrefslogtreecommitdiff
path: root/macros/src/syntax/parse.rs
diff options
context:
space:
mode:
authorGravatar Jorge Aparicio <jorge@japaric.io> 2017-07-04 11:26:11 -0500
committerGravatar Jorge Aparicio <jorge@japaric.io> 2017-07-04 11:26:11 -0500
commit86a360a3964ecb04a37c0424c76d7b43a9fd40fe (patch)
treecbf0ebee17a588f8f004bdd27e590ee6c958761b /macros/src/syntax/parse.rs
parent2bf5401439df4494b33ef87201ee013eb1f167e8 (diff)
downloadrtic-86a360a3964ecb04a37c0424c76d7b43a9fd40fe.tar.gz
rtic-86a360a3964ecb04a37c0424c76d7b43a9fd40fe.tar.zst
rtic-86a360a3964ecb04a37c0424c76d7b43a9fd40fe.zip
rtfm! macro take 2
Diffstat (limited to 'macros/src/syntax/parse.rs')
-rw-r--r--macros/src/syntax/parse.rs496
1 files changed, 496 insertions, 0 deletions
diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs
new file mode 100644
index 00000000..e6d3f461
--- /dev/null
+++ b/macros/src/syntax/parse.rs
@@ -0,0 +1,496 @@
+use std::collections::{HashMap, HashSet};
+
+use syn::{self, DelimToken, Ident, IntTy, Lit, Token, TokenTree};
+use quote::Tokens;
+
+use syntax::{App, Idle, Init, Kind, Resource, Resources, Task, Tasks};
+
+pub fn app(input: &str) -> App {
+ let tts = syn::parse_token_trees(input).unwrap();
+
+ let mut device = None;
+ let mut init = None;
+ let mut idle = None;
+ let mut resources = None;
+ let mut tasks = None;
+
+ let mut tts = tts.into_iter();
+ while let Some(tt) = tts.next() {
+ let id = if let TokenTree::Token(Token::Ident(id)) = tt {
+ id
+ } else {
+ panic!("expected ident, found {:?}", tt);
+ };
+
+ let tt = tts.next();
+ assert_eq!(
+ tt,
+ Some(TokenTree::Token(Token::Colon)),
+ "expected colon, found {:?}",
+ tt
+ );
+
+ match id.as_ref() {
+ "device" => {
+ assert!(device.is_none(), "duplicated device field");
+
+ let mut pieces = vec![];
+
+ loop {
+ if let Some(tt) = tts.next() {
+ if tt == TokenTree::Token(Token::Comma) {
+ break;
+ } else {
+ pieces.push(tt);
+ }
+ } else {
+ panic!("expected path, found EOM");
+ }
+ }
+
+ device = Some(quote!(#(#pieces)*));
+ continue;
+ }
+ "idle" => {
+ assert!(idle.is_none(), "duplicated idle field");
+
+ let tt = tts.next();
+ if let Some(TokenTree::Delimited(block)) = tt {
+ assert_eq!(
+ block.delim,
+ DelimToken::Brace,
+ "expected brace, found {:?}",
+ block.delim
+ );
+
+ idle = Some(super::parse::idle(block.tts));
+ } else {
+ panic!("expected block, found {:?}", tt);
+ }
+ }
+ "init" => {
+ assert!(init.is_none(), "duplicated init field");
+
+ let tt = tts.next();
+ if let Some(TokenTree::Delimited(block)) = tt {
+ assert_eq!(
+ block.delim,
+ DelimToken::Brace,
+ "expected brace, found {:?}",
+ block.delim
+ );
+
+ init = Some(super::parse::init(block.tts));
+ } else {
+ panic!("expected block, found {:?}", tt);
+ }
+ }
+ "resources" => {
+ assert!(resources.is_none(), "duplicated resources field");
+
+ let tt = tts.next();
+ if let Some(TokenTree::Delimited(block)) = tt {
+ assert_eq!(
+ block.delim,
+ DelimToken::Brace,
+ "expected brace, found {:?}",
+ block.delim
+ );
+
+ resources = Some(super::parse::resources(block.tts));
+ }
+ }
+ "tasks" => {
+ assert!(tasks.is_none(), "duplicated tasks field");
+
+ let tt = tts.next();
+ if let Some(TokenTree::Delimited(block)) = tt {
+ assert_eq!(
+ block.delim,
+ DelimToken::Brace,
+ "expected brace, found {:?}",
+ block.delim
+ );
+
+ tasks = Some(super::parse::tasks(block.tts));
+ }
+ }
+ id => panic!("unexpected field {}", id),
+ }
+
+ let tt = tts.next();
+ assert_eq!(
+ tt,
+ Some(TokenTree::Token(Token::Comma)),
+ "expected comma, found {:?}",
+ tt
+ );
+ }
+
+ App {
+ device: device.expect("device field is missing"),
+ idle: idle.expect("idle field is missing"),
+ init: init.expect("init field is missing"),
+ resources: resources.expect("resources field is missing"),
+ tasks: tasks.expect("tasks field is missing"),
+ }
+}
+
+fn idle_init(
+ tts: Vec<TokenTree>,
+ allows_locals: bool,
+) -> (Option<Resources>, Tokens, HashSet<Ident>) {
+ let mut tts = tts.into_iter();
+
+ let mut local = None;
+ let mut path = None;
+ let mut resources = None;
+ while let Some(tt) = tts.next() {
+ let id = if let TokenTree::Token(Token::Ident(id)) = tt {
+ id
+ } else {
+ panic!("expected ident, found {:?}", tt);
+ };
+
+ let tt = tts.next();
+ assert_eq!(
+ tt,
+ Some(TokenTree::Token(Token::Colon)),
+ "expected colon, found {:?}",
+ tt
+ );
+
+ match id.as_ref() {
+ "local" if allows_locals => {
+ assert!(local.is_none(), "duplicated local field");
+
+ let tt = tts.next();
+ if let Some(TokenTree::Delimited(block)) = tt {
+ assert_eq!(
+ block.delim,
+ DelimToken::Brace,
+ "expected brace, found {:?}",
+ block.delim
+ );
+
+ local = Some(super::parse::resources(block.tts));
+ } else {
+ panic!("expected block, found {:?}", tt);
+ }
+ }
+ "path" => {
+ assert!(path.is_none(), "duplicated path field");
+
+ let mut pieces = vec![];
+ loop {
+ let tt = tts.next().expect("expected comma, found EOM");
+
+ if tt == TokenTree::Token(Token::Comma) {
+ path = Some(quote!(#(#pieces)*));
+ break;
+ } else {
+ pieces.push(tt);
+ }
+ }
+
+ continue;
+ }
+ "resources" => {
+ assert!(resources.is_none(), "duplicated resources field");
+
+ let tt = tts.next();
+ if let Some(TokenTree::Delimited(array)) = tt {
+ assert_eq!(
+ array.delim,
+ DelimToken::Bracket,
+ "expected bracket, found {:?}",
+ array.delim
+ );
+
+ resources = Some(super::parse::idents(array.tts));
+
+ } else {
+ panic!("expected array, found {:?}", tt);
+ }
+ }
+ id => panic!("unexpected field {}", id),
+ }
+
+ let tt = tts.next();
+ assert_eq!(
+ tt,
+ Some(TokenTree::Token(Token::Comma)),
+ "expected comma, found {:?}",
+ tt
+ );
+ }
+
+ (
+ local,
+ path.expect("path field is missing"),
+ resources.unwrap_or(HashSet::new()),
+ )
+}
+
+pub fn idle(tts: Vec<TokenTree>) -> Idle {
+ let (locals, path, resources) = idle_init(tts, true);
+
+ Idle {
+ local: locals.expect("local field is missing"),
+ path,
+ resources,
+ }
+}
+
+pub fn init(tts: Vec<TokenTree>) -> Init {
+ let (_, path, resources) = idle_init(tts, false);
+
+ Init { path, resources }
+}
+
+fn idents(tts: Vec<TokenTree>) -> HashSet<Ident> {
+ let mut idents = HashSet::new();
+
+ let mut tts = tts.into_iter();
+ while let Some(tt) = tts.next() {
+ if let TokenTree::Token(Token::Ident(id)) = tt {
+ assert!(!idents.contains(&id), "ident {} already listed", id);
+ idents.insert(id);
+
+ if let Some(tt) = tts.next() {
+ assert_eq!(tt, TokenTree::Token(Token::Comma));
+ } else {
+ break;
+ }
+ } else {
+ panic!("expected ident, found {:?}", tt);
+ };
+ }
+
+ idents
+}
+
+pub fn resources(tts: Vec<TokenTree>) -> Resources {
+ let mut resources = HashMap::new();
+
+ let mut tts = tts.into_iter();
+ while let Some(tt) = tts.next() {
+ let name = if let TokenTree::Token(Token::Ident(ident)) = tt {
+ ident
+ } else {
+ panic!("expected ident, found {:?}", tt);
+ };
+
+ assert!(
+ !resources.contains_key(&name),
+ "resource {} already listed",
+ name
+ );
+
+ let tt = tts.next();
+ assert_eq!(
+ tt,
+ Some(TokenTree::Token(Token::Colon)),
+ "expected comma, found {:?}",
+ tt
+ );
+
+ let mut pieces = vec![];
+ loop {
+ if let Some(tt) = tts.next() {
+ if tt == TokenTree::Token(Token::Eq) {
+ break;
+ } else {
+ pieces.push(tt);
+ }
+ } else {
+ panic!("expected type, found EOM");
+ }
+ }
+
+ let ty = quote!(#(#pieces)*);
+
+ let mut pieces = vec![];
+ loop {
+ if let Some(tt) = tts.next() {
+ if tt == TokenTree::Token(Token::Semi) {
+ break;
+ } else {
+ pieces.push(tt);
+ }
+ } else {
+ panic!("expected expression, found EOM");
+ }
+ }
+
+ let expr = quote!(#(#pieces)*);
+
+ let resource = Resource { expr, ty };
+ resources.insert(name, resource);
+ }
+
+ resources
+}
+
+pub fn tasks(tts: Vec<TokenTree>) -> Tasks {
+ let mut tasks = HashMap::new();
+
+ let mut tts = tts.into_iter();
+ while let Some(tt) = tts.next() {
+ let name = if let TokenTree::Token(Token::Ident(ident)) = tt {
+ ident
+ } else {
+ panic!("expected ident, found {:?}", tt);
+ };
+
+ let tt = tts.next();
+ assert_eq!(
+ tt,
+ Some(TokenTree::Token(Token::Colon)),
+ "expected colon, found {:?}",
+ tt
+ );
+
+ let tt = tts.next();
+ if let Some(TokenTree::Delimited(block)) = tt {
+ assert_eq!(
+ block.delim,
+ DelimToken::Brace,
+ "expected brace, found {:?}",
+ block.delim
+ );
+
+ assert!(!tasks.contains_key(&name), "task {} already listed", name);
+ tasks.insert(name, super::parse::task(block.tts));
+ } else {
+ panic!("expected block, found {:?}", tt);
+ }
+
+ let tt = tts.next();
+ assert_eq!(
+ tt,
+ Some(TokenTree::Token(Token::Comma)),
+ "expected comma, found {:?}",
+ tt
+ );
+ }
+
+ tasks
+}
+
+/// Parses the body of a task
+///
+/// ```
+/// enabled: true,
+/// priority: 1,
+/// resources: [R1, TIM2],
+/// ```
+///
+/// the `enabled` field is optional and distinguishes interrupts from
+/// exceptions. Interrupts have an `enabled` field, whereas exceptions don't.
+fn task(tts: Vec<TokenTree>) -> Task {
+ let mut enabled = None;
+ let mut priority = None;
+ let mut resources = None;
+
+ let mut tts = tts.into_iter();
+ while let Some(tt) = tts.next() {
+ let ident = if let TokenTree::Token(Token::Ident(ident)) = tt {
+ ident
+ } else {
+ panic!("expected ident, found {:?}", tt);
+ };
+
+ let tt = tts.next();
+ assert_eq!(
+ tt,
+ Some(TokenTree::Token(Token::Colon)),
+ "expected colon, found {:?}",
+ tt
+ );
+
+ match ident.as_ref() {
+ "enabled" => {
+ assert!(enabled.is_none(), "duplicated enabled field");
+
+ let tt = tts.next();
+
+ if let Some(TokenTree::Token(Token::Literal(lit))) = tt {
+ if let Lit::Bool(b) = lit {
+ enabled = Some(b);
+ } else {
+ panic!("`enabled` value must be a boolean");
+ }
+ } else {
+ panic!("expected literal, found {:?}", tt);
+ }
+ }
+ "priority" => {
+ assert!(priority.is_none(), "duplicated priority field");
+
+ let tt = tts.next();
+
+ if let Some(TokenTree::Token(Token::Literal(lit))) = tt {
+ if let Lit::Int(val, ty) = lit {
+ assert_eq!(
+ ty,
+ IntTy::Unsuffixed,
+ "`priority` value must be an unsuffixed value"
+ );
+
+ assert!(
+ val < 256,
+ "`priority` value must be less than 256"
+ );
+
+ priority = Some(val as u8);
+ } else {
+ panic!("enabled value must be a boolean");
+ }
+ } else {
+ panic!("expected literal, found {:?}", tt);
+ }
+ }
+ "resources" => {
+ assert!(resources.is_none(), "duplicated resources field");
+
+ let tt = tts.next();
+ if let Some(TokenTree::Delimited(block)) = tt {
+ assert_eq!(
+ block.delim,
+ DelimToken::Bracket,
+ "expected bracket, found {:?}",
+ block.delim
+ );
+
+ resources = Some(super::parse::idents(block.tts));
+ } else {
+ panic!("expected block, found {:?}", tt);
+ }
+ }
+ id => panic!("unexpected field {}", id),
+ }
+
+ let tt = tts.next();
+ assert_eq!(
+ tt,
+ Some(TokenTree::Token(Token::Comma)),
+ "expected comma, found {:?}",
+ tt
+ );
+ }
+
+ let resources = resources.expect("resources field is missing");
+ let priority = priority.expect("priority field is missing");
+ let kind = if let Some(enabled) = enabled {
+ Kind::Interrupt { enabled }
+ } else {
+ Kind::Exception
+ };
+
+ Task {
+ kind,
+ priority,
+ resources,
+ }
+}