extern crate std;
use std::error::Error as FmtError;
use compiler::lexer;
use compiler::lexer::LexemeKind;
use self::Error::*;
use std::rc::Rc;
use std::ops::Deref;
use std::cell::RefCell;
use config;
use fetcher;
use hook;
#[derive(Debug,PartialEq)]
pub enum Error {
InvalidSequence,
ExpectedIdentifier,
TooManyBraces,
NotEnoughBraces,
InvalidPropertyName,
InvalidPropertyType,
NoSubcomponents,
RuntimeError,
NoFetchMethod,
MissingRequiredProperty,
ConflictingProperties,
InvalidUnpackMethod,
ReservedHook,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.description())
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
InvalidSequence => "Invalid sequence of tokens",
ExpectedIdentifier => "An identifier was expected",
TooManyBraces => "Brace '{' does not have a matching closing brace '}'",
NotEnoughBraces => "Brace '}' does not have a matching opening brace '{'",
InvalidPropertyName => "Invalid property name",
InvalidPropertyType => "Invalid property type",
NoSubcomponents => "At least one subcomponent is required",
RuntimeError => "An unknown runtime error occurred",
NoFetchMethod => "No fetch method have been defined",
MissingRequiredProperty => "A required property is not implemented",
ConflictingProperties => "Two or more properties are in conflict",
InvalidUnpackMethod => "Invalid unpack method",
ReservedHook => "Collision between a user hook and a reserved command",
}
}
}
pub enum PropertyValue<'a> {
StringValue(&'a String),
BooleanValue(bool),
StringListValue(&'a Vec<String>),
EmptyValue,
}
impl<'a> std::fmt::Display for PropertyValue<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
PropertyValue::StringValue(val) => write!(f, "{}", val),
PropertyValue::BooleanValue(ref val) => write!(f, "{}", val),
PropertyValue::StringListValue(val) => {
let mut text = String::new();
for elem in val.iter() {
text.push_str(elem);
text.push(' ');
}
write!(f, "{}", text)
},
PropertyValue::EmptyValue => write!(f, "None"),
}
}
}
pub trait Property {
fn value_get(&self) -> PropertyValue;
}
pub struct Identifier {
name: String,
}
impl Property for () {
fn value_get(&self) -> PropertyValue {
PropertyValue::EmptyValue
}
}
impl Property for Identifier {
fn value_get(&self) -> PropertyValue {
PropertyValue::StringValue(&self.name)
}
}
impl Property for Vec<String> {
fn value_get(&self) -> PropertyValue {
PropertyValue::StringListValue(self)
}
}
impl Property for String {
fn value_get(&self) -> PropertyValue {
PropertyValue::StringValue(self)
}
}
impl Property for bool {
fn value_get(&self) -> PropertyValue {
PropertyValue::BooleanValue(*self)
}
}
enum Sequence {
Property,
BlockStart,
BlockEnd,
StringList,
Invalid,
}
impl Identifier {
pub fn new(name: String) -> Identifier {
Identifier {
name: name,
}
}
}
pub struct Parser {
cfg: Rc<config::Config>,
components: Vec<RefCell<config::Component>>,
}
impl Parser {
pub fn components_get(&self) -> &Vec<RefCell<config::Component>> {
&self.components
}
pub fn config_get(&self) -> &config::Config {
&self.cfg
}
pub fn new() -> Parser {
Parser {
cfg: Rc::new(config::Config::new()),
components: Vec::new(),
}
}
fn name_prop_get(component: &str, cfg: &config::Config) -> Result<String, Error> {
let key = format!("/subcomponents/{}/name", component);
if let Some(prop) = cfg.get(&key) {
match prop.deref().value_get() {
PropertyValue::StringValue(val) => { return Ok(val.clone()); },
_ => { return Err(Error::InvalidPropertyType); },
}
}
Err(Error::MissingRequiredProperty)
}
fn path_prop_get(component: &str, cfg: &config::Config) -> Result<String, Error> {
let key = format!("/subcomponents/{}/path", component);
if let Some(prop) = cfg.get(&key) {
match prop.deref().value_get() {
PropertyValue::StringValue(val) => { return Ok(val.clone()); },
_ => { return Err(Error::InvalidPropertyType); },
}
}
Err(Error::MissingRequiredProperty)
}
fn depends_prop_get(component: &str, cfg: &config::Config) -> Result<Vec<String>, Error> {
let key = format!("/subcomponents/{}/depends", component);
if let Some(prop) = cfg.get(&key) {
match prop.deref().value_get() {
PropertyValue::StringValue(val) => { return Ok(vec![val.clone()]); },
PropertyValue::StringListValue(val) => { return Ok(val.clone()); },
_ => {
error!("Property 'depends' of component '{}' shall be a list \
of components",
component);
return Err(Error::InvalidPropertyType);
}
}
}
Ok(Vec::new())
}
fn parse_hook_exec(component: &str, cfg: &config::Config, hook: &str) -> Result<String, Error> {
let key = format!("/subcomponents/{}/{}/exec", component, hook);
if let Some(prop) = cfg.get(&key) {
match prop.deref().value_get() {
PropertyValue::StringValue(val) => { return Ok(val.clone()); },
_ => { return Err(Error::InvalidPropertyType); }
}
}
Err(Error::MissingRequiredProperty)
}
fn parse_hook_args(component: &str, cfg: &config::Config, hook: &str) -> Result<Option<Vec<String>>, Error> {
let key = format!("/subcomponents/{}/{}/args", component, hook);
if let Some(prop) = cfg.get(&key) {
match prop.deref().value_get() {
PropertyValue::StringValue(val) => { return Ok(Some(vec![val.clone()])); },
PropertyValue::StringListValue(val) => { return Ok(Some(val.clone())); },
_ => { return Err(Error::InvalidPropertyType); }
}
}
Ok(None)
}
fn parse_hook_cwd(component: &str, cfg: &config::Config, hook: &str) -> Result<Option<String>, Error> {
let key = format!("/subcomponents/{}/{}/cwd", component, hook);
if let Some(prop) = cfg.get(&key) {
match prop.deref().value_get() {
PropertyValue::StringValue(val) => { return Ok(Some(val.clone())); },
_ => { return Err(Error::InvalidPropertyType); }
}
}
Ok(None)
}
fn parse_component(component: &str, cfg: &config::Config) -> Result<config::Component, Error> {
let name = Parser::name_prop_get(component, cfg)?;
let path = Parser::path_prop_get(component, cfg)?;
let dependencies = Parser::depends_prop_get(component, cfg)?;
let hooks_names = cfg.get_hooks_for_component(component)?;
let mut hooks = Vec::new();
for hook_ref in hooks_names {
let exec = Parser::parse_hook_exec(component, cfg, &hook_ref)?;
let args = Parser::parse_hook_args(component, cfg, &hook_ref)?;
let cwd = match Parser::parse_hook_cwd(component, cfg, &hook_ref)? {
Some(cwd_arg) => { cwd_arg },
None => { path.clone() },
};
let hook = hook::Hook::new(hook_ref, exec, cwd, args);
hooks.push(hook);
}
Ok(config::Component::new(String::from(component), name, path, dependencies, hooks))
}
fn collect_components(&mut self) -> Result<(), Error> {
let components = self.cfg.collect_components()?;
for component in &components {
let comp = Parser::parse_component(component, &self.cfg)?;
self.components.push(RefCell::new(comp));
}
Ok(())
}
fn token_value_check(&self, tok: &LexemeKind) -> bool {
match *tok {
LexemeKind::Identifier | LexemeKind::String => true,
_ => false,
}
}
fn tokens_list_items_check(&self, tok_left: &LexemeKind, tok_right: &LexemeKind) -> bool {
if tok_right == &LexemeKind::ListSeparator {
if self.token_value_check(tok_left) {
return true;
}
}
false
}
fn sequence_get(&self, tokens: &[lexer::Lexeme]) -> (Sequence, usize) {
let tokens_count = tokens.len();
macro_rules! tok {
($x:expr) => {{
let kind = tokens[$x].kind_get();
kind
}}
}
if tokens_count >= 1 {
let seq = [tok!(0)];
let comp = [&LexemeKind::BlockEnd];
if seq == comp {
return (Sequence::BlockEnd, 1);
}
}
if tokens_count >= 6 {
let seq_pre = [tok!(0), tok!(1), tok!(2), tok!(3)];
let comp = [
&LexemeKind::Identifier, &LexemeKind::PropertyEnd,
&LexemeKind::String, &LexemeKind::ListSeparator,
];
if seq_pre == comp {
let mut i = 2;
while i + 2 <= tokens_count {
let left = tokens[i].kind_get();
let right = tokens[i + 1].kind_get();
if self.tokens_list_items_check(left, right) {
} else if right == &LexemeKind::StatementEnd {
return (Sequence::StringList, i + 2);
} else {
break;
}
i += 2;
}
}
}
if tokens_count >= 4 {
let seq = [tok!(0), tok!(1), tok!(3)];
let comp = [
&LexemeKind::Identifier, &LexemeKind::PropertyEnd,
&LexemeKind::StatementEnd,
];
if (seq == comp) && self.token_value_check(tok!(2)) {
return (Sequence::Property, 4);
}
}
if tokens_count >= 2 {
let seq = [tok!(0), tok!(1)];
let comp = [&LexemeKind::Identifier, &LexemeKind::BlockStart];
if seq == comp {
return (Sequence::BlockStart, 2);
}
}
error!("[{}] syntax error near l{}:c{}",
tokens[0].file, tokens[0].line, tokens[0].column);
(Sequence::Invalid, 0)
}
pub fn parse(&mut self, lexer: &lexer::Lexer) -> Result<(), Error> {
let mut iter = 0;
let mut blocks_match = 0;
let tokens = lexer.tokens_get();
let iter_max = tokens.len();
macro_rules! config_get {
() => {{
if let Some(config) = Rc::get_mut(&mut self.cfg) {
config
} else {
error!("Failed to get a mutable reference on the config");
return Err(Error::RuntimeError);
}
}}
}
macro_rules! token_extract {
($x:expr) => {{
match lexer.token_extract(iter + $x) {
Some(val) => val,
None => {
error!("Missing value or identifier");
return Err(Error::ExpectedIdentifier);
}
}
}}
}
let mut pkey = String::from("/");
while iter < iter_max {
let (seq, count) = self.sequence_get(&tokens[iter..]);
match seq {
Sequence::BlockStart => {
blocks_match += 1;
let tok = token_extract!(0);
pkey.push_str(tok);
pkey.push_str("/");
let config = config_get!();
config.set(pkey.clone(), Box::new(()));
},
Sequence::BlockEnd => {
blocks_match -= 1;
if pkey.chars().last().unwrap() == '/' {
pkey.pop();
}
if let Some(index) = pkey.rfind('/') {
for _iter in index..pkey.len()-1 {
pkey.pop();
}
}
},
Sequence::StringList => {
let prop_name = token_extract!(0);
let elements = ((count - 3) / 2) + 1;
let mut list = Vec::new();
for i in 0..elements {
if let Some(tok) = lexer.token_extract(iter + ((i + 1) * 2)) {
list.push(String::from(tok));
}
}
let mut key = pkey.clone();
key.push_str(prop_name);
trace!("Parsed property list \"{}\" -> {}", key, list.value_get());
if self.cfg.get(&key).is_some() {
warn!("Overriding property {}", key);
}
let config = config_get!();
config.set(key, Box::new(list));
},
Sequence::Property => {
let prop_tok = token_extract!(0);
let val_tok = token_extract!(2);
let val = String::from(val_tok);
let data: Box<Property>;
let mut key = pkey.clone();
key.push_str(prop_tok);
match *tokens[iter + 2].kind_get() {
LexemeKind::String => {
data = Box::new(val);
},
LexemeKind::Identifier => {
match val_tok {
"true" => {
data = Box::new(true);
},
"false" => {
data = Box::new(false);
},
_ => {
data = Box::new(Identifier::new(val));
}
}
},
_ => {
return Err(Error::InvalidSequence);
}
}
trace!("Parsed property \"{}\" -> {}", key, data.value_get());
if self.cfg.get(&key).is_some() {
warn!("Overriding property {}", key);
}
let config = config_get!();
config.set(key, data);
},
Sequence::Invalid => {
error!("Invalid sequence of tokens. Expected a propery or a group declaration.");
return Err(Error::InvalidSequence);
},
}
iter += count;
}
if blocks_match != 0 {
error!("Unbalanced number of braces.");
if blocks_match > 0 {
return Err(Error::TooManyBraces);
} else {
return Err(Error::NotEnoughBraces);
}
}
try!(self.collect_components());
if self.components.is_empty() {
return Err(NoSubcomponents);
}
try!(fetcher::parse(self));
self.cfg.check_unused()
}
}
#[cfg(test)]
mod tests {
extern crate std;
use compiler;
use compiler::parser::Error;
fn test_file_get(name: &str, ext: &str) -> std::path::PathBuf {
let mut path = std::path::PathBuf::new();
path.push("tests");
path.push("parser");
path.push(name);
path.set_extension(ext);
path
}
fn test_source_get(name: &str) -> std::path::PathBuf {
test_file_get(name, "sub")
}
fn test_check_run(parser_test: &str, expected: Option<Error>) {
let source = test_source_get(parser_test);
let source_path = source.as_path();
let mut lexer = compiler::lexer::new();
match lexer.lex(source_path) {
Ok(_) => {
let mut parser = compiler::parser::Parser::new();
let err = parser.parse(&lexer).err();
println!("Parser error: {:?}. Expected: {:?}.", err, expected);
assert!(err == expected);
}
Err(err) => {
println!("*** Lexer error: {}", err);
assert!(false);
}
}
}
#[test] fn parse_valid_01() { test_check_run("ok-01", None); }
#[test] fn parse_valid_02() { test_check_run("ok-02", None); }
#[test] fn parse_valid_03() { test_check_run("ok-03", None); }
#[test] fn parse_valid_04() { test_check_run("ok-04", None); }
#[test] fn parse_valid_05() { test_check_run("ok-05", None); }
#[test] fn parse_valid_06() { test_check_run("ok-06", None); }
#[test] fn parse_invalid_01()
{ test_check_run("fail-01", Some(Error::NoSubcomponents)); }
#[test] fn parse_invalid_02()
{ test_check_run("fail-02", Some(Error::NoSubcomponents)); }
#[test] fn parse_invalid_03()
{ test_check_run("fail-03", Some(Error::MissingRequiredProperty)); }
#[test] fn parse_invalid_04()
{ test_check_run("fail-04", Some(Error::MissingRequiredProperty)); }
#[test] fn parse_invalid_05()
{ test_check_run("fail-05", Some(Error::ConflictingProperties)); }
#[test] fn parse_invalid_06()
{ test_check_run("fail-06", Some(Error::TooManyBraces)); }
#[test] fn parse_invalid_07()
{ test_check_run("fail-07", Some(Error::NotEnoughBraces)); }
#[test] fn parse_invalid_08()
{ test_check_run("fail-08", Some(Error::InvalidPropertyName)); }
#[test] fn parse_invalid_10()
{ test_check_run("fail-10", Some(Error::ReservedHook)); }
#[test] fn parse_invalid_11()
{ test_check_run("fail-11", Some(Error::MissingRequiredProperty)); }
}