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