Шаблон посетителя и генерация кода компилятора, как обрабатывать присвоение?

Для генерации кода на моем языке программирования я использую шаблон посетителя и хотел бы найти лучший способ обработки операторов присваивания.

Моя виртуальная машина зарегистрирована на основе, и каждый посещаемый узел выражения просто PUSH номер регистра в глобальный стек, поэтому, когда я посещаю узел двоичного выражения, я выполняю такой код:

static void visit_binary_expr (gvisitor_t *self, gnode_binary_expr_t *node) {
    DECLARE_CODE();

    bool is_assignment = (node->op == TOK_OP_ASSIGN);
    if (is_assignment) {
        // assignment is right associative
        visit(node->right);
        visit(node->left);
    } else {
        // visiting binary operation from left to right
        visit(node->left);
        visit(node->right);
    }

    if (!is_assignment) {
        uint32_t r3 = ircode_register_pop(code);
        uint32_t r2 = ircode_register_pop(code);
        uint32_t r1 = ircode_register_push_temp(code);

        opcode_t op = token2opcode(node->op);
        ircode_add(code, op, r1, r2, r3);
    }
}

С помощью этого кода я могу обрабатывать такие инструкции, как: a + b Предполагая, что переменная a в регистре 1 и переменная b в регистре 2 сгенерированный код будет:

ADD 3 1 2

Проблема в том, что для присваивания требуется другой набор инструкций, а наличия в стеке только номеров регистров недостаточно. Например, чтобы получить доступ (прочитать) к глобальной переменной, я должен использовать инструкцию GLOAD, а для сохранения (записи) в глобальную переменную я должен использовать инструкцию GSTORE.

В настоящее время я решаю проблему, сохраняя логическое значение is_assignment в каждом узле, поэтому я могу рекурсивно проверять, какую инструкцию генерировать, но для этого требуется много логики, распределенной по каждому посещаемому узлу, и я действительно хотел бы найти более элегантный способ, где только функция visit_binary_expr может решить, какую инструкцию генерировать лучше всего.




Ответы (1)


Поскольку присваивание сильно отличается от других бинарных операций (оно имеет побочный эффект изменения левого операнда), имеет смысл обрабатывать его как совершенно отдельную операцию, никак не связанную с бинарными операциями. В этом случае у вас будет что-то вроде visit_assignment со вторым аргументом соответствующего типа.

Тогда можно было бы избежать всех проверок, которые присутствуют в текущем коде. Кроме того, в зависимости от того, какие цели позволяет ваш язык, при обработке цели назначения может использоваться другой набор функций обхода, другой посетитель или тот же посетитель с флагом, указывающим, что обрабатывается цель, а не регулярное выражение. . Решение о том, какой подход лучше, зависит от языка и кода, который необходимо сгенерировать.

person Alexander Kogtenkov    schedule 25.04.2016