diff --git a/gdb-port/ast.py b/gdb-port/ast.py new file mode 100644 index 0000000000000000000000000000000000000000..59810a1682630759f7d9a98af0c9ba82bbea1fc7 --- /dev/null +++ b/gdb-port/ast.py @@ -0,0 +1,117 @@ +class HParseResult: + #HParseResult_t = gdb.lookup_type("HParseResult") + HParseResult_t_p = gdb.lookup_type("HParseResult").pointer() + HParsedToken_t_p = gdb.lookup_type("HParsedToken").pointer() + + def __init__(self, address): + # Note to self: Address has to be an integer and not string + # Otherwise all hell breaks loose + self.address = address + self.has_token = self.read_AST_not_null() + self.ast = None + if self.has_token: + self.ast = self.make_HParsedToken() + + self.bit_length = self.read_bit_length() + self.arena = self.read_arena() + + # AST is not null + def read_AST_not_null(self): + res = gdb.Value(self.address).cast(HParseResult_t_p) + if res['ast'] == 0: + return False + # Note that the ast could still be an invalid pointer + # This is for combinators that return a valid HParseResult with NULL ast + return True + + def read_token(self): + res = gdb.Value(self.address).cast(HParseResult_t_p) + return gdb.Value(res['ast']) + + def make_HParsedToken(self): + if self.has_token: + tok = self.read_token() + return gdb.Value(tok).dereference() + #TODO: instantiate HParsedToken class + + def read_bit_length(self): + res = gdb.Value(self.address).cast(HParseResult_t_p) + return res['bit_length'] + + def read_arena(self): + res = gdb.Value(self.address).cast(HParseResult_t_p) + return res['arena'] + +class HParsedToken: + # This will break if the HTokenType enum changes in Hammer + # It's not expected to change, but the workaround for it would be to extract it at runtime. + # Additionally, parsers may declare their own HTokenTypes, which *do* have to be handled at runtime. + # TODO: unneeded, remove + token_types = { + 0: gdb.lookup_type("void").pointer(), # TT_INVALID, shouldn't access data + 1: gdb.lookup_type("void").pointer(), # TT_NONE, shouldn't access data + 2: gdb.lookup_type("HBytes"), # TT_BYTES + 4: gdb.lookup_type("int64_t"), # TT_SINT + 8: gdb.lookup_type("uint64_t"), # TT_UINT + 12: gdb.lookup_type("double"), # TT_DOUBLE + 13: gdb.lookup_type("float"), # TT_FLOAT + 16: gdb.lookup_type("HCountedArray").pointer(), # TT_SEQUENCE + # TT_RESERVED_1, value determined by compiler + 32: gdb.lookup_type("void").pointer(), # TT_ERR, shouldn't access data + 64: gdb.lookup_type("void").pointer(), # TT_USER, this one actually uses void* + # TT_MAX, value determined at compile time + } + + token_union_members = { + 2: 'bytes', + 4: 'sint', + 8: 'uint', + 12: 'dbl', + 13: 'flt', + 16: 'seq', + 64: 'user' + } + + TT_MAX = gdb.lookup_type("enum HTokenType_").fields()[-1].enumval + # These enum values have no token data + no_token_data = [v.enumval for v in gdb.lookup_type("enum HTokenType_").fields() if v.name in ["TT_INVALID", "TT_RESERVED_1", "TT_ERR", "TT_NONE", "TT_MAX"]] + + #void_ptr = gdb.lookup_type("void").pointer() + #HParsedToken_t = gdb.lookup_type("HParsedToken") + HParsedToken_t_p = gdb.lookup_type("HParsedToken").pointer() + + + def __init__(self, address, parent, token_type=None, children=[]): + self.address = address + self.parent = parent + self.children = children + self.token_type = token_type or self.read_token_type() + + #TODO + self.token = self.read_token_val() + self.data = self.read_token_data() + + def read_token_val(self): + tok = gdb.Value(self.address).cast(HParsedToken_t_p) + return tok + + def read_token_type(self): + tok = gdb.Value(self.address).cast(HParsedToken_t_p) + return tok['token_type'] + + def has_token_data(self, token_type): + return token_type not in no_token_data + + + def read_token_data(self): + tok = gdb.Value(self.address).cast(HParsedToken_t_p) + if self.has_token_data(self.token_type): + # We default to using the 'user' field. Also covers custom token types + member = 'user' + if self.token_type < TT_MAX: + member = token_union_members.setdefault(self.token_type, "user") + data = tok[member] + return data + # Token type is one of the enum values known not to have data + else: + return None