Подтвердить что ты не робот

Приоритет оператора Crockfords Top Down

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

Итак, я начал читать статью Дугласа Крокфорда Приоритет сверху вниз.

Примечание. Вероятно, вы должны прочитать статью, если хотите более глубокое понимание контекста фрагментов кода ниже

Мне трудно понять, как должен работать оператор var и оператор присваивания =.

D.C. определяет оператор присваивания, например

var assignment = function (id) {
    return infixr(id, 10, function (left) {
        if (left.id !== "." && left.id !== "[" &&
                left.arity !== "name") {
            left.error("Bad lvalue.");
        }
        this.first = left;
        this.second = expression(9);
        this.assignment = true;
        this.arity = "binary";
        return this;
    });
};
assignment("=");  

Примечание: [[значение]] относится к токену, упрощенному к его значению

Теперь, если функция выражения достигает, например, [[t]],[[=]],[[2]], результат [[=]].led - это что-то вроде этого.

{
    "arity": "binary",
    "value": "=",
    "assignment": true, //<-
    "first": {
        "arity": "name",
        "value": "t"
    },
    "second": {
        "arity": "literal",
        "value": "2"
    }
}

D.C. делает функцию assignment, потому что

мы хотим, чтобы он делал два дополнительных бита бизнеса: рассмотрите левый операнд, чтобы убедиться, что он является надлежащим значением l, и установить член назначения, чтобы мы могли позже быстро идентифицировать.

Что имеет смысл для меня до такой степени, что он вводит var, который определяется следующим образом.

Оператор var определяет одну или несколько переменных в текущем блоке. Каждое имя может необязательно сопровождаться = и инициализирующим выражением.

stmt("var", function () {
    var a = [], n, t;
    while (true) {
        n = token;
        if (n.arity !== "name") {
            n.error("Expected a new variable name.");
        }
        scope.define(n);
        advance();
        if (token.id === "=") {
            t = token;
            advance("=");
            t.first = n;
            t.second = expression(0);
            t.arity = "binary";
            a.push(t);
        }
        if (token.id !== ",") {
            break;
        }
        advance(",");
    }
    advance(";");
    return a.length === 0 ? null : a.length === 1 ? a[0] : a;
});

Теперь, если парсер достигает набора токенов типа [[var]],[[t]],[[=]],[[1]], сгенерированное дерево будет выглядеть примерно так.

{
    "arity": "binary",
    "value": "=",
    "first": {
        "arity": "name",
        "value": "t"
    },
    "second": {
        "arity": "literal",
        "value": "1"
    }
}

Ключевой частью моего вопроса является часть if (token.id === "=") {...}.

Я не понимаю, почему мы называем

    t = token;
    advance("=");
    t.first = n;
    t.second = expression(0);
    t.arity = "binary";
    a.push(t);

а не

    t = token;
    advance("=");
    t.led (n);
    a.push(t);  

в части ....

который назвал бы наши операторы [[=]] led (функция назначения), которая делает

убедитесь, что это правильный lvalue, и установить член назначения, чтобы мы могли быстрее быстро идентифицировать операторы присваивания. например,

{
    "arity": "binary",
    "value": "=",
    "assignment": true,
    "first": {
        "arity": "name",
        "value": "t"
    },
    "second": {
        "arity": "literal",
        "value": "1"
    }
}

так как нет оператора с a lbp между 0 и 10, вызов expression(0) vs. expression (9) не имеет значения. (!(0<0) && !(9<0) && 0<10 && 9<10))

И условие token.id === "=" не позволяет назначать члену объекта как token.id либо '[', либо '.', а t.led не будет вызываться.

Мой короткий вопрос:

Почему мы не вызываем, необязательно, после следующего объявления переменной, доступную функцию led операторов присваивания. Но вместо этого вручную установите члены first и second оператора, но не член assignment?

Вот два скрипта, разбирающих простую строку. Используя оригинальный код и один с помощью присваивания операторов led.

4b9b3361

Ответ 1

При разборе языка важны две вещи: семантика и синтаксис.

