DEV: More of the ucg commandline works now.

This commit is contained in:
Jeremy Wall 2019-09-02 17:22:58 -05:00
parent b3fd37a6b5
commit d092fba5e8
7 changed files with 150 additions and 136 deletions

View File

@ -88,10 +88,10 @@ impl AssertCollector {
/// Builder handles building ucg code for a single file. /// Builder handles building ucg code for a single file.
pub struct FileBuilder<'a, Stdout, Stderr> pub struct FileBuilder<'a, Stdout, Stderr>
where where
Stdout: std::io::Write, Stdout: std::io::Write + Clone,
Stderr: std::io::Write, Stderr: std::io::Write + Clone,
{ {
environment: Rc<RefCell<Environment<Stdout, Stderr>>>, pub environment: Rc<RefCell<Environment<Stdout, Stderr>>>,
working_dir: PathBuf, working_dir: PathBuf,
// FIXME(jwall): These need to be compiled and added to the op cache. // FIXME(jwall): These need to be compiled and added to the op cache.
// specifically in the environment. // specifically in the environment.
@ -104,8 +104,8 @@ where
impl<'a, Stdout, Stderr> FileBuilder<'a, Stdout, Stderr> impl<'a, Stdout, Stderr> FileBuilder<'a, Stdout, Stderr>
where where
Stdout: std::io::Write, Stdout: std::io::Write + Clone,
Stderr: std::io::Write, Stderr: std::io::Write + Clone,
{ {
/// Constructs a new Builder. /// Constructs a new Builder.
pub fn new<P: Into<PathBuf>>( pub fn new<P: Into<PathBuf>>(
@ -148,7 +148,7 @@ where
f.read_to_string(&mut s)?; f.read_to_string(&mut s)?;
let input = OffsetStrIter::new(&s).with_src_file(file.clone()); let input = OffsetStrIter::new(&s).with_src_file(file.clone());
// TODO(jwall): Pass in the file name? // TODO(jwall): Pass in the file name?
let eval_result = self.eval_input(input); let eval_result = self.eval_input(input, Some(file.clone()));
match eval_result { match eval_result {
Ok(v) => { Ok(v) => {
self.last = Some(v); self.last = Some(v);
@ -187,10 +187,13 @@ where
} }
/// Builds a list of parsed UCG Statements. /// Builds a list of parsed UCG Statements.
pub fn eval_stmts(&mut self, ast: Vec<Statement>) -> BuildResult { pub fn eval_stmts(&mut self, ast: Vec<Statement>, path: Option<PathBuf>) -> BuildResult {
// We should probably stash this in an op_cache somewhere? // We should probably stash this in an op_cache somewhere?
let ops = translate::AST::translate(ast, &self.working_dir); let ops = translate::AST::translate(ast, &self.working_dir);
let mut vm = VM::new(Rc::new(ops), self.environment.clone(), &self.working_dir); let mut vm = VM::new(Rc::new(ops), self.environment.clone(), &self.working_dir);
if path.is_some() {
vm.set_path(path.unwrap());
}
if self.validate_mode { if self.validate_mode {
vm.enable_validate_mode(); vm.enable_validate_mode();
} }
@ -199,10 +202,14 @@ where
Ok(()) Ok(())
} }
pub fn eval_input(&mut self, input: OffsetStrIter) -> Result<Rc<Val>, Box<dyn Error>> { pub fn eval_input(
&mut self,
input: OffsetStrIter,
path: Option<PathBuf>,
) -> Result<Rc<Val>, Box<dyn Error>> {
match parse(input.clone(), None) { match parse(input.clone(), None) {
Ok(stmts) => { Ok(stmts) => {
self.eval_stmts(stmts)?; self.eval_stmts(stmts, path)?;
if let Some(v) = self.out.clone() { if let Some(v) = self.out.clone() {
return Ok(v); return Ok(v);
} }
@ -214,7 +221,7 @@ where
/// Evaluate an input string as UCG. /// Evaluate an input string as UCG.
pub fn eval_string(&mut self, input: &str) -> Result<Rc<Val>, Box<dyn Error>> { pub fn eval_string(&mut self, input: &str) -> Result<Rc<Val>, Box<dyn Error>> {
self.eval_input(OffsetStrIter::new(input)) self.eval_input(OffsetStrIter::new(input), None)
} }
pub fn eval_expr(&mut self, expr: Expression) -> Result<Rc<Val>, Box<dyn Error>> { pub fn eval_expr(&mut self, expr: Expression) -> Result<Rc<Val>, Box<dyn Error>> {

View File

@ -55,6 +55,8 @@ impl<'a> Entry<'a> {
v v
} }
}; };
Ok(OpPointer::new(cached).with_path(path.into())) let mut ptr = OpPointer::new(cached);
ptr.set_path(path.into());
Ok(ptr)
} }
} }

View File

@ -22,6 +22,7 @@ use super::pointer::OpPointer;
use super::Error; use super::Error;
use super::Value; use super::Value;
use crate::build::AssertCollector; use crate::build::AssertCollector;
use crate::build::Val;
use crate::convert::{ConverterRegistry, ImporterRegistry}; use crate::convert::{ConverterRegistry, ImporterRegistry};
use crate::iter::OffsetStrIter; use crate::iter::OffsetStrIter;
use crate::parse::parse; use crate::parse::parse;
@ -29,8 +30,8 @@ use crate::parse::parse;
// Shared Environmental between VM's for runtime usage. // Shared Environmental between VM's for runtime usage.
pub struct Environment<Stdout, Stderr> pub struct Environment<Stdout, Stderr>
where where
Stdout: Write, Stdout: Write + Clone,
Stderr: Write, Stderr: Write + Clone,
{ {
pub val_cache: BTreeMap<String, Rc<Value>>, pub val_cache: BTreeMap<String, Rc<Value>>,
pub op_cache: cache::Ops, pub op_cache: cache::Ops,
@ -43,7 +44,7 @@ where
pub out_lock: BTreeSet<PathBuf>, pub out_lock: BTreeSet<PathBuf>,
} }
impl<Stdout: Write, Stderr: Write> Environment<Stdout, Stderr> { impl<Stdout: Write + Clone, Stderr: Write + Clone> Environment<Stdout, Stderr> {
pub fn new(out: Stdout, err: Stderr) -> Self { pub fn new(out: Stdout, err: Stderr) -> Self {
Self::new_with_vars(out, err, BTreeMap::new()) Self::new_with_vars(out, err, BTreeMap::new())
} }
@ -111,4 +112,32 @@ impl<Stdout: Write, Stderr: Write> Environment<Stdout, Stderr> {
pub fn reset_out_lock_for_path<P: AsRef<Path>>(&mut self, path: P) { pub fn reset_out_lock_for_path<P: AsRef<Path>>(&mut self, path: P) {
self.out_lock.remove(path.as_ref()); self.out_lock.remove(path.as_ref());
} }
pub fn stdout(&self) -> Stdout {
self.stdout.clone()
}
pub fn stderr(&self) -> Stderr {
self.stderr.clone()
}
pub fn convert_val(&mut self, typ: &str, writer: &mut dyn Write, val: Rc<Val>) -> bool {
match self.converter_registry.get_converter(typ) {
Some(c) => {
if let Err(e) = c.convert(val, writer) {
writeln!(&mut self.stderr, "{}", e).unwrap();
return false;
}
}
None => {
writeln!(
&mut self.stderr,
"No such format {}\nrun `ucg converters` to see available formats.",
typ
)
.unwrap();
return false;
}
}
return true;
}
} }

View File

@ -36,9 +36,8 @@ impl OpPointer {
} }
} }
pub fn with_path(mut self, path: PathBuf) -> Self { pub fn set_path(&mut self, path: PathBuf) {
self.path = Some(path); self.path = Some(path);
self
} }
pub fn next(&mut self) -> Option<&Op> { pub fn next(&mut self) -> Option<&Op> {

View File

@ -65,10 +65,10 @@ impl Builtins {
pos: Position, pos: Position,
) -> Result<(), Error> ) -> Result<(), Error>
where where
P: AsRef<Path>, P: AsRef<Path> + Debug,
WP: Into<PathBuf> + Clone + Debug, WP: Into<PathBuf> + Clone + Debug,
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
{ {
match h { match h {
Hook::Import => self.import(working_dir, stack, env, import_stack, pos), Hook::Import => self.import(working_dir, stack, env, import_stack, pos),
@ -168,8 +168,8 @@ impl Builtins {
pos: Position, pos: Position,
) -> Result<(), Error> ) -> Result<(), Error>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
P: Into<PathBuf> + Clone + Debug, P: Into<PathBuf> + Clone + Debug,
{ {
let path = stack.pop(); let path = stack.pop();
@ -219,8 +219,8 @@ impl Builtins {
pos: Position, pos: Position,
) -> Result<(), Error> ) -> Result<(), Error>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
P: Into<PathBuf> + Clone + Debug, P: Into<PathBuf> + Clone + Debug,
{ {
let path = stack.pop(); let path = stack.pop();
@ -282,8 +282,8 @@ impl Builtins {
env: Rc<RefCell<Environment<O, E>>>, env: Rc<RefCell<Environment<O, E>>>,
) -> Result<(), Error> ) -> Result<(), Error>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
{ {
if let Some((tuple, tpl_pos)) = stack.pop() { if let Some((tuple, tpl_pos)) = stack.pop() {
if let &Value::C(Tuple(ref tuple_flds, _)) = tuple.as_ref() { if let &Value::C(Tuple(ref tuple_flds, _)) = tuple.as_ref() {
@ -334,7 +334,7 @@ impl Builtins {
return Ok(()); return Ok(());
} }
fn out<P: AsRef<Path>, O, E>( fn out<P, O, E>(
&self, &self,
path: Option<P>, path: Option<P>,
stack: &mut Vec<(Rc<Value>, Position)>, stack: &mut Vec<(Rc<Value>, Position)>,
@ -342,17 +342,19 @@ impl Builtins {
pos: Position, pos: Position,
) -> Result<(), Error> ) -> Result<(), Error>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
P: AsRef<Path> + Debug,
{ {
let mut writer: Box<dyn std::io::Write> = if let Some(path) = path { let write_path: Option<PathBuf> = if let Some(path) = path {
if env.borrow().get_out_lock_for_path(path.as_ref()) { let write_path = path.as_ref().to_path_buf();
if env.borrow().get_out_lock_for_path(&path) {
return Err(Error::new( return Err(Error::new(
format!("You can only have one output per file"), format!("You can only have one output per file"),
pos)); pos));
} }
env.borrow_mut().set_out_lock_for_path(path.as_ref()); env.borrow_mut().set_out_lock_for_path(path.as_ref());
Box::new(File::create(path)?) Some(write_path)
} else { } else {
if env.borrow().get_out_lock_for_path("/dev/stdout") { if env.borrow().get_out_lock_for_path("/dev/stdout") {
return Err(Error::new( return Err(Error::new(
@ -360,7 +362,7 @@ impl Builtins {
pos)); pos));
} }
env.borrow_mut().set_out_lock_for_path("/dev/stdout"); env.borrow_mut().set_out_lock_for_path("/dev/stdout");
Box::new(std::io::stdout()) None
}; };
let val = stack.pop(); let val = stack.pop();
if let Some((val, val_pos)) = val { if let Some((val, val_pos)) = val {
@ -368,7 +370,15 @@ impl Builtins {
let c_type = stack.pop(); let c_type = stack.pop();
if let Some((c_type_val, c_type_pos)) = c_type { if let Some((c_type_val, c_type_pos)) = c_type {
if let &Value::P(Primitive::Str(ref c_type)) = c_type_val.as_ref() { if let &Value::P(Primitive::Str(ref c_type)) = c_type_val.as_ref() {
let stdout = env.borrow().stdout();
if let Some(c) = env.borrow().converter_registry.get_converter(c_type) { if let Some(c) = env.borrow().converter_registry.get_converter(c_type) {
let mut writer: Box<dyn std::io::Write> = match write_path {
Some(p) => {
let p = p.with_extension(c.file_ext());
Box::new(File::create(&p)?)
},
None => Box::new(stdout),
};
if let Err(e) = c.convert(Rc::new(val), &mut writer) { if let Err(e) = c.convert(Rc::new(val), &mut writer) {
return Err(Error::new(format!("{}", e), pos.clone())); return Err(Error::new(format!("{}", e), pos.clone()));
} }
@ -396,8 +406,8 @@ impl Builtins {
pos: Position, pos: Position,
) -> Result<(), Error> ) -> Result<(), Error>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
{ {
let val = stack.pop(); let val = stack.pop();
if let Some((val, val_pos)) = val { if let Some((val, val_pos)) = val {
@ -442,8 +452,8 @@ impl Builtins {
pos: Position, pos: Position,
) -> Result<(), Error> ) -> Result<(), Error>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
{ {
// get the list from the stack // get the list from the stack
let (list, list_pos) = if let Some(list) = stack.pop() { let (list, list_pos) = if let Some(list) = stack.pop() {
@ -554,8 +564,8 @@ impl Builtins {
pos: Position, pos: Position,
) -> Result<(), Error> ) -> Result<(), Error>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
{ {
// get the list from the stack // get the list from the stack
let (list, list_pos) = if let Some(list) = stack.pop() { let (list, list_pos) = if let Some(list) = stack.pop() {
@ -700,8 +710,8 @@ impl Builtins {
pos: Position, pos: Position,
) -> Result<(), Error> ) -> Result<(), Error>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
{ {
// get the list from the stack // get the list from the stack
let (list, list_pos) = if let Some(list) = stack.pop() { let (list, list_pos) = if let Some(list) = stack.pop() {
@ -839,8 +849,8 @@ impl Builtins {
env: Rc<RefCell<Environment<O, E>>>, env: Rc<RefCell<Environment<O, E>>>,
) -> Result<(), Error> ) -> Result<(), Error>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
{ {
let (val, val_pos) = if let Some(val) = stack.pop() { let (val, val_pos) = if let Some(val) = stack.pop() {
val val

View File

@ -43,8 +43,8 @@ fn construct_reserved_word_set() -> BTreeSet<&'static str> {
pub struct VM<O, E> pub struct VM<O, E>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
{ {
working_dir: PathBuf, working_dir: PathBuf,
stack: Vec<(Rc<Value>, Position)>, stack: Vec<(Rc<Value>, Position)>,
@ -60,8 +60,8 @@ where
impl<'a, O, E> VM<O, E> impl<'a, O, E> VM<O, E>
where where
O: std::io::Write, O: std::io::Write + Clone,
E: std::io::Write, E: std::io::Write + Clone,
{ {
pub fn new<P: Into<PathBuf>>( pub fn new<P: Into<PathBuf>>(
ops: Rc<PositionMap>, ops: Rc<PositionMap>,
@ -90,6 +90,10 @@ where
} }
} }
pub fn set_path(&mut self, path: PathBuf) {
self.ops.set_path(path);
}
pub fn to_new_pointer(mut self, ops: OpPointer) -> Self { pub fn to_new_pointer(mut self, ops: OpPointer) -> Self {
self.ops = ops; self.ops = ops;
self self

View File

@ -43,12 +43,6 @@ fn do_flags<'a, 'b>() -> clap::App<'a, 'b> {
(author: crate_authors!()) (author: crate_authors!())
(about: "Universal Configuration Grammar compiler.") (about: "Universal Configuration Grammar compiler.")
(@arg nostrict: --("no-strict") "Turn off strict checking.") (@arg nostrict: --("no-strict") "Turn off strict checking.")
(@subcommand eval =>
(about: "Evaluate an expression with an optional ucg file as context.")
(@arg expr: --expr -e +takes_value +required "Expression to evaluate.")
(@arg target: --format +takes_value "Output type. (flags, json, env, exec) defaults to json.")
(@arg INPUT: "ucg file to use as context for the expression.")
)
(@subcommand repl => (@subcommand repl =>
(about: "Start the ucg repl for interactive evaluation.") (about: "Start the ucg repl for interactive evaluation.")
) )
@ -82,33 +76,67 @@ fn do_flags<'a, 'b>() -> clap::App<'a, 'b> {
) )
} }
fn run_converter(c: &dyn traits::Converter, v: Rc<Val>, f: Option<&str>) -> traits::ConvertResult { struct StdoutWrapper(io::Stdout);
let mut file: Box<dyn std::io::Write> = match f {
Some(f) => { impl StdoutWrapper {
let mut path_buf = PathBuf::from(f); fn new() -> Self {
path_buf.set_extension(c.file_ext()); Self(io::stdout())
let new_path = path_buf.to_str().unwrap(); }
Box::new(File::create(&new_path)?)
}
None => Box::new(io::stdout()),
};
let result = c.convert(v, file.as_mut());
file.flush()?;
result
} }
impl io::Write for StdoutWrapper {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
impl Clone for StdoutWrapper {
fn clone(&self) -> Self {
Self(io::stdout())
}
}
struct StderrWrapper(io::Stderr);
impl StderrWrapper {
fn new() -> Self {
Self(io::stderr())
}
}
impl io::Write for StderrWrapper {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
}
impl Clone for StderrWrapper {
fn clone(&self) -> Self {
Self(io::stderr())
}
}
// TODO(jwall): Build sharable stdout stderr providers.
fn build_file<'a>( fn build_file<'a>(
file: &'a str, file: &'a str,
validate: bool, validate: bool,
strict: bool, strict: bool,
import_paths: &'a Vec<PathBuf>, import_paths: &'a Vec<PathBuf>,
) -> Result<build::FileBuilder<'a, Stdout, Stderr>, Box<dyn Error>> { ) -> Result<build::FileBuilder<'a, StdoutWrapper, StderrWrapper>, Box<dyn Error>> {
let mut file_path_buf = PathBuf::from(file); let mut file_path_buf = PathBuf::from(file);
if file_path_buf.is_relative() { if file_path_buf.is_relative() {
file_path_buf = std::env::current_dir()?.join(file_path_buf); file_path_buf = std::env::current_dir()?.join(file_path_buf);
} }
let out = std::io::stdout(); let out = StdoutWrapper::new();
let err = std::io::stderr(); let err = StderrWrapper::new();
let mut builder = build::FileBuilder::new(std::env::current_dir()?, import_paths, out, err); let mut builder = build::FileBuilder::new(std::env::current_dir()?, import_paths, out, err);
// FIXME(jwall): builder.set_strict(strict); // FIXME(jwall): builder.set_strict(strict);
if validate { if validate {
@ -217,69 +245,6 @@ fn visit_ucg_files(
Ok(result) Ok(result)
} }
fn inspect_command(matches: &clap::ArgMatches, import_paths: &Vec<PathBuf>, strict: bool) {
let file = matches.value_of("INPUT");
let sym = matches.value_of("expr");
let target = matches.value_of("target").unwrap_or("json");
let mut builder = build::FileBuilder::new(
std::env::current_dir().unwrap(),
import_paths,
io::stdout(),
io::stderr(),
);
// FIXME(jwall): builder.set_strict(strict);
// FIXME(jwall): Converting a value should be built into our builder?
//match registry.get_converter(target) {
// Some(converter) => {
// if let Some(file) = file {
// if let Err(e) = builder.build(file) {
// eprintln!("{:?}", e);
// process::exit(1);
// }
// }
// let val = match sym {
// Some(sym_name) => {
// let normalized = if !sym_name.ends_with(";") {
// let mut temp = sym_name.to_owned();
// temp.push_str(";");
// temp
// } else {
// sym_name.to_owned()
// };
// let mut builder = builder.clone_builder();
// match builder.eval_string(&normalized) {
// Ok(v) => Some(v.clone()),
// Err(e) => {
// eprintln!("{}", e);
// process::exit(1);
// }
// }
// }
// None => builder.last,
// };
// match val {
// Some(value) => {
// // We use None here because we always output to stdout for an inspect.
// run_converter(converter, value, None).unwrap();
// println!("");
// process::exit(0);
// }
// None => {
// eprintln!("No value.");
// process::exit(1);
// }
// }
// }
// None => {
// eprintln!(
// "No such format {}\nrun `ucg converters` to see available formats.",
// target
// );
// process::exit(1);
// }
//}
}
fn build_command(matches: &clap::ArgMatches, import_paths: &Vec<PathBuf>, strict: bool) { fn build_command(matches: &clap::ArgMatches, import_paths: &Vec<PathBuf>, strict: bool) {
let files = matches.values_of("INPUT"); let files = matches.values_of("INPUT");
let recurse = matches.is_present("recurse"); let recurse = matches.is_present("recurse");
@ -474,8 +439,8 @@ fn do_repl(import_paths: &Vec<PathBuf>) -> std::result::Result<(), Box<dyn Error
let mut builder = build::FileBuilder::new( let mut builder = build::FileBuilder::new(
std::env::current_dir()?, std::env::current_dir()?,
import_paths, import_paths,
io::stdout(), StdoutWrapper::new(),
io::stderr(), StderrWrapper::new(),
); );
// loop // loop
let mut lines = ucglib::io::StatementAccumulator::new(); let mut lines = ucglib::io::StatementAccumulator::new();
@ -575,9 +540,7 @@ fn main() {
} else { } else {
true true
}; };
if let Some(matches) = app_matches.subcommand_matches("eval") { if let Some(matches) = app_matches.subcommand_matches("build") {
inspect_command(matches, &import_paths, strict);
} else if let Some(matches) = app_matches.subcommand_matches("build") {
build_command(matches, &import_paths, strict); build_command(matches, &import_paths, strict);
} else if let Some(matches) = app_matches.subcommand_matches("test") { } else if let Some(matches) = app_matches.subcommand_matches("test") {
test_command(matches, &import_paths, strict); test_command(matches, &import_paths, strict);