Select Git revision
parse.rs 19.79 KiB
use std::collections::{HashMap, HashSet};
use std::iter::Peekable;
use std::slice::Iter;
use syn::{self, DelimToken, Ident, IntTy, Lit, Path, Token, BinOpToken, TokenTree, Ty};
use error::*;
use {Cro, Ips, Ops, App, Idle, Init, Resources, Static, Statics, Task, Tasks, Expr};
/// Parses the contents of `app! { $App }`
pub fn app(input: &str) -> Result<App> {
let tts = syn::parse_token_trees(input)?;
let mut device = None;
let mut idle = None;
let mut init = None;
let mut resources = None;
let mut tasks = None;
fields(&tts, |key, tts| {
match key.as_ref() {
"device" => {
ensure!(device.is_none(), "duplicated `device` field");
device = Some(::parse::path(tts).chain_err(|| "parsing `device`")?);
}
"idle" => {
ensure!(idle.is_none(), "duplicated `idle` field");
idle = Some(::parse::idle(tts).chain_err(|| "parsing `idle`")?);
}
"init" => {
ensure!(init.is_none(), "duplicated `init` field");
init = Some(::parse::init(tts).chain_err(|| "parsing `init`")?);
}
"resources" => {
ensure!(resources.is_none(), "duplicated `resources` field");
resources = Some(::parse::statics(tts).chain_err(|| "parsing `resources`")?);
}
"tasks" => {
ensure!(tasks.is_none(), "duplicated `tasks` field");
tasks = Some(::parse::tasks(tts).chain_err(|| "parsing `tasks`")?);
}
_ => bail!("unknown field: `{}`", key),
}
Ok(())
})?;
Ok(App {
_extensible: (),
device: device.ok_or("`device` field is missing")?,
idle,
init,
resources,
tasks,
})
}
/// Parses a boolean
fn bool(tt: Option<&TokenTree>) -> Result<bool> {
if let Some(&TokenTree::Token(Token::Literal(Lit::Bool(bool)))) = tt {
Ok(bool)
} else {
bail!("expected boolean, found {:?}", tt);
}
}
/// Parses a delimited token tree
fn delimited<R, F>(tts: &mut Peekable<Iter<TokenTree>>, delimiter: DelimToken, f: F) -> Result<R>
where
F: FnOnce(&[TokenTree]) -> Result<R>,
{
let tt = tts.next();
if let Some(&TokenTree::Delimited(ref block)) = tt {
ensure!(
block.delim == delimiter,
"expected {:?}, found {:?}",
delimiter,
block.delim
);
f(&block.tts)
} else {
bail!("expected a Delimited sequence, found {:?}", tt);
}
}
/// Parses `$($Ident: $($tt)*,)*`
fn fields<F>(tts: &[TokenTree], mut f: F) -> Result<()>
where
F: FnMut(&Ident, &mut Peekable<Iter<TokenTree>>) -> Result<()>,
{
let mut tts = tts.iter().peekable();
while let Some(tt) = tts.next() {
let ident = if let TokenTree::Token(Token::Ident(ref id)) = *tt {
id
} else {
bail!("expected Ident, found {:?}", tt);
};
let tt = tts.next();
if let Some(&TokenTree::Token(Token::Colon)) = tt {
} else {
bail!("expected Colon, found {:?}", tt);
}
f(ident, &mut tts)?;
let tt = tts.next();
match tt {
None |
Some(&TokenTree::Token(Token::Comma)) => {}
_ => bail!("expected Comma, found {:?}", tt),
}
}
Ok(())
}
/// Parses the LHS of `idle: { $Idle }`
fn idle(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Idle> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut path = None;
let mut resources = None;
::parse::fields(tts, |key, tts| {
match key.as_ref() {
"path" => {
ensure!(path.is_none(), "duplicated `path` field");
path = Some(::parse::path(tts)?);
}
"resources" => {
ensure!(resources.is_none(), "duplicated `resources` field");
resources = Some(::parse::resources(tts).chain_err(|| "parsing `resources`")?);
}
_ => bail!("unknown field: `{}`", key),
}
Ok(())
})?;
Ok(Idle {
_extensible: (),
path,
resources,
})
})
}
/// Parses the LHS of `init: { $Init }`
fn init(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Init> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut path = None;
::parse::fields(tts, |key, tts| {
match key.as_ref() {
"path" => {
ensure!(path.is_none(), "duplicated `path` field");
path = Some(::parse::path(tts)?);
}
_ => bail!("unknown field: `{}`", key),
}
Ok(())
})?;
Ok(Init {
_extensible: (),
path,
})
})
}
/// Parses `[$($Ident,)*]`
fn resources(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Resources> {
::parse::delimited(tts, DelimToken::Bracket, |tts| {
let mut idents = HashSet::new();
let mut tts = tts.iter().peekable();
while let Some(tt) = tts.next() {
if let &TokenTree::Token(Token::Ident(ref ident)) = tt {
ensure!(!idents.contains(ident), "ident {} listed more than once");
idents.insert(ident.clone());
if let Some(tt) = tts.next() {
ensure!(
tt == &TokenTree::Token(Token::Comma),
"expected Comma, found {:?}",
tt
);
if tts.peek().is_none() {
break;
}
} else {
break;
}
} else {
bail!("expected Ident, found {:?}", tt);
}
}
Ok(idents)
})
}
/// Parses `$Ty = $Expr`
fn static_(tts: &mut Iter<TokenTree>) -> Result<Static> {
let mut fragments = vec![];
loop {
if let Some(tt) = tts.next() {
if tt == &TokenTree::Token(Token::Eq) {
break;
} else {
fragments.push(tt);
}
} else {
bail!("expected Equal, found end of macro");
}
}
let ty = syn::parse_type(&format!("{}", quote!(#(#fragments)*)))?;
let mut fragments = vec![];
loop {
if let Some(tt) = tts.next() {
if tt == &TokenTree::Token(Token::Semi) {
break;
} else {
fragments.push(tt);
}
} else {
bail!("expected Semicolon, found end of macro");
}
}
ensure!(!fragments.is_empty(), "initial value is missing");
let expr = quote!(#(#fragments)*);
Ok(Static {
_extensible: (),
expr,
ty,
})
}
/// Parses `$($Ident: $Ty = $Expr;)*`
fn statics(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Statics> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut statics = HashMap::new();
let mut tts = tts.iter();
while let Some(tt) = tts.next() {
match tt {
&TokenTree::Token(Token::Ident(ref id)) if id.as_ref() == "static" => {}
_ => {
bail!("expected keyword `static`, found {:?}", tt);
}
}
let tt = tts.next();
let ident = if let Some(&TokenTree::Token(Token::Ident(ref id))) = tt {
id
} else {
bail!("expected Ident, found {:?}", tt);
};
ensure!(
!statics.contains_key(ident),
"resource {} listed more than once",
ident
);
let tt = tts.next();
if let Some(&TokenTree::Token(Token::Colon)) = tt {
} else {
bail!("expected Colon, found {:?}", tt);
}
statics.insert(
ident.clone(),
::parse::static_(&mut tts).chain_err(
|| format!("parsing `{}`", ident),
)?,
);
}
Ok(statics)
})
}
/// Parses a `Path` from `$($tt)*`
fn path(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Path> {
let mut fragments = vec![];
loop {
if let Some(tt) = tts.peek() {
if tt == &&TokenTree::Token(Token::Comma) {
break;
} else {
fragments.push(tt.clone());
}
} else {
bail!("expected Comma, found end of macro")
}
tts.next();
}
// bail!("in path fragments, {:? }===== {:?}", fragments, tts);
Ok(syn::parse_path(&format!("{}", quote!(#(#fragments)*)))?)
}
/// Parses the LHS of `$Ident: { .. }`
fn task(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Task> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut enabled = None;
let mut path = None;
let mut priority = None;
let mut resources = None;
::parse::fields(tts, |key, tts| {
match key.as_ref() {
"enabled" => {
ensure!(enabled.is_none(), "duplicated `enabled` field");
enabled = Some(::parse::bool(tts.next()).chain_err(|| "parsing `enabled`")?);
}
"path" => {
ensure!(path.is_none(), "duplicated `path` field");
path = Some(::parse::path(tts).chain_err(|| "parsing `path`")?);
}
"priority" => {
ensure!(priority.is_none(), "duplicated `priority` field");
priority = Some(::parse::u8(tts.next()).chain_err(|| "parsing `priority`")?);
}
"resources" => {
ensure!(resources.is_none(), "duplicated `resources` field");
resources = Some(::parse::resources(tts).chain_err(|| "parsing `resources`")?);
}
_ => bail!("unknown field: `{}`", key),
}
Ok(())
})?;
Ok(Task {
_extensible: (),
enabled,
path,
priority,
resources,
})
})
}
/// Parses `$($Ident: { $Task })*`
fn tasks(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Tasks> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut tasks = HashMap::new();
::parse::fields(tts, |key, tts| {
ensure!(
!tasks.contains_key(key),
"task {} listed more than once",
key
);
tasks.insert(
key.clone(),
::parse::task(tts).chain_err(
|| format!("parsing task `{}`", key),
)?,
);
Ok(())
})?;
Ok(tasks)
})
}
/// Parses an integer with type `u8`
fn u8(tt: Option<&TokenTree>) -> Result<u8> {
if let Some(&TokenTree::Token(Token::Literal(Lit::Int(priority, IntTy::Unsuffixed)))) = tt {
ensure!(priority < 256, "{} is out of the `u8` range", priority);
Ok(priority as u8)
} else {
bail!("expected integer, found {:?}", tt);
}
}
/// Parses the contents of `crc! { $Crc }`
pub fn cro(input: &str) -> Result<Cro> {
let tts = syn::parse_token_trees(input)?;
let mut ips = None;
let mut ops = None;
fields(&tts, |key, tts| {
match key.as_ref() {
"ips" => {
ensure!(ips.is_none(), "duplicated `ips` field");
ips = Some(::parse::ips(tts).chain_err(|| "parsing `ips`")?);
}
"ops" => {
ensure!(ops.is_none(), "duplicated `ops` field");
ops = Some(::parse::ops(tts).chain_err(|| "parsing `ops`")?);
}
_ => bail!("unknown field: `{}`", key),
}
Ok(())
})?;
Ok(Cro { ips, ops })
}
/*
/// Parses `$($Ident: $Path,)*`
fn crcs(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Crcs> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut crcs = HashMap::new();
let mut tts = tts.iter().peekable();
while let Some(tt) = tts.next() {
let ident = if let &TokenTree::Token(Token::Ident(ref id)) = tt {
id
} else {
bail!("expected Ident, found {:?}", tt);
};
ensure!(
!crcs.contains_key(ident),
"crc {} listed more than once",
ident
);
let tt = tts.next();
if let Some(&TokenTree::Token(Token::Colon)) = tt {
} else {
bail!("expected Colon, found {:?}", tt);
}
let path = ::parse::path(&mut tts).chain_err(|| "parsing `path`")?;
tts.next();
crcs.insert(ident.clone(), path);
}
Ok(crcs)
})
}
*/
/// Parses `$($Ident: $Ty > $Path,)*`
fn ips(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Ips> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut ips = HashMap::new();
let mut tts = tts.iter().peekable();
while let Some(tt) = tts.next() {
let ident = if let &TokenTree::Token(Token::Ident(ref id)) = tt {
id
} else {
bail!("expected Ident, found {:?}", tt);
};
ensure!(
!ips.contains_key(ident),
"ip {} listed more than once",
ident
);
let tt = tts.next();
if let Some(&TokenTree::Token(Token::Colon)) = tt {
} else {
bail!("expected Colon, found {:?}", tt);
}
let mut ty_arg = None;
let mut ty_ret = syn::parse_type(&"()")?;
let mut op_sync = None;
let _ = ::parse::delimited(&mut tts, DelimToken::Brace, |tts| {
fields(&tts, |key, mut tts| {
match key.as_ref() {
"sig" => {
let tt = tts.next();
if let Some(&TokenTree::Token(Token::Ident(ref id))) = tt {
ensure!(id.as_ref() == "fn", "expected `fn` Ident, found {:?}", tt);
} else {
bail!("expected `fn` Ident, found {:?}", tt);
}
ty_arg = Some(::parse::delimited(&mut tts, DelimToken::Paren, |tts| {
Ok(syn::parse_type(&format!("{}", quote!(#(#tts)*)))?)
})?);
let tt = tts.peek().cloned();
let ty_ret: Result<Ty> = match tt {
Some(&TokenTree::Token(Token::Comma)) => Ok(
syn::parse_type(&"()")?,
),
Some(&TokenTree::Token(Token::RArrow)) => {
::parse::ty(&TokenTree::Token(Token::Comma), &mut tts)
}
_ => bail!("expected `-> Type,' or `,` found {:?}", tt),
};
}
"sync" => {
op_sync = Some(::parse::resources(&mut tts)?);
// let sync =
// Some(::parse::delimited(&mut tts, DelimToken::Bracket, |tts| {
// let mut tts = tts.iter().peekable();
// let mut idents = Vec::new();
// while let Some(tt) = tts.next() {
// if tts.peek().is_none() {
// break;
// }
// }
// Ok(())
// }));
}
"async" => (),
_ => bail!("unknown field: `{}`", key),
};
Ok(())
})
})?;
let tt = tts.next();
match tt {
None |
Some(&TokenTree::Token(Token::Comma)) => {}
_ => bail!("expected Comma, found {:?}", tt),
}
ips.insert(ident.clone(), (ty_arg, ty_ret, op_sync));
}
Ok(ips)
})
}
/// Parses `$($Ident: $Ty < $Path,)*`
fn ops(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Ops> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut ops = HashMap::new();
let mut tts = tts.iter().peekable();
while let Some(tt) = tts.next() {
let ident = if let &TokenTree::Token(Token::Ident(ref id)) = tt {
id
} else {
bail!("expected Ident, found {:?}", tt);
};
ensure!(
!ops.contains_key(ident),
"op {} listed more than once",
ident
);
let tt = tts.next();
if let Some(&TokenTree::Token(Token::Colon)) = tt {
} else {
bail!("expected Colon, found {:?}", tt);
}
let tt = tts.next();
if let Some(&TokenTree::Token(Token::Ident(ref id))) = tt {
ensure!(id.as_ref() == "fn", "expected `fn` Ident, found {:?}", tt);
} else {
bail!("expected `fn` Ident, found {:?}", tt);
}
let ty_arg = Some(::parse::delimited(
&mut tts,
DelimToken::Paren,
|tts| if tts.is_empty() {
Ok(Ty::Tup(vec![]))
} else {
Ok(syn::parse_type(&format!("{}", quote!(#(#tts)*)))?)
},
)?);
let tt = tts.peek().cloned();
let ty_ret: Result<Ty> = match tt {
Some(&TokenTree::Token(Token::Comma)) => Ok(syn::parse_type(&"()")?),
Some(&TokenTree::Token(Token::RArrow)) => {
tts.next();
::parse::ty(&TokenTree::Token(Token::Comma), &mut tts)
}
_ => bail!("expected `-> Type,' or `,` found {:?}", tt),
};
let tt = tts.next();
match tt {
None |
Some(&TokenTree::Token(Token::Comma)) => {}
_ => bail!("expected Comma, found {:?}", tt),
}
ops.insert(ident.clone(), (ty_arg, ty_ret?));
}
Ok(ops)
})
}
/// Parses `$Ty `
fn ty(token: &TokenTree, tts: &mut Peekable<Iter<TokenTree>>) -> Result<Ty> {
let mut fragments = vec![];
loop {
if let Some(tt) = tts.peek().cloned() {
if tt == token {
break;
} else {
fragments.push(tt);
tts.next();
}
} else {
bail!("expected `{:?}`, found end of macro", token);
}
}
Ok(syn::parse_type(&format!("{}", quote!(#(#fragments)*)))?)
}
/// Parses `$Expr `
fn expr(token: &TokenTree, tts: &mut Peekable<Iter<TokenTree>>) -> Result<::Expr> {
let mut fragments = vec![];
loop {
if let Some(tt) = tts.next() {
if tt == token {
break;
} else {
fragments.push(tt);
}
} else {
bail!("expected `{:?}`, found end of macro", token);
}
}
Ok(quote!(#(#fragments)*))
}