Какие конструкции Javascript делает JsLex неправильно lex?
JsLex - это лексер Javascript, который я написал на Python. Это делает хорошую работу для работы дня (или около того), но я уверен, что есть случаи, когда это получается неправильно. В частности, он ничего не понимает относительно вставки точек с запятой, и, вероятно, есть способы, которые важны для lexing. Я просто не знаю кто они.
Какой код Javascript делает JsLex lex неправильно? Меня особенно интересует действительный источник Javascript, в котором JsLex неправильно определяет литералы регулярных выражений.
Просто чтобы прояснить, под лексизмом я подразумеваю идентификацию токенов в исходном файле. JsLex не пытается разобрать Javascript, а тем более выполнить его. Я написал JsLex для полного лексинга, хотя, если честно, я был бы счастлив, если бы он только смог успешно найти все литералы регулярных выражений.
5 ответов
Интересно, что я попробовал ваш лексер на коде моего лексера / оценщика, написанного на 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 (вся часть "следующие символы") следующим выражением:
([^/]|(?<!\\)(?<=\\)/)*
Идея состоит в том, чтобы позволить все, кроме /
также разрешить \/
, но не разрешать \\/
,
Изменить: еще один интересный случай, проходит после исправления, но может быть интересно добавить в качестве встроенного теста:
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
,
Я сам думал о проблемах написания лексера для JavaScript, и я просто наткнулся на вашу реализацию в поиске хороших методов. Я нашел случай, когда у тебя не получилось, и подумал, что поделюсь, если тебе все еще интересно:
var g = 3, x = { valueOf: function() { return 6;} } /2/g;
Косые черты должны быть проанализированы как операторы деления, в результате чего x присваивается числовое значение 1. Ваш лексер считает, что это регулярное выражение. Невозможно правильно обработать все варианты этого случая, не поддерживая стек контекстов группировки, чтобы различать конец блока (ожидание регулярного выражения), конец оператора функции (ожидаемое регулярное выражение), конец выражения функции (ожидать разделение), и конец литерала объекта (ожидаемое разделение).
Простота вашего решения для решения этой сложной проблемы очень классная, но я заметил, что она не совсем справляется с изменением something.property
синтаксис для ES5, который позволяет зарезервированные слова после .
, То есть, a.if = 'foo'; (function () {a.if /= 3;});
, является допустимым утверждением в некоторых недавних реализациях.
Если я не ошибаюсь, есть только одно использование .
в любом случае для свойств, поэтому исправление может добавлять дополнительное состояние после .
который принимает только токен identifierName (который используется идентификатором, но не отклоняет зарезервированные слова), вероятно, сработает. (Очевидно, что состояние div следует как обычно.)
Пример: первое появление / 2 /i
ниже (назначение a
) должен маркировать как 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, 3-е издание - декабрь 1999 г., с. 11
Работает ли он правильно для этого кода (у него не должно быть точки с запятой; при правильной лексизации выдается ошибка)?
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);
}