Skip to content

Commit

Permalink
Apply AllExport option
Browse files Browse the repository at this point in the history
  • Loading branch information
magicant committed Oct 30, 2022
1 parent 8e01e54 commit f4dbb89
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 17 deletions.
3 changes: 1 addition & 2 deletions yash-builtin/src/readonly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,12 @@ pub fn builtin_main_sync(env: &mut Env, args: Vec<Field>) -> Result {
let var = Variable::new(var_value)
.set_assigned_location(origin.clone())
.make_read_only(origin);
// TODO Apply all-export option

let mut name = value;
name.truncate(eq_index);
// TODO reject invalid name

match env.variables.assign(Scope::Global, name, var) {
match env.assign_variable(Scope::Global, name, var) {
Ok(_old_value) => (),
Err(ReadOnlyError {
name,
Expand Down
53 changes: 53 additions & 0 deletions yash-env/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ use self::job::JobSet;
use self::job::Pid;
use self::job::WaitStatus;
use self::job::WaitStatusEx;
use self::option::AllExport;
use self::option::OptionSet;
use self::option::{Off, On};
use self::semantics::ExitStatus;
use self::stack::Frame;
use self::stack::Stack;
Expand All @@ -62,6 +64,9 @@ use self::system::SignalHandling;
pub use self::system::System;
use self::trap::Signal;
use self::trap::TrapSet;
use self::variable::ReadOnlyError;
use self::variable::Scope;
use self::variable::Variable;
use self::variable::VariableSet;
use async_trait::async_trait;
use futures_util::task::noop_waker_ref;
Expand Down Expand Up @@ -409,6 +414,25 @@ impl Env {
};
}
}

/// Assigns a variable.
///
/// This function is a thin wrapper around [`VariableSet::assign`] that
/// automatically applies the `AllExport` [shell
/// option](crate::option::Option). You should always prefer this unless you
/// want to ignore the option.
pub fn assign_variable(
&mut self,
scope: Scope,
name: String,
value: Variable,
) -> Result<Option<Variable>, ReadOnlyError> {
let value = match self.options.get(AllExport) {
On => value.export(),
Off => value,
};
self.variables.assign(scope, name, value)
}
}

#[async_trait(?Send)]
Expand Down Expand Up @@ -738,4 +762,33 @@ mod tests {
);
assert_eq!(env.jobs.get(job_3).unwrap().status, WaitStatus::StillAlive);
}

#[test]
fn assign_variable_with_all_export_off() {
let mut env = Env::new_virtual();
let a = Variable::new("A");
let result = env.assign_variable(Scope::Global, "a".to_string(), a.clone());
assert_eq!(result, Ok(None));
let b = Variable::new("B").export();
let result = env.assign_variable(Scope::Global, "b".to_string(), b.clone());
assert_eq!(result, Ok(None));

assert_eq!(env.variables.get("a").unwrap(), &a);
assert_eq!(env.variables.get("b").unwrap(), &b);
}

#[test]
fn assign_variable_with_all_export_on() {
let mut env = Env::new_virtual();
env.options.set(AllExport, On);
let a = Variable::new("A");
let result = env.assign_variable(Scope::Global, "a".to_string(), a.clone());
assert_eq!(result, Ok(None));
let b = Variable::new("B").export();
let result = env.assign_variable(Scope::Global, "b".to_string(), b.clone());
assert_eq!(result, Ok(None));

assert_eq!(env.variables.get("a").unwrap(), &a.export());
assert_eq!(env.variables.get("b").unwrap(), &b);
}
}
7 changes: 7 additions & 0 deletions yash-env/src/variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,13 @@ impl VariableSet {
/// existing read-only value, the assignment fails unless the new variable
/// is a local variable that hides the read-only.
///
/// If an existing same-named variable is exported, this function also
/// exports the new value. Otherwise, the value is assigned without
/// modification. To apply the `AllExport` [shell
/// option](crate::option::Option), you should prefer the
/// [`Env::assign_variable`] function rather than calling this function
/// directly.
///
/// The behavior of assignment depends on the `scope`:
///
/// - If the scope is `Global`, the assignment overwrites a visible existing
Expand Down
10 changes: 4 additions & 6 deletions yash-semantics/src/assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,10 @@ pub async fn perform_assignment(
is_exported: export,
read_only_location: None,
};
env.variables
.assign(scope, name, value)
.map_err(|e| Error {
cause: ErrorCause::AssignReadOnly(e),
location: assign.location.clone(),
})?;
env.assign_variable(scope, name, value).map_err(|e| Error {
cause: ErrorCause::AssignReadOnly(e),
location: assign.location.clone(),
})?;
Ok(exit_status)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub async fn execute(

for Field { value, origin } in values {
let var = Variable::new(value).set_assigned_location(origin);
match env.variables.assign(Scope::Global, name.value.clone(), var) {
match env.assign_variable(Scope::Global, name.value.clone(), var) {
Ok(_) => match body.execute(env).await {
Break(Divert::Break { count: 0 }) => break,
Break(Divert::Break { count }) => return Break(Divert::Break { count: count - 1 }),
Expand Down
11 changes: 5 additions & 6 deletions yash-semantics/src/expansion/initial/arith.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ use yash_env::variable::ReadOnlyError;
use yash_env::variable::Scope::Global;
use yash_env::variable::Value::Scalar;
use yash_env::variable::Variable;
use yash_env::variable::VariableSet;
use yash_syntax::source::Code;
use yash_syntax::source::Location;
use yash_syntax::source::Source;
Expand Down Expand Up @@ -188,7 +187,7 @@ pub fn convert_error_cause(
}

struct VarEnv<'a> {
variables: &'a mut VariableSet,
env: &'a mut yash_env::Env,
expression: &'a str,
expansion_location: &'a Location,
}
Expand All @@ -198,7 +197,7 @@ impl<'a> yash_arith::Env for VarEnv<'a> {

#[rustfmt::skip]
fn get_variable(&self, name: &str) -> Option<&str> {
if let Some(Variable { value: Scalar(value), .. }) = self.variables.get(name) {
if let Some(Variable { value: Scalar(value), .. }) = self.env.variables.get(name) {
Some(value)
} else {
None
Expand All @@ -220,8 +219,8 @@ impl<'a> yash_arith::Env for VarEnv<'a> {
});
let location = Location { code, range };
let value = Variable::new(value).set_assigned_location(location);
self.variables
.assign(Global, name.to_owned(), value)
self.env
.assign_variable(Global, name.to_owned(), value)
.map(drop)
}
}
Expand All @@ -235,7 +234,7 @@ pub async fn expand(text: &Text, location: &Location, env: &mut Env<'_>) -> Resu
let result = eval(
&expression,
&mut VarEnv {
variables: &mut env.inner.variables,
env: env.inner,
expression: &expression,
expansion_location: location,
},
Expand Down
3 changes: 1 addition & 2 deletions yash-semantics/src/expansion/initial/param/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,7 @@ async fn assign(
let variable = Variable::new(skip_quotes(variable_value).strip().collect::<String>())
.set_assigned_location(location.clone());
env.inner
.variables
.assign(Scope::Global, name, variable)
.assign_variable(Scope::Global, name, variable)
.map_err(|e| Error {
cause: ErrorCause::AssignReadOnly(e),
location: location.clone(),
Expand Down

0 comments on commit f4dbb89

Please sign in to comment.