github.com/cloudwego/frugal@v0.1.15/hir2go.py (about)

     1  #!/usr/bin/env python3
     2  # -*- coding: utf-8 -*-
     3  
     4  import string
     5  
     6  from typing import List
     7  from typing import Tuple
     8  from typing import Union
     9  from dataclasses import dataclass
    10  
    11  @dataclass
    12  class Imm:
    13      val: int
    14  
    15      def __str__(self) -> str:
    16          return '%d' % self.val
    17  
    18      def __repr__(self) -> str:
    19          return '$%d' % self.val
    20  
    21  @dataclass
    22  class Reg:
    23      id: int
    24      ptr: bool
    25  
    26      def __str__(self) -> str:
    27          return 'hir.%c%s' % (
    28              'P' if self.ptr else 'R',
    29              self.id if self.id >= 0 else 'n' if self.ptr else 'z'
    30          )
    31  
    32      def __repr__(self) -> str:
    33          if self.id < 0:
    34              return '%nil' if self.ptr else '%z'
    35          else:
    36              return '%%%c%d' % ('p' if self.ptr else 'r', self.id)
    37  
    38  @dataclass
    39  class Mem:
    40      offs: int
    41      base: Reg
    42  
    43      def __str__(self) -> str:
    44          return '%s, %d' % (self.base, self.offs)
    45  
    46      def __repr__(self) -> str:
    47          return '%d(%s)' % (self.offs, self.base)
    48  
    49  @dataclass
    50  class Func:
    51      addr: int
    52      name: str
    53  
    54      def __str__(self) -> str:
    55          return 'hir.LookupCallByName("%s")' % self.name
    56  
    57      def __repr__(self) -> str:
    58          if not self.name:
    59              return '*%#x' % self.addr
    60          else:
    61              return '*%#x[%s]' % (self.addr, self.name)
    62  
    63  @dataclass
    64  class Label:
    65      name: str
    66  
    67      def __str__(self) -> str:
    68          return '"%s"' % self.name
    69  
    70      def __repr__(self) -> str:
    71          return self.name
    72  
    73  @dataclass
    74  class RegSeq:
    75      regs: List[Reg]
    76  
    77      def __repr__(self) -> str:
    78          return '{%s}' % ', '.join(str(v) for v in self.regs)
    79  
    80      def to_string(self, is_args: bool) -> str:
    81          return ''.join(
    82              '.\n%6c%c%-5d(%s)' % (' ', 'A' if is_args else 'R', i, r)
    83              for i, r in enumerate(self.regs)
    84          )
    85  
    86  @dataclass
    87  class SwitchCase:
    88      val: Imm
    89      ref: Label
    90  
    91      def __repr__(self) -> str:
    92          return 'case %s: %s' % (self.val, self.ref)
    93  
    94  @dataclass
    95  class SwitchCaseSeq:
    96      cases: List[SwitchCase]
    97  
    98      def __str__(self) -> str:
    99          tab = {
   100              p.val.val: str(p.ref)
   101              for p in self.cases
   102          }
   103          return '[]string { %s }' % (', '.join(
   104              tab.get(i, '""')
   105              for i in range(max(tab) + 1)
   106          ))
   107  
   108      def __repr__(self) -> str:
   109          return '{%s}' % ', '.join('%s' % v for v in self.cases)
   110  
   111  Operand = Union[
   112      Imm,
   113      Reg,
   114      Mem,
   115      Func,
   116      Label,
   117      RegSeq,
   118      SwitchCase,
   119      SwitchCaseSeq,
   120  ]
   121  
   122  def parse_int(arg: str) -> Tuple[int, str]:
   123      j = len(arg)
   124      for i, cc in enumerate(arg):
   125          if cc not in (string.hexdigits + 'obxOBX+-'):
   126              j = i
   127              break
   128      if not j:
   129          raise SyntaxError('invalid integer near ' + repr(arg))
   130      val = arg[:j]
   131      arg = arg[j:].strip()
   132      if val.startswith('0b') or val.startswith('0B'):
   133          return int(val, 2), arg
   134      elif val.startswith('0x') or val.startswith('0X'):
   135          return int(val, 16), arg
   136      elif val.startswith('0'):
   137          return int(val, 8), arg
   138      else:
   139          return int(val), arg
   140  
   141  def parse_arg(arg: str) -> Tuple[Operand, str]:
   142      if arg.startswith('$'):
   143          ret, arg = parse_int(arg[1:])
   144          return Imm(ret), arg
   145      elif arg.startswith('*'):
   146          name = ''
   147          addr, arg = parse_int(arg[1:])
   148          if arg and arg[0] == '[':
   149              end = arg.find(']', 1)
   150              if end < 0:
   151                  raise SyntaxError('invalid function name near ' + repr(arg))
   152              name = arg[1:end]
   153              arg = arg[end + 1:].strip()
   154          return Func(addr, name), arg
   155      elif arg.startswith('%'):
   156          if arg.startswith('%z'):
   157              return Reg(-1, False), arg[2:].strip()
   158          if arg.startswith('%nil'):
   159              return Reg(-1, True), arg[4:].strip()
   160          if len(arg) < 3:
   161              raise SyntaxError('invalid register near ' + repr(arg))
   162          if arg[1] == 'p':
   163              ptr = True
   164          elif arg[1] == 'r':
   165              ptr = False
   166          else:
   167              raise SyntaxError('invalid register near ' + repr(arg))
   168          j = len(arg)
   169          for i, cc in enumerate(arg[2:], 2):
   170              if not cc.isdigit():
   171                  j = i
   172                  break
   173          if j <= 2:
   174              raise SyntaxError('invalid register near ' + repr(arg))
   175          else:
   176              return Reg(int(arg[2:j]), ptr), arg[j:].strip()
   177      elif arg.startswith('{'):
   178          ret = []
   179          kind = None
   180          rems = arg[1:].strip()
   181          while not rems.startswith('}'):
   182              item, rems = parse_arg(rems)
   183              ret.append(item)
   184              if kind is None:
   185                  kind = type(item)
   186              elif not isinstance(item, kind):
   187                  raise SyntaxError('inconsistent operand list near ' + repr(rems))
   188              if not rems.startswith('}'):
   189                  if rems.startswith(','):
   190                      rems = rems[1:].strip()
   191                  else:
   192                      raise SyntaxError('"," expected between operand list items')
   193          if not rems.startswith('}'):
   194              raise SyntaxError('invalid operand list near ' + repr(arg))
   195          elif kind is SwitchCase:
   196              return SwitchCaseSeq(ret), rems[1:].strip()
   197          elif kind is Reg:
   198              if len(ret) > 8:
   199                  raise SyntaxError('too many registers near ' + repr(arg))
   200              else:
   201                  return RegSeq(ret), rems[1:].strip()
   202          else:
   203              raise SyntaxError('invalid operand list item type near ' + repr(arg))
   204      elif arg.startswith('case '):
   205          arg = arg[5:].strip()
   206          imm, rem = parse_arg(arg)
   207          if not isinstance(imm, Imm):
   208              raise SyntaxError('immediate value expected near ' + repr(arg))
   209          if not rem or rem[0] != ':':
   210              raise SyntaxError('":" expected near ' + repr(rem))
   211          rem = rem[1:].strip()
   212          ref, arg = parse_arg(rem)
   213          if not isinstance(ref, Label):
   214              raise SyntaxError('label reference expected near ' + repr(arg))
   215          else:
   216              return SwitchCase(imm, ref), arg
   217      elif arg and arg[0].isdigit():
   218          off, arg = parse_int(arg)
   219          if not arg or arg[0] != '(':
   220              raise SyntaxError('invalid memory address near ' + repr(arg))
   221          arg = arg[1:].strip()
   222          base, rem = parse_arg(arg)
   223          if not rem or rem[0] != ')':
   224              raise SyntaxError('")" expected near ' + repr(rem))
   225          elif not isinstance(base, Reg):
   226              raise SyntaxError('invalid memory address near ' + repr(arg))
   227          else:
   228              return Mem(off, base), rem[1:].strip()
   229      elif arg and arg[0].isidentifier():
   230          j = len(arg)
   231          for i, cc in enumerate(arg):
   232              if cc not in (string.digits + string.ascii_letters + '_'):
   233                  j = i
   234                  break
   235          return Label(arg[:j]), arg[j:].strip()
   236      else:
   237          raise SyntaxError('invalid operand near ' + repr(arg))
   238  
   239  def parse_args(args: str) -> List[Operand]:
   240      ret = []
   241      args = args.strip()
   242      while args:
   243          if ret:
   244              if args[0] == ',':
   245                  args = args[1:].strip()
   246              else:
   247                  raise SyntaxError('"," expected between operands')
   248          val, args = parse_arg(args)
   249          ret.append(val)
   250      return ret
   251  
   252  with open("decoder.hir") as fp:
   253      hir = []
   254      cont = 0
   255      for line in fp.read().splitlines():
   256          if not cont:
   257              hir.append(line)
   258          else:
   259              hir[-1] += '\n' + line
   260          cont += line.count('{')
   261          cont -= line.count('}')
   262  
   263  for line in hir:
   264      line = line.strip()
   265      if not line:
   266          continue
   267      if line.endswith(':'):
   268          print('    p.Label ("%s")' % line[:-1])
   269          continue
   270      ins, *args = line.split(None, 1)
   271      ins, args = ins.lower(), sum((parse_args(v.strip()) for v in args), [])
   272      if ins == 'ret':
   273          if len(args) != 1 or not isinstance(args[0], RegSeq):
   274              raise SyntaxError('invalid "ret" operands near ' + repr(line))
   275          else:
   276              print('    p.RET   ()' + args[0].to_string(False))
   277      elif ins in {'ccall', 'gcall', 'icall'}:
   278          if len(args) != 3 or not isinstance(args[0], Func) or not isinstance(args[1], RegSeq) or not isinstance(args[2], RegSeq):
   279              raise SyntaxError('invalid "%s" operands near %r' % (ins, line))
   280          else:
   281              print('    p.%-6s(%s)%s%s' % (ins.upper(), args[0], args[1].to_string(True), args[2].to_string(False)))
   282      else:
   283          print('    p.%-6s(%s)' % (ins.upper(), ', '.join(map(str, args))))