Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gn_ast.py

  Sprache: Python
 

# Lint as: python3
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Helper script to use GN's JSON interface to make changes.

AST implementation details:
  https://gn.googlesource.com/gn/+/refs/heads/main/src/gn/parse_tree.cc

To dump an AST:
  gn format --dump-tree=json BUILD.gn > foo.json
"""

from __future__ import annotations

import dataclasses
import functools
import json
import subprocess
from typing import Callable, Dict, List, Optional, Tuple, TypeVar

NODE_CHILD = 'child'
NODE_TYPE = 'type'
NODE_VALUE = 'value'

_T = TypeVar('_T')


def _create_location_node(begin_line=1):
    return {
        'begin_column'1,
        'begin_line': begin_line,
        'end_column'2,
        'end_line': begin_line,
    }


def _wrap(node: dict):
    kind = node[NODE_TYPE]
    if kind == 'LIST':
        return StringList(node)
    if kind == 'BLOCK':
        return BlockWrapper(node)
    return NodeWrapper(node)


def _unwrap(thing):
    if isinstance(thing, NodeWrapper):
        return thing.node
    return thing


def _find_node(root_node: dict, target_node: dict):
    def recurse(node: dict) -> Optional[Tuple[dict, int]]:
        children = node.get(NODE_CHILD)
        if children:
            for i, child in enumerate(children):
                if child is target_node:
                    return node, i
                ret = recurse(child)
                if ret is not None:
                    return ret
        return None

    ret = recurse(root_node)
    if ret is None:
        raise Exception(
            f'Node not found: {target_node}\nLooked in: {root_node}')
    return ret

@dataclasses.dataclass
class NodeWrapper:
    """Base class for all wrappers."""
    node: dict

    @property
    def node_type(self) -> str:
        return self.node[NODE_TYPE]

    @property
    def node_value(self) -> str:
        return self.node[NODE_VALUE]

    @property
    def node_children(self) -> List[dict]:
        return self.node[NODE_CHILD]

    @functools.cached_property
    def first_child(self):
        return _wrap(self.node_children[0])

    @functools.cached_property
    def second_child(self):
        return _wrap(self.node_children[1])

    def is_list(self):
        return self.node_type == 'LIST'

    def is_identifier(self):
        return self.node_type == 'IDENTIFIER'

    def visit_nodes(self, callback: Callable[[dict],
                                             Optional[_T]]) -> List[_T]:
        ret = []

        def recurse(root: dict):
            value = callback(root)
            if value is not None:
                ret.append(value)
                return
            children = root.get(NODE_CHILD)
            if children:
                for child in children:
                    recurse(child)

        recurse(self.node)
        return ret

    def set_location_recursive(self, line):
        def helper(n: dict):
            loc = n.get('location')
            if loc:
                loc['begin_line'] = line
                loc['end_line'] = line

        self.visit_nodes(helper)

    def add_child(self, node, *, before=None):
        node = _unwrap(node)
        if before is None:
            self.node_children.append(node)
        else:
            before = _unwrap(before)
            parent_node, child_idx = _find_node(self.node, before)
            parent_node[NODE_CHILD].insert(child_idx, node)

            # Prevent blank lines between |before| and |node|.
            target_line = before['location']['begin_line']
            _wrap(node).set_location_recursive(target_line)

    def remove_child(self, node):
        node = _unwrap(node)
        parent_node, child_idx = _find_node(self.node, node)
        parent_node[NODE_CHILD].pop(child_idx)


@dataclasses.dataclass
class BlockWrapper(NodeWrapper):
    """Wraps a BLOCK node."""
    def __post_init__(self):
        assert self.node_type == 'BLOCK'

    def find_assignments(self, var_name=None):
        def match_fn(node: dict):
            assignment = AssignmentWrapper.from_node(node)
            if not assignment:
                return None
            if var_name is None or var_name == assignment.variable_name:
                return assignment
            return None

        return self.visit_nodes(match_fn)


@dataclasses.dataclass
class AssignmentWrapper(NodeWrapper):
    """Wraps a =, +=, or -= BINARY node where the LHS is an identifier."""
    def __post_init__(self):
        assert self.node_type == 'BINARY'

    @property
    def variable_name(self):
        return self.first_child.node_value

    @property
    def value(self):
        return self.second_child

    @property
    def list_value(self):
        ret = self.second_child
        assert isinstance(ret, StringList), 'Found: ' + ret.node_type
        return ret

    @property
    def operation(self):
        """The assignment operation. Either "=" or "+="."""
        return self.node_value

    @property
    def is_append(self):
        return self.operation == '+='

    def value_as_string_list(self):
        return StringList(self.value.node)

    @staticmethod
    def from_node(node: dict) -> Optional[AssignmentWrapper]:
        if node.get(NODE_TYPE) != 'BINARY':
            return None
        children = node[NODE_CHILD]
        assert len(children) == 2, (
            'Binary nodes should have two child nodes, but the node is: '
            f'{node}')
        left_child, right_child = children
        if left_child.get(NODE_TYPE) != 'IDENTIFIER':
            return None
        if node.get(NODE_VALUE) not in ('=''+=''-='):
            return None
        return AssignmentWrapper(node)

    @staticmethod
    def create(variable_name, value, operation='='):
        value_node = _unwrap(value)
        id_node = {
            'location': _create_location_node(),
            'type''IDENTIFIER',
            'value': variable_name,
        }
        return AssignmentWrapper({
            'location': _create_location_node(),
            'child': [id_node, value_node],
            'type''BINARY',
            'value': operation,
        })

    @staticmethod
    def create_list(variable_name, operation='='):
        return AssignmentWrapper.create(variable_name,
                                        StringList.create(),
                                        operation=operation)


@dataclasses.dataclass
class StringList(NodeWrapper):
    """Wraps a list node that contains only string literals."""
    def __post_init__(self):
        assert self.is_list()

        self.literals: List[str] = [
            x[NODE_VALUE].strip('"'for x in self.node_children
            if x[NODE_TYPE] == 'LITERAL'
        ]

    def add_literal(self, value: str):
        # For lists of deps, gn format will sort entries, but it will not
        # move entries past comment boundaries. Insert at the front by default
        # so that if sorting moves the value, and there is a comment boundary,
        # it will end up before the comment instead of immediately after the
        # comment (which likely does not apply to it).
        self.literals.insert(0, value)
        self.node_children.insert(
            0, {
                'location': _create_location_node(),
                'type''LITERAL',
                'value': f'"{value}"',
            })

    def remove_literal(self, value: str):
        self.literals.remove(value)
        quoted = f'"{value}"'
        children = self.node_children
        for i, node in enumerate(children):
            if node[NODE_VALUE] == quoted:
                children.pop(i)
                break
        else:
            raise ValueError(f'Did not find child with value {quoted}')

    @staticmethod
    def create() -> StringList:
        return StringList({
            'location': _create_location_node(),
            'begin_token''[',
            'child': [],
            'end': {
                'location': _create_location_node(),
                'type''END',
                'value'']'
            },
            'type''LIST',
        })


class Target(NodeWrapper):
    """Wraps a target node.

    A target node is any function besides "template" with exactly two children:
      * Child 1: LIST with single string literal child
      * Child 2: BLOCK

    This does not actually find all targets. E.g. ignores those that use an
    expression for a name, or that use "target(type, name)".
    """
    def __init__(self, function_node: dict, name_node: dict):
        super().__init__(function_node)
        self.name_node = name_node

    @property
    def name(self) -> str:
        return self.name_node[NODE_VALUE].strip('"')

    # E.g. "android_library"
    @property
    def type(self) -> str:
        return self.node[NODE_VALUE]

    @property
    def block(self) -> BlockWrapper:
        block = self.second_child
        assert isinstance(block, BlockWrapper)
        return block

    def set_name(self, value):
        self.name_node[NODE_VALUE] = f'"{value}"'

    @staticmethod
    def from_node(node: dict) -> Optional[Target]:
        """Returns a Target if |node| is a target, None otherwise."""
        if node.get(NODE_TYPE) != 'FUNCTION':
            return None
        if node.get(NODE_VALUE) == 'template':
            return None
        children = node.get(NODE_CHILD)
        if not children or len(children) != 2:
            return None
        func_params_node, block_node = children
        if block_node.get(NODE_TYPE) != 'BLOCK':
            return None
        if func_params_node.get(NODE_TYPE) != 'LIST':
            return None
        param_nodes = func_params_node.get(NODE_CHILD)
        if param_nodes is None or len(param_nodes) != 1:
            return None
        name_node = param_nodes[0]
        if name_node.get(NODE_TYPE) != 'LITERAL':
            return None
        return Target(function_node=node, name_node=name_node)


class BuildFile:
    """Represents the contents of a BUILD.gn file."""
    def __init__(self, path: str, root_node: dict):
        self.block = BlockWrapper(root_node)
        self.path = path
        self._original_content = json.dumps(root_node)

    def write_changes(self) -> bool:
        """Returns whether there were any changes."""
        new_content = json.dumps(self.block.node)
        if new_content == self._original_content:
            return False
        output = subprocess.check_output(
            ['gn''format''--read-tree=json', self.path],
            text=True,
            input=new_content)
        if 'Wrote rebuilt from json to' not in output:
            raise Exception('JSON was invalid')
        return True

    @functools.cached_property
    def targets(self) -> List[Target]:
        return self.block.visit_nodes(Target.from_node)

    @functools.cached_property
    def targets_by_name(self) -> Dict[str, Target]:
        return {t.name: t for t in self.targets}

    @staticmethod
    def from_file(path):
        output = subprocess.check_output(
            ['gn''format''--dump-tree=json', path], text=True)
        return BuildFile(path, json.loads(output))

Messung V0.5 in Prozent
C=98 H=94 G=95

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-08-28) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=476070
#Domains=661743