diff options
author | Mu Qiao <qiaomuf@gentoo.org> | 2011-07-27 19:06:53 +0800 |
---|---|---|
committer | Mu Qiao <qiaomuf@gentoo.org> | 2011-08-02 15:52:18 +0800 |
commit | 2afd34d4fef6dbdfb9afa2a371303dcf2fa4ab6e (patch) | |
tree | 0d80d50146d1eaca9d56b229e584703026f8798d | |
parent | Parser: support empty command with redirection (diff) | |
download | libbash-2afd34d4fef6dbdfb9afa2a371303dcf2fa4ab6e.tar.gz libbash-2afd34d4fef6dbdfb9afa2a371303dcf2fa4ab6e.tar.bz2 libbash-2afd34d4fef6dbdfb9afa2a371303dcf2fa4ab6e.zip |
Builtin: reimplement the local built-in
Now the local built-in is not handled only in parser grammar so that
expansions can happen for the arguments. '=' is not checked in the local
and export built-in anymore because we do not generate empty AST for
"export foo".
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | bashast/bashast.g | 39 | ||||
-rw-r--r-- | bashast/gunit/array.gunit | 2 | ||||
-rw-r--r-- | bashast/gunit/simp_command.gunit | 2 | ||||
-rw-r--r-- | bashast/libbashWalker.g | 6 | ||||
-rw-r--r-- | scripts/command_execution.bash | 2 | ||||
-rw-r--r-- | scripts/function_def.bash | 3 | ||||
-rw-r--r-- | src/builtins/export_builtin.cpp | 11 | ||||
-rw-r--r-- | src/builtins/local_builtin.cpp | 43 | ||||
-rw-r--r-- | src/builtins/local_builtin.h | 46 | ||||
-rw-r--r-- | src/core/bash_ast.cpp | 6 | ||||
-rw-r--r-- | src/core/bash_ast.h | 2 | ||||
-rw-r--r-- | src/cppbash_builtin.cpp | 2 | ||||
-rwxr-xr-x | test/verify_error_output_test.sh | 2 |
14 files changed, 133 insertions, 35 deletions
diff --git a/Makefile.am b/Makefile.am index ba77230..1aa58d9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -203,6 +203,8 @@ libcppbash_la_SOURCES = src/common.h \ src/builtins/eval_builtin.h \ src/builtins/export_builtin.cpp \ src/builtins/export_builtin.h \ + src/builtins/local_builtin.cpp \ + src/builtins/local_builtin.h \ src/builtins/declare_builtin.cpp \ src/builtins/declare_builtin.h \ src/builtins/boolean_builtins.h \ diff --git a/bashast/bashast.g b/bashast/bashast.g index dc0ff1b..cb0a63d 100644 --- a/bashast/bashast.g +++ b/bashast/bashast.g @@ -373,12 +373,15 @@ command_atom : (FOR|SELECT|IF|WHILE|UNTIL|CASE|LPAREN|LBRACE|LLPAREN|LSQUARE|TEST_EXPR) => compound_command | FUNCTION BLANK string_expr_no_reserved_word ((BLANK? parens wspace?)|wspace) compound_command -> ^(FUNCTION string_expr_no_reserved_word compound_command) - | (name (LSQUARE|EQUALS|PLUS EQUALS)|LOCAL) => variable_definitions + | (name (LSQUARE|EQUALS|PLUS EQUALS)) => variable_definitions ( (BLANK bash_command) => BLANK bash_command -> bash_command variable_definitions | -> ^(VARIABLE_DEFINITIONS variable_definitions) ) - | (EXPORT) => EXPORT BLANK export_item -> ^(STRING EXPORT) ^(STRING ^(DOUBLE_QUOTED_STRING export_item)) + | (EXPORT) => EXPORT BLANK builtin_variable_definition_item + -> ^(STRING EXPORT) ^(STRING ^(DOUBLE_QUOTED_STRING builtin_variable_definition_item)) + | (LOCAL) => LOCAL BLANK builtin_variable_definition_item + -> ^(STRING LOCAL) ^(STRING ^(DOUBLE_QUOTED_STRING builtin_variable_definition_item)) | command_name ( (BLANK? parens) => BLANK? parens wspace? compound_command @@ -403,10 +406,7 @@ command_name | {LA(1) == GREATER_THAN}? => redirection_atom -> ^(STRING NAME) redirection_atom; variable_definitions - : ( - variable_definition_atom ((BLANK name (LSQUARE|EQUALS|PLUS EQUALS)) => BLANK! variable_definition_atom)* - | (LOCAL) => LOCAL BLANK! local_item ((BLANK name) => BLANK! local_item)* - ); + : variable_definition_atom ((BLANK name (LSQUARE|EQUALS|PLUS EQUALS)) => BLANK! variable_definition_atom)* ; variable_definition_atom : name LSQUARE BLANK? explicit_arithmetic BLANK? RSQUARE EQUALS string_expr? @@ -445,26 +445,26 @@ array_atom | ); -local_item - : variable_definition_atom - | name -> ^(EQUALS name) - | MINUS op=LETTER { -#ifdef OUTPUT_C - std::string option = get_string(op); - if(option != "i" && option != "a") - throw libbash::unsupported_exception("We do not support -" + option + " for local"); -#endif - } ->; -export_item +builtin_variable_definition_item : ((~EOL) => expansion_base)+; +#ifdef OUTPUT_C +builtin_variable_definitions[bool local] + : {$local}? => (builtin_variable_definition_atom) (BLANK builtin_variable_definition_atom)* + -> ^(LIST ^(COMMAND ^(VARIABLE_DEFINITIONS LOCAL builtin_variable_definition_atom+))) + | {!$local}? => (builtin_variable_definition_atom) (BLANK builtin_variable_definition_atom)* + -> ^(LIST ^(COMMAND ^(VARIABLE_DEFINITIONS builtin_variable_definition_atom+))); +#else builtin_variable_definitions : (builtin_variable_definition_atom) (BLANK builtin_variable_definition_atom)* - -> ^(LIST ^(COMMAND ^(VARIABLE_DEFINITIONS builtin_variable_definition_atom +))); + -> ^(LIST ^(COMMAND ^(VARIABLE_DEFINITIONS builtin_variable_definition_atom+))); +#endif builtin_variable_definition_atom : variable_definition_atom - | name ->; + // We completely ignore the options for export, local and readonly for now + | (MINUS LETTER BLANK) => MINUS LETTER -> + | name -> ^(EQUALS name ^(STRING ^(VAR_REF name))); bash_command : string_expr_no_reserved_word ((BLANK bash_command_arguments) => BLANK! bash_command_arguments)*; @@ -702,6 +702,7 @@ expansion_base | (ESC DQUOTE) => ESC DQUOTE -> DQUOTE | (ESC TICK) => ESC TICK -> TICK | (ESC DOLLAR) => ESC DOLLAR -> DOLLAR + | (brace_expansion) => brace_expansion | .; all_expansions diff --git a/bashast/gunit/array.gunit b/bashast/gunit/array.gunit index fe49511..514eb53 100644 --- a/bashast/gunit/array.gunit +++ b/bashast/gunit/array.gunit @@ -32,7 +32,7 @@ variable_definition_atom: "asdf+=(a)" -> (PLUS_ASSIGN asdf (ARRAY (STRING a))) builtin_variable_definitions: -"asdf=(a b c d) ade acd=bde" -> (LIST (COMMAND (VARIABLE_DEFINITIONS (= asdf (ARRAY (STRING a) (STRING b) (STRING c) (STRING d))) (= acd (STRING bde))))) +"asdf=(a b c d) ade acd=bde" -> (LIST (COMMAND (VARIABLE_DEFINITIONS (= asdf (ARRAY (STRING a) (STRING b) (STRING c) (STRING d))) (EQUALS ade (STRING (VAR_REF ade))) (= acd (STRING bde))))) variable_reference: "$asdf" -> (VAR_REF asdf) diff --git a/bashast/gunit/simp_command.gunit b/bashast/gunit/simp_command.gunit index fdd4346..2d98061 100644 --- a/bashast/gunit/simp_command.gunit +++ b/bashast/gunit/simp_command.gunit @@ -27,7 +27,7 @@ command_atom: "./foobär" -> (STRING . / foob ä r) "cat ~/Documents/todo.txt" -> (STRING cat) (STRING ~ / Documents / todo . txt) "dodir ${foo}/${bar}" -> (STRING dodir) (STRING (VAR_REF foo) / (VAR_REF bar)) -"local a=123 b=(1 2 3) c" -> (VARIABLE_DEFINITIONS local (= a (STRING 123)) (= b (ARRAY (STRING 1) (STRING 2) (STRING 3))) (EQUALS c)) +"local a=123 b=(1 2 3) c" -> (STRING local) (STRING (DOUBLE_QUOTED_STRING a = 123 b = ( 1 2 3 ) c)) "echo {}{}}{{{}}{{}" -> (STRING echo) (STRING { } { } } { { { } } { { }) "echo \"ab#af ###\" #abc" -> (STRING echo) (STRING (DOUBLE_QUOTED_STRING ab # af # # #)) diff --git a/bashast/libbashWalker.g b/bashast/libbashWalker.g index 7deb461..65fade5 100644 --- a/bashast/libbashWalker.g +++ b/bashast/libbashWalker.g @@ -581,12 +581,16 @@ simple_command execute_command[std::string& name, std::vector<std::string>& libbash_args] @declarations { - interpreter::local_scope current_scope(*walker); + std::unique_ptr<interpreter::local_scope> current_scope; std::unique_ptr<std::ostream> out; std::unique_ptr<std::ostream> err; std::unique_ptr<std::istream> in; bool redirection = false; } +@init { + if(name != "local") + current_scope.reset(new interpreter::local_scope(*walker)); +} :var_def[true]* (redirect[out, err, in]{ redirection = true; })* { // Empty command, still need to run bash redirection if(name.empty()) diff --git a/scripts/command_execution.bash b/scripts/command_execution.bash index 132d72d..1721fa9 100644 --- a/scripts/command_execution.bash +++ b/scripts/command_execution.bash @@ -34,9 +34,11 @@ function unset_inner() function unset_outer() { local FOO006=1 FOO007=2 + local gjl_${FOO006}="${FOO007}" unset_inner echo "FOO006=$FOO006 in unset_outer" echo "FOO007=$FOO007 in unset_outer" + echo "gjl_1=${gjl_1}" unset FOO006 echo "FOO006=$FOO006 in unset_outer" } diff --git a/scripts/function_def.bash b/scripts/function_def.bash index 609d8b4..6f463a4 100644 --- a/scripts/function_def.bash +++ b/scripts/function_def.bash @@ -47,8 +47,7 @@ func_nested1() { echo $foo_nested ${bar_nested[0]} $localbar } func_nested2() { - local foo_nested=hi bar_nested=(1 2 - 3) localbar + local foo_nested=hi bar_nested=(1 2 3) localbar localbar=1 echo ${bar_nested[@]} echo ${not_exist[@]} diff --git a/src/builtins/export_builtin.cpp b/src/builtins/export_builtin.cpp index 2086cea..e08bfb5 100644 --- a/src/builtins/export_builtin.cpp +++ b/src/builtins/export_builtin.cpp @@ -23,6 +23,8 @@ #include "builtins/export_builtin.h" +#include <functional> + #include <sstream> #include "core/bash_ast.h" @@ -34,13 +36,8 @@ int export_builtin::exec(const std::vector<std::string>& bash_args) for(auto iter = bash_args.begin(); iter != bash_args.end(); ++iter) script << *iter; - // Check if there is variable definition. If there isn't, parser_builtin_variable_definitions - // will generate empty AST, which is not what we want. - if(script.str().find("=") != std::string::npos) - { - bash_ast ast(script, &bash_ast::parser_builtin_variable_definitions); - ast.interpret_with(_walker); - } + bash_ast ast(script, std::bind(&bash_ast::parser_builtin_variable_definitions, std::placeholders::_1, false)); + ast.interpret_with(_walker); return 0; } diff --git a/src/builtins/local_builtin.cpp b/src/builtins/local_builtin.cpp new file mode 100644 index 0000000..e28741f --- /dev/null +++ b/src/builtins/local_builtin.cpp @@ -0,0 +1,43 @@ +/* + Please use git log for copyright holder and year information + + This file is part of libbash. + + libbash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + libbash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libbash. If not, see <http://www.gnu.org/licenses/>. +*/ +/// +/// \file local_builtin.h +/// \brief class that implements the local builtin +/// + +#include "builtins/local_builtin.h" + +#include <functional> + +#include <sstream> + +#include "core/bash_ast.h" +#include "core/interpreter.h" + +int local_builtin::exec(const std::vector<std::string>& bash_args) +{ + std::stringstream script; + for(auto iter = bash_args.begin(); iter != bash_args.end(); ++iter) + script << *iter; + + bash_ast ast(script, std::bind(&bash_ast::parser_builtin_variable_definitions, std::placeholders::_1, true)); + ast.interpret_with(_walker); + + return 0; +} diff --git a/src/builtins/local_builtin.h b/src/builtins/local_builtin.h new file mode 100644 index 0000000..e475ac4 --- /dev/null +++ b/src/builtins/local_builtin.h @@ -0,0 +1,46 @@ +/* + Please use git log for copyright holder and year information + + This file is part of libbash. + + libbash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + libbash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with libbash. If not, see <http://www.gnu.org/licenses/>. +*/ +/// +/// \file local_builtin.h +/// \brief class that implements the local builtin +/// + +#ifndef LIBBASH_BUILTINS_local_BUILTIN_H_ +#define LIBBASH_BUILTINS_local_BUILTIN_H_ + +#include "cppbash_builtin.h" + +/// +/// \class local_builtin +/// \brief the local builtin for bash +/// +class local_builtin: public virtual cppbash_builtin +{ + public: + BUILTIN_CONSTRUCTOR(local) + + /// + /// \brief runs the local builtin on the supplied arguments + /// \param bash_args the arguments to the local builtin + /// \return exit status of local + /// + virtual int exec(const std::vector<std::string>& bash_args); +}; + +#endif diff --git a/src/core/bash_ast.cpp b/src/core/bash_ast.cpp index 5b2ac68..ee4fe13 100644 --- a/src/core/bash_ast.cpp +++ b/src/core/bash_ast.cpp @@ -27,6 +27,7 @@ #include <thread> #include <boost/algorithm/string/erase.hpp> +#include <boost/algorithm/string/trim.hpp> #include <boost/numeric/conversion/cast.hpp> #include "core/exceptions.h" @@ -41,6 +42,7 @@ void bash_ast::read_script(const std::istream& source) stream << source.rdbuf(); script = stream.str(); boost::algorithm::erase_all(script, "\\\n"); + boost::trim_if(script, boost::is_any_of(" \t\n")); } bash_ast::bash_ast(const std::istream& source, @@ -237,9 +239,9 @@ pANTLR3_BASE_TREE bash_ast::parser_all_expansions(libbashParser_Ctx_struct* pars return parser->all_expansions(parser).tree; } -pANTLR3_BASE_TREE bash_ast::parser_builtin_variable_definitions(libbashParser_Ctx_struct* parser) +pANTLR3_BASE_TREE bash_ast::parser_builtin_variable_definitions(libbashParser_Ctx_struct* parser, bool local) { - return parser->builtin_variable_definitions(parser).tree; + return parser->builtin_variable_definitions(parser, local).tree; } void bash_ast::call_function(plibbashWalker ctx, diff --git a/src/core/bash_ast.h b/src/core/bash_ast.h index bd7ecf8..2e0c30e 100644 --- a/src/core/bash_ast.h +++ b/src/core/bash_ast.h @@ -117,7 +117,7 @@ public: /// \brief the functor for parser builtin_variable_definitions rule /// \param parser the pointer to the parser - static pANTLR3_BASE_TREE parser_builtin_variable_definitions(libbashParser_Ctx_struct* parser); + static pANTLR3_BASE_TREE parser_builtin_variable_definitions(libbashParser_Ctx_struct* parser, bool local); /// /// \brief interpret the script with a given interpreter diff --git a/src/cppbash_builtin.cpp b/src/cppbash_builtin.cpp index 03620d1..452fe64 100644 --- a/src/cppbash_builtin.cpp +++ b/src/cppbash_builtin.cpp @@ -35,6 +35,7 @@ #include "builtins/echo_builtin.h" #include "builtins/eval_builtin.h" #include "builtins/export_builtin.h" +#include "builtins/local_builtin.h" #include "builtins/inherit_builtin.h" #include "builtins/let_builtin.h" #include "builtins/return_builtin.h" @@ -59,6 +60,7 @@ cppbash_builtin::builtins_type& cppbash_builtin::builtins() { {"echo", boost::factory<echo_builtin*>()}, {"eval", boost::factory<eval_builtin*>()}, {"export", boost::factory<export_builtin*>()}, + {"local", boost::factory<local_builtin*>()}, {"declare", boost::factory<declare_builtin*>()}, {"source", boost::factory<source_builtin*>()}, {"shift", boost::factory<shift_builtin*>()}, diff --git a/test/verify_error_output_test.sh b/test/verify_error_output_test.sh index cbf6cf2..f6d96ed 100755 --- a/test/verify_error_output_test.sh +++ b/test/verify_error_output_test.sh @@ -2,4 +2,4 @@ illegal="${srcdir}/scripts/illegal_script.sh" output=$(./variable_printer "$illegal" 2>&1) -[[ $output == "${illegal}(1) : error 1 : Unexpected token, at offset 3"* ]] +[[ $output == *"(1) : error 1 : Unexpected token, at offset"* ]] |