Recursively handle nodes in the 010 AST

        :node: TODO
        :scope: TODO
        :ctxt: TODO
        :stream: TODO
        :returns: TODO
427266
def _handle_node(self, node, scope=None, ctxt=None, stream=None):
        
        if scope is None:
            if self._scope is None:
                self._scope = scope = self._create_scope()
            else:
                scope = self._scope

        if ctxt is None and self._ctxt is not None:
            ctxt = self._ctxt
        else:
            self._ctxt = ctxt

        if type(node) is tuple:
            node = node[1]

        # TODO probably a better way to do this...
        # this occurs with if-statements that have a single statement
        # instead of a compound statement (no curly braces)
        elif type(node) is list and len(list(filter(lambda x: isinstance(x, AST.Node), node))) == len(node):
            node = AST.Compound(
                block_items=node,
                coord=node[0].coord
            )
            return self._handle_node(node, scope, ctxt, stream)

        # need to check this so that debugger-eval'd statements
        # don't mess with the current state
        if not self._no_debug:
            self._coord = node.coord

        self._dlog("handling node type {}, line {}".format(node.__class__.__name__, node.coord.line if node.coord is not None else "?"))
        self._log.inc()

        breakable = self._node_is_breakable(node)

        if breakable and not self._no_debug and self._break_type != self.BREAK_NONE:
            # always break
            if self._break_type == self.BREAK_INTO:
                self._break_level = self._scope.level()
                self.debugger.cmdloop()

            # level <= _break_level
            elif self._break_type == self.BREAK_OVER:
                if self._scope.level() <= self._break_level:
                    self._break_level = self._scope.level()
                    self.debugger.cmdloop()
                else:
                    pass

        if node.__class__ not in self._node_switch:
            raise errors.UnsupportedASTNode(node.coord, node.__class__.__name__)

        res = self._node_switch[node.__class__](node, scope, ctxt, stream)

        self._log.dec()

        return res
48935
def serialize(node, stream=None, Dumper=Dumper, **kwds):
    
    return serialize_all([node], stream, Dumper=Dumper, **kwds)
637201
def _handle_struct(self, node, scope, ctxt, stream):
        
        self._dlog("handling struct")

        if node.args is not None:
            for param in node.args.params:
                param.is_func_param = True

        # it's actually being defined
        if node.decls is not None:
            struct_cls = StructUnionDef("struct", self, node)

            if node.name is not None:
                scope.add_type_class(node.name, struct_cls)

            return struct_cls

        # it's declaring a struct field. E.g.
        #    struct IFD subDir;
        else:
            return scope.get_type(node.name)
233661
def _handle_identifier_type(self, node, scope, ctxt, stream):
        
        self._dlog("handling identifier")

        cls = self._resolve_to_field_class(node.names, scope)
        return cls
927849
def _handle_compound(self, node, scope, ctxt, stream):
        
        self._dlog("handling compound statement")
        #scope.push()

        try:
            for child in node.children():
                self._handle_node(child, scope, ctxt, stream)

        # in case a return occurs, be sure to pop the scope
        # (returns are implemented by raising an exception)
        finally:
            #scope.pop()
            pass
984409
def _handle_type_decl(self, node, scope, ctxt, stream):
        
        self._dlog("handling type decl")
        decl = self._handle_node(node.type, scope, ctxt, stream)
        return decl
412664
def _handle_metadata(self, node, scope, ctxt, stream):
        
        self._dlog("handling node metadata {}".format(node.metadata.keyvals))

        keyvals = node.metadata.keyvals

        metadata_info = []

        if "watch" in node.metadata.keyvals or "update" in keyvals:
            metadata_info.append(
                self._handle_watch_metadata(node, scope, ctxt, stream)
            )

        if "packtype" in node.metadata.keyvals or "packer" in keyvals:
            metadata_info.append(
                self._handle_packed_metadata(node, scope, ctxt, stream)
            )

        return metadata_info
326412
def _handle_constant(self, node, scope, ctxt, stream):
        
        self._dlog("handling constant type {}".format(node.type))
        switch = {
            "int": (self._str_to_int, self._choose_const_int_class),
            "long": (self._str_to_int, self._choose_const_int_class),
            # TODO this isn't quite right, but py010parser wouldn't have
            # parsed it if it wasn't correct...
            "float": (lambda x: float(x.lower().replace("f", "")), fields.Float),
            "double": (float, fields.Double),

            # cut out the quotes
            "char": (lambda x: ord(utils.string_escape(x[1:-1])), fields.Char),

            # TODO should this be unicode?? will probably bite me later...
            # cut out the quotes
            "string": (lambda x: str(utils.string_escape(x[1:-1])), fields.String)
        }

        if node.type in switch:
            #return switch[node.type](node.value)
            conversion,field_cls = switch[node.type]
            val = conversion(node.value)

            if hasattr(field_cls, "__call__") and not type(field_cls) is type:
                field_cls = field_cls(val)

            field = field_cls()
            field._pfp__set_value(val)
            return field

        raise UnsupportedConstantType(node.coord, node.type)
