mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
REFACTOR: Clarify the notion of working directory vs file.
This commit is contained in:
parent
b51231b3cb
commit
3f69896a08
120
src/build/mod.rs
120
src/build/mod.rs
@ -53,6 +53,7 @@ impl MacroDef {
|
|||||||
/// Expands a ucg Macro using the given arguments into a new Tuple.
|
/// Expands a ucg Macro using the given arguments into a new Tuple.
|
||||||
pub fn eval(
|
pub fn eval(
|
||||||
&self,
|
&self,
|
||||||
|
// TODO(jwall): This should come from the macrodef instead.
|
||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
parent_builder: &FileBuilder,
|
parent_builder: &FileBuilder,
|
||||||
mut args: Vec<Rc<Val>>,
|
mut args: Vec<Rc<Val>>,
|
||||||
@ -71,15 +72,13 @@ impl MacroDef {
|
|||||||
}
|
}
|
||||||
// If the args don't match the types required by the expressions then that is a TypeFail.
|
// If the args don't match the types required by the expressions then that is a TypeFail.
|
||||||
// If the expressions reference Symbols not defined in the MacroDef that is also an error.
|
// If the expressions reference Symbols not defined in the MacroDef that is also an error.
|
||||||
// TODO(jwall): We should probably enforce that the Expression Symbols must be in argdefs rules
|
|
||||||
// at Macro definition time not evaluation time.
|
|
||||||
let mut build_output = HashMap::<PositionedItem<String>, Rc<Val>>::new();
|
let mut build_output = HashMap::<PositionedItem<String>, Rc<Val>>::new();
|
||||||
for (i, arg) in args.drain(0..).enumerate() {
|
for (i, arg) in args.drain(0..).enumerate() {
|
||||||
build_output
|
build_output
|
||||||
.entry(self.argdefs[i].clone())
|
.entry(self.argdefs[i].clone())
|
||||||
.or_insert(arg.clone());
|
.or_insert(arg.clone());
|
||||||
}
|
}
|
||||||
let mut b = parent_builder.clone_builder(root);
|
let mut b = parent_builder.clone_builder();
|
||||||
if let Some(ref scope) = self.scope {
|
if let Some(ref scope) = self.scope {
|
||||||
b.scope = scope.spawn_child();
|
b.scope = scope.spawn_child();
|
||||||
}
|
}
|
||||||
@ -102,7 +101,10 @@ pub struct AssertCollector {
|
|||||||
|
|
||||||
/// Builder handles building ucg code for a single file.
|
/// Builder handles building ucg code for a single file.
|
||||||
pub struct FileBuilder<'a> {
|
pub struct FileBuilder<'a> {
|
||||||
file: PathBuf,
|
// FIXME(jwall): This should probably become a working directory instead.
|
||||||
|
working_dir: PathBuf,
|
||||||
|
// FIXME(jwall): All of the below is build context shared amongst
|
||||||
|
// various builders.
|
||||||
std: Rc<HashMap<String, &'static str>>,
|
std: Rc<HashMap<String, &'static str>>,
|
||||||
import_path: &'a Vec<PathBuf>,
|
import_path: &'a Vec<PathBuf>,
|
||||||
validate_mode: bool,
|
validate_mode: bool,
|
||||||
@ -148,28 +150,26 @@ macro_rules! eval_binary_expr {
|
|||||||
impl<'a> FileBuilder<'a> {
|
impl<'a> FileBuilder<'a> {
|
||||||
/// Constructs a new Builder.
|
/// Constructs a new Builder.
|
||||||
pub fn new<P: Into<PathBuf>>(
|
pub fn new<P: Into<PathBuf>>(
|
||||||
file: P,
|
working_dir: P,
|
||||||
import_paths: &'a Vec<PathBuf>,
|
import_paths: &'a Vec<PathBuf>,
|
||||||
cache: Rc<RefCell<assets::Cache>>,
|
cache: Rc<RefCell<assets::Cache>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let env_vars: Vec<(String, String)> = env::vars().collect();
|
let env_vars: Vec<(String, String)> = env::vars().collect();
|
||||||
let scope = scope::Scope::new(Rc::new(Val::Env(env_vars)));
|
let scope = scope::Scope::new(Rc::new(Val::Env(env_vars)));
|
||||||
Self::new_with_scope(file, import_paths, cache, scope)
|
Self::new_with_scope(working_dir, import_paths, cache, scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a new Builder with a provided scope.
|
/// Constructs a new Builder with a provided scope.
|
||||||
pub fn new_with_scope<P: Into<PathBuf>>(
|
pub fn new_with_scope<P: Into<PathBuf>>(
|
||||||
file: P,
|
working_dir: P,
|
||||||
import_paths: &'a Vec<PathBuf>,
|
import_paths: &'a Vec<PathBuf>,
|
||||||
cache: Rc<RefCell<assets::Cache>>,
|
cache: Rc<RefCell<assets::Cache>>,
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let file = file.into();
|
|
||||||
let std = Rc::new(stdlib::get_libs());
|
|
||||||
FileBuilder {
|
FileBuilder {
|
||||||
// Our import stack is initialized with ourself.
|
// Our import stack is initialized with ourself.
|
||||||
file: file,
|
working_dir: working_dir.into(),
|
||||||
std: std,
|
std: Rc::new(stdlib::get_libs()),
|
||||||
import_path: import_paths,
|
import_path: import_paths,
|
||||||
validate_mode: false,
|
validate_mode: false,
|
||||||
assert_collector: AssertCollector {
|
assert_collector: AssertCollector {
|
||||||
@ -188,9 +188,9 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone_builder<P: Into<PathBuf>>(&self, file: P) -> Self {
|
pub fn clone_builder(&self) -> Self {
|
||||||
FileBuilder {
|
FileBuilder {
|
||||||
file: file.into(),
|
working_dir: self.working_dir.clone(),
|
||||||
std: self.std.clone(),
|
std: self.std.clone(),
|
||||||
import_path: self.import_path,
|
import_path: self.import_path,
|
||||||
validate_mode: false,
|
validate_mode: false,
|
||||||
@ -211,10 +211,39 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(jwall): With builder pattern
|
||||||
pub fn set_build_output(&mut self, scope: ValueMap) {
|
pub fn set_build_output(&mut self, scope: ValueMap) {
|
||||||
self.scope.build_output = scope;
|
self.scope.build_output = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds a ucg file at the named path.
|
||||||
|
pub fn build<P: Into<PathBuf>>(&mut self, file: P) -> BuildResult {
|
||||||
|
let file = file.into();
|
||||||
|
self.working_dir = file.parent().unwrap().to_path_buf();
|
||||||
|
let mut f = File::open(&file)?;
|
||||||
|
let mut s = String::new();
|
||||||
|
f.read_to_string(&mut s)?;
|
||||||
|
let input = OffsetStrIter::new(&s).with_src_file(file.clone());
|
||||||
|
let eval_result = self.eval_input(input);
|
||||||
|
match eval_result {
|
||||||
|
Ok(v) => {
|
||||||
|
self.last = Some(v);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let err = simple_error::SimpleError::new(
|
||||||
|
format!(
|
||||||
|
"Error building file: {}\n{}",
|
||||||
|
file.to_string_lossy(),
|
||||||
|
e.as_ref()
|
||||||
|
)
|
||||||
|
.as_ref(),
|
||||||
|
);
|
||||||
|
Err(Box::new(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn merge_build_output(&mut self, scope: ValueMap, clobber: bool) {
|
pub fn merge_build_output(&mut self, scope: ValueMap, clobber: bool) {
|
||||||
for (name, value) in scope.iter() {
|
for (name, value) in scope.iter() {
|
||||||
if !clobber && !self.scope.build_output.contains_key(name) {
|
if !clobber && !self.scope.build_output.contains_key(name) {
|
||||||
@ -317,38 +346,12 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Non file builder specific.
|
||||||
/// 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))
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileBuilder specific
|
|
||||||
/// Builds a ucg file at the named path.
|
|
||||||
pub fn build(&mut self) -> BuildResult {
|
|
||||||
let mut f = File::open(&self.file)?;
|
|
||||||
let mut s = String::new();
|
|
||||||
f.read_to_string(&mut s)?;
|
|
||||||
let input = OffsetStrIter::new(&s).with_src_file(self.file.clone());
|
|
||||||
let eval_result = self.eval_input(input);
|
|
||||||
match eval_result {
|
|
||||||
Ok(v) => {
|
|
||||||
self.last = Some(v);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let err = simple_error::SimpleError::new(
|
|
||||||
format!(
|
|
||||||
"Error building file: {}\n{}",
|
|
||||||
self.file.to_string_lossy(),
|
|
||||||
e.as_ref()
|
|
||||||
)
|
|
||||||
.as_ref(),
|
|
||||||
);
|
|
||||||
Err(Box::new(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_reserved_word(name: &str) -> bool {
|
fn check_reserved_word(name: &str) -> bool {
|
||||||
match name {
|
match name {
|
||||||
"self" | "assert" | "true" | "false" | "let" | "import" | "as" | "select" | "macro"
|
"self" | "assert" | "true" | "false" | "let" | "import" | "as" | "select" | "macro"
|
||||||
@ -372,7 +375,8 @@ impl<'a> FileBuilder<'a> {
|
|||||||
) -> Result<PathBuf, Box<dyn Error>> {
|
) -> Result<PathBuf, Box<dyn Error>> {
|
||||||
// Try a relative path first.
|
// Try a relative path first.
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
let mut normalized = self.file.parent().unwrap().to_path_buf();
|
// TODO(jwall): Change this to take a root directory.
|
||||||
|
let mut normalized = self.working_dir.clone();
|
||||||
if path.is_relative() {
|
if path.is_relative() {
|
||||||
normalized.push(&path);
|
normalized.push(&path);
|
||||||
// First see if the normalized file exists or not.
|
// First see if the normalized file exists or not.
|
||||||
@ -405,7 +409,8 @@ impl<'a> FileBuilder<'a> {
|
|||||||
let result = match maybe_asset {
|
let result = match maybe_asset {
|
||||||
Some(v) => v.clone(),
|
Some(v) => v.clone(),
|
||||||
None => {
|
None => {
|
||||||
let mut b = self.clone_builder(self.file.clone());
|
// TODO(jwall): This does not need to be a FileBuilder specifically
|
||||||
|
let mut b = self.clone_builder();
|
||||||
b.eval_string(self.std.get(&def.path.fragment).unwrap())?;
|
b.eval_string(self.std.get(&def.path.fragment).unwrap())?;
|
||||||
b.get_outputs_as_val()
|
b.get_outputs_as_val()
|
||||||
}
|
}
|
||||||
@ -441,8 +446,8 @@ impl<'a> FileBuilder<'a> {
|
|||||||
let result = match maybe_asset {
|
let result = match maybe_asset {
|
||||||
Some(v) => v.clone(),
|
Some(v) => v.clone(),
|
||||||
None => {
|
None => {
|
||||||
let mut b = self.clone_builder(normalized.clone());
|
let mut b = self.clone_builder();
|
||||||
b.build()?;
|
b.build(&normalized)?;
|
||||||
b.get_outputs_as_val()
|
b.get_outputs_as_val()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1099,7 +1104,8 @@ impl<'a> FileBuilder<'a> {
|
|||||||
let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone();
|
let maybe_tpl = mod_def.clone().arg_tuple.unwrap().clone();
|
||||||
if let &Val::Tuple(ref src_fields) = maybe_tpl.as_ref() {
|
if let &Val::Tuple(ref src_fields) = maybe_tpl.as_ref() {
|
||||||
// 1. First we create a builder.
|
// 1. First we create a builder.
|
||||||
let mut b = self.clone_builder(self.file.clone());
|
// TODO(jwall): This file should optionally come from the module def itself.
|
||||||
|
let mut b = self.clone_builder();
|
||||||
b.is_module = true;
|
b.is_module = true;
|
||||||
// 2. We construct an argument tuple by copying from the defs
|
// 2. We construct an argument tuple by copying from the defs
|
||||||
// argset.
|
// argset.
|
||||||
@ -1172,7 +1178,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
argvals.push(self.eval_expr(arg, scope)?);
|
argvals.push(self.eval_expr(arg, scope)?);
|
||||||
}
|
}
|
||||||
return Ok(m.eval(self.file.clone(), self, argvals)?);
|
return Ok(m.eval(self.working_dir.clone(), self, argvals)?);
|
||||||
}
|
}
|
||||||
Err(Box::new(error::BuildError::new(
|
Err(Box::new(error::BuildError::new(
|
||||||
// We should pretty print the selectors here.
|
// We should pretty print the selectors here.
|
||||||
@ -1187,17 +1193,19 @@ impl<'a> FileBuilder<'a> {
|
|||||||
Ok(Rc::new(Val::Macro(def.clone())))
|
Ok(Rc::new(Val::Macro(def.clone())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(jwall): This stays with the FileBuilder specifically.
|
||||||
fn file_dir(&self) -> PathBuf {
|
fn file_dir(&self) -> PathBuf {
|
||||||
return if self.file.is_file() {
|
return if self.working_dir.is_file() {
|
||||||
// Only use the dirname portion if the root is a file.
|
// Only use the dirname portion if the root is a file.
|
||||||
self.file.parent().unwrap().to_path_buf()
|
self.working_dir.parent().unwrap().to_path_buf()
|
||||||
} else {
|
} else {
|
||||||
// otherwise use clone of the root..
|
// otherwise use clone of the root.
|
||||||
self.file.clone()
|
self.working_dir.clone()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_module_def(&self, def: &ModuleDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
fn eval_module_def(&self, def: &ModuleDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
||||||
|
// TODO(jwall): This should actually be passed in to here.
|
||||||
let root = self.file_dir();
|
let root = self.file_dir();
|
||||||
// Always work on a copy. The original should not be modified.
|
// Always work on a copy. The original should not be modified.
|
||||||
let mut def = def.clone();
|
let mut def = def.clone();
|
||||||
@ -1259,7 +1267,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for item in elems.iter() {
|
for item in elems.iter() {
|
||||||
let argvals = vec![item.clone()];
|
let argvals = vec![item.clone()];
|
||||||
let val = def.eval(self.file.clone(), self, argvals)?;
|
let val = def.eval(self.working_dir.clone(), self, argvals)?;
|
||||||
match typ {
|
match typ {
|
||||||
ProcessingOpType::Map => {
|
ProcessingOpType::Map => {
|
||||||
out.push(val.clone());
|
out.push(val.clone());
|
||||||
@ -1288,7 +1296,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for &(ref name, ref val) in fs {
|
for &(ref name, ref val) in fs {
|
||||||
let argvals = vec![Rc::new(Val::Str(name.val.clone())), val.clone()];
|
let argvals = vec![Rc::new(Val::Str(name.val.clone())), val.clone()];
|
||||||
let result = def.eval(self.file.clone(), self, argvals)?;
|
let result = def.eval(self.working_dir.clone(), self, argvals)?;
|
||||||
match typ {
|
match typ {
|
||||||
ProcessingOpType::Map => {
|
ProcessingOpType::Map => {
|
||||||
if let &Val::List(ref fs) = result.as_ref() {
|
if let &Val::List(ref fs) = result.as_ref() {
|
||||||
@ -1365,7 +1373,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
&Val::List(ref elems) => {
|
&Val::List(ref elems) => {
|
||||||
for item in elems.iter() {
|
for item in elems.iter() {
|
||||||
let argvals = vec![acc.clone(), item.clone()];
|
let argvals = vec![acc.clone(), item.clone()];
|
||||||
let result = macdef.eval(self.file.clone(), self, argvals)?;
|
let result = macdef.eval(self.working_dir.clone(), self, argvals)?;
|
||||||
acc = result;
|
acc = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1376,14 +1384,14 @@ impl<'a> FileBuilder<'a> {
|
|||||||
Rc::new(Val::Str(name.val.clone())),
|
Rc::new(Val::Str(name.val.clone())),
|
||||||
val.clone(),
|
val.clone(),
|
||||||
];
|
];
|
||||||
let result = macdef.eval(self.file.clone(), self, argvals)?;
|
let result = macdef.eval(self.working_dir.clone(), self, argvals)?;
|
||||||
acc = result;
|
acc = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Val::Str(ref s) => {
|
&Val::Str(ref s) => {
|
||||||
for gc in s.graphemes(true) {
|
for gc in s.graphemes(true) {
|
||||||
let argvals = vec![acc.clone(), Rc::new(Val::Str(gc.to_string()))];
|
let argvals = vec![acc.clone(), Rc::new(Val::Str(gc.to_string()))];
|
||||||
let result = macdef.eval(self.file.clone(), self, argvals)?;
|
let result = macdef.eval(self.working_dir.clone(), self, argvals)?;
|
||||||
acc = result;
|
acc = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1410,7 +1418,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
for gc in s.graphemes(true) {
|
for gc in s.graphemes(true) {
|
||||||
let arg = Rc::new(Val::Str(gc.to_string()));
|
let arg = Rc::new(Val::Str(gc.to_string()));
|
||||||
let out = def.eval(self.file.clone(), self, vec![arg])?;
|
let out = def.eval(self.working_dir.clone(), self, vec![arg])?;
|
||||||
match typ {
|
match typ {
|
||||||
ProcessingOpType::Filter => {
|
ProcessingOpType::Filter => {
|
||||||
match out.as_ref() {
|
match out.as_ref() {
|
||||||
|
13
src/main.rs
13
src/main.rs
@ -87,14 +87,14 @@ fn build_file<'a>(
|
|||||||
) -> Result<build::FileBuilder<'a>, Box<dyn Error>> {
|
) -> Result<build::FileBuilder<'a>, 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().unwrap().join(file_path_buf);
|
file_path_buf = std::env::current_dir()?.join(file_path_buf);
|
||||||
}
|
}
|
||||||
let mut builder = build::FileBuilder::new(file_path_buf, import_paths, cache);
|
let mut builder = build::FileBuilder::new(std::env::current_dir()?, import_paths, cache);
|
||||||
builder.set_strict(strict);
|
builder.set_strict(strict);
|
||||||
if validate {
|
if validate {
|
||||||
builder.enable_validate_mode();
|
builder.enable_validate_mode();
|
||||||
}
|
}
|
||||||
builder.build()?;
|
builder.build(file_path_buf)?;
|
||||||
if validate {
|
if validate {
|
||||||
println!("{}", builder.assert_collector.summary);
|
println!("{}", builder.assert_collector.summary);
|
||||||
}
|
}
|
||||||
@ -247,12 +247,13 @@ fn inspect_command(
|
|||||||
let file = matches.value_of("INPUT").unwrap();
|
let file = matches.value_of("INPUT").unwrap();
|
||||||
let sym = matches.value_of("expr");
|
let sym = matches.value_of("expr");
|
||||||
let target = matches.value_of("target").unwrap();
|
let target = matches.value_of("target").unwrap();
|
||||||
let mut builder = build::FileBuilder::new(file, import_paths, cache);
|
let mut builder =
|
||||||
|
build::FileBuilder::new(std::env::current_dir().unwrap(), import_paths, cache);
|
||||||
builder.set_strict(strict);
|
builder.set_strict(strict);
|
||||||
match registry.get_converter(target) {
|
match registry.get_converter(target) {
|
||||||
Some(converter) => {
|
Some(converter) => {
|
||||||
// TODO(jwall): We should warn if this is a test file.
|
// TODO(jwall): We should warn if this is a test file.
|
||||||
let result = builder.build();
|
let result = builder.build(file);
|
||||||
if !result.is_ok() {
|
if !result.is_ok() {
|
||||||
eprintln!("{:?}", result.err().unwrap());
|
eprintln!("{:?}", result.err().unwrap());
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
@ -266,7 +267,7 @@ fn inspect_command(
|
|||||||
} else {
|
} else {
|
||||||
sym_name.to_owned()
|
sym_name.to_owned()
|
||||||
};
|
};
|
||||||
let mut builder = builder.clone_builder("/eval");
|
let mut builder = builder.clone_builder();
|
||||||
match builder.eval_string(&normalized) {
|
match builder.eval_string(&normalized) {
|
||||||
Ok(v) => Some(v.clone()),
|
Ok(v) => Some(v.clone()),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user