# Copyright 2016 The Gemmlowp Authors. All rights reserved. # # 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. """CC code emitter. Used by generators to programatically prepare C++ code. Contains some simple tools that allow generating nicely indented code and do basic correctness checking. """ class Error(Exception): """Module level error.""" class NamespaceError(Error): """Invalid namespace operation.""" class HeaderError(Error): """Invalid cc header structure.""" class ClassError(Error): """Invalid class syntax.""" class CCEmitter(object): """Emits c++ code.""" def __init__(self, debug=False): self.indent = '' self.debug = debug self.namespaces = [] self.classes = [] self.header_name = None def PushIndent(self): self.indent += ' ' def PopIndent(self): self.indent = self.indent[:-2] def EmitIndented(self, what): print(self.indent + what) def EmitNewline(self): print('') def EmitPreprocessor1(self, op, param): print('#%s %s' % (op, param)) def EmitPreprocessor(self, op): print('#%s' % op) def EmitInclude(self, include): self.EmitPreprocessor1('include', include) def EmitAssign(self, variable, value): self.EmitBinaryOp(variable, '=', value) def EmitAssignIncrement(self, variable, value): self.EmitBinaryOp(variable, '+=', value) def EmitBinaryOp(self, operand_1, op, operand_2): self.EmitCode('%s %s %s' % (operand_1, op, operand_2)) def EmitCall(self, function, params=None): if not params: params = [] self.EmitCode('%s(%s)' % (function, ', '.join(map(str, params)))) def EmitCode(self, code): self.EmitIndented('%s;' % code) def EmitCodeNoSemicolon(self, code): self.EmitIndented('%s' % code) def EmitDeclare(self, decl_type, name, value): self.EmitAssign('%s %s' % (decl_type, name), value) def EmitAssert(self, assert_expression): if self.debug: self.EmitCall1('assert', assert_expression) def EmitHeaderBegin(self, header_name, includes=None): if includes is None: includes = [] if self.header_name: raise HeaderError('Header already defined.') self.EmitPreprocessor1('ifndef', (header_name + '_H_').upper()) self.EmitPreprocessor1('define', (header_name + '_H_').upper()) self.EmitNewline() if includes: for include in includes: self.EmitInclude(include) self.EmitNewline() self.header_name = header_name def EmitHeaderEnd(self): if not self.header_name: raise HeaderError('Header undefined.') self.EmitPreprocessor1('endif', ' // %s' % (self.header_name + '_H_').upper()) self.header_name = None def EmitMemberFunctionBegin(self, class_name, class_template_params, class_specializations, function_name, function_params, return_type): """Emit member function of a template/specialized class.""" if class_template_params or class_specializations: self.EmitIndented('template<%s>' % ', '.join(class_template_params)) if class_specializations: class_name += '<%s>' % ', '.join(map(str, class_specializations)) self.EmitIndented('%s %s::%s(%s) {' % ( return_type, class_name, function_name, ', '.join(['%s %s' % (t, n) for (t, n) in function_params]))) self.PushIndent() def EmitFunctionBegin(self, function_name, params, return_type): self.EmitIndented('%s %s(%s) {' % (return_type, function_name, ', '.join(['%s %s' % (t, n) for (t, n) in params]))) self.PushIndent() def EmitFunctionEnd(self): self.PopIndent() self.EmitIndented('}') self.EmitNewline() def EmitClassBegin(self, class_name, template_params, specializations, base_classes): """Emit class block header.""" self.classes.append(class_name) if template_params or specializations: self.EmitIndented('template<%s>' % ', '.join(template_params)) class_name_extended = class_name if specializations: class_name_extended += '<%s>' % ', '.join(map(str, specializations)) if base_classes: class_name_extended += ' : ' + ', '.join(base_classes) self.EmitIndented('class %s {' % class_name_extended) self.PushIndent() def EmitClassEnd(self): if not self.classes: raise ClassError('No class on stack.') self.classes.pop() self.PopIndent() self.EmitIndented('};') self.EmitNewline() def EmitAccessModifier(self, modifier): if not self.classes: raise ClassError('No class on stack.') self.PopIndent() self.EmitIndented(' %s:' % modifier) self.PushIndent() def EmitNamespaceBegin(self, namespace): self.EmitCodeNoSemicolon('namespace %s {' % namespace) self.namespaces.append(namespace) def EmitNamespaceEnd(self): if not self.namespaces: raise NamespaceError('No namespace on stack.') self.EmitCodeNoSemicolon('} // namespace %s' % self.namespaces.pop()) def EmitComment(self, comment): self.EmitIndented('// ' + comment) def EmitOpenBracket(self, pre_bracket=None): if pre_bracket: self.EmitIndented('%s {' % pre_bracket) else: self.EmitIndented('{') self.PushIndent() def EmitCloseBracket(self): self.PopIndent() self.EmitIndented('}') def EmitSwitch(self, switch): self.EmitOpenBracket('switch (%s)' % switch) def EmitSwitchEnd(self): self.EmitCloseBracket() def EmitCase(self, value): self.EmitCodeNoSemicolon('case %s:' % value) def EmitBreak(self): self.EmitCode('break') def EmitIf(self, condition): self.EmitOpenBracket('if (%s)' % condition) def EmitElse(self): self.PopIndent() self.EmitCodeNoSemicolon('} else {') self.PushIndent() def EmitEndif(self): self.EmitCloseBracket() def Scope(self, scope, value): return '%s::%s' % (scope, value)