Реализация двоичного дерева для языкового анализа - дочерний элемент как узел: не работает

Некоторое время я пытался реализовать AST на С++ для хранения данных, полученных из языка ML, вот инструкция, которую мой AST может записать:

var foo = 8;

Лексер изолирует токены, а синтаксический анализатор делает вывод, что это объявление переменной, поэтому он изолирует все:

foo = 8

Из этого было легко построить временный AST:

    =  
  /   \
foo    8

Но я все еще не могу справиться с дочерними узлами:

foo = 2 + 4

Or

foo : integer = 2 + 4

Итак, кто должен дать это:

         =      
       /   \    
      /     \   
     :       +  
    / \     / \ 
   /   \   2   4
 foo integer     

Вот моя попытка реализации:

*.hpp

enum NodeTypes { /* ... */ };

struct Node {
    token_t NodeValue;
    NodeTypes NodeType;
    Node *LeftChild = NULL;
    Node *RightChild = NULL;
    Node(token_t value, NodeTypes type);
    void InsertLeft(token_t NodeValue, NodeTypes NodeType = NOTHING);
    void InsertRight(token_t NodeValue, NodeTypes NodeType = NOTHING);
    void BrowseUp();
};

*.cpp

Node(token_t value, NodeTypes type) {
    NodeValue = value;
    NodeType = type;
}
void InsertLeft(token_t value, NodeTypes type) {
    if (LeftChild == NULL)
        LeftChild = new Node(value, type);
    else {
        Node NewNode = Node(value, type);
        NewNode.LeftChild = LeftChild;
        LeftChild = &NewNode;
    }
}
void InsertRight(token_t value, NodeTypes type) {
    if (RightChild == NULL)
        RightChild = new Node(value, type);
    else {
        Node NewNode = Node(value, type);
        NewNode.RightChild = RightChild;
        RightChild = &NewNode;
    }
}
void BrowseUp() {
    std::cout << NodeValue.value << " ";
    if (LeftChild) LeftChild->BrowseUp();
    if (RightChild) RightChild->BrowseUp();
}

Используй это:

Node main = Node(NodePosition, NodeType);
SetMainAst(main, expr);
main.BrowseUp();

SetMainAst:

void SetMainAst(Node &node, Expr expr, NodeTypes type = NodeTypes::NOTHING) {
    std::array<Expr, 3> exp = CutExpr(expr, GetNodePosition(expr));
    Expr left = exp[0], right = exp[2];
    token_t value = exp[1][0];

    if (type == NOTHING) node.NodeValue = value;

    if (!ContainNodes(left)) node.InsertLeft(left[0]);
    else SetMainAst(node, left, DetermineFirstNode(expr));
    if (!ContainNodes(right)) node.InsertRight(right[0]);
    else SetMainAst(node, right, DetermineFirstNode(expr));
}

CutExpr() позволяет разрезать выражение на 3 части:

  • lзначение;
  • узел;
  • значение.

Я помог себе с этим (это на питоне, но я переписал на С++).

С одним выражением узла это творит чудеса. Но при наличии более одного узла он больше не работает: BrowseUp() останавливает программу после отображения основного узла (т.е. знака равенства в данном случае).

Я действительно не понимаю, но я хорошо следовал руководству и думаю, что хорошо расшифровал на С++... Может быть, это проблема указателя/ссылки?

Буду очень признателен, если вы поможете мне решить эту проблему (которая беспокоит меня уже 3 дня).


person Foxy    schedule 15.08.2018    source источник
comment
LeftChild = &NewNode; -- указатель на локальную переменную, которая переживает локальную область видимости   -  person Yakk - Adam Nevraumont    schedule 15.08.2018
comment
Если вы посмотрите на ошибку, на которую указали другие, взгляните на более широкую картину, чтобы понять, почему то, что вы делаете, не может работать. Как бы вы реализовали деструктор для своего дерева? Если вы реализовали его, вызвав delete на каждом узле, вы понимаете, что некоторые из ваших узлов не были созданы с помощью new, поэтому вы бы застряли.   -  person PaulMcKenzie    schedule 15.08.2018
comment
Дополнительный просмотр: (Внимание: более часа) презентация Herb Sutter Leak Freedom by Default   -  person user4581301    schedule 15.08.2018
comment
Обратите внимание, что ваша ошибка на самом деле не имеет ничего общего с AST и деревьями, а имеет отношение к фундаментальным знаниям C++. Сначала вы должны узнать о правильном управлении памятью в C++ и области видимости переменных (возможно, реализовать простое двоичное дерево с правильным построением и уничтожения). Это не похоже на Python или подобные языки, где сборка мусора удаляет из вас этот аспект программирования.   -  person PaulMcKenzie    schedule 15.08.2018


Ответы (1)


Этот

    Node NewNode = Node(value, type);
    NewNode.LeftChild = LeftChild;
    LeftChild = &NewNode;

неверно, потому что вы сохраняете указатель на объект, который должен быть уничтожен (когда вы выходите из инструкции if ... else).

Вы, вероятно, хотите что-то вроде этого

    Node* NewNode = new Node(value, type);
    NewNode->LeftChild = LeftChild;
    LeftChild = NewNode;

Вы переписываете с Python, в котором есть сборка мусора, на C++, в котором этого нет. Поэтому вы должны добавить управление памятью самостоятельно.

person john    schedule 15.08.2018