github.com/razvanm/vanadium-go-1.3@v0.0.0-20160721203343-4a65068e5915/src/runtime/ppapi/mkzfile.py (about)

     1  #!/usr/bin/env python
     2  # -*- coding: utf-8 -*-
     3  
     4  # Copyright 2014 The Go Authors. All rights reserved.
     5  # Use of this source code is governed by a BSD-style
     6  # license that can be found in the LICENSE file.
     7  
     8  """This is a code generator.  It reads configuration information that
     9  describes types, functions, and enums; and it uses the info to expand
    10  code templates.  The config contains lines of the following form:
    11  
    12  Enumeration: <type> is the Go type name, and <pattern> is a regular expression
    13  for finding constants in the PPAPI header files.
    14  
    15    //enum <type> <pattern>
    16  
    17  For example, the following defines a type Error containing all constants
    18  matching the pattern PP_ERROR_.
    19  
    20    //enum Error PP_ERROR_
    21  
    22  Types: define a type <type> with a machine representation <mtype>.  The machine
    23  types includes the usual integer types int32, int64; floats float32 and float64;
    24  pointers *; and structs with size in bytes struct[16].
    25  
    26    //type <type> <mtype>
    27  
    28    //type pp_Bool int32
    29    //type pp_Var  struct[16]
    30  
    31  Callbacks: specify a callback with the given parameters.
    32  
    33    //callback f(a1 t1, ..., aN, tN) returnType
    34  
    35    //callback ppp_did_create(instance pp_Instance, argc uint32, argn **byte, argv **byte) pp_Bool
    36  
    37  Functions: specify a function call with the given parameters.  This is a call
    38  into PPAPI.
    39  
    40    //func f(a1 t1, ..., aN, tN) returnType = interface[index]
    41  
    42    //func ppb_var_from_utf8(data *byte, len uint32) pp_Var = PPB_VAR[2]
    43  
    44  To run this, you will need a copy of the NaCl SDK, downloadable from
    45  https://developer.chrome.com/native-client/sdk/download.
    46  
    47  Let $NACL_SDK be the location of the NaCl SDK, and $PEPPER be the version of
    48  pepper (e.g. pepper_34).  This script scans the header files in that package and
    49  performs a template expansion.  The following command can be used to extract the
    50  PPAPI constants.
    51  
    52  ./mkzfile.py --include=$NACL_SDK/$PEPPER/include/ppapi/c consts.got > zconsts.go
    53  """
    54  
    55  __author__ = 'jyh@google.com (Jason Hickey)'
    56  
    57  import argparse
    58  import fileinput
    59  import glob
    60  import re
    61  import string
    62  import sys
    63  from jinja2 import Environment, Template
    64  
    65  parser = argparse.ArgumentParser(description='Process some integers.')
    66  parser.add_argument('--include', nargs=1, help='PPAPI include directory from the NaCl SDK')
    67  parser.add_argument('template', nargs='+', help='template files')
    68  
    69  # Size/align for machine kinds.
    70  sizes = {
    71      'void': (0, 0),
    72      'int32': (4, 4),
    73      'int64': (8, 8),
    74      'float32': (4, 4),
    75      'float64': (8, 8),
    76      '*': (4, 4),
    77      }
    78  
    79  # align aligns an address to the next larger boundary.
    80  def align(addr, n):
    81    return (addr + n - 1) & ~(n - 1)
    82  
    83  # sizeof returns a triple of the kind name, the number of bytes needed to
    84  # represent a value of that kind, and the alignment.
    85  def sizeof(kind):
    86    if kind.startswith('*'):
    87      return ('*', 4, 4)
    88    elif kind.startswith('struct'):
    89      m = re.search(r'struct\[(\w+)(,(\w+))?\]', kind)
    90      if m == None:
    91        raise Exception("Malformed struct type: " + kind)
    92      a = 4
    93      if m.group(3) != None:
    94        a = int(m.group(3))
    95      return ('struct', int(m.group(1)), a)
    96    else:
    97      n, a = sizes[kind]
    98      return (kind, n, a)
    99  
   100  # Type represents a type, where <name> is the name of the type, <kind> is the
   101  # machine kind, and <size> is the sizeof that type.
   102  class Type:
   103    """Type represents base type"""
   104    def __init__(self, name, kind, builtin=False):
   105      self.name = name
   106      self.kind, self.size, self.align = sizeof(kind)
   107      self.builtin = builtin
   108  
   109  # Arg represents a function parameter, including the name and the type.
   110  class Arg:
   111    """Argument"""
   112    def __init__(self, n, ty):
   113      self.name = n
   114      self.type = ty
   115  
   116  # Func represents a function declaration, declared in the input file.
   117  class Func:
   118    """Func represents a function description"""
   119    def __init__(self, name, args, result, interface, index):
   120      self.name = name
   121      self.args = args
   122      self.result = result
   123      self.goresult = result.name
   124      self.interface = interface
   125      self.index = index
   126      self.structReturn = False
   127  
   128  # builtin_types is a dictionary containing the predefined base types.
   129  def builtin_types():
   130    return {
   131      '':        Type('void', 'void', True),
   132      'void':    Type('void', 'void', True),
   133      'bool':    Type('bool', 'int32', True),
   134      'int16':   Type('int16', 'int32', True),
   135      'uint16':  Type('uint16', 'int32', True),
   136      'int32':   Type('int32', 'int32', True),
   137      'uint32':  Type('uint32', 'int32', True),
   138      'int64':   Type('int64', 'int64', True),
   139      'uint64':  Type('uint64', 'int64', True),
   140      'uintptr': Type('uintptr', 'int32', True),
   141      'float32': Type('float32', 'float32', True),
   142      'float64': Type('float64', 'float64', True),
   143      }
   144  
   145  class Processor:
   146    """Processor scans the header and template files and performs the template expansion"""
   147  
   148    # Const definitions scanned from the include files.
   149    consts = {}
   150  
   151    # Scanned configuration info.
   152    enums = {}
   153    types = builtin_types()
   154    callbacks = []
   155    functions = []
   156  
   157    # typeof returns the type for a name.
   158    def typeof(self, ty):
   159      if ty.startswith('*'):
   160        return Type(ty, '*')
   161      if ty not in self.types:
   162        raise Exception("undefined type: " + ty)
   163      return self.types[ty]
   164  
   165    # parseArg parses an argument into a pair (name, type).
   166    def parseArg(self, arg, line):
   167      parts = arg.split()
   168      if len(parts) != 2:
   169        raise Exception("Argument should have the form 'name type': " + arg + " : " + line)
   170      return Arg(parts[0], self.typeof(parts[1]))
   171  
   172    # parseArgs splits the parameter list for a function.
   173    def parseArgs(self, args, line):
   174      if args == "":
   175        return []
   176      return [self.parseArg(arg.strip(), line) for arg in string.split(args, ',')]
   177  
   178    # framesize returns the total size for all arguments.
   179    def framesize(self, func):
   180      size = 0
   181      for arg in func.args:
   182        if arg.type.align > 0:
   183          size = align(size, arg.type.align)
   184        size += arg.type.size
   185      return size
   186  
   187    # updateStructReturn converts the function definition when the return value is
   188    # a struct that is larger than 8 bytes.  If so, the result is passed by
   189    # reference as the first arg.
   190    def updateStructReturn(self, func):
   191      if func.result.kind == 'struct':
   192        func.args.insert(0, Arg('return_struct', Type('*' + func.result.name, '*')))
   193        func.goresult = 'void'
   194        func.structReturn = True
   195  
   196    # findEnumType finds the type for a const.
   197    def findEnumType(self, c):
   198      for r, ty in self.enums.items():
   199        m = re.match(r, c)
   200        if m:
   201          return ty
   202  
   203    # scanIncludeFiles collect the constants from the include files.
   204    def scanIncludeFiles(self, files):
   205      for line in fileinput.input(files):
   206        m = re.search(r'(PP\w*)\s*=\s*([^,\n\r]+),?\s*$', line)
   207        if m:
   208          self.consts[m.group(1)] = m.group(2)
   209  
   210    # scanArch reads the architecture from the files.
   211    def scanArch(self, files):
   212      for line in fileinput.input(files):
   213        if line.startswith('//sizeof'):
   214          # //enum regex typename
   215          m = re.search(r'^//sizeof\s+(\w+)\s+(\d+)\s+(\d+)$', line)
   216          if m == None:
   217            raise Exception('Malformed //sizeof: ' + line)
   218          sizes[m.group(1)] = (int(m.group(2)), int(m.group(3)))
   219      self.types = builtin_types()
   220  
   221    # scanConfig reads the configuration from a set of files.
   222    def scanConfig(self, files):
   223      for line in fileinput.input(files):
   224        if line.startswith('//enum'):
   225          # //enum regex typename
   226          m = re.search(r'^//enum\s+(\w+)\s+(.*)$', line)
   227          if m == None:
   228            raise Exception('Malformed //enum: ' + line)
   229          self.enums[m.group(2)] = m.group(1)
   230          self.types[m.group(1)] = Type(m.group(1), 'int32')
   231  
   232        elif line.startswith('//type'):
   233          # //type name name
   234          m = re.search(r'^//type\s+(\w+)\s+([][,\w*]+)\s*(\w*)\s*$', line)
   235          if m == None:
   236            raise Exception('Malformed //type: ' + line)
   237          self.types[m.group(1)] = Type(m.group(1), m.group(2), m.group(3) <> '')
   238  
   239        elif line.startswith('//func'):
   240          # //func name(a1 t1, a2 t2, ..., aN tN) ty = x[i]
   241          m = re.search(r'^//func\s+(\w+)[(]([\w\s,*]*)[)]\s*([\w*]*)\s*=\s*(\w+)\s*\[\s*(\w+)\s*\]\s*$', line)
   242          if m == None:
   243            raise Exception("Malformed function: " + line)
   244          func = Func(m.group(1), self.parseArgs(m.group(2), line), self.typeof(m.group(3)), m.group(4), m.group(5))
   245          self.updateStructReturn(func)
   246          self.functions.append(func)
   247  
   248        elif line.startswith('//callback'):
   249          # //callback name(a1 t1, a2 t2, ..., aN tN) ty
   250          m = re.search(r'^//callback\s+(\w+)[(]([\w\s,*]*)[)]\s*([\w*]*)\s*$', line)
   251          if m == None:
   252            raise Exception("Malformed function: " + line)
   253          func = Func(m.group(1), self.parseArgs(m.group(2), line), self.typeof(m.group(3)), None, None)
   254          self.callbacks.append(func)
   255  
   256    # compile returns the dictionary used by the templates.
   257    def compile(self):
   258      enums = []
   259      for ty in self.enums.values():
   260        enums.append(ty)
   261      consts = []
   262      for c, v in self.consts.items():
   263        ty = self.findEnumType(c)
   264        if ty:
   265          consts.append((c, ty, v))
   266      return { 'callbacks': self.callbacks,
   267               'functions': self.functions,
   268               'consts': consts,
   269               'enums': enums,
   270               'types': self.types,
   271             }
   272  
   273    # printTemplates expands and prints all template files.
   274    def printTemplates(self, templates):
   275      state = self.compile()
   276      for file in templates:
   277        text = ''
   278        with open(file) as f:
   279          text = f.read()
   280        text = text.decode('utf-8')
   281        tmpl = Template(text)
   282        tmpl.globals['framesize'] = self.framesize
   283        tmpl.globals['max'] = max
   284        tmpl.globals['align'] = align
   285        text = tmpl.render(**state)
   286        print text.encode('utf-8')
   287  
   288  # Preprocess the input files.
   289  def main():
   290    args = parser.parse_args()
   291    print '//', string.join(sys.argv)
   292    print '// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT'
   293    print
   294    p = Processor()
   295    if args.include != None:
   296      for dir in args.include:
   297        p.scanIncludeFiles(glob.glob(dir + '/*.h'))
   298    p.scanArch(args.template)
   299    p.scanConfig(args.template)
   300    p.printTemplates(args.template)
   301  
   302  # This is the standard boilerplate that calls the main() function.
   303  if __name__ == '__main__':
   304    main()