aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bashast/bashast.g12
-rw-r--r--bashast/gunit/array.gunit12
-rw-r--r--bashast/libbashWalker.g44
-rw-r--r--scripts/function_def.bash8
-rw-r--r--scripts/function_def.bash.result8
-rw-r--r--scripts/var_def.bash3
-rw-r--r--scripts/var_def.bash.result2
-rw-r--r--scripts/var_expansion.bash25
-rw-r--r--scripts/var_expansion.bash.result23
-rw-r--r--src/core/interpreter.cpp134
-rw-r--r--src/core/interpreter.h24
-rwxr-xr-xtest/script_compiler.sh5
12 files changed, 179 insertions, 121 deletions
diff --git a/bashast/bashast.g b/bashast/bashast.g
index 2115620..fde2c7e 100644
--- a/bashast/bashast.g
+++ b/bashast/bashast.g
@@ -316,15 +316,17 @@ parameter_replace_operator
//either directly or through array
var_name
: num
+ | name LSQUARE AT RSQUARE -> ^(ARRAY name AT)
+ | name LSQUARE TIMES RSQUARE -> ^(ARRAY name TIMES)
| var_name_no_digit
- | TIMES
| DOLLAR
- | AT;
+ | TIMES
+ | AT
+ | POUND;
//Inside arithmetic we can't allow digits
var_name_no_digit
- : name^ LSQUARE! (AT|TIMES|explicit_arithmetic) RSQUARE!
- | name
- | POUND;
+ : name^ LSQUARE! (explicit_arithmetic) RSQUARE!
+ | name;
//with bang the array syntax is used for array indexes
var_name_for_bang
: num|name|POUND;
diff --git a/bashast/gunit/array.gunit b/bashast/gunit/array.gunit
index b9cb121..3a15c20 100644
--- a/bashast/gunit/array.gunit
+++ b/bashast/gunit/array.gunit
@@ -43,11 +43,11 @@ var_ref:
"${asdf[4]//pattern}" -> (VAR_REF (REPLACE_ALL (asdf 4) (STRING pattern)))
"${asdf}" -> (VAR_REF asdf)
"${#asdf[0]}" -> (VAR_REF (# (asdf 0)))
-"${asdf[@]}" -> (VAR_REF (asdf @))
-"${asdf[*]}" -> (VAR_REF (asdf *))
+"${asdf[@]}" -> (VAR_REF (ARRAY asdf @))
+"${asdf[*]}" -> (VAR_REF (ARRAY asdf *))
"${#asdf[@]}" -> (VAR_REF (# (asdf ARRAY_SIZE)))
"${#asdf[*]}" -> (VAR_REF (# (asdf ARRAY_SIZE)))
-"${asdf[@]:0:1}" -> (VAR_REF (OFFSET (asdf @) 0 1))
-"${asdf[*]#path}" -> (VAR_REF (LAZY_REMOVE_AT_START (asdf *) (STRING path)))
-"${asdf[@]%word}" -> (VAR_REF (LAZY_REMOVE_AT_END (asdf @) (STRING word)))
-"${asdf[*]/pattern/string}" -> (VAR_REF (REPLACE_FIRST (asdf *) (STRING pattern) (STRING string)))
+"${asdf[@]:0:1}" -> (VAR_REF (OFFSET (ARRAY asdf @) 0 1))
+"${asdf[*]#path}" -> (VAR_REF (LAZY_REMOVE_AT_START (ARRAY asdf *) (STRING path)))
+"${asdf[@]%word}" -> (VAR_REF (LAZY_REMOVE_AT_END (ARRAY asdf @) (STRING word)))
+"${asdf[*]/pattern/string}" -> (VAR_REF (REPLACE_FIRST (ARRAY asdf *) (STRING pattern) (STRING string)))
diff --git a/bashast/libbashWalker.g b/bashast/libbashWalker.g
index 962a57d..8b0e74c 100644
--- a/bashast/libbashWalker.g
+++ b/bashast/libbashWalker.g
@@ -367,12 +367,20 @@ var_name returns[std::string libbash_value, unsigned index]
@init {
$var_name.index = 0;
}
- :libbash_string=num { $libbash_value = libbash_string; }
+ :libbash_string=num {
+ $index = boost::lexical_cast<unsigned>(libbash_string);
+ $libbash_value = ($index != 0 ? "*" : "0");
+ }
|name {
$libbash_value = $name.libbash_value;
$index = $name.index;
- }
- |TIMES { $libbash_value = "*"; };
+ };
+
+array_name returns[std::string libbash_value]
+ :^(ARRAY name (AT|TIMES)) { $libbash_value = $name.libbash_value; }
+ // We do not care the difference between TIMES and AT for now
+ |TIMES { $libbash_value = "*"; }
+ |AT { $libbash_value = "*"; };
var_expansion returns[std::string libbash_value]
@declarations {
@@ -389,6 +397,12 @@ var_expansion returns[std::string libbash_value]
|^(USE_ALTERNATE_WHEN_UNSET_OR_NULL var_name libbash_word=word) {
libbash_value = walker->do_alternate_expansion($var_name.libbash_value, libbash_word, $var_name.index);
}
+ |(^(OFFSET array_name arithmetics arithmetics)) => ^(OFFSET libbash_name=array_name offset=arithmetics length=arithmetics) {
+ libbash_value = walker->do_subarray_expansion(libbash_name, offset, length);
+ }
+ |(^(OFFSET array_name offset=arithmetics)) => ^(OFFSET libbash_name=array_name offset=arithmetics) {
+ libbash_value = walker->do_subarray_expansion(libbash_name, offset);
+ }
|(^(OFFSET var_name arithmetics arithmetics)) => ^(OFFSET var_name offset=arithmetics length=arithmetics) {
libbash_value = walker->do_substring_expansion($var_name.libbash_value, offset, length, $var_name.index);
}
@@ -455,27 +469,11 @@ word returns[std::string libbash_value]
//variable reference
var_ref [bool double_quoted] returns[std::string libbash_value]
- :^(VAR_REF name) {
- $libbash_value = walker->resolve<std::string>($name.libbash_value, $name.index);
- }
- |^(VAR_REF libbash_string=num) {
- $libbash_value = walker->resolve<std::string>(libbash_string);
- }
- |^(VAR_REF ^(libbash_string=name_base AT)) { walker->get_all_elements(libbash_string, $libbash_value); }
- |^(VAR_REF ^(libbash_string=name_base TIMES)) {
- if(double_quoted)
- walker->get_all_elements_IFS_joined(libbash_string, $libbash_value);
- else
- walker->get_all_elements(libbash_string, $libbash_value);
- }
- |^(VAR_REF TIMES) {
- if(double_quoted)
- walker->get_all_elements_IFS_joined("*", $libbash_value);
- else
- walker->get_all_elements("*", $libbash_value);
+ :^(VAR_REF var_name) {
+ $libbash_value = walker->resolve<std::string>($var_name.libbash_value, $var_name.index);
}
- |^(VAR_REF AT) { std::cerr << "$@ has not been implemented yet" << std::endl; }
- |^(VAR_REF POUND) { std::cerr << "$# has not been implemented yet" << std::endl; }
+ |^(VAR_REF libbash_string=array_name) { walker->get_all_elements_IFS_joined(libbash_string, $libbash_value); }
+ |^(VAR_REF POUND) { $libbash_value = boost::lexical_cast<std::string>(walker->get_array_length("*")); }
|^(VAR_REF QMARK) { $libbash_value = walker->get_status<std::string>(); }
|^(VAR_REF MINUS) { std::cerr << "$- has not been implemented yet" << std::endl; }
|^(VAR_REF BANG) { std::cerr << "$! has not been implemented yet" << std::endl; }
diff --git a/scripts/function_def.bash b/scripts/function_def.bash
index 53e5ec8..8b7bd50 100644
--- a/scripts/function_def.bash
+++ b/scripts/function_def.bash
@@ -50,6 +50,14 @@ func_nested2() {
local foo_nested=hi bar_nested=(1 2
3) localbar
localbar=1
+ echo ${bar_nested[@]}
+ echo ${not_exist[@]}
+ echo ${#bar_nested[@]}
+ echo ${#non_exist[@]}
+ echo ${#foo_nested}
+ echo ${#non_exist}
+ echo ${foo_nested:-wrong}
+ echo ${non_exist:-right}
func_nested1
}
func_nested2
diff --git a/scripts/function_def.bash.result b/scripts/function_def.bash.result
index cebbfae..a4af30a 100644
--- a/scripts/function_def.bash.result
+++ b/scripts/function_def.bash.result
@@ -1,3 +1,11 @@
+1 2 3
+
+3
+0
+2
+0
+hi
+right
hi 1 1
overloaded let
diff --git a/scripts/var_def.bash b/scripts/var_def.bash
index f832a96..0989f9a 100644
--- a/scripts/var_def.bash
+++ b/scripts/var_def.bash
@@ -35,9 +35,6 @@ ARRAY10="${ARRAY05[*]}"
FOO001="networkmanager"
FOO002="0.8.2"
FOO003=${FOO001}-${FOO002}
-FOO004=$*
-FOO004=$@
-FOO004=$#
FOO004=$?
FOO004=$-
FOO004=$!
diff --git a/scripts/var_def.bash.result b/scripts/var_def.bash.result
index 121c267..b9b415e 100644
--- a/scripts/var_def.bash.result
+++ b/scripts/var_def.bash.result
@@ -1,5 +1,3 @@
-$@ has not been implemented yet
-$# has not been implemented yet
$- has not been implemented yet
$! has not been implemented yet
ARRAY01=1 2 3 4 5
diff --git a/scripts/var_expansion.bash b/scripts/var_expansion.bash
index d38d8aa..9f42f19 100644
--- a/scripts/var_expansion.bash
+++ b/scripts/var_expansion.bash
@@ -84,12 +84,21 @@ FOO081=${FOO039//@([a-c]|[k-m])}
target="abc123abc"
FOO082="${target##+(ab[c])*([[:digit:]])}"
function positional_parameter_test(){
- FOO083=${*}
- FOO084=${*:1}
- FOO085=${*:1:2}
- FOO086=${*: -1}
- FOO087=${*: -2:5}
- FOO088=${*:0}
+ FOO083=$*
+ FOO084=${*}
+ FOO085=${*:1}
+ FOO086=${*:1:2}
+ FOO087=${*: -1}
+ FOO088=${*: -2:5}
+ FOO089=${*:0}
+ FOO090=$@
+ FOO091=${@}
+ FOO092=${@:1}
+ FOO093=${@:1:2}
+ FOO094=${@: -1}
+ FOO095=${@: -2:5}
+ FOO096=${@:0}
+ echo $#
}
positional_parameter_test 1 2 3 4 5
target="abc*abc"
@@ -97,3 +106,7 @@ echo ${target/*}
echo ${target/'*'}
echo ${target/"*"}
: ${FOO089:=}
+ARRAY=(1 2 3 4 5)
+echo ${ARRAY[@]:1}
+echo ${ARRAY[@]:1:3}
+echo $#
diff --git a/scripts/var_expansion.bash.result b/scripts/var_expansion.bash.result
index c3f30c8..bd83c62 100644
--- a/scripts/var_expansion.bash.result
+++ b/scripts/var_expansion.bash.result
@@ -1,7 +1,11 @@
+5
abcabc
abcabc
-ARRAY=hi hello 1 2 3
+2 3 4 5
+2 3 4
+0
+ARRAY=1 2 3 4 5
ARRAY2=hello
EAPI=3
EAPI4=4
@@ -88,9 +92,16 @@ FOO081=Heo Word
FOO082=abc
FOO083=1 2 3 4 5
FOO084=1 2 3 4 5
-FOO085=1 2
-FOO086=5
-FOO087=4 5
-FOO088=filename 1 2 3 4 5
-FOO089=
+FOO085=1 2 3 4 5
+FOO086=1 2
+FOO087=5
+FOO088=4 5
+FOO089=@srcdir@/scripts/var_expansion.bash 1 2 3 4 5
+FOO090=1 2 3 4 5
+FOO091=1 2 3 4 5
+FOO092=1 2 3 4 5
+FOO093=1 2
+FOO094=5
+FOO095=4 5
+FOO096=@srcdir@/scripts/var_expansion.bash 1 2 3 4 5
target=abc*abc
diff --git a/src/core/interpreter.cpp b/src/core/interpreter.cpp
index fedbee4..78ffd89 100644
--- a/src/core/interpreter.cpp
+++ b/src/core/interpreter.cpp
@@ -90,22 +90,12 @@ std::shared_ptr<variable> interpreter::resolve_variable(const std::string& name)
if(name.empty())
return std::shared_ptr<variable>();
- // positional parameter
- if(isdigit(name[0]) && !local_members.empty())
+ BOOST_REVERSE_FOREACH(auto& frame, local_members)
{
- auto iter_local = local_members.back().find(name);
- if(iter_local != local_members.back().end())
+ auto iter_local = frame.find(name);
+ if(iter_local != frame.end())
return iter_local->second;
}
- else
- {
- BOOST_REVERSE_FOREACH(auto& frame, local_members)
- {
- auto iter_local = frame.find(name);
- if(iter_local != frame.end())
- return iter_local->second;
- }
- }
auto iter_global = members.find(name);
if(iter_global == members.end())
@@ -116,53 +106,22 @@ std::shared_ptr<variable> interpreter::resolve_variable(const std::string& name)
bool interpreter::is_unset_or_null(const std::string& name,
const unsigned index) const
{
- auto i = members.find(name);
- if(i == members.end())
+ auto var = resolve_variable(name);
+ if(!var)
return true;
- else
- return i->second->is_null(index);
+ return var->is_null(index);
}
-// This method temporarily supports array offset expansion for $* and $@.
-// That logic will be refactored and applied to normal array variables in future.
std::string interpreter::get_substring(const std::string& name,
long long offset,
unsigned length,
const unsigned index) const
{
- if(name != "*" && name != "@")
- {
- std::string value = resolve<std::string>(name, index);
- if(!get_real_offset(offset, boost::numeric_cast<unsigned>(value.size())))
- return "";
- // After get_real_offset, we know offset can be cast to unsigned.
- return value.substr(boost::numeric_cast<std::string::size_type>(offset), length);
- }
- else
- {
- std::vector<std::string> array;
- // ${*:1} has the same content as ${*}, ${*:0} contains current script name as the first element
- if(offset > 0)
- offset--;
- else if(offset == 0)
- // Need to replace this with the real script name
- array.push_back("filename");
- // We do not support arrays that have size bigger than numeric_limits<unsigned>::max()
- if(resolve_array(name, array) && get_real_offset(offset, boost::numeric_cast<unsigned>(array.size())))
- {
- // We do not support arrays that have size bigger than numeric_limits<unsigned>::max()
- // After get_real_offset, we know offset can be cast to unsigned.
- unsigned max_length = boost::numeric_cast<unsigned>(array.size()) - boost::numeric_cast<unsigned>(offset);
- if(length > max_length)
- length = max_length;
-
- auto start = array.begin() + boost::numeric_cast<std::vector<std::string>::difference_type>(offset);
- auto end = array.begin() + boost::numeric_cast<std::vector<std::string>::difference_type>(offset + length);
- return boost::algorithm::join(std::vector<std::string>(start, end), resolve<std::string>("IFS").substr(0, 1));
- }
- else
- return "";
- }
+ std::string value = resolve<std::string>(name, index);
+ if(!get_real_offset(offset, boost::numeric_cast<unsigned>(value.size())))
+ return "";
+ // After get_real_offset, we know offset can be cast to unsigned.
+ return value.substr(boost::numeric_cast<std::string::size_type>(offset), length);
}
const std::string interpreter::do_substring_expansion(const std::string& name,
@@ -183,6 +142,54 @@ const std::string interpreter::do_substring_expansion(const std::string& name,
return get_substring(name, offset, boost::numeric_cast<unsigned>(length), index);
}
+std::string interpreter::get_subarray(const std::string& name,
+ long long offset,
+ unsigned length) const
+{
+ std::vector<std::string> array;
+ if(name == "*" || name == "@")
+ {
+ // ${*:1} has the same content as ${*}, ${*:0} contains current script name as the first element
+ if(offset > 0)
+ offset--;
+ else if(offset == 0)
+ array.push_back(resolve<std::string>("0"));
+ }
+ // We do not support arrays that have size bigger than numeric_limits<unsigned>::max()
+ if(resolve_array(name, array) && get_real_offset(offset, boost::numeric_cast<unsigned>(array.size())))
+ {
+ // We do not support arrays that have size bigger than numeric_limits<unsigned>::max()
+ // After get_real_offset, we know offset can be cast to unsigned.
+ unsigned max_length = boost::numeric_cast<unsigned>(array.size()) - boost::numeric_cast<unsigned>(offset);
+ if(length > max_length)
+ length = max_length;
+
+ auto start = array.begin() + boost::numeric_cast<std::vector<std::string>::difference_type>(offset);
+ auto end = array.begin() + boost::numeric_cast<std::vector<std::string>::difference_type>(offset + length);
+ return boost::algorithm::join(std::vector<std::string>(start, end), resolve<std::string>("IFS").substr(0, 1));
+ }
+ else
+ {
+ return "";
+ }
+}
+
+const std::string interpreter::do_subarray_expansion(const std::string& name,
+ long long offset) const
+{
+ return get_subarray(name, offset, std::numeric_limits<unsigned>::max());
+}
+
+const std::string interpreter::do_subarray_expansion(const std::string& name,
+ long long offset,
+ int length) const
+{
+ if(length < 0)
+ throw interpreter_exception("length of substring expression should be greater or equal to zero");
+
+ return get_subarray(name, offset, boost::numeric_cast<unsigned>(length));
+}
+
std::string interpreter::do_replace_expansion(const std::string& name,
std::function<void(std::string&)> replacer,
const unsigned index) const
@@ -195,19 +202,18 @@ std::string interpreter::do_replace_expansion(const std::string& name,
std::string::size_type interpreter::get_length(const std::string& name,
const unsigned index) const
{
- auto i = members.find(name);
- if(i == members.end())
+ auto var = resolve_variable(name);
+ if(!var)
return 0;
- return i->second->get_length(index);
+ return var->get_length(index);
}
variable::size_type interpreter::get_array_length(const std::string& name) const
{
- auto i = members.find(name);
- if(i == members.end())
+ auto var = resolve_variable(name);
+ if(!var)
return 0;
- else
- return i->second->get_array_length();
+ return var->get_array_length();
}
void interpreter::get_all_elements_joined(const std::string& name,
@@ -255,14 +261,10 @@ void interpreter::define_function_arguments(scope& current_stack,
{
std::map<unsigned, std::string> positional_args;
- for(auto i = 0u; i != arguments.size(); ++i)
- {
- const std::string& name = boost::lexical_cast<std::string>(i + 1);
- current_stack[name].reset(new variable(name, arguments[i]));
- positional_args[i] = arguments[i];
- }
+ for(auto i = 1u; i <= arguments.size(); ++i)
+ positional_args[i] = arguments[i - 1];
- define("*", positional_args);
+ current_stack["*"].reset(new variable("*", positional_args, true));
}
void interpreter::call(const std::string& name,
diff --git a/src/core/interpreter.h b/src/core/interpreter.h
index 937edd8..460a2e3 100644
--- a/src/core/interpreter.h
+++ b/src/core/interpreter.h
@@ -93,6 +93,10 @@ class interpreter: public boost::noncopyable
long long offset,
unsigned length,
const unsigned index) const;
+
+ std::string get_subarray(const std::string& name,
+ long long offset,
+ unsigned length) const;
public:
typedef std::map<std::string, bool>::const_iterator option_iterator;
@@ -194,11 +198,11 @@ public:
template <typename T>
bool resolve_array(const std::string& name, std::vector<T>& values) const
{
- auto i = members.find(name);
- if(i == members.end())
+ auto var = resolve_variable(name);
+ if(!var)
return false;
- i->second->get_all_values(values);
+ var->get_all_values(values);
return true;
}
@@ -398,6 +402,20 @@ public:
int length,
const unsigned index) const;
+ /// \brief perform subarray expansion
+ /// \param the offset of the subarray
+ /// \return the expansion result
+ const std::string do_subarray_expansion(const std::string& name,
+ long long offset) const;
+
+ /// \brief perform subarray expansion
+ /// \param the offset of the subarray
+ /// \param the length of the subarray
+ /// \return the expansion result
+ const std::string do_subarray_expansion(const std::string& name,
+ long long offset,
+ int length) const;
+
/// \brief perform replacement expansion
/// \param the name of the varaible that needs to be expanded
/// \param the function object used to perform expansion
diff --git a/test/script_compiler.sh b/test/script_compiler.sh
index 021855e..6ff5672 100755
--- a/test/script_compiler.sh
+++ b/test/script_compiler.sh
@@ -1,11 +1,14 @@
#!/bin/sh
declare -i error=0
+result=$(mktemp)
for script in $@
do
- ./variable_printer $script 2>&1 | diff -u $script.result -
+ sed "s/@srcdir@/$srcdir/" $script.result > $result
+ ./variable_printer $script 2>&1 | diff -u $result -
error+=$?
done
+rm -rf $result
exit $error