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 #include "tensorflow_lite_support/codegen/utils.h"
16
17 #include <cstdarg>
18
19 namespace tflite {
20 namespace support {
21 namespace codegen {
22
Warning(const char * format,...)23 int ErrorReporter::Warning(const char* format, ...) {
24 va_list args;
25 va_start(args, format);
26 return Report("[WARN] ", format, args);
27 }
28
Error(const char * format,...)29 int ErrorReporter::Error(const char* format, ...) {
30 va_list args;
31 va_start(args, format);
32 return Report("[ERROR] ", format, args);
33 }
34
Report(const char * prefix,const char * format,va_list args)35 int ErrorReporter::Report(const char* prefix, const char* format,
36 va_list args) {
37 char buf[1024];
38 int formatted = vsnprintf(buf, sizeof(buf), format, args);
39 buffer_ << prefix << buf << std::endl;
40 return formatted;
41 }
42
GetMessage()43 std::string ErrorReporter::GetMessage() {
44 std::string value = buffer_.str();
45 buffer_.str("");
46 return value;
47 }
48
CodeWriter(ErrorReporter * err)49 CodeWriter::CodeWriter(ErrorReporter* err) : indent_(0), err_(err) {}
50
SetTokenValue(const std::string & token,const std::string & value)51 void CodeWriter::SetTokenValue(const std::string& token,
52 const std::string& value) {
53 value_map_[token] = value;
54 }
55
GetTokenValue(const std::string & token) const56 const std::string CodeWriter::GetTokenValue(const std::string& token) const {
57 auto iter = value_map_.find(token);
58 if (iter == value_map_.end()) {
59 // Typically only Code Generator's call this function (or `Append`). It's
60 // their duty to make sure the token is valid, and requesting for an invalid
61 // token implicits flaws in the code generation logic.
62 err_->Error("Internal: Cannot find value with token '%s'", token.c_str());
63 return "";
64 }
65 return iter->second;
66 }
67
SetIndentString(const std::string & indent_str)68 void CodeWriter::SetIndentString(const std::string& indent_str) {
69 indent_str_ = indent_str;
70 }
71
Indent()72 void CodeWriter::Indent() { indent_++; }
73
Outdent()74 void CodeWriter::Outdent() { indent_--; }
75
GenerateIndent() const76 std::string CodeWriter::GenerateIndent() const {
77 std::string res;
78 res.reserve(indent_str_.size() * indent_);
79 for (int i = 0; i < indent_; i++) {
80 res.append(indent_str_);
81 }
82 return res;
83 }
84
Append(const std::string & text)85 void CodeWriter::Append(const std::string& text) { AppendInternal(text, true); }
86
AppendNoNewLine(const std::string & text)87 void CodeWriter::AppendNoNewLine(const std::string& text) {
88 AppendInternal(text, false);
89 }
90
AppendInternal(const std::string & text,bool newline)91 void CodeWriter::AppendInternal(const std::string& text, bool newline) {
92 // Prefix indent
93 if ((buffer_.empty() // nothing in the buffer
94 || buffer_.back() == '\n') // is on new line
95 // is writing on current line
96 && (!text.empty() && text[0] != '\n' && text[0] != '\r')) {
97 buffer_.append(GenerateIndent());
98 }
99 // State machine variables
100 bool in_token = false;
101 int i = 0;
102 // Rough memory reserve
103 buffer_.reserve(buffer_.size() + text.size());
104 std::string token_buffer;
105 // A simple LL1 analysis
106 while (i < text.size()) {
107 char cur = text[i];
108 char cur_next = i == text.size() - 1 ? '\0' : text[i + 1]; // Set guardian
109 if (!in_token) {
110 if (cur == '{' && cur_next == '{') { // Enter token
111 in_token = true;
112 i += 2;
113 } else if (cur == '\n') { // We need to apply global indent here
114 buffer_.push_back(cur);
115 if (cur_next != '\0' && cur_next != '\n' && cur_next != '\r') {
116 buffer_.append(GenerateIndent());
117 }
118 i += 1;
119 } else {
120 buffer_.push_back(cur);
121 i += 1;
122 }
123 } else {
124 if (cur == '}' && cur_next == '}') { // Close token
125 in_token = false;
126 const auto value = GetTokenValue(token_buffer);
127 buffer_.append(value);
128 token_buffer.clear();
129 i += 2;
130 } else {
131 token_buffer.push_back(cur);
132 i += 1;
133 }
134 }
135 }
136 if (!token_buffer.empty()) {
137 // Typically only Code Generator's call this function. It's
138 // their duty to make sure the code (or template) has valid syntax, and
139 // unclosed "{{...}}" implicits severe error in the template.
140 err_->Error("Internal: Invalid template: {{token}} is not closed.");
141 }
142 if (newline) {
143 buffer_.push_back('\n');
144 }
145 }
146
NewLine()147 void CodeWriter::NewLine() { Append(""); }
148
Backspace(int n)149 void CodeWriter::Backspace(int n) {
150 buffer_.resize(buffer_.size() > n ? buffer_.size() - n : 0);
151 }
152
ToString() const153 std::string CodeWriter::ToString() const { return buffer_; }
154
IsStreamEmpty() const155 bool CodeWriter::IsStreamEmpty() const { return buffer_.empty(); }
156
Clear()157 void CodeWriter::Clear() {
158 buffer_.clear();
159 value_map_.clear();
160 indent_ = 0;
161 }
162
SnakeCaseToCamelCase(const std::string & s)163 std::string SnakeCaseToCamelCase(const std::string& s) {
164 std::string t;
165 t.reserve(s.length());
166 size_t i = 0;
167 // Note: Use simple string += for simplicity.
168 bool cap = false;
169 while (i < s.size()) {
170 const char c = s[i++];
171 if (c == '_') {
172 cap = true;
173 } else if (cap) {
174 t += toupper(c);
175 cap = false;
176 } else {
177 t += c;
178 }
179 }
180 return t;
181 }
182
JoinPath(const std::string & a,const std::string & b)183 std::string JoinPath(const std::string& a, const std::string& b) {
184 if (a.empty()) return b;
185 std::string a_fixed = a;
186 if (!a_fixed.empty() && a_fixed.back() == '/') a_fixed.pop_back();
187 std::string b_fixed = b;
188 if (!b_fixed.empty() && b_fixed.front() == '/') b_fixed.erase(0, 1);
189 return a_fixed + "/" + b_fixed;
190 }
191
192 } // namespace codegen
193 } // namespace support
194 } // namespace tflite
195