forked from chipsalliance/verible
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig_utils.cc
184 lines (169 loc) · 6.84 KB
/
config_utils.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "common/text/config_utils.h"
#include <cstdint>
#include <initializer_list>
#include <limits>
#include <string>
#include <vector>
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "common/util/logging.h"
namespace verible {
using absl::string_view;
using config::NVConfigSpec;
// TODO(hzeller): consider using flex for a more readable tokenization that
// can also much easier deal with whitespaces, strings etc.
absl::Status ParseNameValues(string_view config_string,
const std::initializer_list<NVConfigSpec>& spec) {
if (config_string.empty()) return absl::OkStatus();
for (const string_view single_config : absl::StrSplit(config_string, ';')) {
const std::pair<string_view, string_view> nv_pair =
absl::StrSplit(single_config, ':');
const auto value_config = std::find_if( // linear search
spec.begin(), spec.end(),
[&nv_pair](const NVConfigSpec& s) { return nv_pair.first == s.name; });
if (value_config == spec.end()) {
std::string available;
for (const auto& s : spec) {
if (!available.empty()) available.append(", ");
available.append("'").append(s.name).append("'");
}
const bool plural = spec.size() > 1;
return absl::InvalidArgumentError(absl::StrCat(
nv_pair.first, ": unknown parameter; supported ",
(plural ? "parameters are " : "parameter is "), available));
}
if (!value_config->set_value) return absl::OkStatus(); // consume, not use.
absl::Status result = value_config->set_value(nv_pair.second);
if (!result.ok()) {
// We always prepend the parameter name first in the message for
// diagnostic usefulness of the error message.
// Also, this way, the functor can just worry about parsing and does
// not need to know the name of the value.
return absl::InvalidArgumentError(
absl::StrCat(nv_pair.first, ": ", result.message()));
}
}
return absl::OkStatus();
}
namespace config {
ConfigValueSetter SetInt(int* value, int minimum, int maximum) {
CHECK(value) << "Must provide pointer to integer to store.";
return [value, minimum, maximum](string_view v) {
int parsed_value;
if (!absl::SimpleAtoi(v, &parsed_value)) {
return absl::InvalidArgumentError(
absl::StrCat("'", v, "': Cannot parse integer"));
}
if (parsed_value < minimum || parsed_value > maximum) {
return absl::InvalidArgumentError(absl::StrCat(
parsed_value, " out of range [", minimum, "...", maximum, "]"));
}
*value = parsed_value;
return absl::OkStatus();
};
}
ConfigValueSetter SetInt(int* value) {
return SetInt(value, std::numeric_limits<int>::min(),
std::numeric_limits<int>::max());
}
ConfigValueSetter SetBool(bool* value) {
CHECK(value) << "Must provide pointer to boolean to store.";
return [value](string_view v) {
// clang-format off
if (v.empty() || v == "1"
|| absl::EqualsIgnoreCase(v, "true")
|| absl::EqualsIgnoreCase(v, "on")) {
*value = true;
return absl::OkStatus();
}
if (v == "0"
|| absl::EqualsIgnoreCase(v, "false")
|| absl::EqualsIgnoreCase(v, "off")) {
*value = false;
return absl::OkStatus();
}
// clange-format on
// We accept 1 and 0 silently to encourage somewhat
// consistent use of values.
return absl::InvalidArgumentError("Boolean value should be one of "
"'true', 'on' or 'false', 'off'");
};
}
// TODO(hzeller): For strings, consider to allow quoted strings that then also
// allow C-Escapes. But that would require that ParseNameValues() can properly
// deal with whitespace, which it might after converted to flex.
ConfigValueSetter SetString(std::string* value) {
return [value](string_view v) {
value->assign(v.data(), v.length());
return absl::OkStatus();
};
}
ConfigValueSetter SetStringOneOf(std::string* value,
const std::vector<string_view>& allowed) {
CHECK(value) << "Must provide pointer to string to store.";
return [value, allowed](string_view v) {
auto item = find(allowed.begin(), allowed.end(), v);
if (item == allowed.end()) {
// If the list only contains one element, provide a more suited
// error message.
if (allowed.size() == 1) {
return absl::InvalidArgumentError(
absl::StrCat("Value can only be '", allowed[0], "'; got '",
v, "'"));
} else {
return absl::InvalidArgumentError(
absl::StrCat("Value can only be one of ['",
absl::StrJoin(allowed, "', '"),
"']; got '", v, "'"));
}
}
value->assign(v.data(), v.length());
return absl::OkStatus();
};
}
ConfigValueSetter SetNamedBits(
uint32_t* value,
const std::vector<absl::string_view>& choices) {
CHECK(value) << "Must provide pointer to uint32_t to store.";
CHECK_LE(choices.size(), 32) << "Too many choices for 32-bit bitmap";
return [value, choices](string_view v) {
uint32_t result = 0;
for (auto bitname : absl::StrSplit(v, '|', absl::SkipWhitespace())) {
bitname = absl::StripAsciiWhitespace(bitname);
const auto item_pos = find_if(
choices.begin(), choices.end(),
[bitname](string_view c) {
return absl::EqualsIgnoreCase(bitname, c);
});
if (item_pos == choices.end()) {
return absl::InvalidArgumentError(
absl::StrCat("'", bitname,
"' is not in the available choices {",
absl::StrJoin(choices, ", "), "}"));
}
result |= (1 << (std::distance(choices.begin(), item_pos)));
}
// Parsed all bits successfully.
*value = result;
return absl::OkStatus();
};
}
} // namespace config
} // namespace verible