1 | /* Copyright 2019 The TensorFlow Authors. All Rights Reserved. |
2 | |
3 | Licensed under the Apache License, Version 2.0 (the "License"); |
4 | you may not use this file except in compliance with the License. |
5 | You may obtain a copy of the License at |
6 | |
7 | http://www.apache.org/licenses/LICENSE-2.0 |
8 | |
9 | Unless required by applicable law or agreed to in writing, software |
10 | distributed under the License is distributed on an "AS IS" BASIS, |
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | See the License for the specific language governing permissions and |
13 | limitations under the License. |
14 | ==============================================================================*/ |
15 | |
16 | #include "tensorflow/lite/toco/args.h" |
17 | |
18 | #include <string> |
19 | |
20 | #include "absl/strings/str_split.h" |
21 | |
22 | namespace toco { |
23 | namespace { |
24 | |
25 | // Helper class for SplitStructuredLine parsing. |
26 | class ClosingSymbolLookup { |
27 | public: |
28 | explicit ClosingSymbolLookup(const char* symbol_pairs) |
29 | : closing_(), valid_closing_() { |
30 | // Initialize the opening/closing arrays. |
31 | for (const char* symbol = symbol_pairs; *symbol != 0; ++symbol) { |
32 | unsigned char opening = *symbol; |
33 | ++symbol; |
34 | // If the string ends before the closing character has been found, |
35 | // use the opening character as the closing character. |
36 | unsigned char closing = *symbol != 0 ? *symbol : opening; |
37 | closing_[opening] = closing; |
38 | valid_closing_[closing] = true; |
39 | if (*symbol == 0) break; |
40 | } |
41 | } |
42 | |
43 | ClosingSymbolLookup(const ClosingSymbolLookup&) = delete; |
44 | ClosingSymbolLookup& operator=(const ClosingSymbolLookup&) = delete; |
45 | |
46 | // Returns the closing character corresponding to an opening one, |
47 | // or 0 if the argument is not an opening character. |
48 | char GetClosingChar(char opening) const { |
49 | return closing_[static_cast<unsigned char>(opening)]; |
50 | } |
51 | |
52 | // Returns true if the argument is a closing character. |
53 | bool IsClosing(char c) const { |
54 | return valid_closing_[static_cast<unsigned char>(c)]; |
55 | } |
56 | |
57 | private: |
58 | // Maps an opening character to its closing. If the entry contains 0, |
59 | // the character is not in the opening set. |
60 | char closing_[256]; |
61 | // Valid closing characters. |
62 | bool valid_closing_[256]; |
63 | }; |
64 | |
65 | bool SplitStructuredLine(absl::string_view line, char delimiter, |
66 | const char* symbol_pairs, |
67 | std::vector<absl::string_view>* cols) { |
68 | ClosingSymbolLookup lookup(symbol_pairs); |
69 | |
70 | // Stack of symbols expected to close the current opened expressions. |
71 | std::vector<char> expected_to_close; |
72 | |
73 | ABSL_RAW_CHECK(cols != nullptr, "" ); |
74 | cols->push_back(line); |
75 | for (size_t i = 0; i < line.size(); ++i) { |
76 | char c = line[i]; |
77 | if (expected_to_close.empty() && c == delimiter) { |
78 | // We don't have any open expression, this is a valid separator. |
79 | cols->back().remove_suffix(line.size() - i); |
80 | cols->push_back(line.substr(i + 1)); |
81 | } else if (!expected_to_close.empty() && c == expected_to_close.back()) { |
82 | // Can we close the currently open expression? |
83 | expected_to_close.pop_back(); |
84 | } else if (lookup.GetClosingChar(c)) { |
85 | // If this is an opening symbol, we open a new expression and push |
86 | // the expected closing symbol on the stack. |
87 | expected_to_close.push_back(lookup.GetClosingChar(c)); |
88 | } else if (lookup.IsClosing(c)) { |
89 | // Error: mismatched closing symbol. |
90 | return false; |
91 | } |
92 | } |
93 | if (!expected_to_close.empty()) { |
94 | return false; // Missing closing symbol(s) |
95 | } |
96 | return true; // Success |
97 | } |
98 | |
99 | inline bool TryStripPrefixString(absl::string_view str, |
100 | absl::string_view prefix, |
101 | std::string* result) { |
102 | bool res = absl::ConsumePrefix(&str, prefix); |
103 | result->assign(str.begin(), str.end()); |
104 | return res; |
105 | } |
106 | |
107 | inline bool TryStripSuffixString(absl::string_view str, |
108 | absl::string_view suffix, |
109 | std::string* result) { |
110 | bool res = absl::ConsumeSuffix(&str, suffix); |
111 | result->assign(str.begin(), str.end()); |
112 | return res; |
113 | } |
114 | |
115 | } // namespace |
116 | |
117 | bool Arg<toco::IntList>::Parse(std::string text) { |
118 | parsed_value_.elements.clear(); |
119 | specified_ = true; |
120 | // absl::StrSplit("") produces {""}, but we need {} on empty input. |
121 | // TODO(aselle): Moved this from elsewhere, but ahentz recommends we could |
122 | // use absl::SplitLeadingDec32Values(text.c_str(), &parsed_values_.elements) |
123 | if (!text.empty()) { |
124 | int32_t element; |
125 | for (absl::string_view part : absl::StrSplit(text, ',')) { |
126 | if (!absl::SimpleAtoi(part, &element)) return false; |
127 | parsed_value_.elements.push_back(element); |
128 | } |
129 | } |
130 | return true; |
131 | } |
132 | |
133 | bool Arg<toco::StringMapList>::Parse(std::string text) { |
134 | parsed_value_.elements.clear(); |
135 | specified_ = true; |
136 | |
137 | if (text.empty()) { |
138 | return true; |
139 | } |
140 | |
141 | std::vector<absl::string_view> outer_vector; |
142 | absl::string_view text_disposable_copy = text; |
143 | // TODO(aselle): Change argument parsing when absl supports structuredline. |
144 | SplitStructuredLine(text_disposable_copy, ',', "{}" , &outer_vector); |
145 | for (const absl::string_view& outer_member_stringpiece : outer_vector) { |
146 | std::string outer_member(outer_member_stringpiece); |
147 | if (outer_member.empty()) { |
148 | continue; |
149 | } |
150 | std::string outer_member_copy = outer_member; |
151 | absl::StripAsciiWhitespace(&outer_member); |
152 | if (!TryStripPrefixString(outer_member, "{" , &outer_member)) return false; |
153 | if (!TryStripSuffixString(outer_member, "}" , &outer_member)) return false; |
154 | const std::vector<std::string> inner_fields_vector = |
155 | absl::StrSplit(outer_member, ','); |
156 | |
157 | std::unordered_map<std::string, std::string> element; |
158 | for (const std::string& member_field : inner_fields_vector) { |
159 | std::vector<std::string> outer_member_key_value = |
160 | absl::StrSplit(member_field, ':'); |
161 | if (outer_member_key_value.size() != 2) return false; |
162 | std::string& key = outer_member_key_value[0]; |
163 | std::string& value = outer_member_key_value[1]; |
164 | absl::StripAsciiWhitespace(&key); |
165 | absl::StripAsciiWhitespace(&value); |
166 | if (element.count(key) != 0) return false; |
167 | element[key] = value; |
168 | } |
169 | parsed_value_.elements.push_back(element); |
170 | } |
171 | return true; |
172 | } |
173 | |
174 | } // namespace toco |
175 | |