Skip to content
Snippets Groups Projects
Select Git revision
  • 98c8c8d8b4f13bceb3077f5d9b1635dafe40e17f
  • master default protected
2 results

pwm1.rs

Blame
  • 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)*))
    
    }