mirror of
https://github.com/fluencelabs/lalrpop
synced 2025-03-16 17:00:53 +00:00
Introduce a Configuration
builder for advanced usage; fix docopt handling.
This commit is contained in:
parent
3dd9b2c157
commit
d70cfac7d8
121
lalrpop/src/api/mod.rs
Normal file
121
lalrpop/src/api/mod.rs
Normal file
@ -0,0 +1,121 @@
|
||||
use build;
|
||||
use log::Level;
|
||||
use session::{ColorConfig, Session};
|
||||
use std::default::Default;
|
||||
use std::env::current_dir;
|
||||
use std::error::Error;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Configure various aspects of how LALRPOP works.
|
||||
/// Intended for use within a `build.rs` script.
|
||||
/// To get the default configuration, use `Configuration::new`.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Configuration {
|
||||
session: Session
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
/// Creates the default configuration; equivalent to `Configuration::default`.
|
||||
pub fn new() -> Configuration {
|
||||
Configuration::default()
|
||||
}
|
||||
|
||||
/// Always use ANSI colors in output, even if output does not appear to be a TTY.
|
||||
pub fn always_use_colors(&mut self) -> &mut Configuration {
|
||||
self.session.color_config = ColorConfig::Yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Never use ANSI colors in output, even if output appears to be a TTY.
|
||||
pub fn never_use_colors(&mut self) -> &mut Configuration {
|
||||
self.session.color_config = ColorConfig::No;
|
||||
self
|
||||
}
|
||||
|
||||
/// Use ANSI colors in output if output appears to be a TTY, but
|
||||
/// not otherwise. This is the default.
|
||||
pub fn use_colors_if_tty(&mut self) -> &mut Configuration {
|
||||
self.session.color_config = ColorConfig::IfTty;
|
||||
self
|
||||
}
|
||||
|
||||
/// If true, always convert `.lalrpop` files into `.rs` files, even if the
|
||||
/// `.rs` file is newer. Default is false.
|
||||
pub fn force_build(&mut self, val: bool) -> &mut Configuration {
|
||||
self.session.force_build = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// If true, emit comments into the generated code. This makes the
|
||||
/// generated code significantly larger. Default is false.
|
||||
pub fn emit_comments(&mut self, val: bool) -> &mut Configuration {
|
||||
self.session.emit_comments = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Minimal logs: only for errors that halt progress.
|
||||
pub fn log_quiet(&mut self) -> &mut Configuration {
|
||||
self.session.log.set_level(Level::Taciturn);
|
||||
self
|
||||
}
|
||||
|
||||
/// Informative logs: give some high-level indications of
|
||||
/// progress (default).
|
||||
pub fn log_info(&mut self) -> &mut Configuration {
|
||||
self.session.log.set_level(Level::Informative);
|
||||
self
|
||||
}
|
||||
|
||||
/// Verbose logs: more than info, but still not overwhelming.
|
||||
pub fn log_verbose(&mut self) -> &mut Configuration {
|
||||
self.session.log.set_level(Level::Verbose);
|
||||
self
|
||||
}
|
||||
|
||||
/// Debug logs: better redirect this to a file. Intended for
|
||||
/// debugging LALRPOP itself.
|
||||
pub fn log_debug(&mut self) -> &mut Configuration {
|
||||
self.session.log.set_level(Level::Debug);
|
||||
self
|
||||
}
|
||||
|
||||
/// Process all files in the current directory, which -- unless you
|
||||
/// have changed it -- is typically the root of the crate being compiled.
|
||||
pub fn process_current_dir(&self) -> Result<(), Box<Error>> {
|
||||
self.process_dir(try!(current_dir()))
|
||||
}
|
||||
|
||||
/// Process all `.lalrpop` files in `path`.
|
||||
pub fn process_dir<P:AsRef<Path>>(&self, path: P) -> Result<(), Box<Error>> {
|
||||
let session = Rc::new(self.session.clone());
|
||||
try!(build::process_dir(session, path));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Process the given `.lalrpop` file.
|
||||
pub fn process_file<P:AsRef<Path>>(&self, path: P) -> Result<(), Box<Error>> {
|
||||
let session = Rc::new(self.session.clone());
|
||||
try!(build::process_file(session, path));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Process all files in the current directory, which -- unless you
|
||||
/// have changed it -- is typically the root of the crate being compiled.
|
||||
///
|
||||
/// Equivalent to `Configuration::new().process_current_dir()`.
|
||||
pub fn process_root() -> Result<(), Box<Error>> {
|
||||
Configuration::new().process_current_dir()
|
||||
}
|
||||
|
||||
/// Deprecated in favor of `Configuration`. Try:
|
||||
///
|
||||
/// ```rust
|
||||
/// Configuration::new().force_build(true).process_current_dir()
|
||||
/// ```
|
||||
///
|
||||
/// instead.
|
||||
pub fn process_root_unconditionally() -> Result<(), Box<Error>> {
|
||||
Configuration::new().force_build(true).process_current_dir()
|
||||
}
|
@ -17,7 +17,6 @@ use term;
|
||||
use tls::Tls;
|
||||
use tok;
|
||||
|
||||
use std::env::current_dir;
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -29,33 +28,18 @@ mod fake_term;
|
||||
|
||||
use self::fake_term::FakeTerminal;
|
||||
|
||||
pub fn process_root() -> io::Result<()> {
|
||||
let session = Session::new();
|
||||
process_dir(&session, try!(current_dir()))
|
||||
}
|
||||
|
||||
pub fn process_root_unconditionally() -> io::Result<()> {
|
||||
let mut session = Session::new();
|
||||
session.set_force_build();
|
||||
process_dir(&session, try!(current_dir()))
|
||||
}
|
||||
|
||||
fn process_dir<P:AsRef<Path>>(session: &Session, root_dir: P) -> io::Result<()> {
|
||||
pub fn process_dir<P:AsRef<Path>>(session: Rc<Session>, root_dir: P) -> io::Result<()> {
|
||||
let lalrpop_files = try!(lalrpop_files(root_dir));
|
||||
for lalrpop_file in lalrpop_files {
|
||||
try!(process_file(session, lalrpop_file));
|
||||
try!(process_file(session.clone(), lalrpop_file));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn process_file<P:AsRef<Path>>(session: &Session, lalrpop_file: P) -> io::Result<()> {
|
||||
// Promote the session to an Rc so that we can stick it in TLS. I
|
||||
// don't want this to be part of LALRPOP's "official" interface
|
||||
// yet so don't take an `Rc<Session>` as an argument.
|
||||
let session = Rc::new(session.clone());
|
||||
pub fn process_file<P:AsRef<Path>>(session: Rc<Session>, lalrpop_file: P) -> io::Result<()> {
|
||||
let lalrpop_file: &Path = lalrpop_file.as_ref();
|
||||
let rs_file = lalrpop_file.with_extension("rs");
|
||||
if session.force_build() || try!(needs_rebuild(&lalrpop_file, &rs_file)) {
|
||||
if session.force_build || try!(needs_rebuild(&lalrpop_file, &rs_file)) {
|
||||
log!(session, Informative, "processing file `{}`", lalrpop_file.to_string_lossy());
|
||||
try!(make_read_only(&rs_file, false));
|
||||
try!(remove_old_file(&rs_file));
|
||||
@ -263,7 +247,7 @@ fn report_content(content: &Content) -> term::Result<()> {
|
||||
// FIXME -- can we query the size of the terminal somehow?
|
||||
let canvas = content.emit_to_canvas(80);
|
||||
|
||||
let try_colors = match Tls::session().color_config() {
|
||||
let try_colors = match Tls::session().color_config {
|
||||
ColorConfig::Yes => true,
|
||||
ColorConfig::No => false,
|
||||
ColorConfig::IfTty => atty::is(),
|
||||
|
@ -31,6 +31,7 @@ mod rust;
|
||||
#[macro_use]
|
||||
mod log;
|
||||
|
||||
mod api;
|
||||
mod ascii_canvas;
|
||||
mod build;
|
||||
mod collections;
|
||||
@ -51,10 +52,7 @@ mod util;
|
||||
#[cfg(test)] mod generate;
|
||||
#[cfg(test)] mod test_util;
|
||||
|
||||
pub use build::process_root;
|
||||
pub use build::process_root_unconditionally;
|
||||
pub use build::process_file;
|
||||
pub use log::Level;
|
||||
pub use log::Log;
|
||||
pub use session::ColorConfig;
|
||||
pub use session::Session;
|
||||
pub use api::Configuration;
|
||||
pub use api::process_root;
|
||||
pub use api::process_root_unconditionally;
|
||||
|
||||
|
@ -247,7 +247,7 @@ impl<'ascent,'grammar,W:Write> RecursiveAscent<'ascent,'grammar,W> {
|
||||
};
|
||||
|
||||
// Leave a comment explaining what this state is.
|
||||
if Tls::session().emit_comments() {
|
||||
if Tls::session().emit_comments {
|
||||
rust!(self.out, "// State {}", this_index.0);
|
||||
for item in this_state.items.vec.iter() {
|
||||
rust!(self.out, "// {:?}", item);
|
||||
|
@ -3,7 +3,7 @@ extern crate lalrpop;
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use docopt::Docopt;
|
||||
use lalrpop::{process_file, Level, ColorConfig, Session};
|
||||
use lalrpop::Configuration;
|
||||
use std::env;
|
||||
use std::io::{self, Write};
|
||||
use std::process;
|
||||
@ -20,39 +20,34 @@ fn main1() -> io::Result<()> {
|
||||
.and_then(|d| d.argv(env::args()).decode())
|
||||
.unwrap_or_else(|e| e.exit());
|
||||
|
||||
let mut session = Session::new();
|
||||
let mut config = Configuration::new();
|
||||
|
||||
match args.flag_level.unwrap_or(LevelFlag::Info) {
|
||||
LevelFlag::Quiet => session.set_log_level(Level::Taciturn),
|
||||
LevelFlag::Info => session.set_log_level(Level::Informative),
|
||||
LevelFlag::Verbose => session.set_log_level(Level::Verbose),
|
||||
LevelFlag::Debug => session.set_log_level(Level::Debug),
|
||||
}
|
||||
LevelFlag::Quiet => config.log_quiet(),
|
||||
LevelFlag::Info => config.log_info(),
|
||||
LevelFlag::Verbose => config.log_verbose(),
|
||||
LevelFlag::Debug => config.log_debug(),
|
||||
};
|
||||
|
||||
if args.flag_force {
|
||||
session.set_force_build();
|
||||
}
|
||||
|
||||
if args.flag_help {
|
||||
try!(writeln!(stderr, "{}", USAGE));
|
||||
process::exit(1);
|
||||
config.force_build(true);
|
||||
}
|
||||
|
||||
if args.flag_color {
|
||||
session.set_color_config(ColorConfig::Yes);
|
||||
config.always_use_colors();
|
||||
}
|
||||
|
||||
if args.flag_comments {
|
||||
session.set_emit_comments();
|
||||
config.emit_comments(true);
|
||||
}
|
||||
|
||||
if args.arg_inputs.len() == 0 {
|
||||
try!(writeln!(stderr, "Error: no input files specified! Try -h for help."));
|
||||
try!(writeln!(stderr, "Error: no input files specified! Try --help for help."));
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
for arg in args.arg_inputs {
|
||||
match process_file(&session, &arg) {
|
||||
match config.process_file(&arg) {
|
||||
Ok(()) => { }
|
||||
Err(err) => {
|
||||
try!(writeln!(stderr, "Error encountered processing `{}`: {}",
|
||||
@ -66,10 +61,13 @@ fn main1() -> io::Result<()> {
|
||||
}
|
||||
|
||||
const USAGE: &'static str = "
|
||||
Usage: lalrpop [options] <inputs>...
|
||||
Usage: lalrpop [options] inputs...
|
||||
lalrpop --help
|
||||
|
||||
Convert each of the given inputs (which should be a `.lalrpop` file)
|
||||
into a `.rs` file, just as a `build.rs` script using LALRPOP would do.
|
||||
|
||||
Options:
|
||||
-h, --help Show this message.
|
||||
-l, --level LEVEL Set the debug level. (Default: info)
|
||||
Valid values: quiet, info, verbose, debug.
|
||||
-f, --force Force execution, even if the .lalrpop file is older than the .rs file.
|
||||
@ -82,7 +80,6 @@ struct Args {
|
||||
arg_inputs: Vec<String>,
|
||||
flag_level: Option<LevelFlag>,
|
||||
flag_force: bool,
|
||||
flag_help: bool,
|
||||
flag_color: bool,
|
||||
flag_comments: bool,
|
||||
}
|
||||
|
@ -1,7 +1,14 @@
|
||||
//! Internal configuration and session-specific settings. This is similar
|
||||
//! to `configuration::Configuration`, but it is not exported outside the
|
||||
//! crate. Note that all fields are public and so forth for convenience.
|
||||
|
||||
use std::default::Default;
|
||||
use style::{self, Style};
|
||||
use log::{Log, Level};
|
||||
|
||||
// These two, ubiquitous types are defined here so that their fields can be private
|
||||
// across crate, but visible within the crate:
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ColorConfig {
|
||||
/// Use ANSI colors.
|
||||
@ -20,20 +27,20 @@ pub enum ColorConfig {
|
||||
/// expected to use it.
|
||||
#[derive(Clone)]
|
||||
pub struct Session {
|
||||
log: Log,
|
||||
pub log: Log,
|
||||
|
||||
force_build: bool,
|
||||
pub force_build: bool,
|
||||
|
||||
/// Emit comments in generated code explaining the states and so
|
||||
/// forth.
|
||||
emit_comments: bool,
|
||||
pub emit_comments: bool,
|
||||
|
||||
color_config: ColorConfig,
|
||||
pub color_config: ColorConfig,
|
||||
|
||||
/// Stop after you find `max_errors` errors. If this value is 0,
|
||||
/// report *all* errors. Note that we MAY always report more than
|
||||
/// this value if we so choose.
|
||||
max_errors: usize,
|
||||
pub max_errors: usize,
|
||||
|
||||
// Styles to use when formatting error reports
|
||||
|
||||
@ -102,44 +109,12 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color_config(&self) -> ColorConfig {
|
||||
self.color_config
|
||||
}
|
||||
|
||||
pub fn set_color_config(&mut self, config: ColorConfig) {
|
||||
self.color_config = config;
|
||||
}
|
||||
|
||||
pub fn set_force_build(&mut self) {
|
||||
self.force_build = true;
|
||||
}
|
||||
|
||||
pub fn set_emit_comments(&mut self) {
|
||||
self.emit_comments = true;
|
||||
}
|
||||
|
||||
pub fn set_max_errors(&mut self, errors: usize) {
|
||||
self.max_errors = errors;
|
||||
}
|
||||
|
||||
pub fn set_log_level(&mut self, level: Level) {
|
||||
self.log.set_level(level);
|
||||
}
|
||||
|
||||
/// Indicates whether we should stop after `actual_errors` number
|
||||
/// of errors have been reported.
|
||||
pub fn stop_after(&self, actual_errors: usize) -> bool {
|
||||
self.max_errors != 0 && actual_errors >= self.max_errors
|
||||
}
|
||||
|
||||
pub fn force_build(&self) -> bool {
|
||||
self.force_build
|
||||
}
|
||||
|
||||
pub fn emit_comments(&self) -> bool {
|
||||
self.emit_comments
|
||||
}
|
||||
|
||||
pub fn log<M>(&self, level: Level, message: M)
|
||||
where M: FnOnce() -> String
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user