• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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