Skip to content
Snippets Groups Projects
Commit 59664489 authored by Jorge Aparicio's avatar Jorge Aparicio
Browse files

v0.1.0

parent bef55ad6
No related branches found
No related tags found
No related merge requests found
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
## v0.1.0 - 2017-07-28
- Initial release
[Unreleased]: https://github.com/japaric/rtfm-syntax/compare/v0.1.0...HEAD
[package]
authors = ["Jorge Aparicio <jorge@japaric.io>"]
categories = ["concurrency", "embedded", "no-std"]
description = "Parser of the app! macro used by the Real Time for The Masses (RTFM) framework"
documentation = "https://docs.rs/rtfm-syntax"
keywords = []
license = "MIT OR Apache-2.0"
name = "rtfm-syntax"
repository = "https://github.com/japaric/rtfm-syntax"
version = "0.1.0"
[dependencies]
......
# `rtfm-syntax`
> `rtfm!` macro parser
> Parser of the `app!` macro used by the Real Time For the Masses (RTFM)
> framework
## [Documentation](https://docs.rs/rtfm-syntax)
## License
......
//! Syntax checking pass
use std::collections::HashMap;
use syn::{Ident, Path};
use error::*;
use {util, Idents, Statics};
use {util, Resources, Statics};
/// `$($Ident: { .. },)*`
pub type Tasks = HashMap<Ident, Task>;
/// `app! { .. }`
#[derive(Debug)]
pub struct App {
/// `device: $path`
pub device: Path,
/// `idle: { $Idle }`
pub idle: Idle,
/// `init: { $Init }`
pub init: Init,
/// `resources: $Statics`
pub resources: Statics,
/// `tasks: { $Tasks }`
pub tasks: Tasks,
_extensible: (),
}
/// `idle: { .. }`
#[derive(Debug)]
pub struct Idle {
/// `path: $Path`
pub path: Path,
pub resources: Idents,
/// `resources: $Resources`
pub resources: Resources,
_extensible: (),
}
/// `init: { .. }`
#[derive(Debug)]
pub struct Init {
/// `path: $Path`
pub path: Path,
_extensible: (),
}
/// `$Ident: { .. }`
#[derive(Debug)]
pub struct Task {
/// `enabled: $bool`
pub enabled: Option<bool>,
/// `path: $Path`
pub path: Option<Path>,
/// `priority: $u8`
pub priority: Option<u8>,
pub resources: Idents,
/// `resources: $Resources`
pub resources: Resources,
_extensible: (),
}
/// Checks the syntax of the parsed `app!` macro
pub fn app(app: ::App) -> Result<App> {
Ok(App {
_extensible: (),
device: app.device,
idle: ::check::idle(app.idle).chain_err(|| "checking `idle`")?,
init: ::check::init(app.init).chain_err(|| "checking `init`")?,
......@@ -42,20 +71,6 @@ pub fn app(app: ::App) -> Result<App> {
})
}
fn idents(field: &str, idents: Option<Idents>) -> Result<Idents> {
Ok(if let Some(idents) = idents {
ensure!(
!idents.is_empty(),
"empty `{}` field. It should be removed.",
field
);
idents
} else {
Idents::new()
})
}
fn idle(idle: Option<::Idle>) -> Result<Idle> {
Ok(if let Some(idle) = idle {
ensure!(
......@@ -64,14 +79,16 @@ fn idle(idle: Option<::Idle>) -> Result<Idle> {
);
Idle {
_extensible: (),
path: ::check::path("idle", idle.path)
.chain_err(|| "checking `path`")?,
resources: ::check::idents("resources", idle.resources)?,
resources: ::check::resources("resources", idle.resources)?,
}
} else {
Idle {
_extensible: (),
path: util::mk_path("idle"),
resources: Idents::new(),
resources: Resources::new(),
}
})
}
......@@ -80,6 +97,7 @@ fn init(init: Option<::Init>) -> Result<Init> {
Ok(if let Some(init) = init {
if let Some(path) = init.path {
Init {
_extensible: (),
path: ::check::path("init", Some(path))
.chain_err(|| "checking `path`")?,
}
......@@ -88,6 +106,7 @@ fn init(init: Option<::Init>) -> Result<Init> {
}
} else {
Init {
_extensible: (),
path: util::mk_path("init"),
}
})
......@@ -107,6 +126,20 @@ fn path(default: &str, path: Option<Path>) -> Result<Path> {
})
}
fn resources(field: &str, idents: Option<Resources>) -> Result<Resources> {
Ok(if let Some(idents) = idents {
ensure!(
!idents.is_empty(),
"empty `{}` field. It should be removed.",
field
);
idents
} else {
Resources::new()
})
}
fn statics(field: &str, statics: Option<Statics>) -> Result<Statics> {
Ok(if let Some(statics) = statics {
ensure!(
......@@ -136,10 +169,11 @@ fn tasks(tasks: Option<::Tasks>) -> Result<Tasks> {
Ok((
name,
Task {
_extensible: (),
enabled: task.enabled,
path: task.path,
priority: task.priority,
resources: ::check::idents(
resources: ::check::resources(
"resources",
task.resources,
)?,
......
//! Errors
error_chain!();
//! Parser of the `app!` macro used by the Real Time For the Masses (RTFM)
//! framework
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![deny(warnings)]
#[macro_use]
......@@ -19,50 +23,78 @@ use syn::{Ident, Path, Ty};
use error::*;
pub type Idents = HashSet<Ident>;
/// A rust expression
pub type Expr = Tokens;
/// `[$($ident),*]`
pub type Resources = HashSet<Ident>;
/// `$(static $Ident: $Ty = $expr;)*`
pub type Statics = HashMap<Ident, Static>;
/// `$($Ident: { .. },)*`
pub type Tasks = HashMap<Ident, Task>;
/// `app! { .. }`
#[derive(Debug)]
pub struct App {
/// `device: $path`
pub device: Path,
/// `idle: { $Idle }`
pub idle: Option<Idle>,
/// `init: { $Init }`
pub init: Option<Init>,
/// `resources: $Resources`
pub resources: Option<Statics>,
/// `tasks: { $Tasks }`
pub tasks: Option<Tasks>,
_extensible: (),
}
/// `init`
/// `idle: { .. }`
#[derive(Debug)]
pub struct Init {
pub struct Idle {
/// `path: $Path`
pub path: Option<Path>,
/// `resources: $Resources`
pub resources: Option<Resources>,
_extensible: (),
}
/// `idle`
/// `init: { .. }`
#[derive(Debug)]
pub struct Idle {
pub struct Init {
/// `path: $Path`
pub path: Option<Path>,
pub resources: Option<Idents>,
_extensible: (),
}
/// `$Ident: { .. }`
#[derive(Debug)]
pub struct Task {
/// `enabled: $bool`
pub enabled: Option<bool>,
/// `path: $Path`
pub path: Option<Path>,
/// `priority: $u8`
pub priority: Option<u8>,
pub resources: Option<Idents>,
/// `resources: $Resources`
pub resources: Option<Resources>,
_extensible: (),
}
// `$ident: $ty = $expr;`
/// `static $Ident: $Ty = $Expr;`
#[derive(Debug)]
pub struct Static {
pub expr: Tokens,
/// `$Expr`
pub expr: Expr,
/// `$Ty`
pub ty: Ty,
_extensible: (),
}
impl App {
/// Parses the contents of the `app! { .. }` macro
pub fn parse(input: &str) -> Result<Self> {
parse::app(input)
}
......
......@@ -6,8 +6,9 @@ use syn::{self, DelimToken, Ident, IntTy, Lit, Path, Token, TokenTree};
use error::*;
use {App, Idents, Idle, Init, Static, Statics, Task, Tasks};
use {App, Idle, Init, Resources, Static, Statics, Task, Tasks};
/// Parses the contents of `app! { $App }`
pub fn app(input: &str) -> Result<App> {
let tts = syn::parse_token_trees(input)?;
......@@ -55,6 +56,7 @@ pub fn app(input: &str) -> Result<App> {
})?;
Ok(App {
_extensible: (),
device: device.ok_or("`device` field is missing")?,
idle,
init,
......@@ -63,6 +65,7 @@ pub fn app(input: &str) -> Result<App> {
})
}
/// Parses a boolean
fn bool(tt: Option<&TokenTree>) -> Result<bool> {
if let Some(&TokenTree::Token(Token::Literal(Lit::Bool(bool)))) = tt {
Ok(bool)
......@@ -71,6 +74,7 @@ fn bool(tt: Option<&TokenTree>) -> Result<bool> {
}
}
/// Parses a delimited token tree
fn delimited<R, F>(
tts: &mut Peekable<Iter<TokenTree>>,
delimiter: DelimToken,
......@@ -94,7 +98,7 @@ where
}
}
// `$($key:ident: $($value:tt)*),*[,]`
/// Parses `$($Ident: $($tt)*,)*`
fn fields<F>(tts: &[TokenTree], mut f: F) -> Result<()>
where
F: FnMut(&Ident, &mut Peekable<Iter<TokenTree>>) -> Result<()>,
......@@ -126,42 +130,7 @@ where
Ok(())
}
fn idents(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Idents> {
::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 the LHS of `idle: { $Idle }`
fn idle(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Idle> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut path = None;
......@@ -180,7 +149,7 @@ fn idle(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Idle> {
"duplicated `resources` field"
);
resources = Some(::parse::idents(tts)
resources = Some(::parse::resources(tts)
.chain_err(|| "parsing `resources`")?);
}
_ => bail!("unknown field: `{}`", key),
......@@ -189,10 +158,15 @@ fn idle(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Idle> {
Ok(())
})?;
Ok(Idle { path, resources })
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;
......@@ -210,11 +184,51 @@ fn init(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Init> {
Ok(())
})?;
Ok(Init { path })
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)
})
}
/// `$ty:ty = $expr:expr`
/// Parses `$Ty = $Expr`
fn static_(tts: &mut Iter<TokenTree>) -> Result<Static> {
let mut fragments = vec![];
loop {
......@@ -247,10 +261,14 @@ fn static_(tts: &mut Iter<TokenTree>) -> Result<Static> {
ensure!(!fragments.is_empty(), "initial value is missing");
let expr = quote!(#(#fragments)*);
Ok(Static { expr, ty })
Ok(Static {
_extensible: (),
expr,
ty,
})
}
/// $($ident:ident: $ty:ty = $expr:expr);*
/// Parses `$($Ident: $Ty = $Expr;)*`
fn statics(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Statics> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut statics = HashMap::new();
......@@ -296,6 +314,7 @@ fn statics(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Statics> {
})
}
/// Parses a `Path` from `$($tt)*`
fn path(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Path> {
let mut fragments = vec![];
......@@ -316,6 +335,7 @@ fn path(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Path> {
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;
......@@ -350,7 +370,7 @@ fn task(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Task> {
"duplicated `resources` field"
);
resources = Some(::parse::idents(tts)
resources = Some(::parse::resources(tts)
.chain_err(|| "parsing `resources`")?);
}
_ => bail!("unknown field: `{}`", key),
......@@ -360,6 +380,7 @@ fn task(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Task> {
})?;
Ok(Task {
_extensible: (),
enabled,
path,
priority,
......@@ -368,6 +389,7 @@ fn task(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Task> {
})
}
/// Parses `$($Ident: { $Task })*`
fn tasks(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Tasks> {
::parse::delimited(tts, DelimToken::Brace, |tts| {
let mut tasks = HashMap::new();
......@@ -392,6 +414,7 @@ fn tasks(tts: &mut Peekable<Iter<TokenTree>>) -> Result<Tasks> {
})
}
/// Parses an integer with type `u8`
fn u8(tt: Option<&TokenTree>) -> Result<u8> {
if let Some(
&TokenTree::Token(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment