mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
FIX: Restore the env variable functionality
This commit is contained in:
parent
5a20012fcb
commit
a8164a1f06
@ -13,6 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
@ -24,7 +25,9 @@ fn assert_build(input: &str) {
|
|||||||
let i_paths = Vec::new();
|
let i_paths = Vec::new();
|
||||||
let out_buffer: Vec<u8> = Vec::new();
|
let out_buffer: Vec<u8> = Vec::new();
|
||||||
let err_buffer: Vec<u8> = Vec::new();
|
let err_buffer: Vec<u8> = Vec::new();
|
||||||
let env = RefCell::new(Environment::new(out_buffer, err_buffer));
|
let mut env_vars = BTreeMap::new();
|
||||||
|
env_vars.insert("FOO".to_owned(), "bar".to_owned());
|
||||||
|
let env = RefCell::new(Environment::new_with_vars(out_buffer, err_buffer, env_vars));
|
||||||
let mut b = FileBuilder::new("<Eval>", &i_paths, &env);
|
let mut b = FileBuilder::new("<Eval>", &i_paths, &env);
|
||||||
b.enable_validate_mode();
|
b.enable_validate_mode();
|
||||||
b.eval_string(input).unwrap();
|
b.eval_string(input).unwrap();
|
||||||
@ -148,6 +151,47 @@ fn test_type_checks() {
|
|||||||
assert_build(include_str!("../../integration_tests/types_test.ucg"));
|
assert_build(include_str!("../../integration_tests/types_test.ucg"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_environment_variable_exists() {
|
||||||
|
assert_build("assert { ok = env.FOO == \"bar\", desc = \"env var $FOO is bar\"};");
|
||||||
|
}
|
||||||
|
// TODO(jwall): that shadowing the env variable is not allowed?
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_env_as_field_name_works() {
|
||||||
|
assert_build(
|
||||||
|
"let tpl = { env = \"quux\" };
|
||||||
|
assert {
|
||||||
|
ok = env.FOO == \"bar\",
|
||||||
|
desc = \"env var $FOO is bar\",
|
||||||
|
};
|
||||||
|
assert {
|
||||||
|
ok = tpl.env == \"quux\",
|
||||||
|
desc = \"tpl.env is quux\",
|
||||||
|
};
|
||||||
|
|
||||||
|
let tpl2 = { env = { bar = 2 } };
|
||||||
|
assert {
|
||||||
|
ok = tpl2.env.bar == 2,
|
||||||
|
desc = \"tpl2.env.bar is 2\",
|
||||||
|
};
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// TODO(jwall): tests for missing values.
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_env_is_not_a_valid_binding() {
|
||||||
|
assert_build_failure(
|
||||||
|
"let env = 1;",
|
||||||
|
vec![
|
||||||
|
Regex::new("Cannot use binding env").unwrap(),
|
||||||
|
Regex::new("It is a reserved word").unwrap(),
|
||||||
|
Regex::new("at line: 1 column: 5").unwrap(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "UserDefined: I am a failure!")]
|
#[should_panic(expected = "UserDefined: I am a failure!")]
|
||||||
fn test_declarative_failures_are_caused_by_msg() {
|
fn test_declarative_failures_are_caused_by_msg() {
|
||||||
|
@ -17,16 +17,16 @@ use std::io::{Read, Write};
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::cache;
|
|
||||||
use super::pointer::OpPointer;
|
use super::pointer::OpPointer;
|
||||||
use super::Error;
|
use super::Error;
|
||||||
use super::Value;
|
use super::{cache, Primitive};
|
||||||
use crate::build::stdlib;
|
use super::{Composite, Value};
|
||||||
use crate::build::AssertCollector;
|
use crate::build::AssertCollector;
|
||||||
use crate::build::Val;
|
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;
|
||||||
|
use crate::{ast::Position, build::stdlib};
|
||||||
|
|
||||||
// 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>
|
||||||
@ -47,6 +47,7 @@ where
|
|||||||
|
|
||||||
impl<Stdout: Write + Clone, Stderr: Write + Clone> 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 {
|
||||||
|
// TODO(jwall): populate this with environment variables?
|
||||||
Self::new_with_vars(out, err, BTreeMap::new())
|
Self::new_with_vars(out, err, BTreeMap::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +67,16 @@ impl<Stdout: Write + Clone, Stderr: Write + Clone> Environment<Stdout, Stderr> {
|
|||||||
return me;
|
return me;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_env_vars_tuple(&self) -> Value {
|
||||||
|
let mut fields = Vec::new();
|
||||||
|
let mut positions = Vec::new();
|
||||||
|
for (key, val) in self.env_vars.iter() {
|
||||||
|
fields.push((key.clone(), Rc::new(Value::P(Primitive::Str(val.clone())))));
|
||||||
|
positions.push((Position::new(0, 0, 0), Position::new(0, 0, 0)));
|
||||||
|
}
|
||||||
|
Value::C(Composite::Tuple(fields, positions))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_cached_path_val(&self, path: &String) -> Option<Rc<Value>> {
|
pub fn get_cached_path_val(&self, path: &String) -> Option<Rc<Value>> {
|
||||||
self.val_cache.get(path).cloned()
|
self.val_cache.get(path).cloned()
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,7 @@ impl VM {
|
|||||||
Op::Val(p) => self.push(Rc::new(P(p.clone())), pos)?,
|
Op::Val(p) => self.push(Rc::new(P(p.clone())), pos)?,
|
||||||
Op::Cast(t) => self.op_cast(t)?,
|
Op::Cast(t) => self.op_cast(t)?,
|
||||||
Op::Sym(s) => self.push(Rc::new(S(s.clone())), pos)?,
|
Op::Sym(s) => self.push(Rc::new(S(s.clone())), pos)?,
|
||||||
Op::DeRef(s) => self.op_deref(s.clone(), &pos)?,
|
Op::DeRef(s) => self.op_deref(s.clone(), env, &pos)?,
|
||||||
Op::Add => self.op_add(pos)?,
|
Op::Add => self.op_add(pos)?,
|
||||||
Op::Mod => self.op_mod(pos)?,
|
Op::Mod => self.op_mod(pos)?,
|
||||||
Op::Sub => self.op_sub(pos)?,
|
Op::Sub => self.op_sub(pos)?,
|
||||||
@ -242,8 +242,17 @@ impl VM {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn op_deref(&mut self, name: String, pos: &Position) -> Result<(), Error> {
|
fn op_deref<O, E>(
|
||||||
let (val, _) = self.get_binding(&name, pos)?.clone();
|
&mut self,
|
||||||
|
name: String,
|
||||||
|
env: &RefCell<Environment<O, E>>,
|
||||||
|
pos: &Position,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
O: std::io::Write + Clone,
|
||||||
|
E: std::io::Write + Clone,
|
||||||
|
{
|
||||||
|
let (val, _) = self.get_binding(&name, env, pos)?.clone();
|
||||||
self.push(val, pos.clone())
|
self.push(val, pos.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1078,9 +1087,29 @@ impl VM {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_binding(&self, name: &str, pos: &Position) -> Result<(Rc<Value>, Position), Error> {
|
pub fn get_binding<O, E>(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
env: &RefCell<Environment<O, E>>,
|
||||||
|
pos: &Position,
|
||||||
|
) -> Result<(Rc<Value>, Position), Error>
|
||||||
|
where
|
||||||
|
O: std::io::Write + Clone,
|
||||||
|
E: std::io::Write + Clone,
|
||||||
|
{
|
||||||
|
// TODO(jwall): Handle environment variables here?
|
||||||
let tpl = if name == "self" {
|
let tpl = if name == "self" {
|
||||||
self.self_stack.last().cloned()
|
self.self_stack.last().cloned()
|
||||||
|
} else if name == "env" {
|
||||||
|
let candidate = self.symbols.get(name);
|
||||||
|
if candidate.is_some() {
|
||||||
|
candidate
|
||||||
|
} else {
|
||||||
|
Some((
|
||||||
|
Rc::new(env.borrow().get_env_vars_tuple()),
|
||||||
|
Position::new(0, 0, 0),
|
||||||
|
))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.symbols.get(name)
|
self.symbols.get(name)
|
||||||
};
|
};
|
||||||
|
30
src/main.rs
30
src/main.rs
@ -426,7 +426,11 @@ fn env_help() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_repl(import_paths: &Vec<PathBuf>, strict: bool) -> std::result::Result<(), Box<dyn Error>> {
|
fn do_repl(
|
||||||
|
import_paths: &Vec<PathBuf>,
|
||||||
|
strict: bool,
|
||||||
|
env: &RefCell<Environment<StdoutWrapper, StderrWrapper>>,
|
||||||
|
) -> std::result::Result<(), Box<dyn Error>> {
|
||||||
let config = rustyline::Config::builder();
|
let config = rustyline::Config::builder();
|
||||||
let mut editor = rustyline::Editor::<()>::with_config(
|
let mut editor = rustyline::Editor::<()>::with_config(
|
||||||
config
|
config
|
||||||
@ -458,10 +462,6 @@ fn do_repl(import_paths: &Vec<PathBuf>, strict: bool) -> std::result::Result<(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let env = std::cell::RefCell::new(build::opcode::Environment::new(
|
|
||||||
StdoutWrapper::new(),
|
|
||||||
StderrWrapper::new(),
|
|
||||||
));
|
|
||||||
let mut builder = build::FileBuilder::new(std::env::current_dir()?, import_paths, &env);
|
let mut builder = build::FileBuilder::new(std::env::current_dir()?, import_paths, &env);
|
||||||
builder.set_strict(strict);
|
builder.set_strict(strict);
|
||||||
|
|
||||||
@ -469,8 +469,12 @@ fn do_repl(import_paths: &Vec<PathBuf>, strict: bool) -> std::result::Result<(),
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn repl(import_paths: &Vec<PathBuf>, strict: bool) {
|
fn repl(
|
||||||
if let Err(e) = do_repl(import_paths, strict) {
|
import_paths: &Vec<PathBuf>,
|
||||||
|
strict: bool,
|
||||||
|
env: &RefCell<Environment<StdoutWrapper, StderrWrapper>>,
|
||||||
|
) {
|
||||||
|
if let Err(e) = do_repl(import_paths, strict, env) {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
@ -482,7 +486,15 @@ fn main() {
|
|||||||
// FIXME(jwall): Do we want these to be shared or not?
|
// FIXME(jwall): Do we want these to be shared or not?
|
||||||
let registry = ConverterRegistry::make_registry();
|
let registry = ConverterRegistry::make_registry();
|
||||||
let mut import_paths = Vec::new();
|
let mut import_paths = Vec::new();
|
||||||
let env = RefCell::new(Environment::new(StdoutWrapper::new(), StderrWrapper::new()));
|
let mut env_vars = BTreeMap::new();
|
||||||
|
for (var, val) in std::env::vars() {
|
||||||
|
env_vars.insert(var, val);
|
||||||
|
}
|
||||||
|
let env = RefCell::new(Environment::new_with_vars(
|
||||||
|
StdoutWrapper::new(),
|
||||||
|
StderrWrapper::new(),
|
||||||
|
env_vars,
|
||||||
|
));
|
||||||
if let Some(mut p) = dirs::home_dir() {
|
if let Some(mut p) = dirs::home_dir() {
|
||||||
p.push(".ucg");
|
p.push(".ucg");
|
||||||
// Attempt to create directory if it doesn't exist.
|
// Attempt to create directory if it doesn't exist.
|
||||||
@ -518,7 +530,7 @@ fn main() {
|
|||||||
} else if let Some(_) = app_matches.subcommand_matches("env") {
|
} else if let Some(_) = app_matches.subcommand_matches("env") {
|
||||||
env_help()
|
env_help()
|
||||||
} else if let Some(_) = app_matches.subcommand_matches("repl") {
|
} else if let Some(_) = app_matches.subcommand_matches("repl") {
|
||||||
repl(&import_paths, strict)
|
repl(&import_paths, strict, &env)
|
||||||
} else if let Some(matches) = app_matches.subcommand_matches("fmt") {
|
} else if let Some(matches) = app_matches.subcommand_matches("fmt") {
|
||||||
if let Err(e) = fmt_command(matches) {
|
if let Err(e) = fmt_command(matches) {
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
|
@ -805,11 +805,33 @@ make_fn!(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Verify binding is not a reserved word.
|
||||||
|
macro_rules! match_binding_name {
|
||||||
|
($i:expr,) => {{
|
||||||
|
use abortable_parser::{Error, Result};
|
||||||
|
let mut _i = $i.clone();
|
||||||
|
match match_type!(_i, BAREWORD) {
|
||||||
|
Result::Complete(i, t) => {
|
||||||
|
if t.fragment == "env" {
|
||||||
|
return Result::Abort(Error::new(
|
||||||
|
format!("Cannot use binding {}. It is a reserved word.", t.fragment),
|
||||||
|
Box::new($i.clone()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Result::Complete(i, t)
|
||||||
|
}
|
||||||
|
Result::Incomplete(i) => Result::Incomplete(i),
|
||||||
|
Result::Fail(e) => Result::Fail(e),
|
||||||
|
Result::Abort(e) => Result::Abort(e),
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
make_fn!(
|
make_fn!(
|
||||||
let_stmt_body<SliceIter<Token>, Statement>,
|
let_stmt_body<SliceIter<Token>, Statement>,
|
||||||
do_each!(
|
do_each!(
|
||||||
pos => pos,
|
pos => pos,
|
||||||
name => wrap_err!(match_type!(BAREWORD), "Expected name for binding"),
|
name => wrap_err!(match_binding_name!(), "Expected name for binding"),
|
||||||
_ => optional!(trace_parse!(shape_suffix)),
|
_ => optional!(trace_parse!(shape_suffix)),
|
||||||
_ => punct!("="),
|
_ => punct!("="),
|
||||||
val => trace_parse!(wrap_err!(expression, "Expected Expression to bind")),
|
val => trace_parse!(wrap_err!(expression, "Expected Expression to bind")),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user