diff options
author | 2017-07-04 11:26:11 -0500 | |
---|---|---|
committer | 2017-07-04 11:26:11 -0500 | |
commit | 86a360a3964ecb04a37c0424c76d7b43a9fd40fe (patch) | |
tree | cbf0ebee17a588f8f004bdd27e590ee6c958761b /macros/src/syntax/parse.rs | |
parent | 2bf5401439df4494b33ef87201ee013eb1f167e8 (diff) | |
download | rtic-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.rs | 496 |
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, + } +} |