From d092fba5e88d570f417d88b2cc3ccaba5f5098cd Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Mon, 2 Sep 2019 17:22:58 -0500 Subject: [PATCH] DEV: More of the ucg commandline works now. --- src/build/mod.rs | 27 +++--- src/build/opcode/cache.rs | 4 +- src/build/opcode/environment.rs | 35 +++++++- src/build/opcode/pointer.rs | 3 +- src/build/opcode/runtime.rs | 62 ++++++++------ src/build/opcode/vm.rs | 12 ++- src/main.rs | 143 ++++++++++++-------------------- 7 files changed, 150 insertions(+), 136 deletions(-) diff --git a/src/build/mod.rs b/src/build/mod.rs index 4a572f7..968919f 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -88,10 +88,10 @@ impl AssertCollector { /// Builder handles building ucg code for a single file. pub struct FileBuilder<'a, Stdout, Stderr> where - Stdout: std::io::Write, - Stderr: std::io::Write, + Stdout: std::io::Write + Clone, + Stderr: std::io::Write + Clone, { - environment: Rc>>, + pub environment: Rc>>, working_dir: PathBuf, // FIXME(jwall): These need to be compiled and added to the op cache. // specifically in the environment. @@ -104,8 +104,8 @@ where impl<'a, Stdout, Stderr> FileBuilder<'a, Stdout, Stderr> where - Stdout: std::io::Write, - Stderr: std::io::Write, + Stdout: std::io::Write + Clone, + Stderr: std::io::Write + Clone, { /// Constructs a new Builder. pub fn new>( @@ -148,7 +148,7 @@ where f.read_to_string(&mut s)?; let input = OffsetStrIter::new(&s).with_src_file(file.clone()); // 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 { Ok(v) => { self.last = Some(v); @@ -187,10 +187,13 @@ where } /// Builds a list of parsed UCG Statements. - pub fn eval_stmts(&mut self, ast: Vec) -> BuildResult { + pub fn eval_stmts(&mut self, ast: Vec, path: Option) -> BuildResult { // We should probably stash this in an op_cache somewhere? let ops = translate::AST::translate(ast, &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 { vm.enable_validate_mode(); } @@ -199,10 +202,14 @@ where Ok(()) } - pub fn eval_input(&mut self, input: OffsetStrIter) -> Result, Box> { + pub fn eval_input( + &mut self, + input: OffsetStrIter, + path: Option, + ) -> Result, Box> { match parse(input.clone(), None) { Ok(stmts) => { - self.eval_stmts(stmts)?; + self.eval_stmts(stmts, path)?; if let Some(v) = self.out.clone() { return Ok(v); } @@ -214,7 +221,7 @@ where /// Evaluate an input string as UCG. pub fn eval_string(&mut self, input: &str) -> Result, Box> { - self.eval_input(OffsetStrIter::new(input)) + self.eval_input(OffsetStrIter::new(input), None) } pub fn eval_expr(&mut self, expr: Expression) -> Result, Box> { diff --git a/src/build/opcode/cache.rs b/src/build/opcode/cache.rs index 604027b..267a74c 100644 --- a/src/build/opcode/cache.rs +++ b/src/build/opcode/cache.rs @@ -55,6 +55,8 @@ impl<'a> Entry<'a> { v } }; - Ok(OpPointer::new(cached).with_path(path.into())) + let mut ptr = OpPointer::new(cached); + ptr.set_path(path.into()); + Ok(ptr) } } diff --git a/src/build/opcode/environment.rs b/src/build/opcode/environment.rs index 189aded..1fd1380 100644 --- a/src/build/opcode/environment.rs +++ b/src/build/opcode/environment.rs @@ -22,6 +22,7 @@ use super::pointer::OpPointer; use super::Error; use super::Value; use crate::build::AssertCollector; +use crate::build::Val; use crate::convert::{ConverterRegistry, ImporterRegistry}; use crate::iter::OffsetStrIter; use crate::parse::parse; @@ -29,8 +30,8 @@ use crate::parse::parse; // Shared Environmental between VM's for runtime usage. pub struct Environment where - Stdout: Write, - Stderr: Write, + Stdout: Write + Clone, + Stderr: Write + Clone, { pub val_cache: BTreeMap>, pub op_cache: cache::Ops, @@ -43,7 +44,7 @@ where pub out_lock: BTreeSet, } -impl Environment { +impl Environment { pub fn new(out: Stdout, err: Stderr) -> Self { Self::new_with_vars(out, err, BTreeMap::new()) } @@ -111,4 +112,32 @@ impl Environment { pub fn reset_out_lock_for_path>(&mut self, path: P) { 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) -> 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; + } } diff --git a/src/build/opcode/pointer.rs b/src/build/opcode/pointer.rs index 8d9b00a..46f4cbf 100644 --- a/src/build/opcode/pointer.rs +++ b/src/build/opcode/pointer.rs @@ -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 } pub fn next(&mut self) -> Option<&Op> { diff --git a/src/build/opcode/runtime.rs b/src/build/opcode/runtime.rs index 524feb6..4253802 100644 --- a/src/build/opcode/runtime.rs +++ b/src/build/opcode/runtime.rs @@ -65,10 +65,10 @@ impl Builtins { pos: Position, ) -> Result<(), Error> where - P: AsRef, + P: AsRef + Debug, WP: Into + Clone + Debug, - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, { match h { Hook::Import => self.import(working_dir, stack, env, import_stack, pos), @@ -168,8 +168,8 @@ impl Builtins { pos: Position, ) -> Result<(), Error> where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, P: Into + Clone + Debug, { let path = stack.pop(); @@ -219,8 +219,8 @@ impl Builtins { pos: Position, ) -> Result<(), Error> where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, P: Into + Clone + Debug, { let path = stack.pop(); @@ -282,8 +282,8 @@ impl Builtins { env: Rc>>, ) -> Result<(), Error> where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, { if let Some((tuple, tpl_pos)) = stack.pop() { if let &Value::C(Tuple(ref tuple_flds, _)) = tuple.as_ref() { @@ -334,7 +334,7 @@ impl Builtins { return Ok(()); } - fn out, O, E>( + fn out( &self, path: Option

, stack: &mut Vec<(Rc, Position)>, @@ -342,17 +342,19 @@ impl Builtins { pos: Position, ) -> Result<(), Error> where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, + P: AsRef + Debug, { - let mut writer: Box = if let Some(path) = path { - if env.borrow().get_out_lock_for_path(path.as_ref()) { + let write_path: Option = if let Some(path) = path { + let write_path = path.as_ref().to_path_buf(); + if env.borrow().get_out_lock_for_path(&path) { return Err(Error::new( format!("You can only have one output per file"), pos)); } env.borrow_mut().set_out_lock_for_path(path.as_ref()); - Box::new(File::create(path)?) + Some(write_path) } else { if env.borrow().get_out_lock_for_path("/dev/stdout") { return Err(Error::new( @@ -360,7 +362,7 @@ impl Builtins { pos)); } env.borrow_mut().set_out_lock_for_path("/dev/stdout"); - Box::new(std::io::stdout()) + None }; let val = stack.pop(); if let Some((val, val_pos)) = val { @@ -368,7 +370,15 @@ impl Builtins { let c_type = stack.pop(); 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() { + let stdout = env.borrow().stdout(); if let Some(c) = env.borrow().converter_registry.get_converter(c_type) { + let mut writer: Box = 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) { return Err(Error::new(format!("{}", e), pos.clone())); } @@ -396,8 +406,8 @@ impl Builtins { pos: Position, ) -> Result<(), Error> where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, { let val = stack.pop(); if let Some((val, val_pos)) = val { @@ -442,8 +452,8 @@ impl Builtins { pos: Position, ) -> Result<(), Error> where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, { // get the list from the stack let (list, list_pos) = if let Some(list) = stack.pop() { @@ -554,8 +564,8 @@ impl Builtins { pos: Position, ) -> Result<(), Error> where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, { // get the list from the stack let (list, list_pos) = if let Some(list) = stack.pop() { @@ -700,8 +710,8 @@ impl Builtins { pos: Position, ) -> Result<(), Error> where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, { // get the list from the stack let (list, list_pos) = if let Some(list) = stack.pop() { @@ -839,8 +849,8 @@ impl Builtins { env: Rc>>, ) -> Result<(), Error> where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, { let (val, val_pos) = if let Some(val) = stack.pop() { val diff --git a/src/build/opcode/vm.rs b/src/build/opcode/vm.rs index c96c551..c211034 100644 --- a/src/build/opcode/vm.rs +++ b/src/build/opcode/vm.rs @@ -43,8 +43,8 @@ fn construct_reserved_word_set() -> BTreeSet<&'static str> { pub struct VM where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, { working_dir: PathBuf, stack: Vec<(Rc, Position)>, @@ -60,8 +60,8 @@ where impl<'a, O, E> VM where - O: std::io::Write, - E: std::io::Write, + O: std::io::Write + Clone, + E: std::io::Write + Clone, { pub fn new>( ops: Rc, @@ -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 { self.ops = ops; self diff --git a/src/main.rs b/src/main.rs index ad9cb07..dc0b2cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,12 +43,6 @@ fn do_flags<'a, 'b>() -> clap::App<'a, 'b> { (author: crate_authors!()) (about: "Universal Configuration Grammar compiler.") (@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 => (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, f: Option<&str>) -> traits::ConvertResult { - let mut file: Box = match f { - Some(f) => { - let mut path_buf = PathBuf::from(f); - path_buf.set_extension(c.file_ext()); - 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 +struct StdoutWrapper(io::Stdout); + +impl StdoutWrapper { + fn new() -> Self { + Self(io::stdout()) + } } +impl io::Write for StdoutWrapper { + fn write(&mut self, buf: &[u8]) -> io::Result { + 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 { + 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>( file: &'a str, validate: bool, strict: bool, import_paths: &'a Vec, -) -> Result, Box> { +) -> Result, Box> { let mut file_path_buf = PathBuf::from(file); if file_path_buf.is_relative() { file_path_buf = std::env::current_dir()?.join(file_path_buf); } - let out = std::io::stdout(); - let err = std::io::stderr(); + let out = StdoutWrapper::new(); + let err = StderrWrapper::new(); let mut builder = build::FileBuilder::new(std::env::current_dir()?, import_paths, out, err); // FIXME(jwall): builder.set_strict(strict); if validate { @@ -217,69 +245,6 @@ fn visit_ucg_files( Ok(result) } -fn inspect_command(matches: &clap::ArgMatches, import_paths: &Vec, 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, strict: bool) { let files = matches.values_of("INPUT"); let recurse = matches.is_present("recurse"); @@ -474,8 +439,8 @@ fn do_repl(import_paths: &Vec) -> std::result::Result<(), Box