ascesis 0.0.6

A language for analysis and synthesis of cause-effect synchronised interacting systems
Documentation
#[macro_use]
extern crate log;

use std::{fmt, error::Error};
use rand::{thread_rng, Rng};
use fern::colors::{Color, ColoredLevelConfig};
use ascesis::{Axiom, grammar::Grammar, sentence::Generator};

#[derive(Debug)]
struct RexError(String);

impl fmt::Display for RexError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl Error for RexError {}

fn random_phrase(axiom: &Axiom) -> Result<String, Box<dyn Error>> {
    let grammar = Grammar::of_ascesis();
    trace!("{:?}", grammar);

    let generator = Generator::new(&grammar);

    let mut all_phrases: Vec<_> = generator.rooted(axiom.symbol())?.iter().collect();

    if all_phrases.is_empty() {
        Err(Box::new(RexError(format!("Random phrase generation failed for {:?}.", axiom))))
    } else {
        let mut rng = thread_rng();
        let result = all_phrases.remove(rng.gen_range(0, all_phrases.len()));

        Ok(result)
    }
}

fn get_axiom_and_phrase(maybe_arg: Option<&str>) -> Result<(Axiom, String), Box<dyn Error>> {
    if let Some(axiom) = {
        if let Some(arg) = maybe_arg {
            let arg = arg.trim();
            if arg.starts_with(|c: char| c.is_uppercase()) {
                Axiom::from_known_symbol(arg)
            } else {
                None
            }
        } else {
            Axiom::from_known_symbol("Rex")
        }
    } {
        let phrase = random_phrase(&axiom)?;
        info!("{:?} generated \"{}\"", axiom, phrase);

        Ok((axiom, phrase))
    } else {
        let arg = maybe_arg.unwrap();
        let axiom = Axiom::guess_from_phrase(arg);
        let phrase = arg.to_owned();
        info!("{:?} guessed from \"{}\"", axiom, phrase);

        Ok((axiom, phrase))
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let args = clap::App::new("Rex")
        .version(env!("CARGO_PKG_VERSION"))
        .author(env!("CARGO_PKG_AUTHORS"))
        .about("Ascesis parsing demo")
        .args_from_usage(
            "[SENTENCE_OR_AXIOM] 'sentence or axiom to parse (default: \'Rex\')'
             -v, --verbose...    'level of verbosity'",
        )
        .get_matches();

    let log_level = match args.occurrences_of("verbose") {
        0 => log::LevelFilter::Info,
        1 => log::LevelFilter::Debug,
        _ => log::LevelFilter::Trace,
    };

    let colors = ColoredLevelConfig::new()
        .trace(Color::Blue)
        .debug(Color::Yellow)
        .info(Color::Green)
        .warn(Color::Magenta)
        .error(Color::Red);

    let console_logger = fern::Dispatch::new()
        .format(move |out, message, record| match record.level() {
            log::Level::Info => out.finish(format_args!("{}.", message)),
            log::Level::Warn | log::Level::Debug => {
                out.finish(format_args!("[{}]\t{}.", colors.color(record.level()), message))
            }
            _ => out.finish(format_args!(
                "[{}]\t\x1B[{}m{}.\x1B[0m",
                colors.color(record.level()),
                colors.get_color(&record.level()).to_fg_str(),
                message
            )),
        })
        .level(log_level)
        .chain(std::io::stdout());

    let root_logger = fern::Dispatch::new().chain(console_logger);
    root_logger.apply().unwrap_or_else(|err| eprintln!("[ERROR] {}.", err));

    let maybe_arg = args.value_of("SENTENCE_OR_AXIOM");
    let (axiom, phrase) = get_axiom_and_phrase(maybe_arg)?;

    let result = axiom.parse(phrase)?;
    println!("{:?}", result);

    Ok(())
}