Commit d032f7d9 authored by Michael Aaron Murphy's avatar Michael Aaron Murphy

Improve variable performance w/ a CGS

parent 68c6a072
This diff is collapsed.
......@@ -88,6 +88,7 @@ atty = "0.2"
permutate = "0.3"
dirs = "1.0"
nix = "0.14"
froggy = "0.4.4"
[target."cfg(all(unix, not(target_os = \"redox\")))".dependencies]
users = "0.9"
......
......@@ -5,7 +5,7 @@ use std::{cell::RefCell, fs::File, path::Path, rc::Rc};
use ai_behavior::{Action, Sequence, Wait, WaitForever, While};
use ion_shell::{
builtins::{BuiltinFunction, Status},
types, Shell, Value,
types, Shell,
};
use piston_window::*;
use sprite::*;
......@@ -161,9 +161,9 @@ fn main() {
if e.render_args().is_some() {
// Get the on_render function
if let Some(Value::Function(function)) = shell.variables().get("on_render") {
if let Some(function) = shell.variables().get_func("on_render").cloned() {
// and then call it. N.B.: the first argument must always be ion
if let Err(why) = shell.execute_function(&function.clone(), &["ion"]) {
if let Err(why) = shell.execute_function(&function, &["ion"]) {
// check for errors
eprintln!("window example: error in on_render callback: {}", why);
}
......@@ -171,10 +171,10 @@ fn main() {
}
if let Some(Button::Keyboard(key)) = e.press_args() {
if let Some(Value::Function(function)) = shell.variables().get("on_key") {
if let Some(function) = shell.variables().get_func("on_key").cloned() {
if let Err(why) =
// provide a parameter for the callback
shell.execute_function(&function.clone(), &["ion", &key.code().to_string()])
shell.execute_function(&function, &["ion", &key.code().to_string()])
{
eprintln!("window example: error in on_key callback: {}", why);
}
......@@ -182,9 +182,9 @@ fn main() {
}
if let Some([x, y]) = e.mouse_cursor_args() {
if let Some(Value::Function(function)) = shell.variables().get("on_mouse") {
if let Some(function) = shell.variables().get_func("on_mouse").cloned() {
if let Err(why) = shell
.execute_function(&function.clone(), &["ion", &x.to_string(), &y.to_string()])
.execute_function(&function, &["ion", &x.to_string(), &y.to_string()])
{
eprintln!("window example: error in on_mouse callback: {}", why);
}
......
......@@ -55,6 +55,8 @@ impl<K: Hash + Eq, V: Clone> Scopes<K, V> {
}
}
pub fn current_scope(&self) -> &Scope<K, V> { &self.scopes[self.current] }
pub fn pop_scope(&mut self) {
self.scopes[self.current].clear();
self.current -= 1;
......
......@@ -16,3 +16,4 @@ hashbrown = "0.5"
small = { git = "https://gitlab.redox-os.org/redox-os/small", features = ["std"] }
smallvec = "0.6"
itertools = "0.8"
shrinkwraprs = "0.2.1"
#[macro_use]
extern crate shrinkwraprs;
mod math;
mod modification;
pub mod types;
......@@ -9,18 +12,76 @@ pub use self::{
use itertools::Itertools;
use std::fmt;
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ValueKind {
Alias,
Array,
BTreeMap,
Function,
HashMap,
Str,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Value<T> {
Str(types::Str),
Alias(types::Alias),
Array(types::Array<T>),
HashMap(types::HashMap<T>),
BTreeMap(types::BTreeMap<T>),
Function(T),
HashMap(types::HashMap<T>),
None,
Str(types::Str),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ValueRef<'a, T> {
Alias(types::AliasRef<'a>),
Array(&'a types::ArrayRef<T>),
BTreeMap(&'a types::BTreeMap<T>),
Function(&'a T),
HashMap(&'a types::HashMap<T>),
None,
Str(&'a types::StrRef),
}
impl<T: Eq> Eq for Value<T> {}
impl<'a, T: Clone> ValueRef<'a, T> {
pub fn to_owned(self) -> Value<T> {
match self {
ValueRef::Alias(value) => Value::Alias(types::Alias((*value).into())),
ValueRef::Array(value) => Value::Array(value.to_owned()),
ValueRef::BTreeMap(value) => Value::BTreeMap(value.clone()),
ValueRef::Function(value) => Value::Function(value.clone()),
ValueRef::HashMap(value) => Value::HashMap(value.to_owned()),
ValueRef::Str(value) => Value::Str(value.into()),
ValueRef::None => Value::None,
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum ValueRefMut<'a, T> {
Alias(types::AliasRefMut<'a>),
Array(&'a mut types::ArrayRef<T>),
BTreeMap(&'a mut types::BTreeMap<T>),
Function(&'a mut T),
HashMap(&'a mut types::HashMap<T>),
None,
Str(&'a mut types::StrRef),
}
impl<'a, T: Clone> ValueRefMut<'a, T> {
pub fn to_owned(self) -> Value<T> {
match self {
ValueRefMut::Alias(value) => Value::Alias(types::Alias((&*value.0).into())),
ValueRefMut::Array(value) => Value::Array(value.to_owned()),
ValueRefMut::BTreeMap(value) => Value::BTreeMap(value.clone()),
ValueRefMut::Function(value) => Value::Function(value.clone()),
ValueRefMut::HashMap(value) => Value::HashMap(value.to_owned()),
ValueRefMut::Str(value) => Value::Str((&*value).into()),
ValueRefMut::None => Value::None,
}
}
}
// this one’s only special because of the lifetime parameter
impl<'a, T> From<&'a str> for Value<T> {
......
......@@ -4,33 +4,30 @@ use small;
use std::{
collections::BTreeMap as StdBTreeMap,
iter::FromIterator,
ops::{Deref, DerefMut},
};
pub type Array<T> = Vec<Value<T>>;
pub type ArrayRef<T> = [Value<T>];
pub type HashMap<T> = HashbrownMap<Str, Value<T>>;
pub type BTreeMap<T> = StdBTreeMap<Str, Value<T>>;
pub type Str = small::String;
pub type StrRef = str;
#[derive(Clone, Debug, PartialEq, Hash, Eq, Default)]
#[derive(Clone, Debug, PartialEq, Hash, Eq, Default, Shrinkwrap)]
pub struct Alias(pub Str);
impl Alias {
pub fn empty() -> Self { Alias(Str::with_capacity(1)) }
}
impl Deref for Alias {
type Target = Str;
#[derive(Clone, Debug, PartialEq, Hash, Eq, Default, Shrinkwrap)]
pub struct AliasRef<'a>(pub &'a StrRef);
fn deref(&self) -> &Self::Target { &self.0 }
}
#[derive(Debug, PartialEq, Hash, Eq, Default, Shrinkwrap)]
pub struct AliasRefMut<'a>(pub &'a mut StrRef);
impl DerefMut for Alias {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
impl<'a> From<&'a mut Alias> for AliasRefMut<'a> {
fn from(alias: &'a mut Alias) -> Self { AliasRefMut(alias.0.as_mut_str()) }
}
impl Into<Str> for Alias {
fn into(self) -> Str { self.0 }
impl<'a> From<&'a Alias> for AliasRef<'a> {
fn from(alias: &'a Alias) -> Self { AliasRef(&**alias) }
}
impl<T> FromIterator<Value<T>> for Value<T> {
......
use super::InteractiveShell;
use ion_shell::{builtins::Status, Value};
use ion_shell::{builtins::Status, ValueRef};
use regex::Regex;
use std::time::{SystemTime, UNIX_EPOCH};
......@@ -26,7 +26,8 @@ impl<'a> InteractiveShell<'a> {
/// Updates the history ignore patterns. Call this whenever HISTORY_IGNORE
/// is changed.
pub fn ignore_patterns(&self) -> IgnoreSetting {
if let Some(Value::Array(patterns)) = self.shell.borrow().variables().get("HISTORY_IGNORE")
if let Some(ValueRef::Array(patterns)) =
self.shell.borrow().variables().get("HISTORY_IGNORE")
{
let mut settings = IgnoreSetting::default();
// for convenience and to avoid typos
......
......@@ -11,7 +11,7 @@ use ion_shell::{
builtins::{man_pages, BuiltinFunction, Status},
expansion::Expander,
parser::Terminator,
types, IonError, PipelineError, Shell, Signal, Value,
types, IonError, PipelineError, Shell, Signal,
};
use itertools::Itertools;
use liner::{Buffer, Context, KeyBindings};
......@@ -247,8 +247,8 @@ impl<'a> InteractiveShell<'a> {
Err(IonError::PipelineExecutionError(
PipelineError::CommandNotFound(command),
)) => {
if let Some(Value::Function(func)) =
shell.variables().get("COMMAND_NOT_FOUND").cloned()
if let Some(func) =
shell.variables().get_func("COMMAND_NOT_FOUND").cloned()
{
if let Err(why) =
shell.execute_function(&func, &["ion", &command])
......
use super::Status;
use crate as ion_shell;
use crate::{
shell::{Shell, Value},
shell::{Shell, ValueRef},
types,
};
use builtins_proc::builtin;
......@@ -29,8 +29,8 @@ pub fn which(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
match get_command_info(command, shell) {
Ok(c_type) => match c_type.as_ref() {
"alias" => {
if let Some(Value::Alias(ref alias)) = shell.variables().get(&**command) {
println!("{}: alias to {}", command, &**alias);
if let Some(ValueRef::Alias(alias)) = shell.variables().get(&**command) {
println!("{}: alias to {}", command, alias.0);
}
}
"function" => println!("{}: function", command),
......@@ -45,8 +45,8 @@ pub fn which(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
fn get_command_info<'a>(command: &str, shell: &mut Shell<'_>) -> Result<Cow<'a, str>, ()> {
match shell.variables().get(command) {
Some(Value::Alias(_)) => Ok("alias".into()),
Some(Value::Function(_)) => Ok("function".into()),
Some(ValueRef::Alias(_)) => Ok("alias".into()),
Some(ValueRef::Function(_)) => Ok("function".into()),
_ if shell.builtins().contains(command) => Ok("builtin".into()),
_ => {
let paths = env::var_os("PATH").unwrap_or_else(|| "/bin".into());
......
......@@ -3,7 +3,7 @@ use std::{fs, os::unix::fs::PermissionsExt};
use super::Status;
use crate as ion_shell;
use crate::{
shell::{Shell, Value},
shell::{Shell, ValueRef},
types,
};
use builtins_proc::builtin;
......@@ -168,7 +168,7 @@ fn file_has_execute_permission(filepath: &str) -> bool {
/// Returns true if the variable is an array and the array is not empty
fn array_var_is_not_empty(arrayvar: &str, shell: &Shell<'_>) -> bool {
match shell.variables().get(arrayvar) {
Some(Value::Array(array)) => !array.is_empty(),
Some(ValueRef::Array(array)) => !array.is_empty(),
_ => false,
}
}
......@@ -183,12 +183,9 @@ fn string_var_is_not_empty(stringvar: &str, shell: &Shell<'_>) -> bool {
/// Returns true if a function with the given name is defined
fn function_is_defined(function: &str, shell: &Shell<'_>) -> bool {
if let Some(Value::Function(_)) = shell.variables().get(function) {
true
} else {
false
}
shell.variables().is_func(function)
}
#[cfg(test)]
mod tests {
use super::*;
......@@ -196,6 +193,7 @@ mod tests {
flow_control::Function,
parser::lexers::assignments::{KeyBuf, Primitive},
shell::flow_control::Statement,
shell::variables::Value,
types,
};
use std::rc::Rc;
......
......@@ -35,7 +35,7 @@ pub use self::{
};
use crate as ion_shell;
use crate::{
shell::{Shell, Value},
shell::{Shell, Value, ValueRef},
types,
};
use builtins_proc::builtin;
......@@ -281,34 +281,37 @@ DESCRIPTION
"
)]
pub fn cd(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
let dir_stack = &mut shell.directory_stack;
let variables = &mut shell.variables;
let err = match args.get(1) {
Some(dir) => {
let dir = dir.as_ref();
if let Some(Value::Array(cdpath)) = shell.variables().get("CDPATH").cloned() {
if let Some(ValueRef::Array(cdpath)) = variables.get("CDPATH") {
if dir == "-" {
shell.dir_stack_mut().switch_to_previous_directory()
dir_stack.switch_to_previous_directory()
} else {
let check_cdpath_first = cdpath
.iter()
.map(|path| {
let path_dir = format!("{}/{}", path, dir);
shell.dir_stack_mut().change_and_push_dir(&path_dir)
dir_stack.change_and_push_dir(&path_dir)
})
.find(Result::is_ok)
.unwrap_or_else(|| shell.dir_stack_mut().change_and_push_dir(dir));
shell.dir_stack_mut().popd(1);
.unwrap_or_else(|| dir_stack.change_and_push_dir(dir));
dir_stack.popd(1);
check_cdpath_first
}
} else {
shell.dir_stack_mut().change_and_push_dir(dir)
dir_stack.change_and_push_dir(dir)
}
}
None => shell.dir_stack_mut().switch_to_home_directory(),
None => dir_stack.switch_to_home_directory(),
};
match err {
Ok(()) => {
if let Some(Value::Function(function)) = shell.variables().get("CD_CHANGE").cloned() {
if let Some(function) = shell.variables().get_func("CD_CHANGE").cloned() {
let _ = shell.execute_function(&function, &["ion"]);
}
Status::SUCCESS
......
use super::Status;
use crate as ion_shell;
use crate::{
shell::{variables::Value, Shell},
shell::{
variables::{Value, ValueRef},
Shell,
},
types,
};
use builtins_proc::builtin;
......@@ -71,7 +74,7 @@ pub fn set(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
match positionals {
None => (),
Some(kind) => {
if let Some(Value::Array(array)) = shell.variables().get("args") {
if let Some(ValueRef::Array(array)) = shell.variables().get("args") {
let command = array[0].clone();
// This used to take a `&[String]` but cloned them all, so although
// this is non-ideal and could probably be better done with `Rc`, it
......
......@@ -56,8 +56,10 @@
//! for _ in 0..255 {
//! i += 1;
//! // call a user-defined callback function named on_update
//! if let Some(Value::Function(function)) = shell.variables().get("on_update") {
//! if let Err(why) = shell.execute_function(&function.clone(), &["ion", &i.to_string()]) {
//! if let Some(Value::Function(function)) = {
//! shell.variables().get_func("on_update").cloned()
//! } {
//! if let Err(why) = shell.execute_function(&function, &["ion", &i.to_string()]) {
//! println!("ERROR: my-application: error in on_update callback: {}", why);
//! }
//! }
......
......@@ -148,7 +148,9 @@ impl<'b> Shell<'b> {
let lhs = self.variables.get(key.name).ok_or_else(|| {
format!("cannot update non existing variable `{}`", key.name)
})?;
let val = apply(operator, lhs, rhs).map_err(|_| {
// TODO: Prevent lhs allocation.
let val = apply(operator, &lhs.to_owned(), rhs).map_err(|_| {
format!(
"type error: variable `{}` of type `{}` does not support operator",
key.name, key.kind
......
......@@ -10,7 +10,7 @@ use crate::{
Expander, ForValueExpression,
},
parser::{parse_and_validate, StatementSplitter, Terminator},
shell::{IonError, Job, Value},
shell::{IonError, Job, Value, ValueRef},
types,
};
use err_derive::Error;
......@@ -490,8 +490,8 @@ impl<'a> Shell<'a> {
// let pattern_is_array = is_array(&value);
let previous_bind = case.binding.as_ref().and_then(|bind| {
if is_array {
let out = if let Some(Value::Array(array)) = self.variables.get(bind) {
Some(Value::Array(array.clone()))
let out = if let Some(ValueRef::Array(array)) = self.variables.get(bind) {
Some(Value::Array(array.to_owned()))
} else {
None
};
......@@ -499,8 +499,8 @@ impl<'a> Shell<'a> {
.set(bind, value.iter().cloned().map(Value::Str).collect::<Value<_>>());
out
} else {
let out = if let Some(Value::Str(val)) = self.variables.get(bind) {
Some(Value::Str(val.clone()))
let out = if let Some(ValueRef::Str(val)) = self.variables.get(bind) {
Some(Value::Str(val.into()))
} else {
None
};
......@@ -565,8 +565,8 @@ fn expand_pipeline<'a>(
let mut statements = Vec::new();
while let Some(item) = item_iter.next() {
if let Some(Value::Alias(alias)) = shell.variables.get(&item.job.args[0]) {
statements = StatementSplitter::new(alias.0.as_str())
if let Some(ValueRef::Alias(alias)) = shell.variables.get(&item.job.args[0]) {
statements = StatementSplitter::new(alias.0)
.map(|stmt| parse_and_validate(stmt?, &shell.builtins).map_err(Into::into))
.collect::<std::result::Result<_, IonError>>()?;
......
......@@ -2,7 +2,7 @@ use super::{IonError, Shell};
use crate::{
builtins::{self, BuiltinFunction},
expansion::{self, pipelines::RedirectFrom, Expander},
types, Value,
types,
};
use std::{fmt, fs::File, iter, path::Path, str};
......@@ -42,7 +42,7 @@ impl<'a> Job<'a> {
iter::once("cd".into()).chain(args).collect(),
self.redirection,
)
} else if let Some(Value::Function(_)) = shell.variables.get(&self.args[0]) {
} else if shell.variables.is_func(&self.args[0]) {
RefinedJob::function(self.args.clone(), self.redirection)
} else if let Some(builtin) = self.builtin {
RefinedJob::builtin(builtin, args, self.redirection)
......
......@@ -27,7 +27,7 @@ pub use self::{
job_control::{BackgroundEvent, BackgroundProcess},
PipelineError,
},
variables::Value,
variables::{Value, ValueRef, ValueRefMut},
};
use crate::{
assignments::value_check,
......@@ -116,11 +116,11 @@ pub struct Shell<'a> {
/// started.
builtins: BuiltinMap<'a>,
/// Contains the aliases, strings, and array variable maps.
variables: Variables<'a>,
pub(crate) variables: Variables<'a>,
/// Contains the current state of flow control parameters.
flow_control: Block<'a>,
/// Contains the directory stack parameters.
directory_stack: DirectoryStack,
pub(crate) directory_stack: DirectoryStack,
/// When a command is executed, the final result of that command is stored
/// here.
previous_status: Status,
......@@ -329,10 +329,11 @@ impl<'a> Shell<'a> {
self.execute_pipeline(pipeline).map_err(Into::into)
} else if let Some(main) = self.builtins.get(pipeline.items[0].command()) {
Ok(main(&pipeline.items[0].job.args, self))
} else if let Some(Value::Function(function)) =
self.variables.get(&pipeline.items[0].job.args[0]).cloned()
{
function.execute(self, &pipeline.items[0].job.args).map(|_| self.previous_status)
} else if let Some(function) = self.variables.get_func(&pipeline.items[0].job.args[0]) {
function
.clone()
.execute(self, &pipeline.items[0].job.args)
.map(|_| self.previous_status)
} else {
self.execute_pipeline(pipeline).map_err(Into::into)
}?;
......@@ -440,15 +441,15 @@ impl<'a> Shell<'a> {
.ok_or_else(|| "index value does not exist".to_string())?;
match lhs {
Value::HashMap(hmap) => {
ValueRefMut::HashMap(hmap) => {
let _ = hmap.insert(index, value);
Ok(())
}
Value::BTreeMap(bmap) => {
ValueRefMut::BTreeMap(bmap) => {
let _ = bmap.insert(index, value);
Ok(())
}
Value::Array(array) => {
ValueRefMut::Array(array) => {
let index_num = index.parse::<usize>().map_err(|_| {
format!("index variable is not a numeric value: `{}`", index)
})?;
......
......@@ -16,7 +16,7 @@ use self::{job_control::ProcessState, pipes::TeePipe};
use super::{
job::{RefinedJob, TeeItem, Variant},
signals::{self, SignalHandler},
IonError, Shell, Value,
IonError, Shell,
};
use crate::{
builtins::Status,
......@@ -337,7 +337,7 @@ impl<'b> Shell<'b> {
}
fn exec_function<S: AsRef<str>>(&mut self, name: &str, args: &[S]) -> Result<Status, IonError> {
if let Some(Value::Function(function)) = self.variables.get(name).cloned() {
if let Some(function) = self.variables.get_func(name).cloned() {
function.execute(self, args).map(|_| self.previous_status)
} else {
unreachable!()
......
use super::{fork::Capture, sys::variables, variables::Value, IonError, PipelineError, Shell};
use super::{
fork::Capture,
sys::variables,
variables::{Value, ValueRef},
IonError, PipelineError, Shell,
};
use crate::{
expansion::{Error, Expander, Result, Select},
types,
......@@ -41,7 +46,7 @@ impl<'a, 'b> Expander for Shell<'b> {
selection: &Select<types::Str>,
) -> Result<types::Args, Self::Error> {
match self.variables.get(name) {
Some(Value::Array(array)) => match selection {
Some(ValueRef::Array(array)) => match selection {
Select::All => {
Ok(types::Args::from_iter(array.iter().map(|x| format!("{}", x).into())))
}
......@@ -69,7 +74,7 @@ impl<'a, 'b> Expander for Shell<'b> {
.ok_or(Error::OutOfBound),
Select::Key(_) => Err(Error::InvalidIndex(selection.clone(), "array", name.into())),
},
Some(Value::HashMap(hmap)) => match selection {
Some(ValueRef::HashMap(hmap)) => match selection {
Select::All => {
let mut array = types::Args::new();
for (key, value) in hmap.iter() {
......@@ -108,7 +113,7 @@ impl<'a, 'b> Expander for Shell<'b> {
Err(Error::InvalidIndex(selection.clone(), "hashmap", name.into()))
}
},
Some(Value::BTreeMap(bmap)) => match selection {
Some(ValueRef::BTreeMap(bmap)) => match selection {
Select::All => {
let mut array = types::Args::new();
for (key, value) in bmap.iter() {
......@@ -154,11 +159,11 @@ impl<'a, 'b> Expander for Shell<'b> {
fn map_keys(&self, name: &str, sel: &Select<types::Str>) -> Result<types::Args, Self::Error> {
match self.variables.get(name) {
Some(&Value::HashMap(ref map)) => {
Some(ValueRef::HashMap(ref map)) => {
Self::select(map.keys().map(|x| format!("{}", x).into()), sel, map.len())
.ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into()))
}
Some(&Value::BTreeMap(ref map)) => {
Some(ValueRef::BTreeMap(ref map)) => {
Self::select(map.keys().map(|x| format!("{}", x).into()), sel, map.len())
.ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into()))
}
......@@ -169,11 +174,11 @@ impl<'a, 'b> Expander for Shell<'b> {
fn map_values(&self, name: &str, sel: &Select<types::Str>) -> Result<types::Args, Self::Error> {
match self.variables.get(name) {
Some(&Value::HashMap(ref map)) => {
Some(ValueRef::HashMap(ref map)) => {
Self::select(map.values().map(|x| format!("{}", x).into()), sel, map.len())
.ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into()))
}
Some(&Value::BTreeMap(ref map)) => {
Some(ValueRef::BTreeMap(ref map)) => {
Self::select(map.values().map(|x| format!("{}", x).into()), sel, map.len())
.ok_or_else(|| Error::InvalidIndex(sel.clone(), "map-like", name.into()))
}
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment