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))))