diff options
author | Petteri Räty <petsku@petteriraty.eu> | 2011-05-11 10:15:58 +0300 |
---|---|---|
committer | Petteri Räty <petsku@petteriraty.eu> | 2011-05-11 10:15:58 +0300 |
commit | af3866332694ab98054f042c95080d130e310a06 (patch) | |
tree | 93c054fa7d477b3a655bd4b6f0dfc5b61ba766fc | |
parent | Parser: rename MATCH_PATTERN to MATCH_ANY (diff) | |
parent | Walker: support patterns in variable expansion (diff) | |
download | libbash-af3866332694ab98054f042c95080d130e310a06.tar.gz libbash-af3866332694ab98054f042c95080d130e310a06.tar.bz2 libbash-af3866332694ab98054f042c95080d130e310a06.zip |
Merge remote-tracking branch 'mu/pattern_matching'
Conflicts:
bashast/bashast.g
-rw-r--r-- | bashast/bashast.g | 10 | ||||
-rw-r--r-- | bashast/features_script/features.sh.ast | 2 | ||||
-rw-r--r-- | bashast/gunit/compound.gunit | 10 | ||||
-rw-r--r-- | bashast/gunit/param_main.gunit | 2 | ||||
-rw-r--r-- | bashast/libbashWalker.g | 194 | ||||
-rw-r--r-- | scripts/compound_command.bash | 57 | ||||
-rw-r--r-- | scripts/compound_command.bash.result | 8 | ||||
-rw-r--r-- | scripts/var_expansion.bash | 10 | ||||
-rw-r--r-- | scripts/var_expansion.bash.result | 10 | ||||
-rw-r--r-- | src/core/interpreter.cpp | 49 | ||||
-rw-r--r-- | src/core/interpreter.h | 33 |
11 files changed, 268 insertions, 117 deletions
diff --git a/bashast/bashast.g b/bashast/bashast.g index 220d01f..c4156ec 100644 --- a/bashast/bashast.g +++ b/bashast/bashast.g @@ -70,6 +70,8 @@ tokens{ EXTENDED_MATCH_AT_LEAST_ONE; MATCH_ANY; MATCH_ANY_EXCEPT; + MATCH_ALL; + MATCH_ONE; CHARACTER_CLASS; EQUIVALENCE_CLASS; COLLATING_SYMBOL; @@ -416,8 +418,8 @@ fname_part | res_word_str; //non-quoted string part rule, allows expansions nqstr_part - : bracket_pattern_match - | extended_pattern_match + : extended_pattern_match + | bracket_pattern_match | var_ref | command_sub | arithmetic_expansion @@ -452,7 +454,9 @@ bracket_pattern_match : LSQUARE RSQUARE (BANG|CARET) pattern_match* RSQUARE -> ^(MATCH_ANY_EXCEPT RSQUARE pattern_match*) | LSQUARE RSQUARE pattern_match* RSQUARE -> ^(MATCH_ANY RSQUARE pattern_match*) | LSQUARE (BANG|CARET) pattern_match+ RSQUARE -> ^(MATCH_ANY_EXCEPT pattern_match+) - | LSQUARE pattern_match+ RSQUARE -> ^(MATCH_ANY pattern_match+); + | LSQUARE pattern_match+ RSQUARE -> ^(MATCH_ANY pattern_match+) + | TIMES -> MATCH_ALL + | QMARK -> MATCH_ONE; //allowable patterns with bracket pattern matching pattern_match : pattern_class_match diff --git a/bashast/features_script/features.sh.ast b/bashast/features_script/features.sh.ast index b9b6c62..920a23a 100644 --- a/bashast/features_script/features.sh.ast +++ b/bashast/features_script/features.sh.ast @@ -1 +1 @@ -(LIST (function (STRING lots_o_echo) (CURRENT_SHELL (LIST (COMMAND (STRING echo) (STRING (DOUBLE_QUOTED_STRING The number of tests that have failed : (VAR_REF failedtests)))) (COMMAND (STRING echo) (STRING (SINGLE_QUOTED_STRING $ failedtests))) (COMMAND (STRING echo) (STRING (VAR_REF failedtests)))))) (function (STRING do_some_arith) (CURRENT_SHELL (LIST (COMPOUND_ARITH (* 5 4)) (COMPOUND_ARITH (** 5 4)) (COMPOUND_ARITH (+ (VAR_REF (VAR_REF failedtests)) (/ 5 4))) (COMPOUND_ARITH (+ (VAR_REF (VAR_REF z)) (MINUS_SIGN 3)))))) (function (STRING arrays) (SUBSHELL (LIST (VARIABLE_DEFINITIONS (= asdf (ARRAY (STRING a) (STRING b) (STRING c) (STRING d)))) (COMMAND (STRING echo) (STRING (VAR_REF (asdf 3)))) (VARIABLE_DEFINITIONS (= foo (ARRAY (STRING (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING 6))))) (STRING b) (STRING c) (STRING d)))) (VARIABLE_DEFINITIONS (= (arr (VAR_REF foo)) (STRING 3))) (VARIABLE_DEFINITIONS (= bar (ARRAY (STRING a) (STRING b) (= 5 (STRING c)))))))) (COMMAND (STRING echo) (STRING (BRACE_EXP (STRING a) (STRING b)))) (COMMAND (STRING echo) (STRING (BRACE_EXP (.. a d)))) (COMMAND (STRING echo) (STRING (BRACE_EXP (BRACE_EXP (STRING a) (STRING b)) (STRING c) (STRING d)))) (COMMAND (STRING echo) (STRING a (BRACE_EXP (STRING b) (STRING c)))) (COMMAND (STRING (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING foobar)))))) (| (COMMAND (STRING ls)) (COMMAND (STRING grep) (STRING gunit) (REDIR >> (STRING filelist)))) (case (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING asdf)))) (CASE_PATTERN (STRING gz) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING yay)))) (CASE_PATTERN (STRING bzip) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING three)))) (CASE_PATTERN (STRING *) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING woo))))) (for each (STRING (COMMAND_SUB (LIST (| (COMMAND (STRING ls)) (COMMAND (STRING grep) (STRING log)))))) (LIST (COMMAND (STRING echo) (STRING (VAR_REF each))) (COMMAND (STRING cat) (STRING each)))) (CFOR (FOR_INIT (+ 5 3)) (FOR_COND (+ 6 2)) (LIST (COMMAND (STRING echo) (STRING yay))) (FOR_MOD (+ 3 1))) (select each (COMMAND_SUB (LIST (| (COMMAND (STRING ls)) (COMMAND (STRING grep) (STRING output))))) (LIST (COMMAND (STRING echo) (STRING asdf) (STRING 2) (REDIR > (STRING / dev / null))))) (IF_STATEMENT (if (LIST (COMMAND (STRING echo) (STRING yay2))) (LIST (COMMAND (STRING echo) (STRING yay))))) (until (LIST (COMPOUND_COND (KEYWORD_TEST (a (STRING this / is . afile))))) (LIST (COMMAND (STRING touch) (STRING this / is . afile)))) (while (LIST (COMPOUND_COND (BUILTIN_TEST (n (STRING foobar))))) (LIST (COMMAND (STRING echo) (STRING (DOUBLE_QUOTED_STRING file found))))) (IF_STATEMENT (if (LIST (COMPOUND_COND (BUILTIN_TEST (eq (STRING 5) (STRING 6))))) (LIST (COMMAND (STRING echo) (STRING (DOUBLE_QUOTED_STRING something ' s wrong)))))) (COMMAND (STRING echo) (STRING this) (STRING command) (STRING has) (STRING multiple) (STRING arguments)) (COMMAND (STRING wc) (PROCESS_SUBSTITUTION < (LIST (COMMAND (STRING cat) (STRING / usr / share / dict / linux . words))))) (|| (&& (&& (&& (COMMAND (STRING cd) (STRING build)) (COMMAND (STRING . / configure))) (COMMAND (STRING make))) (COMMAND (STRING make_install))) (COMMAND (STRING echo) (STRING fail))) (COMMAND (STRING cd) (STRING / usr / bin)) (| (COMMAND (STRING ls) (STRING - al)) (COMMAND (STRING grep) (STRING more))) (VARIABLE_DEFINITIONS (= asdf (STRING parameters))) (COMMAND (STRING (VAR_REF (USE_DEFAULT_WHEN_UNSET_OR_NULL asdf (STRING foo))))) (COMMAND (STRING (VAR_REF (OFFSET asdf 8)))) (COMMAND (STRING (VAR_REF (! asdf *)))) (COMMAND (STRING (VAR_REF (! asdf @)))) (COMMAND (STRING (VAR_REF (# foo)))) (COMMAND (STRING (VAR_REF (REPLACE_FIRST replaice (STRING with) (STRING pattern))))) (COMMAND (STRING (VAR_REF (LAZY_REMOVE_AT_START asdf (STRING bar))))) (COMMAND (STRING (VAR_REF (REPLACE_AT_START asdf (STRING bar))))) (COMMAND (STRING (VAR_REF (LAZY_REMOVE_AT_END asdf (STRING bar))))) (COMMAND (STRING (VAR_REF (LAZY_REMOVE_AT_END asdf (STRING bar))))) (COMMAND (STRING (VAR_REF 1)) (STRING (VAR_REF @)) (STRING (VAR_REF *))) (COMMAND (STRING (VAR_REF ?))) (COMMAND (STRING (VAR_REF (REPLACE_ALL PV (STRING .) (STRING _))))) (COMMAND (STRING (VAR_REF (REPLACE_AT_START PV (STRING foo) (STRING bar))))) (COMMAND (STRING (VAR_REF (REPLACE_AT_END PV (STRING foo) (STRING bar))))) (VARIABLE_DEFINITIONS (= MY_PN (STRING (VAR_REF (REPLACE_FIRST PN (STRING asterisk -)))))) (| (COMMAND (STRING cat) (STRING asdf)) (COMMAND (STRING grep) (STRING three) (STRING 2) (REDIR >& 1) (REDIR > (STRING / dev / null)))) (COMMAND (STRING echo) (STRING asdf) (REDIR >> (STRING APPEND))) (COMMAND (STRING echo) (STRING cat) (<<< (STRING word)))) +(LIST (function (STRING lots_o_echo) (CURRENT_SHELL (LIST (COMMAND (STRING echo) (STRING (DOUBLE_QUOTED_STRING The number of tests that have failed : (VAR_REF failedtests)))) (COMMAND (STRING echo) (STRING (SINGLE_QUOTED_STRING $ failedtests))) (COMMAND (STRING echo) (STRING (VAR_REF failedtests)))))) (function (STRING do_some_arith) (CURRENT_SHELL (LIST (COMPOUND_ARITH (* 5 4)) (COMPOUND_ARITH (** 5 4)) (COMPOUND_ARITH (+ (VAR_REF (VAR_REF failedtests)) (/ 5 4))) (COMPOUND_ARITH (+ (VAR_REF (VAR_REF z)) (MINUS_SIGN 3)))))) (function (STRING arrays) (SUBSHELL (LIST (VARIABLE_DEFINITIONS (= asdf (ARRAY (STRING a) (STRING b) (STRING c) (STRING d)))) (COMMAND (STRING echo) (STRING (VAR_REF (asdf 3)))) (VARIABLE_DEFINITIONS (= foo (ARRAY (STRING (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING 6))))) (STRING b) (STRING c) (STRING d)))) (VARIABLE_DEFINITIONS (= (arr (VAR_REF foo)) (STRING 3))) (VARIABLE_DEFINITIONS (= bar (ARRAY (STRING a) (STRING b) (= 5 (STRING c)))))))) (COMMAND (STRING echo) (STRING (BRACE_EXP (STRING a) (STRING b)))) (COMMAND (STRING echo) (STRING (BRACE_EXP (.. a d)))) (COMMAND (STRING echo) (STRING (BRACE_EXP (BRACE_EXP (STRING a) (STRING b)) (STRING c) (STRING d)))) (COMMAND (STRING echo) (STRING a (BRACE_EXP (STRING b) (STRING c)))) (COMMAND (STRING (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING foobar)))))) (| (COMMAND (STRING ls)) (COMMAND (STRING grep) (STRING gunit) (REDIR >> (STRING filelist)))) (case (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING asdf)))) (CASE_PATTERN (STRING gz) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING yay)))) (CASE_PATTERN (STRING bzip) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING three)))) (CASE_PATTERN (STRING MATCH_ALL) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING woo))))) (for each (STRING (COMMAND_SUB (LIST (| (COMMAND (STRING ls)) (COMMAND (STRING grep) (STRING log)))))) (LIST (COMMAND (STRING echo) (STRING (VAR_REF each))) (COMMAND (STRING cat) (STRING each)))) (CFOR (FOR_INIT (+ 5 3)) (FOR_COND (+ 6 2)) (LIST (COMMAND (STRING echo) (STRING yay))) (FOR_MOD (+ 3 1))) (select each (COMMAND_SUB (LIST (| (COMMAND (STRING ls)) (COMMAND (STRING grep) (STRING output))))) (LIST (COMMAND (STRING echo) (STRING asdf) (STRING 2) (REDIR > (STRING / dev / null))))) (IF_STATEMENT (if (LIST (COMMAND (STRING echo) (STRING yay2))) (LIST (COMMAND (STRING echo) (STRING yay))))) (until (LIST (COMPOUND_COND (KEYWORD_TEST (a (STRING this / is . afile))))) (LIST (COMMAND (STRING touch) (STRING this / is . afile)))) (while (LIST (COMPOUND_COND (BUILTIN_TEST (n (STRING foobar))))) (LIST (COMMAND (STRING echo) (STRING (DOUBLE_QUOTED_STRING file found))))) (IF_STATEMENT (if (LIST (COMPOUND_COND (BUILTIN_TEST (eq (STRING 5) (STRING 6))))) (LIST (COMMAND (STRING echo) (STRING (DOUBLE_QUOTED_STRING something ' s wrong)))))) (COMMAND (STRING echo) (STRING this) (STRING command) (STRING has) (STRING multiple) (STRING arguments)) (COMMAND (STRING wc) (PROCESS_SUBSTITUTION < (LIST (COMMAND (STRING cat) (STRING / usr / share / dict / linux . words))))) (|| (&& (&& (&& (COMMAND (STRING cd) (STRING build)) (COMMAND (STRING . / configure))) (COMMAND (STRING make))) (COMMAND (STRING make_install))) (COMMAND (STRING echo) (STRING fail))) (COMMAND (STRING cd) (STRING / usr / bin)) (| (COMMAND (STRING ls) (STRING - al)) (COMMAND (STRING grep) (STRING more))) (VARIABLE_DEFINITIONS (= asdf (STRING parameters))) (COMMAND (STRING (VAR_REF (USE_DEFAULT_WHEN_UNSET_OR_NULL asdf (STRING foo))))) (COMMAND (STRING (VAR_REF (OFFSET asdf 8)))) (COMMAND (STRING (VAR_REF (! asdf *)))) (COMMAND (STRING (VAR_REF (! asdf @)))) (COMMAND (STRING (VAR_REF (# foo)))) (COMMAND (STRING (VAR_REF (REPLACE_FIRST replaice (STRING with) (STRING pattern))))) (COMMAND (STRING (VAR_REF (LAZY_REMOVE_AT_START asdf (STRING bar))))) (COMMAND (STRING (VAR_REF (REPLACE_AT_START asdf (STRING bar))))) (COMMAND (STRING (VAR_REF (LAZY_REMOVE_AT_END asdf (STRING bar))))) (COMMAND (STRING (VAR_REF (LAZY_REMOVE_AT_END asdf (STRING bar))))) (COMMAND (STRING (VAR_REF 1)) (STRING (VAR_REF @)) (STRING (VAR_REF *))) (COMMAND (STRING (VAR_REF ?))) (COMMAND (STRING (VAR_REF (REPLACE_ALL PV (STRING .) (STRING _))))) (COMMAND (STRING (VAR_REF (REPLACE_AT_START PV (STRING foo) (STRING bar))))) (COMMAND (STRING (VAR_REF (REPLACE_AT_END PV (STRING foo) (STRING bar))))) (VARIABLE_DEFINITIONS (= MY_PN (STRING (VAR_REF (REPLACE_FIRST PN (STRING asterisk -)))))) (| (COMMAND (STRING cat) (STRING asdf)) (COMMAND (STRING grep) (STRING three) (STRING 2) (REDIR >& 1) (REDIR > (STRING / dev / null)))) (COMMAND (STRING echo) (STRING asdf) (REDIR >> (STRING APPEND))) (COMMAND (STRING echo) (STRING cat) (<<< (STRING word)))) diff --git a/bashast/gunit/compound.gunit b/bashast/gunit/compound.gunit index 398bb3d..a7ce999 100644 --- a/bashast/gunit/compound.gunit +++ b/bashast/gunit/compound.gunit @@ -65,7 +65,7 @@ echo three ;; *) echo woo ;; -esac" -> (case (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING asdf)))) (CASE_PATTERN (STRING gz) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING yay)))) (CASE_PATTERN (STRING bzip) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING three)))) (CASE_PATTERN (STRING *) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING woo))))) +esac" -> (case (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING asdf)))) (CASE_PATTERN (STRING gz) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING yay)))) (CASE_PATTERN (STRING bzip) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING three)))) (CASE_PATTERN (STRING MATCH_ALL) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING woo))))) "case `echo asdf` in gz) @@ -75,10 +75,10 @@ esac" -> (case (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING asdf)))) (CASE_ echo three ;; *) echo woo -esac" -> (case (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING asdf)))) (CASE_PATTERN (STRING gz) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING yay)))) (CASE_PATTERN (STRING bzip) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING three)))) (CASE_PATTERN (STRING *) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING woo))))) +esac" -> (case (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING asdf)))) (CASE_PATTERN (STRING gz) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING yay)))) (CASE_PATTERN (STRING bzip) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING three)))) (CASE_PATTERN (STRING MATCH_ALL) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING woo))))) "case `echo asdf` in gz|asdf) echo yay ;; bzip) echo three ;; *) echo woo esac" FAIL -"case `echo asdf` in gz|asdf) echo yay ;; bzip) echo three ;; *) echo woo ;; esac" -> (case (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING asdf)))) (CASE_PATTERN (STRING gz) (STRING asdf) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING yay)))) (CASE_PATTERN (STRING bzip) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING three)))) (CASE_PATTERN (STRING *) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING woo))))) +"case `echo asdf` in gz|asdf) echo yay ;; bzip) echo three ;; *) echo woo ;; esac" -> (case (COMMAND_SUB (LIST (COMMAND (STRING echo) (STRING asdf)))) (CASE_PATTERN (STRING gz) (STRING asdf) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING yay)))) (CASE_PATTERN (STRING bzip) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING three)))) (CASE_PATTERN (STRING MATCH_ALL) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING woo))))) for_expr: "for each in `ls |grep log`; do @@ -147,7 +147,7 @@ case_expr: echo \"Usage: $0 start|stop\" >&2 exit 3 ;; -esac" -> (case (STRING (DOUBLE_QUOTED_STRING (VAR_REF 1))) (CASE_PATTERN (STRING *) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING (DOUBLE_QUOTED_STRING Usage : (VAR_REF 0) start | stop)) (REDIR >& 2)) (COMMAND (STRING exit) (STRING 3))))) +esac" -> (case (STRING (DOUBLE_QUOTED_STRING (VAR_REF 1))) (CASE_PATTERN (STRING MATCH_ALL) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING (DOUBLE_QUOTED_STRING Usage : (VAR_REF 0) start | stop)) (REDIR >& 2)) (COMMAND (STRING exit) (STRING 3))))) "case $asdf in a) @@ -168,4 +168,4 @@ stop) echo \"Usage: $0 start|stop\" >&2 exit 3 ;; -esac" -> (case (STRING (DOUBLE_QUOTED_STRING (VAR_REF 1))) (CASE_PATTERN (STRING stop)) (CASE_PATTERN (STRING *) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING (DOUBLE_QUOTED_STRING Usage : (VAR_REF 0) start | stop)) (REDIR >& 2)) (COMMAND (STRING exit) (STRING 3))))) +esac" -> (case (STRING (DOUBLE_QUOTED_STRING (VAR_REF 1))) (CASE_PATTERN (STRING stop)) (CASE_PATTERN (STRING MATCH_ALL) CASE_COMMAND (LIST (COMMAND (STRING echo) (STRING (DOUBLE_QUOTED_STRING Usage : (VAR_REF 0) start | stop)) (REDIR >& 2)) (COMMAND (STRING exit) (STRING 3))))) diff --git a/bashast/gunit/param_main.gunit b/bashast/gunit/param_main.gunit index 54a2edb..5fd818e 100644 --- a/bashast/gunit/param_main.gunit +++ b/bashast/gunit/param_main.gunit @@ -39,7 +39,7 @@ var_ref: "${foo##bar}" -> (VAR_REF (REPLACE_AT_START foo (STRING bar))) "${foo%bar}" -> (VAR_REF (LAZY_REMOVE_AT_END foo (STRING bar))) "${foo%%bar}" -> (VAR_REF (REPLACE_AT_END foo (STRING bar))) -"${foo%; *}" -> (VAR_REF (LAZY_REMOVE_AT_END foo (STRING ; *))) +"${foo%; *}" -> (VAR_REF (LAZY_REMOVE_AT_END foo (STRING ; MATCH_ALL))) "${foo%/}" -> (VAR_REF (LAZY_REMOVE_AT_END foo (STRING /))) "${this/is/pattern}"->(VAR_REF (REPLACE_FIRST this (STRING is) (STRING pattern))) //Test positional/special parameters diff --git a/bashast/libbashWalker.g b/bashast/libbashWalker.g index f39e603..307333d 100644 --- a/bashast/libbashWalker.g +++ b/bashast/libbashWalker.g @@ -27,9 +27,12 @@ options @includes{ + #include <memory> #include <string> #include <vector> + #include <boost/xpressive/xpressive.hpp> + class interpreter; void set_interpreter(interpreter* w); @@ -86,6 +89,29 @@ options SEEK(INDEX() + index - 3); CONSUME(); } + + // The method is used to append a pattern with another one. Because it's not allowed to append an empty pattern, + // we need the argument 'do_append' to indicate whether the pattern is empty. 'do_append' will be set to true after + // the first assignment. + inline void append(boost::xpressive::sregex& pattern, const boost::xpressive::sregex& new_pattern, bool& do_append) + { + using namespace boost::xpressive; + if(do_append) + { + pattern = sregex(pattern >> new_pattern); + } + else + { + pattern = new_pattern; + do_append = true; + } + } + + bool match(const std::string& target, + const boost::xpressive::sregex& pattern) + { + return boost::xpressive::regex_match(target, pattern); + } } start: list|EOF; @@ -149,15 +175,98 @@ string_expr returns[std::string libbash_value, bool quoted] $quoted = true; } :^(STRING ( + string_part { + $libbash_value += $string_part.libbash_value; + $quoted = $string_part.quoted; + } + )+); + +string_part returns[std::string libbash_value, bool quoted] +@init { + $quoted = false; +} + :(DOUBLE_QUOTED_STRING) => + ^(DOUBLE_QUOTED_STRING (libbash_string=double_quoted_string { + $libbash_value += libbash_string; + $quoted = true; + })*) + |(ARITHMETIC_EXPRESSION) => + ^(ARITHMETIC_EXPRESSION value=arithmetics { + $libbash_value = boost::lexical_cast<std::string>(value); + }) + |(var_ref[false]) => libbash_string=var_ref[false] { + $libbash_value = libbash_string; + } + |libbash_string=command_substitution { + $libbash_value = libbash_string; + } + |(libbash_string=any_string { + $libbash_value = libbash_string; + }); + +bash_pattern[boost::xpressive::sregex& pattern, bool greedy] +@declarations { + using namespace boost::xpressive; + bool do_append = false; + bool negation; + std::string pattern_str; +} + :^(STRING ( (DOUBLE_QUOTED_STRING) => - ^(DOUBLE_QUOTED_STRING (libbash_string=double_quoted_string { $libbash_value += libbash_string; })*) - |(ARITHMETIC_EXPRESSION) => - ^(ARITHMETIC_EXPRESSION value=arithmetics { - $libbash_value += boost::lexical_cast<std::string>(value); $quoted = false; - }) - |(var_ref[false]) => libbash_string=var_ref[false] { $libbash_value += libbash_string; $quoted = false; } - |libbash_string=command_substitution { $libbash_value += libbash_string; $quoted = false; } - |(libbash_string=any_string { $libbash_value += libbash_string; $quoted = false; }) + ^(DOUBLE_QUOTED_STRING (libbash_string=double_quoted_string { + append($pattern, as_xpr(libbash_string), do_append); + })*) + |(MATCH_ALL) => MATCH_ALL { + if($greedy) + append($pattern, *_, do_append); + else + append($pattern, -*_, do_append); + } + |(MATCH_ONE) => MATCH_ONE { + append($pattern, _, do_append); + } + |(MATCH_ANY_EXCEPT|MATCH_ANY) => + ^((MATCH_ANY_EXCEPT { negation = true; } | MATCH_ANY { negation = false; }) + (s=string_part { pattern_str += s.libbash_value; })+) { + if(pattern_str.empty()) + return; + + // deal with the first character specially + int index = 0; + auto char_set = set = as_xpr(pattern_str[0]); + if( index + 1 < pattern_str.size() && pattern_str[index + 1] == '-') + { + char_set = set[range(pattern_str[index], pattern_str[index + 2])]; + index += 3; + } + else + { + ++index; + } + + // handle all characters in the pattern + while(index < pattern_str.size()) + { + if( index + 1 < pattern_str.size() && pattern_str[index + 1] == '-') + { + char_set |= range(pattern_str[index], pattern_str[index + 2]); + index += 3; + } + else + { + char_set |= pattern_str[index]; + ++index; + } + } + + if(negation) + append($pattern, ~char_set, do_append); + else + append($pattern, char_set, do_append); + } + |(libbash_string=any_string { + append($pattern, as_xpr(libbash_string), do_append); + }) )+); //double quoted string rule, allows expansions @@ -186,6 +295,11 @@ var_name returns[std::string libbash_value, unsigned index] }; var_expansion returns[std::string libbash_value] +@declarations { + using namespace boost::xpressive; + sregex replace_pattern; + bool greedy; +} :^(USE_DEFAULT_WHEN_UNSET_OR_NULL var_name libbash_word=word) { libbash_value = walker->do_default_expansion($var_name.libbash_value, libbash_word, $var_name.index); } @@ -209,56 +323,54 @@ var_expansion returns[std::string libbash_value] libbash_value = boost::lexical_cast<std::string>(walker->get_array_length(libbash_name)); } )) - |^(REPLACE_ALL var_name pattern=string_expr (replacement=string_expr)?) { + |^(REPLACE_ALL var_name bash_pattern[replace_pattern, true] (replacement=string_expr)?) { libbash_value = walker->do_replace_expansion($var_name.libbash_value, std::bind(&interpreter::replace_all, std::placeholders::_1, - pattern.libbash_value, + replace_pattern, replacement.libbash_value), $var_name.index); } - |^(REPLACE_AT_END var_name pattern=string_expr (replacement=string_expr)?) { + |^(REPLACE_AT_END var_name bash_pattern[replace_pattern, true] (replacement=string_expr)?) { + replace_pattern = sregex(replace_pattern >> eos); libbash_value = walker->do_replace_expansion($var_name.libbash_value, - std::bind(&interpreter::replace_at_end, + std::bind(&interpreter::replace_all, std::placeholders::_1, - pattern.libbash_value, + replace_pattern, replacement.libbash_value), $var_name.index); } - |^(REPLACE_AT_START var_name pattern=string_expr (replacement=string_expr)?) { + |^(LAZY_REMOVE_AT_END var_name bash_pattern[replace_pattern, false] (replacement=string_expr)?) { + replace_pattern = sregex(bos >> (s1=*_) >> replace_pattern >> eos); libbash_value = walker->do_replace_expansion($var_name.libbash_value, - std::bind(&interpreter::replace_at_start, + std::bind(&interpreter::lazy_remove_at_end, std::placeholders::_1, - pattern.libbash_value, - replacement.libbash_value), + replace_pattern), $var_name.index); } - |^(REPLACE_FIRST var_name pattern=string_expr (replacement=string_expr)?) { + |^((REPLACE_AT_START { greedy = true; }|LAZY_REMOVE_AT_START { greedy = false; }) + var_name bash_pattern[replace_pattern, greedy] (replacement=string_expr)?) { + replace_pattern = sregex(bos >> replace_pattern); libbash_value = walker->do_replace_expansion($var_name.libbash_value, - std::bind(&interpreter::replace_first, + std::bind(&interpreter::replace_all, std::placeholders::_1, - pattern.libbash_value, + replace_pattern, replacement.libbash_value), $var_name.index); } - |^(LAZY_REMOVE_AT_START var_name pattern=string_expr) { + |^(REPLACE_FIRST var_name bash_pattern[replace_pattern, true] (replacement=string_expr)?) { libbash_value = walker->do_replace_expansion($var_name.libbash_value, - std::bind(&interpreter::lazy_remove_at_start, - std::placeholders::_1, - pattern.libbash_value), - $var_name.index); - } - |^(LAZY_REMOVE_AT_END var_name pattern=string_expr) { - libbash_value = walker->do_replace_expansion($var_name.libbash_value, - std::bind(&interpreter::lazy_remove_at_end, + std::bind(&interpreter::replace_first, std::placeholders::_1, - pattern.libbash_value), + replace_pattern, + replacement.libbash_value), $var_name.index); }; word returns[std::string libbash_value] :(num) => libbash_string=num { $libbash_value = libbash_string; } |string_expr { $libbash_value = $string_expr.libbash_value; } + |(VAR_REF) => libbash_string=var_ref[false] { $libbash_value = libbash_string; } |value=arithmetics { $libbash_value = boost::lexical_cast<std::string>(value); }; //variable reference @@ -524,35 +636,29 @@ case_expr case_clause[const std::string& target] returns[bool matched] @declarations { - std::vector<std::string> patterns; + std::vector<boost::xpressive::sregex> patterns; } - :^(CASE_PATTERN (libbash_string=case_pattern { patterns.push_back(libbash_string); })+ { + :^(CASE_PATTERN ( { patterns.push_back(boost::xpressive::sregex()); } bash_pattern[patterns.back(), true])+ { if(LA(1) == CASE_COMMAND) { // omit CASE_COMMAND SEEK(INDEX() + 1); - matched = false; + $matched = false; + for(auto iter = patterns.begin(); iter != patterns.end(); ++iter) { - // pattern matching should happen here in future - if(*iter == "*" || *iter == target) + if(match(target, *iter)) { command_list(ctx); $matched = true; - } - else - { - seek_to_next_tree(ctx); + break; } } + if(!$matched) + seek_to_next_tree(ctx); } }); -case_pattern returns[std::string libbash_value] - :libbash_string=command_substitution { $libbash_value = libbash_string; } - |string_expr { $libbash_value = $string_expr.libbash_value; } - |TIMES { $libbash_value = "*"; }; - command_substitution returns[std::string libbash_value] @declarations { std::stringstream out; diff --git a/scripts/compound_command.bash b/scripts/compound_command.bash index 6365fbe..a75898f 100644 --- a/scripts/compound_command.bash +++ b/scripts/compound_command.bash @@ -85,19 +85,20 @@ fi target=123 case $target in - bcd) + 1.3) echo "Shouldn't print this" ;; - abc) + \d+) echo "Shouldn't print this" ;; - 123) + 456|1?*|789) echo yep ;; 123) echo "Shouldn't print this" ;; esac +target=xyz case $target in bcd) echo "Shouldn't print this" @@ -109,4 +110,54 @@ case $target in echo "default" ;; esac +target=a +case $target in + [def]) + echo "Shouldn't print this" + ;; + [abc]) + echo yep + ;; +esac +case $target in + [def]) + echo "Shouldn't print this" + ;; + [a]) + echo yep + ;; +esac +case $target in + [!abc]) + echo "Shouldn't print this" + ;; + [!def]) + echo yep + ;; +esac +case $target in + [d-z]) + echo "Shouldn't print this" + ;; + [a-c]) + echo yep + ;; +esac +case $target in + [!a-c]) + echo "Shouldn't print this" + ;; + [!d-z]) + echo yep + ;; +esac +target=bar +case $target in + a[a-cx-z]r) + echo "Shouldn't print this" + ;; + b[!d-fx-z]r) + echo yep + ;; +esac echo "case end" diff --git a/scripts/compound_command.bash.result b/scripts/compound_command.bash.result index 69d693d..255c8eb 100644 --- a/scripts/compound_command.bash.result +++ b/scripts/compound_command.bash.result @@ -25,10 +25,16 @@ ghi 1 yep default +yep +yep +yep +yep +yep +yep case end a=1 b=2 file= foo bar foo=ghi i=4 -target=123 +target=bar diff --git a/scripts/var_expansion.bash b/scripts/var_expansion.bash index 5ebd63e..7a371b3 100644 --- a/scripts/var_expansion.bash +++ b/scripts/var_expansion.bash @@ -60,3 +60,13 @@ FOO058=${FOO039%world} FOO059=${FOO039%%world} FOO060=${FOO039%World} FOO061=${FOO039%%World} +FOO062=${FOO039#Hel*} +FOO063=${FOO039##Hel*} +FOO064=${FOO039%*rld} +FOO065=${FOO039%%*rld} +FOO066=${FOO039/l/r} +FOO067=${FOO039//l/r} +FOO068=${FOO039/#He/he} +FOO069=${FOO039/#ello/i} +FOO070=${FOO039/%ld/d} +FOO071=${FOO039/%rl/r} diff --git a/scripts/var_expansion.bash.result b/scripts/var_expansion.bash.result index 50fd5d5..a1fe905 100644 --- a/scripts/var_expansion.bash.result +++ b/scripts/var_expansion.bash.result @@ -62,3 +62,13 @@ FOO058=Hello World FOO059=Hello World FOO060=Hello FOO061=Hello +FOO062=lo World +FOO063= +FOO064=Hello Wo +FOO065= +FOO066=Herlo World +FOO067=Herro Worrd +FOO068=hello World +FOO069=Hello World +FOO070=Hello Word +FOO071=Hello World diff --git a/src/core/interpreter.cpp b/src/core/interpreter.cpp index aa6c4de..1102138 100644 --- a/src/core/interpreter.cpp +++ b/src/core/interpreter.cpp @@ -228,48 +228,33 @@ int interpreter::call(const std::string& name, } void interpreter::replace_all(std::string& value, - const std::string& pattern, + const boost::xpressive::sregex& pattern, const std::string& replacement) { - boost::replace_all(value, pattern, replacement); + value = boost::xpressive::regex_replace(value, + pattern, + replacement, + boost::xpressive::regex_constants::format_literal); } -void interpreter::replace_at_end(std::string& value, - const std::string& pattern, - const std::string& replacement) -{ - if(value.size() >= pattern.size() && - value.substr(value.size() - pattern.size()) == pattern) - value.replace(value.size() - pattern.size(), - pattern.size(), - replacement); -} - -void interpreter::replace_at_start(std::string& value, - const std::string& pattern, - const std::string& replacement) +void interpreter::lazy_remove_at_end(std::string& value, + const boost::xpressive::sregex& pattern) { - if(value.substr(0, pattern.size()) == pattern) - value.replace(0, pattern.size(), replacement); + boost::xpressive::smatch what; + if(boost::xpressive::regex_match(value, + what, + pattern)) + value = what[1]; } void interpreter::replace_first(std::string& value, - const std::string& pattern, + const boost::xpressive::sregex& pattern, const std::string& replacement) { - boost::replace_first(value, pattern, replacement); -} - -void interpreter::lazy_remove_at_start(std::string& value, - const std::string& pattern) -{ - replace_at_start(value, pattern, ""); -} - -void interpreter::lazy_remove_at_end(std::string& value, - const std::string& pattern) -{ - replace_at_end(value, pattern, ""); + value = boost::xpressive::regex_replace(value, + pattern, + replacement, + boost::xpressive::regex_constants::format_literal | boost::xpressive::regex_constants::format_first_only); } void interpreter::trim_trailing_eols(std::string& value) diff --git a/src/core/interpreter.h b/src/core/interpreter.h index 7673479..d71885a 100644 --- a/src/core/interpreter.h +++ b/src/core/interpreter.h @@ -32,6 +32,7 @@ #include <string> #include <antlr3basetree.h> +#include <boost/xpressive/xpressive.hpp> #include "core/symbols.hpp" #include "cppbash_builtin.h" @@ -637,45 +638,23 @@ public: /// \param the pattern used to match the value /// \param the replacement string static void replace_all(std::string& value, - const std::string& pattern, + const boost::xpressive::sregex& pattern, const std::string& replacement); - /// \brief perform expansion like ${var/%foo/bar} - /// \param the value to be expanded - /// \param the pattern used to match the value - /// \param the replacement string - static void replace_at_end(std::string& value, - const std::string& pattern, - const std::string& replacement); - - /// \brief perform expansion like ${var/#foo/bar} + /// \brief perform expansion like ${var%foo} /// \param the value to be expanded /// \param the pattern used to match the value - /// \param the replacement string - static void replace_at_start(std::string& value, - const std::string& pattern, - const std::string& replacement); + static void lazy_remove_at_end(std::string& value, + const boost::xpressive::sregex& pattern); /// \brief perform expansion like ${var/foo/bar} /// \param the value to be expanded /// \param the pattern used to match the value /// \param the replacement string static void replace_first(std::string& value, - const std::string& pattern, + const boost::xpressive::sregex& pattern, const std::string& replacement); - /// \brief perform expansion like ${var#foo} - /// \param the value to be expanded - /// \param the pattern used to match the value - static void lazy_remove_at_start(std::string& value, - const std::string& pattern); - - /// \brief perform expansion like ${var%foo} - /// \param the value to be expanded - /// \param the pattern used to match the value - static void lazy_remove_at_end(std::string& value, - const std::string& pattern); - /// \brief remove trailing EOLs from the value /// \param[in, out] the target static void trim_trailing_eols(std::string& value); |