1303238
def _ensure_node(node: Union[str, AbstractNode]) -> AbstractNode:
    
    if isinstance(node, str):
        return Text(node)
    elif isinstance(node, Node):
        return node
    else:
        raise TypeError('Invalid type to append: {}'.format(node))
1035561
def _handle_struct_ref(self, node, scope, ctxt, stream):
        
        self._dlog("handling struct ref")

        # name
        # field
        struct = self._handle_node(node.name, scope, ctxt, stream)

        try:
            sub_field = getattr(struct, node.field.name)
        except AttributeError as e:
            # should be able to access implicit array items by index OR
            # access the last one's members directly without index
            #
            # E.g.:
            # 
            # local int total_length = 0;
            # while(!FEof()) {
            #     HEADER header;
            #   total_length += header.length;
            # }
            if isinstance(struct, fields.Array) and struct.implicit:
                last_item = struct[-1]
                sub_field = getattr(last_item, node.field.name)
            else:
                raise

        return sub_field
319848
def _handle_param_list(self, node, scope, ctxt, stream):
        
        self._dlog("handling param list")
        # params should be a list of tuples:
        # [(<name>, <field_class>), ...]
        params = []
        for param in node.params:
            self._mark_id_as_lazy(param)
            param_info = self._handle_node(param, scope, ctxt, stream)
            params.append(param_info)

        param_list = functions.ParamListDef(params, node.coord)
        return param_list
442979
def _handle_func_decl(self, node, scope, ctxt, stream):
        
        self._dlog("handling func decl")

        if node.args is not None:
            # could just call _handle_param_list directly...
            for param in node.args.params:
                # see the check in _handle_decl for how this is kept from
                # being added to the local context/scope
                param.is_func_param = True
            params = self._handle_node(node.args, scope, ctxt, stream)
        else:
            params = functions.ParamListDef([], node.coord)

        func_type = self._handle_node(node.type, scope, ctxt, stream)

        func = functions.Function(func_type, params, scope)

        return func
391217
def _handle_typename(self, node, scope, ctxt, stream):
        
        self._dlog("handling typename")

        return self._handle_node(node.type, scope, ctxt, stream)
864021
def _handle_binary_op(self, node, scope, ctxt, stream):
        
        self._dlog("handling binary operation {}".format(node.op))
        switch = {
            "+": lambda x,y: x+y,
            "-": lambda x,y: x-y,
            "*": lambda x,y: x*y,
            "/": lambda x,y: x/y,
            "|": lambda x,y: x|y,
            "^": lambda x,y: x^y,
            "&": lambda x,y: x&y,
            "%": lambda x,y: x%y,
            ">": lambda x,y: x>y,
            "<": lambda x,y: x<y,
            "||": lambda x,y: x or y,
            ">=": lambda x,y: x>=y,
            "<=": lambda x,y: x<=y,
            "==": lambda x,y: x == y,
            "!=": lambda x,y: x != y,
            "&&": lambda x,y: x and y,
            ">>": lambda x,y: x >> y,
            "<<": lambda x,y: x << y,
        }

        left_val = self._handle_node(node.left, scope, ctxt, stream)
        right_val = self._handle_node(node.right, scope, ctxt, stream)

        if node.op not in switch:
            raise errors.UnsupportedBinaryOperator(node.coord, node.op)

        res = switch[node.op](left_val, right_val)

        if type(res) is bool:
            new_res = fields.Int()
            if res:
                new_res._pfp__set_value(1)
            else:
                new_res._pfp__set_value(0)
            res = new_res

        return res
1280033
def run(self, node):
        
        n = super(Transformation, self).run(node)
        if self.update:
            ast.fix_missing_locations(n)
            self.passmanager._cache.clear()
        return n
511770
def iterate(node)
      return unless node.is_a?(AST::Node)

      before    = :"on_#{node.type}"
      after     = :"after_#{node.type}"
      skip_node = catch :skip_child_nodes do
        execute_callback(before, node)
      end

      if skip_node != node
        node.children.each do |child|
          iterate(child) if child.is_a?(AST::Node)
        end
      end

      execute_callback(after, node)
    end
1324210
def _iter_branch(self, node):
        
        if node == BLANK_NODE:
            raise StopIteration

        node_type = self._get_node_type(node)

        if is_key_value_type(node_type):
            nibbles = key_nibbles_from_key_value_node(node)
            key = b'+'.join([to_string(x) for x in nibbles])
            if node_type == NODE_TYPE_EXTENSION:
                sub_tree = self._iter_branch(self._get_inner_node_from_extension(node))
            else:
                sub_tree = [(to_string(NIBBLE_TERMINATOR), node[1])]

            # prepend key of this node to the keys of children
            for sub_key, sub_value in sub_tree:
                full_key = (key + b'+' + sub_key).strip(b'+')
                yield (full_key, sub_value)

        elif node_type == NODE_TYPE_BRANCH:
            for i in range(16):
                sub_tree = self._iter_branch(self._decode_to_node(node[i]))
                for sub_key, sub_value in sub_tree:
                    full_key = (str_to_bytes(str(i)) +
                                b'+' + sub_key).strip(b'+')
                    yield (full_key, sub_value)
            if node[16]:
                yield (to_string(NIBBLE_TERMINATOR), node[-1])