1 |
commit: b37f97adcfaf9aea824aa5f1bacae4c326973434 |
2 |
Author: Mu Qiao <qiaomuf <AT> gentoo <DOT> org> |
3 |
AuthorDate: Mon Apr 11 13:25:42 2011 +0000 |
4 |
Commit: Petteri Räty <betelgeuse <AT> gentoo <DOT> org> |
5 |
CommitDate: Tue Apr 12 07:22:36 2011 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/libbash.git;a=commit;h=b37f97ad |
7 |
|
8 |
Implement array symbol |
9 |
|
10 |
Bash uses normal variables and arrays interchangeably. So we use |
11 |
an internal map no matter if it's an array or variable. |
12 |
|
13 |
--- |
14 |
src/core/interpreter.h | 12 ++++++--- |
15 |
src/core/symbols.hpp | 44 +++++++++++++++++++++++++++------- |
16 |
src/core/tests/interpreter_test.cpp | 27 +++++++++++++++++++++ |
17 |
src/core/tests/symbols_test.cpp | 31 +++++++++++++++++++++++- |
18 |
4 files changed, 100 insertions(+), 14 deletions(-) |
19 |
|
20 |
diff --git a/src/core/interpreter.h b/src/core/interpreter.h |
21 |
index 8fcab60..7155639 100644 |
22 |
--- a/src/core/interpreter.h |
23 |
+++ b/src/core/interpreter.h |
24 |
@@ -363,15 +363,16 @@ public: |
25 |
|
26 |
/// \brief resolve any variable |
27 |
/// \param variable name |
28 |
+ /// \param array index, use index=0 if it's not an array |
29 |
/// \return the value of the variable, call default constructor if |
30 |
// it's undefined |
31 |
template <typename T> |
32 |
- T resolve(const std::string& name) |
33 |
+ T resolve(const std::string& name, const unsigned index=0) |
34 |
{ |
35 |
std::shared_ptr<variable> value = members.resolve(name); |
36 |
if(!value) |
37 |
return T(); |
38 |
- return value->get_value<T>(); |
39 |
+ return value->get_value<T>(index); |
40 |
} |
41 |
|
42 |
/// \brief check whether the value of the variable is null, return true |
43 |
@@ -399,15 +400,18 @@ public: |
44 |
/// it's readonly, will define the variable if it doesn't exist |
45 |
/// \param variable name |
46 |
/// \param new value |
47 |
+ /// \param array index, use index=0 if it's not an array |
48 |
/// \return the new value of the variable |
49 |
template <typename T> |
50 |
- const T& set_value(const std::string& name, const T& new_value) |
51 |
+ const T& set_value(const std::string& name, |
52 |
+ const T& new_value, |
53 |
+ const unsigned index=0) |
54 |
{ |
55 |
std::shared_ptr<variable> value = members.resolve(name); |
56 |
if(!value) |
57 |
define(name, new_value, false); |
58 |
else |
59 |
- value->set_value(new_value); |
60 |
+ value->set_value(new_value, index); |
61 |
return new_value; |
62 |
} |
63 |
|
64 |
|
65 |
diff --git a/src/core/symbols.hpp b/src/core/symbols.hpp |
66 |
index 077065e..f645047 100644 |
67 |
--- a/src/core/symbols.hpp |
68 |
+++ b/src/core/symbols.hpp |
69 |
@@ -25,6 +25,7 @@ |
70 |
#ifndef LIBBASH_CORE_SYMBOLS_HPP_ |
71 |
#define LIBBASH_CORE_SYMBOLS_HPP_ |
72 |
|
73 |
+#include <map> |
74 |
#include <memory> |
75 |
#include <sstream> |
76 |
#include <string> |
77 |
@@ -115,8 +116,10 @@ class variable |
78 |
std::string name; |
79 |
|
80 |
/// \var private::value |
81 |
- /// \brief actual value of the variable |
82 |
- boost::variant<int, std::string> value; |
83 |
+ /// \brief actual value of the variable. We put string in front of int |
84 |
+ /// because we want "" as default string value; Otherwise we |
85 |
+ /// will get "0". |
86 |
+ std::map<int, boost::variant<std::string, int>> value; |
87 |
|
88 |
/// \var private::readonly |
89 |
/// \brief whether the variable is readonly |
90 |
@@ -136,30 +139,43 @@ public: |
91 |
|
92 |
template <typename T> |
93 |
variable(const std::string& name, |
94 |
- T v, |
95 |
+ const T& v, |
96 |
bool ro=false, |
97 |
bool is_null=false) |
98 |
- : name(name), value(v), readonly(ro), null_value(is_null){} |
99 |
+ : name(name), readonly(ro), null_value(is_null) |
100 |
+ { |
101 |
+ value[0] = v; |
102 |
+ } |
103 |
|
104 |
- /// \brief retrieve actual value of the variable |
105 |
+ /// \brief retrieve actual value of the variable, if index is out of bound, |
106 |
+ /// will return the default value of type T |
107 |
/// \return the value of the variable |
108 |
template<typename T> |
109 |
- T get_value() const |
110 |
+ T get_value(const unsigned index=0) const |
111 |
{ |
112 |
static converter<T> visitor; |
113 |
- return boost::apply_visitor(visitor, value); |
114 |
+ |
115 |
+ auto iter = value.find(index); |
116 |
+ if(iter == value.end()) |
117 |
+ return T{}; |
118 |
+ |
119 |
+ return boost::apply_visitor(visitor, iter->second); |
120 |
} |
121 |
|
122 |
/// \brief set the value of the variable, raise exception if it's readonly |
123 |
/// \param the new value to be set |
124 |
+ /// \param array index, use index=0 if it's not an array |
125 |
/// \param whether to set the variable to null value, default is false |
126 |
template <typename T> |
127 |
- void set_value(T new_value, bool is_null=false) |
128 |
+ void set_value(const T& new_value, |
129 |
+ const unsigned index=0, |
130 |
+ bool is_null=false) |
131 |
{ |
132 |
if(readonly) |
133 |
throw interpreter_exception(get_name() + " is readonly variable"); |
134 |
+ |
135 |
null_value = is_null; |
136 |
- value = new_value; |
137 |
+ value[index] = new_value; |
138 |
} |
139 |
|
140 |
/// \brief check whether the value of the variable is null |
141 |
@@ -170,6 +186,16 @@ public: |
142 |
} |
143 |
}; |
144 |
|
145 |
+// specialization for arrays |
146 |
+template <> |
147 |
+inline variable::variable<>(const std::string& name, |
148 |
+ const std::map<int, std::string>& v, |
149 |
+ bool ro, |
150 |
+ bool is_null) |
151 |
+ : name(name), value(v.begin(), v.end()), readonly(ro), null_value(is_null) |
152 |
+{ |
153 |
+} |
154 |
+ |
155 |
/// |
156 |
/// class scope |
157 |
/// \brief implementation for symbol table |
158 |
|
159 |
diff --git a/src/core/tests/interpreter_test.cpp b/src/core/tests/interpreter_test.cpp |
160 |
index 6cc2df9..28f8d35 100644 |
161 |
--- a/src/core/tests/interpreter_test.cpp |
162 |
+++ b/src/core/tests/interpreter_test.cpp |
163 |
@@ -44,6 +44,17 @@ TEST(interpreter, define_resolve_string) |
164 |
EXPECT_STREQ("", walker.resolve<string>("undefined").c_str()); |
165 |
} |
166 |
|
167 |
+TEST(interpreter, define_resolve_array) |
168 |
+{ |
169 |
+ interpreter walker; |
170 |
+ std::map<int, std::string> values = {{0, "1"}, {1, "2"}, {2, "3"}}; |
171 |
+ walker.define("array", values); |
172 |
+ EXPECT_STREQ("1", walker.resolve<string>("array", 0).c_str()); |
173 |
+ EXPECT_STREQ("2", walker.resolve<string>("array", 1).c_str()); |
174 |
+ EXPECT_STREQ("3", walker.resolve<string>("array", 2).c_str()); |
175 |
+ EXPECT_STREQ("", walker.resolve<string>("undefined",100).c_str()); |
176 |
+} |
177 |
+ |
178 |
TEST(interpreter, is_unset_or_null) |
179 |
{ |
180 |
interpreter walker; |
181 |
@@ -91,6 +102,22 @@ TEST(interpreter, set_string_value) |
182 |
EXPECT_STREQ("hi", walker.resolve<string>("astring_ro").c_str()); |
183 |
} |
184 |
|
185 |
+TEST(interpreter, set_array_value) |
186 |
+{ |
187 |
+ interpreter walker; |
188 |
+ std::map<int, std::string> values = {{0, "1"}, {1, "2"}, {2, "3"}}; |
189 |
+ walker.define("array", values); |
190 |
+ EXPECT_STREQ("2", walker.set_value<string>("array", "2", 0).c_str()); |
191 |
+ EXPECT_STREQ("2", walker.resolve<string>("array", 0).c_str()); |
192 |
+ EXPECT_STREQ("out_of_bound", walker.set_value<string>("array", "out_of_bound", 10).c_str()); |
193 |
+ EXPECT_STREQ("out_of_bound", walker.resolve<string>("array",10).c_str()); |
194 |
+ |
195 |
+ walker.define("ro_array", values, true); |
196 |
+ EXPECT_THROW(walker.set_value<string>("ro_array", "hello", 1), |
197 |
+ interpreter_exception); |
198 |
+ EXPECT_STREQ("2", walker.resolve<string>("ro_array", 1).c_str()); |
199 |
+} |
200 |
+ |
201 |
TEST(interperter, substring_expansion_exception) |
202 |
{ |
203 |
interpreter walker; |
204 |
|
205 |
diff --git a/src/core/tests/symbols_test.cpp b/src/core/tests/symbols_test.cpp |
206 |
index 73a6a9c..2b7a65e 100644 |
207 |
--- a/src/core/tests/symbols_test.cpp |
208 |
+++ b/src/core/tests/symbols_test.cpp |
209 |
@@ -65,11 +65,40 @@ TEST(symbol_test, string_variable) |
210 |
EXPECT_EQ(123, int_string.get_value<int>()); |
211 |
} |
212 |
|
213 |
+TEST(symbol_test, array_variable) |
214 |
+{ |
215 |
+ map<int, string> values = {{0, "1"}, {1, "2"}, {2, "3"}}; |
216 |
+ |
217 |
+ // readonly array |
218 |
+ variable ro_array("foo", values, true); |
219 |
+ EXPECT_STREQ("foo", ro_array.get_name().c_str()); |
220 |
+ EXPECT_STREQ("1", ro_array.get_value<string>(0).c_str()); |
221 |
+ EXPECT_STREQ("2", ro_array.get_value<string>(1).c_str()); |
222 |
+ EXPECT_STREQ("3", ro_array.get_value<string>(2).c_str()); |
223 |
+ EXPECT_THROW(ro_array.set_value("4", 0), interpreter_exception); |
224 |
+ EXPECT_STREQ("1", ro_array.get_value<string>(0).c_str()); |
225 |
+ |
226 |
+ // out of bound |
227 |
+ EXPECT_STREQ("", ro_array.get_value<string>(100).c_str()); |
228 |
+ |
229 |
+ // normal array |
230 |
+ variable normal_array("foo", values); |
231 |
+ normal_array.set_value("5", 4); |
232 |
+ EXPECT_STREQ("1", normal_array.get_value<string>(0).c_str()); |
233 |
+ EXPECT_STREQ("2", normal_array.get_value<string>(1).c_str()); |
234 |
+ EXPECT_STREQ("3", normal_array.get_value<string>(2).c_str()); |
235 |
+ EXPECT_STREQ("", normal_array.get_value<string>(3).c_str()); |
236 |
+ EXPECT_STREQ("5", normal_array.get_value<string>(4).c_str()); |
237 |
+ |
238 |
+ // get integer value |
239 |
+ EXPECT_EQ(3, normal_array.get_value<int>(2)); |
240 |
+} |
241 |
+ |
242 |
TEST(symbol_test, is_null) |
243 |
{ |
244 |
variable var("foo", 10); |
245 |
EXPECT_FALSE(var.is_null()); |
246 |
- var.set_value("bar", true); |
247 |
+ var.set_value("bar", 0, true); |
248 |
EXPECT_TRUE(var.is_null()); |
249 |
EXPECT_TRUE(variable("foo", "", false, true).is_null()); |
250 |
} |