github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-tools-src/grumpy_tools/compiler/util.py (about) 1 # coding=utf-8 2 3 # Copyright 2016 Google Inc. All Rights Reserved. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 """Utilities for generating Go code.""" 18 19 from __future__ import unicode_literals 20 21 import codecs 22 import contextlib 23 import string 24 import StringIO 25 import textwrap 26 27 try: 28 string.letters 29 except AttributeError: 30 string.letters = string.ascii_letters 31 32 _SIMPLE_CHARS = set(string.digits + string.letters + string.punctuation + " ") 33 _ESCAPES = {'\t': r'\t', '\r': r'\r', '\n': r'\n', '"': r'\"', '\\': r'\\'} 34 35 36 # This is the max length of a direct allocation tuple supported by the runtime. 37 # This should match the number of specializations found in tuple.go. 38 MAX_DIRECT_TUPLE = 6 39 40 41 class CompileError(Exception): 42 43 def __init__(self, node, msg): 44 if hasattr(node, 'lineno'): 45 msg = 'line {}: {}'.format(node.lineno, msg) 46 super(CompileError, self).__init__(msg) 47 48 49 class ParseError(CompileError): 50 pass 51 52 53 class ImportError(CompileError): # pylint: disable=redefined-builtin 54 pass 55 56 57 class LateFutureError(ImportError): 58 59 def __init__(self, node): 60 msg = 'from __future__ imports must occur at the beginning of the file' 61 super(LateFutureError, self).__init__(node, msg) 62 63 64 class Writer(object): 65 """Utility class for writing blocks of Go code to a file-like object.""" 66 67 def __init__(self, out=None): 68 self.out = codecs.getwriter('utf8')(out or StringIO.StringIO()) 69 self.indent_level = 0 70 71 def getvalue(self): 72 return self.out.getvalue().decode('utf8') 73 74 @contextlib.contextmanager 75 def indent_block(self, n=1): 76 """A context manager that indents by n on entry and dedents on exit.""" 77 self.indent(n) 78 yield 79 self.dedent(n) 80 81 def write(self, output): 82 for line in output.split('\n'): 83 if line: 84 self.out.write(''.join(('\t' * self.indent_level, line, '\n'))) 85 86 def write_block(self, block_, body): 87 """Outputs the boilerplate necessary for code blocks like functions. 88 89 Args: 90 block_: The Block object representing the code block. 91 body: String containing Go code making up the body of the code block. 92 """ 93 self.write('for ; πF.State() >= 0; πF.PopCheckpoint() {') 94 with self.indent_block(): 95 self.write('switch πF.State() {') 96 self.write('case 0:') 97 for checkpoint in block_.checkpoints: 98 self.write_tmpl('case $state: goto Label$state', state=checkpoint) 99 self.write('default: panic("unexpected function state")') 100 self.write('}') 101 # Assume that body is aligned with goto labels. 102 with self.indent_block(-1): 103 self.write(body) 104 self.write('}') 105 106 def write_label(self, label): 107 with self.indent_block(-1): 108 self.write('Label{}:'.format(label)) 109 110 def write_py_context(self, lineno, line): 111 self.write_tmpl('// line $lineno: $line', lineno=lineno, line=line) 112 113 def write_tmpl(self, tmpl, **kwargs): 114 self.write(string.Template(tmpl).substitute(kwargs)) 115 116 def write_checked_call2(self, result, call, *args, **kwargs): 117 return self.write_tmpl(textwrap.dedent("""\ 118 if $result, πE = $call; πE != nil { 119 \tcontinue 120 }"""), result=result.name, call=call.format(*args, **kwargs)) 121 122 def write_checked_call1(self, call, *args, **kwargs): 123 return self.write_tmpl(textwrap.dedent("""\ 124 if πE = $call; πE != nil { 125 \tcontinue 126 }"""), call=call.format(*args, **kwargs)) 127 128 def write_temp_decls(self, block_): 129 all_temps = block_.free_temps | block_.used_temps 130 for temp in sorted(all_temps, key=lambda t: t.name): 131 self.write('var {0} {1}\n_ = {0}'.format(temp.name, temp.type_)) 132 133 def indent(self, n=1): 134 self.indent_level += n 135 136 def dedent(self, n=1): 137 self.indent_level -= n 138 139 140 def go_str(value): 141 """Returns value as a valid Go string literal.""" 142 io = StringIO.StringIO() 143 io.write('"') 144 for c in value: 145 if c in _ESCAPES: 146 io.write(_ESCAPES[c]) 147 elif c in _SIMPLE_CHARS: 148 io.write(c) 149 else: 150 io.write(r'\x{:02x}'.format(ord(c))) 151 io.write('"') 152 return io.getvalue() 153 154 155 def adjust_local_name(name): 156 """Returns a Go identifier for the given Python variable name.""" 157 return 'µ' + name