Семантически, var x=5; и var x;x=5 кажутся очень близкими, если не идентичными (так как в обоих случаях сначала объявляется переменная, а затем этому объявлению присваивается значение. "наблюдается и является правильным по большей части.

Синтаксически, однако, они отличаются (что хорошо видно).

На естественном языке аналогом будет:

  • У мальчика есть яблоко.
  • Есть яблоко, у него есть мальчик.

Теперь, чтобы быть кратким! Давайте рассмотрим два примера.

В то время как два (в значительной степени) означают одно и то же, они явно не являются тем же предложением. Вернуться к JavaScript!

Первый: var x=5 читает следующий путь:

var                      x              =                  5
-----------------------VariableStatement--------------------
var -------------------        VariableDeclarationList 
var -------------------        VariableDeclaration
var            Identifier -------   Initialiser(opt)
var ------------------- x              = AssignmentExpression
var ------------------- x ------------ = LogicalORExpression
var ------------------- x ------------ = LogicalANDExpression
var ------------------- x ------------ = BitwiseORExpression
var ------------------- x ------------ = BitwiseXORExpression
var ------------------- x ------------ = BitwiseANDExpression 
var ------------------- x ------------ = EqualityExpression
var ------------------- x ------------ = ShiftExpression
var ------------------- x ------------ = AdditiveExpression
var ------------------- x ------------ = MultiplicativeExpression
var ------------------- x ------------ = UnaryExpression
var ------------------- x ------------ = PostfixExpression 
var ------------------- x ------------ = NewExpression
var ------------------- x ------------ = MemberExpression
var ------------------- x ------------ = PrimaryExpression
var ------------------- x ------------ = Literal
var ------------------- x ------------ = NumericLiteral
var ------------------- x ------------ = DecimalLiteral
var ------------------- x ------------ = DecimalDigit 
var ------------------- x ------------ = 5

Уф! Все это должно происходить синтаксически для синтаксического анализа var x = 5, конечно, многие из них обрабатывают выражения, но это то, что есть, давайте проверим другую версию.

Это разбивается на два утверждения. var x; x = 5 Первый:

var                      x 
--------VariableStatement---
var ---- VariableDeclarationList 
var ---- VariableDeclaration
var                 Idenfifier (optional initializer not present)
var                      x

Вторая часть x=5, которая является оператором присваивания. Я могу продолжать с тем же выражением безумия - но это почти то же самое.

Итак, в заключение, в то время как два производят один и тот же результат семантически, синтаксически, как указывает официальная грамматика языка, - они разные. Результат в этом случае - действительно то же самое.

Ответ 2

У меня нет времени, чтобы прочитать всю статью, поэтому я не уверен на сто процентов. По-моему, причина в том, что оператор присваивания в выражении var является немного особенным. Он не принимает все возможные значения слева - не допускаются члены объекта (нет операторов . или [). Разрешены только простые имена переменных.

Таким образом, мы не можем использовать обычную функцию assignment, потому что она позволяет все левые значения.

Я совершенно уверен в этом, но следующее - это просто предположение:

Нам нужно было бы вызвать функцию assignment опционально и только после того, как мы проверили, что мы использовали оператор присваивания.

  advance();
  if (token.id === "=") {
      // OK, Now we know that there is an assignment.

Но функция assignment предполагает, что текущий токен является левым значением, а не оператором =.


Я не знаю, почему член assignment не установлен в true. Это зависит от того, что вы хотите сделать с сгенерированным деревом. Опять же, назначение в выражении var является немного особенным, и его было бы невозможно установить.

Ответ 3

Assignment (например, var t; t = 1;) концептуально отличается от initialization (например, var t = 1;), хотя оба результата приводят к изменению состояния памяти. Использование одного и того же фрагмента кода для реализации обоих нежелательно, так как можно изменить независимо друг от друга в будущей версии языка.

Концептуальное различие может быть показано на С++, когда речь идет о перегрузке операторов и конструкторах копирования операторов. Инициализация может вызвать конструктор копирования, назначение может вызвать перегрузку оператора назначения. Присвоение никогда не запускает конструктор копирования, инициализация никогда не использует перегрузку оператора присваивания. См. учебник по конструктору копирования и перегрузке оператора присваивания.

Еще один пример - Strix: далеко не все l-значения могут использоваться после var в JavaScript. Я думаю, что это самая большая разница между ними в JavaScript, если не единственная. Игнорирование очевидного изменения области видимости в var, конечно.

Можно было бы подумать об использовании знака равенства для обоих как совпадение. Паскаль использует := для назначения и = для инициализации. JavaScript также мог бы использовать что-то вроде var t : 1;.