Граф зависимости слова

Граф зависимости слова

Графом зависимости слова в атрибутной грамматике называется ориентированный граф, полученный из аннотированного дерева вывода слова :

  • В каждом узле аннотированного дерева каждому атрибуту сопоставляется вершина графа зависимости
  • Ориентированное ребро соответствует семантическому правилу .
Link to original

Пример построения графа зависимости цепочки

Рассмотрим атрибутную грамматику из прошлой лекции:

Transclude of Лои.-Лекция-2025-10-21#пример-построения-атрибутная-грамматика-атрибутной-грамматики

Пример аннотированного дерева вывода для слова возьмем из той же лекции:

Тогда по определению графа зависимости для слова , граф будет выглядеть следующим образом:

Для более сложных примеров атрибутных грамматик допускаются “фиктивные” атрибуты, которым также будет соответствовать вершина графа зависимости

Очевидно, справедливо утверждение:

Теорема. О вычислении атрибутов

Теорема. О вычислении атрибутов

Все атрибуты в аннотированном дереве слова могут быть вычислены Граф зависимости слова не содержит циклов

Link to original

Синтаксические деревья

Деревья вывода могут быть довольно громоздкими для последующей с ними работы. Существуют методы их эквивалентного “сжатия”.

Один из способов “Сжатия” дерева вывода - построение синтаксического дерева вывода по исходному дереву вывода. Метод применим, если исходная грамматика, по которой построено дерево вывода является операторной грамматикой

Построение синтаксического дерева

Алгоритм. Построение синтаксического дерева по дереву вывода слова

Вход

Дерево вывода слова, построенное по операторной грамматике

Выход

Синтаксическое дерево

Алгоритм

  1. Листья дерева, помеченные терминалами, которые соответствуют какой-то операции в исходной грамматике , удаляются. Соответствующие операторные терминалы связываются с родительской вершиной
  2. Вершины дерева, соответствующие цепным правилам, “выводящих” в итоге сворачиваются в одну вершину и помечаются
Link to original

Пример построения синтаксического дерева

Возьмем

G = \{ E \to E + T | T, T \to T * F | F, E \to (E)|x \}
Link to original

Дерево вывода слова выглядит следующим образом:

Применением правила 1 алгоритма 2 раза получаем дерево

Применением правила 2 алгоритма 3 раза получаем искомое синтаксическое дерево:

Комментарий: значения атрибутов, если грамматика, по которой строится дерево, является атрибутной в построенном синтаксическом дереве записываются возле вершин, соответствующих операторам и операндам

Существенное “сжатие” по сравнению с исходным аннотированным деревом выводом слова

Link to original

Синтаксический ДАГ

Синтаксический ДАГ

Синтаксическим ДАГом для слова в операторной грамматике называется граф, полученный из синтаксического дерева отождествлением изоморфных поддеревьев этого синтаксического дерева

Link to original

Алгоритм. Построение ДАГ по синтаксическому дереву

Алгоритм. Построение ДАГ по синтаксическому дереву

Вход

Синтаксическое дерево

Выход

ДАГ

Идея

Проходим дерево снизу вверх и, включая дедупликацию узлов

Алгоритм

/** Узел исходного синтаксического дерева */
public interface ParseNode {
	/**
	 * Имя операции или идентификатор/константа
	 */
    String op();
    /**
     * Дети (может быть пустым списком)
     */
    List<ParseNode> children();
}
 
import java.util.*;
 
public final class DagNode {
 
    private final String op;
    private final List<DagNode> children;
 
    public DagNode(String op, List<DagNode> children) {
        this.op = op;
        this.children = List.copyOf(children);
    }
 
    public String op() { return op; }
    public List<DagNode> children() { return children; }
 
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof DagNode node)) return false;
        return op.equals(node.op) && children.equals(node.children);
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(op, children);
    }
 
    @Override
    public String toString() {
        return op + children;
    }
}
import java.util.*;
 
public final class DagBuilder {
 
    /** Таблица уникальных узлов DAG */
    private final Map<DagNode, DagNode> unique = new HashMap<>();
 
	/**
	 * @param root синтаксическое дерево
	 */
    public DagNode build(ParseNode root) {
        return buildRecursively(root);
    }
 
    private DagNode buildRecursively(ParseNode node) {
        // Рекурсивно строим DAG для всех детей
        List<DagNode> childDags = new ArrayList<>();
        for (ParseNode child : node.children()) {
            childDags.add(buildRecursively(child));
        }
 
        // Создаём узел-кандитат в DAG
        DagNode candidate = new DagNode(node.op(), childDags);
 
        // Проверяем есть ли уже такой узел
        DagNode existing = unique.get(candidate);
        if (existing != null) {
            // используем существующий узел
            return existing;
        }
 
		// Добавляем узел-кандидат в таблицу
        unique.put(candidate, candidate);
        return candidate;
    }
}
Link to original

Пример построения синтаксического ДАГА

Рассмотрим слово в операторной грамматике

G = \{ E \to E + T | T, T \to T * F | F, E \to (E)|x \}
Link to original

из предыдущего примера

Для построения ДАГ нужно предварительно построить синтаксическое дерево. Положим оно уже построено:

Получаем искомый ДАГ применением алгоритма: