1# Copyright 2016 The Gemmlowp 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"""CC code emitter.
15
16Used by generators to programatically prepare C++ code. Contains some simple
17tools that allow generating nicely indented code and do basic correctness
18checking.
19"""
20
21
22class Error(Exception):
23  """Module level error."""
24
25
26class NamespaceError(Error):
27  """Invalid namespace operation."""
28
29
30class HeaderError(Error):
31  """Invalid cc header structure."""
32
33
34class ClassError(Error):
35  """Invalid class syntax."""
36
37
38class CCEmitter(object):
39  """Emits c++ code."""
40
41  def __init__(self, debug=False):
42    self.indent = ''
43    self.debug = debug
44    self.namespaces = []
45    self.classes = []
46    self.header_name = None
47
48  def PushIndent(self):
49    self.indent += '  '
50
51  def PopIndent(self):
52    self.indent = self.indent[:-2]
53
54  def EmitIndented(self, what):
55    print(self.indent + what)
56
57  def EmitNewline(self):
58    print('')
59
60  def EmitPreprocessor1(self, op, param):
61    print('#%s %s' % (op, param))
62
63  def EmitPreprocessor(self, op):
64    print('#%s' % op)
65
66  def EmitInclude(self, include):
67    self.EmitPreprocessor1('include', include)
68
69  def EmitAssign(self, variable, value):
70    self.EmitBinaryOp(variable, '=', value)
71
72  def EmitAssignIncrement(self, variable, value):
73    self.EmitBinaryOp(variable, '+=', value)
74
75  def EmitBinaryOp(self, operand_1, op, operand_2):
76    self.EmitCode('%s %s %s' % (operand_1, op, operand_2))
77
78  def EmitCall(self, function, params=None):
79    if not params:
80      params = []
81    self.EmitCode('%s(%s)' % (function, ', '.join(map(str, params))))
82
83  def EmitCode(self, code):
84    self.EmitIndented('%s;' % code)
85
86  def EmitCodeNoSemicolon(self, code):
87    self.EmitIndented('%s' % code)
88
89  def EmitDeclare(self, decl_type, name, value):
90    self.EmitAssign('%s %s' % (decl_type, name), value)
91
92  def EmitAssert(self, assert_expression):
93    if self.debug:
94      self.EmitCall1('assert', assert_expression)
95
96  def EmitHeaderBegin(self, header_name, includes=None):
97    if includes is None:
98      includes = []
99    if self.header_name:
100      raise HeaderError('Header already defined.')
101    self.EmitPreprocessor1('ifndef', (header_name + '_H_').upper())
102    self.EmitPreprocessor1('define', (header_name + '_H_').upper())
103    self.EmitNewline()
104    if includes:
105      for include in includes:
106        self.EmitInclude(include)
107      self.EmitNewline()
108    self.header_name = header_name
109
110  def EmitHeaderEnd(self):
111    if not self.header_name:
112      raise HeaderError('Header undefined.')
113    self.EmitPreprocessor1('endif',
114                           ' // %s' % (self.header_name + '_H_').upper())
115    self.header_name = None
116
117  def EmitMemberFunctionBegin(self, class_name, class_template_params,
118                              class_specializations, function_name,
119                              function_params, return_type):
120    """Emit member function of a template/specialized class."""
121    if class_template_params or class_specializations:
122      self.EmitIndented('template<%s>' % ', '.join(class_template_params))
123
124    if class_specializations:
125      class_name += '<%s>' % ', '.join(map(str, class_specializations))
126
127    self.EmitIndented('%s %s::%s(%s) {' % (
128        return_type, class_name, function_name,
129        ', '.join(['%s %s' % (t, n) for (t, n) in function_params])))
130    self.PushIndent()
131
132  def EmitFunctionBegin(self, function_name, params, return_type):
133    self.EmitIndented('%s %s(%s) {' %
134                      (return_type, function_name,
135                       ', '.join(['%s %s' % (t, n) for (t, n) in params])))
136    self.PushIndent()
137
138  def EmitFunctionEnd(self):
139    self.PopIndent()
140    self.EmitIndented('}')
141    self.EmitNewline()
142
143  def EmitClassBegin(self, class_name, template_params, specializations,
144                     base_classes):
145    """Emit class block header."""
146    self.classes.append(class_name)
147    if template_params or specializations:
148      self.EmitIndented('template<%s>' % ', '.join(template_params))
149
150    class_name_extended = class_name
151    if specializations:
152      class_name_extended += '<%s>' % ', '.join(map(str, specializations))
153    if base_classes:
154      class_name_extended += ' : ' + ', '.join(base_classes)
155    self.EmitIndented('class %s {' % class_name_extended)
156    self.PushIndent()
157
158  def EmitClassEnd(self):
159    if not self.classes:
160      raise ClassError('No class on stack.')
161    self.classes.pop()
162    self.PopIndent()
163    self.EmitIndented('};')
164    self.EmitNewline()
165
166  def EmitAccessModifier(self, modifier):
167    if not self.classes:
168      raise ClassError('No class on stack.')
169    self.PopIndent()
170    self.EmitIndented(' %s:' % modifier)
171    self.PushIndent()
172
173  def EmitNamespaceBegin(self, namespace):
174    self.EmitCodeNoSemicolon('namespace %s {' % namespace)
175    self.namespaces.append(namespace)
176
177  def EmitNamespaceEnd(self):
178    if not self.namespaces:
179      raise NamespaceError('No namespace on stack.')
180    self.EmitCodeNoSemicolon('}  // namespace %s' % self.namespaces.pop())
181
182  def EmitComment(self, comment):
183    self.EmitIndented('// ' + comment)
184
185  def EmitOpenBracket(self, pre_bracket=None):
186    if pre_bracket:
187      self.EmitIndented('%s {' % pre_bracket)
188    else:
189      self.EmitIndented('{')
190    self.PushIndent()
191
192  def EmitCloseBracket(self):
193    self.PopIndent()
194    self.EmitIndented('}')
195
196  def EmitSwitch(self, switch):
197    self.EmitOpenBracket('switch (%s)' % switch)
198
199  def EmitSwitchEnd(self):
200    self.EmitCloseBracket()
201
202  def EmitCase(self, value):
203    self.EmitCodeNoSemicolon('case %s:' % value)
204
205  def EmitBreak(self):
206    self.EmitCode('break')
207
208  def EmitIf(self, condition):
209    self.EmitOpenBracket('if (%s)' % condition)
210
211  def EmitElse(self):
212    self.PopIndent()
213    self.EmitCodeNoSemicolon('} else {')
214    self.PushIndent()
215
216  def EmitEndif(self):
217    self.EmitCloseBracket()
218
219  def Scope(self, scope, value):
220    return '%s::%s' % (scope, value)
221