mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
DEV: More of the ucg commandline works now.
This commit is contained in:
parent
b3fd37a6b5
commit
d092fba5e8
@ -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<RefCell<Environment<Stdout, Stderr>>>,
|
||||
pub environment: Rc<RefCell<Environment<Stdout, Stderr>>>,
|
||||
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<P: Into<PathBuf>>(
|
||||
@ -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<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?
|
||||
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<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) {
|
||||
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<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>> {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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<Stdout, Stderr>
|
||||
where
|
||||
Stdout: Write,
|
||||
Stderr: Write,
|
||||
Stdout: Write + Clone,
|
||||
Stderr: Write + Clone,
|
||||
{
|
||||
pub val_cache: BTreeMap<String, Rc<Value>>,
|
||||
pub op_cache: cache::Ops,
|
||||
@ -43,7 +44,7 @@ where
|
||||
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 {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -65,10 +65,10 @@ impl Builtins {
|
||||
pos: Position,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
P: AsRef<Path> + Debug,
|
||||
WP: Into<PathBuf> + 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<PathBuf> + 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<PathBuf> + Clone + Debug,
|
||||
{
|
||||
let path = stack.pop();
|
||||
@ -282,8 +282,8 @@ impl Builtins {
|
||||
env: Rc<RefCell<Environment<O, E>>>,
|
||||
) -> 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<P: AsRef<Path>, O, E>(
|
||||
fn out<P, O, E>(
|
||||
&self,
|
||||
path: Option<P>,
|
||||
stack: &mut Vec<(Rc<Value>, 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<Path> + Debug,
|
||||
{
|
||||
let mut writer: Box<dyn std::io::Write> = if let Some(path) = path {
|
||||
if env.borrow().get_out_lock_for_path(path.as_ref()) {
|
||||
let write_path: Option<PathBuf> = 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<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) {
|
||||
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<RefCell<Environment<O, E>>>,
|
||||
) -> 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
|
||||
|
@ -43,8 +43,8 @@ fn construct_reserved_word_set() -> BTreeSet<&'static str> {
|
||||
|
||||
pub struct VM<O, E>
|
||||
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<Value>, Position)>,
|
||||
@ -60,8 +60,8 @@ where
|
||||
|
||||
impl<'a, O, E> VM<O, E>
|
||||
where
|
||||
O: std::io::Write,
|
||||
E: std::io::Write,
|
||||
O: std::io::Write + Clone,
|
||||
E: std::io::Write + Clone,
|
||||
{
|
||||
pub fn new<P: Into<PathBuf>>(
|
||||
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 {
|
||||
self.ops = ops;
|
||||
self
|
||||
|
143
src/main.rs
143
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<Val>, f: Option<&str>) -> traits::ConvertResult {
|
||||
let mut file: Box<dyn std::io::Write> = 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<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>(
|
||||
file: &'a str,
|
||||
validate: bool,
|
||||
strict: bool,
|
||||
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);
|
||||
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<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) {
|
||||
let files = matches.values_of("INPUT");
|
||||
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(
|
||||
std::env::current_dir()?,
|
||||
import_paths,
|
||||
io::stdout(),
|
||||
io::stderr(),
|
||||
StdoutWrapper::new(),
|
||||
StderrWrapper::new(),
|
||||
);
|
||||
// loop
|
||||
let mut lines = ucglib::io::StatementAccumulator::new();
|
||||
@ -575,9 +540,7 @@ fn main() {
|
||||
} else {
|
||||
true
|
||||
};
|
||||
if let Some(matches) = app_matches.subcommand_matches("eval") {
|
||||
inspect_command(matches, &import_paths, strict);
|
||||
} else if let Some(matches) = app_matches.subcommand_matches("build") {
|
||||
if let Some(matches) = app_matches.subcommand_matches("build") {
|
||||
build_command(matches, &import_paths, strict);
|
||||
} else if let Some(matches) = app_matches.subcommand_matches("test") {
|
||||
test_command(matches, &import_paths, strict);
|
||||
|
Loading…
x
Reference in New Issue
Block a user