Какие конструкции Javascript неправильно использует lex в JsLex? - PullRequest
10 голосов
/ 04 апреля 2011

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

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

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

Ответы [ 5 ]

7 голосов
/ 07 апреля 2011

Интересно, что я попробовал ваш лексер на коде моего лексера / оценщика, написанного на 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 испортилось.Это делает 1 большую строку.Я думаю, что последняя проблема в том, что маркер String слишком жадный.Первый может быть слишком умным регулярным выражением для regex токена.

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

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

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

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

    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.

1 голос
/ 28 октября 2012

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

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

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

1 голос
/ 03 мая 2011

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

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

1 голос
/ 10 апреля 2011

Пример: первое вхождение / 2 /i ниже (присвоение a) должно маркироваться как Div , NumericLiteral , Div , Идентификатор , поскольку он находится в контексте 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, 3-е издание - декабрь 1999 г. , с. 11

0 голосов
/ 04 апреля 2011

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

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);
}
...