<?php

function parseExpr() {
    return parseSum();
}

// парсит сумму чисел или произведений
function parseSum() {
    
    $numbers = array();

    while (true) {
        $numbers[] = parseMultiplication(); // добавляем очередное слагаемое
        if (nextTokenIs('+')) { // если дальше плюс
            consumeToken('+'); // проглотить токен '+'
            continue;
        }
    }

    // прочли всю сумму, создаем узел
    if (count($numbers) > 1) {
        return new Node_Sum($numbers);
    } else {
        return $numbers[0]; // ради одного слагаемого не делаем новый узел
    }
}

function parseMultiplication()
{
    // то же самое, только вызывает parseNumber();
}

function parseNumber()
{
    // читает токен. если там не число то падает с ошибкой «ожидается число»
    $number = readToken(TYPE_NUMBER); 

    // создаем новый узел
    return new Node_Number($number); 
}

У тебя будет что-то похожее только функции будут типа такой: 

function parseTag()
{
    $attrs = [];
    $tagName = getNextToken(TYPE_OPEN_TAG); // читаем открываюий тег

    while (nextTokenIs(TYPE_ATTR)) {
        $attrToken = getNextToken(TYPE_ATTR);
        $attrs[$attrToken['name']] = $attrToken['value'];
    }

    $content = parseContent();

    $closeTag = getNextToken(TYPE_CLOSE_TAG);
    if ($closeTag != $tagName) {
        ошибка, несоответсие откр. и закр. тегов
    }

    return new Node_Tag($tagName, $attrs, $content);
}


