Какие конструкторы Javascript делает JsLex неправильно lex?

JsLex - это лексир Javascript, написанный на Python. Он неплохо работает на дневную работу (или так), но я уверен, что есть случаи, когда это становится неправильным. В частности, он ничего не понимает о вставке с запятой, и, вероятно, есть способы, которые важны для лексинга. Я просто не знаю, кто они.

Какой код Javascript делает JsLex lex неправильно? Меня особенно интересует действующий источник Javascript, где JsLex неправильно идентифицирует литералы регулярных выражений.

Просто, чтобы быть понятным, под "лексированием" я подразумеваю идентификацию токенов в исходном файле. JsLex не пытается анализировать Javascript, а тем более выполнять его. Я написал JsLex для полного лексинга, хотя, честно говоря, я был бы счастлив, если бы просто смог найти все литералы регулярных выражений.

Ответ 1

Интересно, что я пробовал ваш лексер по коду моего lexer/оценщика, написанного в JS;) Вы правы, это не всегда хорошо работает с регулярными выражениями. Вот несколько примеров:

rexl.re = {
  NAME: /^(?!\d)(?:\w)+|^"(?:[^"]|"")+"/,
  UNQUOTED_LITERAL: /^@(?:(?!\d)(?:\w|\:)+|^"(?:[^"]|"")+")\[[^\]]+\]/,
  QUOTED_LITERAL: /^'(?:[^']|'')*'/,
  NUMERIC_LITERAL: /^[0-9]+(?:\.[0-9]*(?:[eE][-+][0-9]+)?)?/,
  SYMBOL: /^(?:==|=|<>|<=|<|>=|>|!~~|!~|~~|~|!==|!=|!~=|!~|!|&|\||\.|\:|,|\(|\)|\[|\]|\{|\}|\?|\:|;|@|\^|\/\+|\/|\*|\+|-)/
};

Это в основном отлично - только UNQUITED_LITERAL не распознается, иначе все в порядке. Но теперь сделайте небольшое дополнение к нему:

rexl.re = {
  NAME: /^(?!\d)(?:\w)+|^"(?:[^"]|"")+"/,
  UNQUOTED_LITERAL: /^@(?:(?!\d)(?:\w|\:)+|^"(?:[^"]|"")+")\[[^\]]+\]/,
  QUOTED_LITERAL: /^'(?:[^']|'')*'/,
  NUMERIC_LITERAL: /^[0-9]+(?:\.[0-9]*(?:[eE][-+][0-9]+)?)?/,
  SYMBOL: /^(?:==|=|<>|<=|<|>=|>|!~~|!~|~~|~|!==|!=|!~=|!~|!|&|\||\.|\:|,|\(|\)|\[|\]|\{|\}|\?|\:|;|@|\^|\/\+|\/|\*|\+|-)/
};
str = '"';

Теперь все после NAME's regexp испортится. Он делает 1 большую строку. Я думаю, что последняя проблема в том, что токен String слишком жадный. Первый может быть слишком умным регулярным выражением для токена regex.

Изменить. Я думаю, что исправлено регулярное выражение для токена regex. В коде замените строки 146-153 (целая часть следующих символов) следующим выражением:

([^/]|(?<!\\)(?<=\\)/)*

Идея состоит в том, чтобы разрешить все, кроме /, также разрешить \/, но не разрешать \\/.

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

    case 'UNQUOTED_LITERAL': 
    case 'QUOTED_LITERAL': {
        this._js =  "e.str(\"" + this.value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\")";
        break;
    }

Изменить. Еще один случай. По-видимому, он слишком жадничает по ключевым словам. См. Случай:

var clazz = function() {
    if (clazz.__) return delete(clazz.__);
    this.constructor = clazz;
    if(constructor)
        constructor.apply(this, arguments);
};

Он лексирует его как: (keyword, const), (id, ructor). То же самое происходит и для идентификатора inherits: in и herits.

Ответ 2

Пример: первое вхождение / 2 /i ниже (присвоение a) должно tokenize как Div, NumericLiteral, Div, Identifier, потому что оно находится в контексте InputElementDiv. Второе вхождение (назначение b) должно быть равнозначно как RegularExpressionLiteral, потому что оно находится в контексте InputElementRegExp.

i = 1;
var a = 1 / 2 /i;
console.info(a); // ⇒ 0.5
console.info(typeof a); // number

var b = 1 + / 2 /i;
console.info(b); // ⇒ 1/2/i
console.info(typeof b); // ⇒ string

Источник:

Для лексической грамматики есть два символа цели. Символ InputElementDiv используется в тех синтаксических контекстах грамматики, где разрешен оператор деления (/) или разделения (/=). Символ InputElementRegExp используется в других контекстах синтаксической грамматики.

Обратите внимание, что в синтаксической грамматике существуют контексты, где синтаксическая грамматика разрешена как делением, так и элементом RegularExpressionLiteral; однако, поскольку лексическая грамматика использует символ цели InputElementDiv в таких случаях, начальная косая черта не распознается как начало литерала регулярного выражения в таком контексте. В качестве обходного пути можно заключить литерал регулярного выражения в круглые скобки. - Стандарт ECMA-262 3rd Edition - декабрь 1999 г., стр. 11

Ответ 3

Простота вашего решения для обработки этой волосатой проблемы очень крутая, но я заметил, что она не совсем справляется с изменением синтаксиса something.property для ES5, что позволяет зарезервировать слова, следующие за .. I.e., a.if = 'foo'; (function () {a.if /= 3;});, является допустимым утверждением в некоторыми недавними реализациями.

Если я ошибаюсь, в любом случае существует только одно использование . для свойств, поэтому исправление может добавить дополнительное состояние, следующее за ., которое принимает только токен identifierName (это то, что использует идентификатор, но оно не отклоняет зарезервированные слова), вероятно, сделает трюк. (Очевидно, состояние div следует, как обычно).

Ответ 4

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

var g = 3, x = { valueOf: function() { return 6;} } /2/g;

Слэши должны анализироваться как операторы деления, в результате чего x присваивается числовое значение 1. Ваш лексер считает, что он является регулярным выражением. Невозможно правильно обрабатывать все варианты этого случая, не поддерживая стек контекстов группировки, чтобы отличать конец блока (ожидание regexp), конец оператора функции (ожидать regexp), конец выражения функции (ожидать деления) и конец объектного литерала (ожидать деления).

Ответ 5

Правильно ли он работает для этого кода (у него не должно быть точки с запятой, оно вызывает ошибку при правильном лексировании)?

function square(num) {
    var result;
    var f = function (x) {
        return x * x;
    }
    (result = f(num));
    return result;
}

Если это так, работает ли он правильно для этого кода, который полагается на вставку с запятой?

function square(num) {
    var f = function (x) {
        return x * x;
    }
    return f(num);
}