1 |
commit: d0a9b80bc20ac38460c73f6b2e1c66c372a42cc1 |
2 |
Author: Mu Qiao <qiaomuf <AT> gentoo <DOT> org> |
3 |
AuthorDate: Mon Apr 18 14:44:11 2011 +0000 |
4 |
Commit: Petteri Räty <betelgeuse <AT> gentoo <DOT> org> |
5 |
CommitDate: Wed Apr 20 12:48:43 2011 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/libbash.git;a=commit;h=d0a9b80b |
7 |
|
8 |
Walker: first support for function arguments |
9 |
|
10 |
Local stack is added to support function arguments. Word splitting |
11 |
will happen for function arguments. Function execution needs proper |
12 |
preparation and cleanup so we move these logic into interpreter |
13 |
class to keep our grammar clean. |
14 |
|
15 |
--- |
16 |
bashast/libbashWalker.g | 77 ++++++++++++++++++++++-------------- |
17 |
scripts/function_def.ebuild | 18 ++++++++ |
18 |
scripts/function_def.ebuild.result | 9 ++++ |
19 |
src/core/interpreter.cpp | 52 ++++++++++++++++++++++++ |
20 |
src/core/interpreter.h | 61 ++++++++++++++++++++-------- |
21 |
5 files changed, 170 insertions(+), 47 deletions(-) |
22 |
|
23 |
diff --git a/bashast/libbashWalker.g b/bashast/libbashWalker.g |
24 |
index 76400f3..33db85c 100644 |
25 |
--- a/bashast/libbashWalker.g |
26 |
+++ b/bashast/libbashWalker.g |
27 |
@@ -29,6 +29,7 @@ options |
28 |
|
29 |
#include <memory> |
30 |
#include <string> |
31 |
+ #include <vector> |
32 |
|
33 |
class interpreter; |
34 |
void set_interpreter(std::shared_ptr<interpreter> w); |
35 |
@@ -37,9 +38,10 @@ options |
36 |
|
37 |
@postinclude{ |
38 |
|
39 |
- #include "core/interpreter.h" |
40 |
#include <boost/format.hpp> |
41 |
|
42 |
+ #include "core/interpreter.h" |
43 |
+ |
44 |
} |
45 |
|
46 |
@members{ |
47 |
@@ -114,31 +116,39 @@ var_def |
48 |
unsigned index = 0; |
49 |
bool is_null = true; |
50 |
} |
51 |
- :^(EQUALS name (libbash_string=string_expr { is_null = false; })?) { |
52 |
- walker->set_value($name.libbash_value, libbash_string, $name.index, is_null); |
53 |
+ :^(EQUALS name (string_expr { is_null = false; })?) { |
54 |
+ walker->set_value($name.libbash_value, $string_expr.libbash_value, $name.index, is_null); |
55 |
} |
56 |
|^(EQUALS libbash_name=name_base ^(ARRAY ( |
57 |
- (libbash_string=string_expr |
58 |
+ (expr=string_expr |
59 |
|^(EQUALS value=arithmetics { |
60 |
set_index(libbash_name, index, value); |
61 |
- } libbash_string=string_expr)) |
62 |
- { values[index++] = libbash_string; })* |
63 |
+ } expr=string_expr)) |
64 |
+ { values[index++] = expr.libbash_value; })* |
65 |
)){ |
66 |
walker->define(libbash_name, values); |
67 |
}; |
68 |
|
69 |
-string_expr returns[std::string libbash_value] |
70 |
+string_expr returns[std::string libbash_value, bool quoted] |
71 |
:^(STRING( |
72 |
- (DOUBLE_QUOTED_STRING) => ^(DOUBLE_QUOTED_STRING (libbash_string=double_quoted_string { $libbash_value += libbash_string; })*) |
73 |
- |(ARITHMETIC_EXPRESSION) => ^(ARITHMETIC_EXPRESSION value=arithmetics { $libbash_value = boost::lexical_cast<std::string>(value); }) |
74 |
- |(var_ref[false]) => libbash_string=var_ref[false] { $libbash_value = libbash_string; } |
75 |
- |(libbash_string=any_string { $libbash_value += libbash_string; })+ |
76 |
+ (DOUBLE_QUOTED_STRING) => |
77 |
+ ^(DOUBLE_QUOTED_STRING (libbash_string=double_quoted_string { $libbash_value += libbash_string; })*) { |
78 |
+ $quoted = true; |
79 |
+ } |
80 |
+ |(ARITHMETIC_EXPRESSION) => |
81 |
+ ^(ARITHMETIC_EXPRESSION value=arithmetics { |
82 |
+ $libbash_value = boost::lexical_cast<std::string>(value); $quoted = false; |
83 |
+ }) |
84 |
+ |(var_ref[false]) => libbash_string=var_ref[false] { $libbash_value = libbash_string; $quoted = false; } |
85 |
+ |(libbash_string=any_string { $libbash_value += libbash_string; $quoted = false; })+ |
86 |
)); |
87 |
|
88 |
//double quoted string rule, allows expansions |
89 |
double_quoted_string returns[std::string libbash_value] |
90 |
:(var_ref[true]) => libbash_string=var_ref[true] { $libbash_value = libbash_string; } |
91 |
- |(ARITHMETIC_EXPRESSION) => ^(ARITHMETIC_EXPRESSION value=arithmetics) { $libbash_value = boost::lexical_cast<std::string>(value); } |
92 |
+ |(ARITHMETIC_EXPRESSION) => ^(ARITHMETIC_EXPRESSION value=arithmetics) { |
93 |
+ $libbash_value = boost::lexical_cast<std::string>(value); |
94 |
+ } |
95 |
|libbash_string=any_string { $libbash_value = libbash_string; }; |
96 |
|
97 |
any_string returns[std::string libbash_value] |
98 |
@@ -185,12 +195,17 @@ var_expansion returns[std::string libbash_value] |
99 |
|
100 |
word returns[std::string libbash_value] |
101 |
:(num) => libbash_string=num { $libbash_value = libbash_string; } |
102 |
- |libbash_string=string_expr { $libbash_value = libbash_string; } |
103 |
+ |string_expr { $libbash_value = $string_expr.libbash_value; } |
104 |
|value=arithmetics { $libbash_value = boost::lexical_cast<std::string>(value); }; |
105 |
|
106 |
//variable reference |
107 |
var_ref [bool double_quoted] returns[std::string libbash_value] |
108 |
- :^(VAR_REF name) { $libbash_value = walker->resolve<std::string>($name.libbash_value, $name.index); } |
109 |
+ :^(VAR_REF name) { |
110 |
+ $libbash_value = walker->resolve<std::string>($name.libbash_value, $name.index); |
111 |
+ } |
112 |
+ |^(VAR_REF libbash_string=num) { |
113 |
+ $libbash_value = walker->resolve<std::string>(libbash_string); |
114 |
+ } |
115 |
|^(VAR_REF ^(libbash_string=name_base AT)) { walker->get_all_elements(libbash_string, $libbash_value); } |
116 |
|^(VAR_REF ^(libbash_string=name_base TIMES)) { |
117 |
if(double_quoted) |
118 |
@@ -205,29 +220,31 @@ command |
119 |
|simple_command; |
120 |
|
121 |
simple_command |
122 |
- :^(COMMAND libbash_string=string_expr argument* var_def*) { |
123 |
- ANTLR3_MARKER func_index; |
124 |
- if((func_index = walker->get_function_index(libbash_string)) != -1) |
125 |
+@declarations { |
126 |
+ std::vector<std::string> libbash_args; |
127 |
+} |
128 |
+ :^(COMMAND string_expr (argument[libbash_args])* var_def*) { |
129 |
+ if(!walker->call($string_expr.libbash_value, |
130 |
+ libbash_args, |
131 |
+ ctx, |
132 |
+ compound_command)) |
133 |
+ throw interpreter_exception("Unimplemented command: " + $string_expr.libbash_value); |
134 |
+ }; |
135 |
+ |
136 |
+argument[std::vector<std::string>& args] |
137 |
+ : string_expr { |
138 |
+ if($string_expr.quoted) |
139 |
{ |
140 |
- // Saving current index |
141 |
- ANTLR3_MARKER curr = INDEX(); |
142 |
- // Push function index into INPUT |
143 |
- INPUT->push(INPUT, func_index); |
144 |
- // Execute function body |
145 |
- compound_command(ctx); |
146 |
- // Reset to the previous index |
147 |
- SEEK(curr); |
148 |
+ args.push_back($string_expr.libbash_value); |
149 |
} |
150 |
else |
151 |
{ |
152 |
- throw interpreter_exception("Unimplemented command: " + libbash_string); |
153 |
+ std::vector<std::string> arguments; |
154 |
+ walker->split_word($string_expr.libbash_value, arguments); |
155 |
+ args.insert(args.end(), arguments.begin(), arguments.end()); |
156 |
} |
157 |
}; |
158 |
|
159 |
-argument |
160 |
- :var_ref[false] |
161 |
- |string_expr; |
162 |
- |
163 |
logic_command_list |
164 |
:command |
165 |
|^(LOGICAND command command) |
166 |
|
167 |
diff --git a/scripts/function_def.ebuild b/scripts/function_def.ebuild |
168 |
index 8316c5f..194d030 100644 |
169 |
--- a/scripts/function_def.ebuild |
170 |
+++ b/scripts/function_def.ebuild |
171 |
@@ -8,3 +8,21 @@ src_unpack |
172 |
src_unpack |
173 |
|
174 |
MY_PV=2.0.3-r1 |
175 |
+ |
176 |
+nested_func_with_args() { |
177 |
+ ARG6=$1 |
178 |
+ ARG7=$3 |
179 |
+} |
180 |
+ |
181 |
+func_with_args() { |
182 |
+ ARG1=$1 |
183 |
+ ARG2=$2 |
184 |
+ ARG3=$3 |
185 |
+ ARG4=$4 |
186 |
+ ARG5=$5 |
187 |
+ nested_func_with_args $4 |
188 |
+} |
189 |
+FOO001="4 5" |
190 |
+ARRAY=(1 2 3) |
191 |
+func_with_args ${ARRAY[@]} $FOO001 |
192 |
+func_with_args 100 $ARG2 $ARG3 $ARG4 |
193 |
|
194 |
diff --git a/scripts/function_def.ebuild.result b/scripts/function_def.ebuild.result |
195 |
index dbbc7eb..de9f3f8 100644 |
196 |
--- a/scripts/function_def.ebuild.result |
197 |
+++ b/scripts/function_def.ebuild.result |
198 |
@@ -1,2 +1,11 @@ |
199 |
+ARG1=100 |
200 |
+ARG2=2 |
201 |
+ARG3=3 |
202 |
+ARG4=4 |
203 |
+ARG5= |
204 |
+ARG6=4 |
205 |
+ARG7= |
206 |
+ARRAY=1 2 3 |
207 |
EAPI=5 |
208 |
+FOO001=4 5 |
209 |
MY_PV=2.0.3-r1 |
210 |
|
211 |
diff --git a/src/core/interpreter.cpp b/src/core/interpreter.cpp |
212 |
index d9004e8..c683315 100644 |
213 |
--- a/src/core/interpreter.cpp |
214 |
+++ b/src/core/interpreter.cpp |
215 |
@@ -24,7 +24,11 @@ |
216 |
|
217 |
#include "core/interpreter.h" |
218 |
|
219 |
+#include <boost/algorithm/string/classification.hpp> |
220 |
#include <boost/algorithm/string/join.hpp> |
221 |
+#include <boost/algorithm/string/split.hpp> |
222 |
+ |
223 |
+#include "libbashWalker.h" |
224 |
|
225 |
void interpreter::get_all_elements_joined(const std::string& name, |
226 |
const std::string& delim, |
227 |
@@ -57,3 +61,51 @@ void interpreter::get_all_elements_IFS_joined(const std::string& name, |
228 |
resolve<std::string>("IFS").substr(0, 1), |
229 |
result); |
230 |
} |
231 |
+ |
232 |
+void interpreter::split_word(const std::string& word, std::vector<std::string>& output) |
233 |
+{ |
234 |
+ const std::string& delimeter = resolve<std::string>("IFS"); |
235 |
+ boost::split(output, word, boost::is_any_of(delimeter), boost::token_compress_on); |
236 |
+} |
237 |
+ |
238 |
+inline void define_function_arguments(std::unique_ptr<scope>& current_stack, |
239 |
+ const std::vector<std::string>& arguments) |
240 |
+{ |
241 |
+ for(auto i = 0u; i != arguments.size(); ++i) |
242 |
+ { |
243 |
+ const std::string& name = boost::lexical_cast<std::string>(i + 1); |
244 |
+ (*current_stack)[name] = std::shared_ptr<variable>(new variable(name, arguments[i])); |
245 |
+ } |
246 |
+} |
247 |
+ |
248 |
+bool interpreter::call(const std::string& name, |
249 |
+ const std::vector<std::string>& arguments, |
250 |
+ plibbashWalker ctx, |
251 |
+ std::function<void(plibbashWalker)> f) |
252 |
+{ |
253 |
+ ANTLR3_MARKER func_index; |
254 |
+ auto iter = functions.find(name); |
255 |
+ if(iter == functions.end()) |
256 |
+ return false; |
257 |
+ func_index = iter->second; |
258 |
+ |
259 |
+ // Prepare function stack and arguments |
260 |
+ local_members.push(std::unique_ptr<scope>(new scope)); |
261 |
+ define_function_arguments(local_members.top(), arguments); |
262 |
+ |
263 |
+ auto INPUT = ctx->pTreeParser->ctnstream; |
264 |
+ auto ISTREAM = INPUT->tnstream->istream; |
265 |
+ // Saving current index |
266 |
+ ANTLR3_MARKER curr = ISTREAM->index(ISTREAM); |
267 |
+ // Push function index into INPUT |
268 |
+ INPUT->push(INPUT, func_index); |
269 |
+ // Execute function body |
270 |
+ f(ctx); |
271 |
+ // Reset to the previous index |
272 |
+ ISTREAM->seek(ISTREAM, curr); |
273 |
+ |
274 |
+ // Clear function stack |
275 |
+ local_members.pop(); |
276 |
+ |
277 |
+ return true; |
278 |
+} |
279 |
|
280 |
diff --git a/src/core/interpreter.h b/src/core/interpreter.h |
281 |
index 7c5acdb..22a083e 100644 |
282 |
--- a/src/core/interpreter.h |
283 |
+++ b/src/core/interpreter.h |
284 |
@@ -29,6 +29,7 @@ |
285 |
|
286 |
#include <functional> |
287 |
#include <memory> |
288 |
+#include <stack> |
289 |
#include <string> |
290 |
|
291 |
#include <antlr3basetree.h> |
292 |
@@ -36,12 +37,15 @@ |
293 |
#include "core/symbols.hpp" |
294 |
#include "libbashLexer.h" |
295 |
|
296 |
+typedef std::unordered_map<std::string, std::shared_ptr<variable>> scope; |
297 |
+struct libbashWalker_Ctx_struct; |
298 |
+typedef struct libbashWalker_Ctx_struct * plibbashWalker; |
299 |
+ |
300 |
/// |
301 |
/// \class interpreter |
302 |
/// \brief implementation for bash interpreter |
303 |
/// |
304 |
class interpreter{ |
305 |
- typedef std::unordered_map<std::string, std::shared_ptr<variable>> scope; |
306 |
|
307 |
/// \var private::members |
308 |
/// \brief global symbol table for variables |
309 |
@@ -51,6 +55,11 @@ class interpreter{ |
310 |
/// \brief global symbol table for functions |
311 |
std::unordered_map<std::string, ANTLR3_MARKER> functions; |
312 |
|
313 |
+ /// \var private::local_members |
314 |
+ /// \brief local scope for function arguments, execution environment and |
315 |
+ /// local variables |
316 |
+ std::stack<std::unique_ptr<scope>> local_members; |
317 |
+ |
318 |
/// \brief calculate the correct offset when offset < 0 and check whether |
319 |
/// the real offset is in legal range |
320 |
/// \param[in,out] a value/result argument referring to offset |
321 |
@@ -376,7 +385,8 @@ public: |
322 |
return new_value; |
323 |
} |
324 |
|
325 |
- /// \brief resolve string/int variable |
326 |
+ /// \brief resolve string/int variable, local scope will be |
327 |
+ /// checked first, then global scope |
328 |
/// \param variable name |
329 |
/// \param array index, use index=0 if it's not an array |
330 |
/// \return the value of the variable, call default constructor if |
331 |
@@ -384,10 +394,17 @@ public: |
332 |
template <typename T> |
333 |
T resolve(const std::string& name, const unsigned index=0) const |
334 |
{ |
335 |
- auto i = members.find(name); |
336 |
- if(i == members.end()) |
337 |
+ if(!local_members.empty()) |
338 |
+ { |
339 |
+ auto iter_local = local_members.top()->find(name); |
340 |
+ if(iter_local != local_members.top()->end()) |
341 |
+ return iter_local->second->get_value<T>(index); |
342 |
+ } |
343 |
+ |
344 |
+ auto iter_global = members.find(name); |
345 |
+ if(iter_global == members.end()) |
346 |
return T(); |
347 |
- return i->second->get_value<T>(index); |
348 |
+ return iter_global->second->get_value<T>(index); |
349 |
} |
350 |
|
351 |
/// \brief resolve array variable |
352 |
@@ -444,7 +461,7 @@ public: |
353 |
return new_value; |
354 |
} |
355 |
|
356 |
- /// \brief define a new variable |
357 |
+ /// \brief define a new global variable |
358 |
/// \param the name of the variable |
359 |
/// \param the value of the variable |
360 |
/// \param whether it's readonly, default is false |
361 |
@@ -470,17 +487,16 @@ public: |
362 |
functions[name] = body_index; |
363 |
} |
364 |
|
365 |
- /// \brief get the index of the function in ANTLR3_COMMON_TREE_NODE_STREAM |
366 |
- /// \param the name of the function |
367 |
- /// \return the index of the function |
368 |
- ANTLR3_MARKER get_function_index(const std::string& name) const |
369 |
- { |
370 |
- auto iter = functions.find(name); |
371 |
- if(iter == functions.end()) |
372 |
- return -1; |
373 |
- return iter->second; |
374 |
- } |
375 |
- |
376 |
+ /// \brief make function call |
377 |
+ /// \param function name |
378 |
+ /// \param function arguments |
379 |
+ /// \param walker context |
380 |
+ /// \param the function that needs to be executed |
381 |
+ /// \return whether the execution is successful |
382 |
+ bool call(const std::string& name, |
383 |
+ const std::vector<std::string>& arguments, |
384 |
+ plibbashWalker ctx, |
385 |
+ std::function<void(plibbashWalker)> f); |
386 |
|
387 |
/// \brief perform ${parameter:−word} expansion |
388 |
/// \param the name of the parameter |
389 |
@@ -570,8 +586,19 @@ public: |
390 |
return i->second->get_array_length(); |
391 |
} |
392 |
|
393 |
+ /// \brief get all array elements concatenated by space |
394 |
+ /// \param the name of the array |
395 |
+ /// \param[out] the concatenated string |
396 |
void get_all_elements(const std::string&, std::string&) const; |
397 |
|
398 |
+ /// \brief get all array elements concatenated by the first character of IFS |
399 |
+ /// \param the name of the array |
400 |
+ /// \param[out] the concatenated string |
401 |
void get_all_elements_IFS_joined(const std::string&, std::string&) const; |
402 |
+ |
403 |
+ /// \brief implementation of word splitting |
404 |
+ /// \param the value of the word |
405 |
+ //. \param[out] the splitted result |
406 |
+ void split_word(const std::string& word, std::vector<std::string>& output); |
407 |
}; |
408 |
#endif |