Как реализовать лексический анализ в Javascript

Привет, народ, спасибо, что читаете

В настоящее время я пытаюсь сделать калькулятор в стиле Google. Вы вводите строку, она определяет, можно ли ее вычислить, и возвращает результат.

Я медленно начал с основ: + - / * и обработки скобок.

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

Этот вид работы легко применим к таким языкам, как Lex и Yacc, за исключением того, что я разрабатываю приложение только для Javascript.

Я попытался транскрибировать идею в Javascript, но не могу понять, как правильно и красиво обрабатывать все, особенно вложенные скобки.


Анализ

Давайте определим, что такое запрос калькулятора:

// NON TERMINAL EXPRESSIONS //
query     -> statement
query     -> ε // means end of query

statement -> statement operator statement
statement -> ( statement )
statement -> prefix statement
statement -> number

number    -> integer
number    -> float

// TERMINAL EXPRESSIONS //
operator  -> [+*/%^-]

prefix    -> -

integer   -> [0-9]+

float     -> [0-9]+[.,][0-9]+

Javascript

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

(я добавил пробелы, чтобы было читабельнее)

var calcPat = 
/^ (\s*
    ( ([+/*%^-]) | ([0-9]+) | ([0-9]+[.,][0-9]+) | (\() | (\)) )
)+ \s* $/;

Если этот тест проходит успешно, запрос является лексически правильным и требует проверки грамматики, чтобы определить, можно ли его вычислить. Это сложная часть

Я не буду вставлять код, потому что он не чист и непонятен, но я собираюсь объяснить процесс, которому я следовал, и почему я застрял:

Я создал метод isStatement(string), который должен вызывать себя рекурсивно. Основная идея состоит в том, чтобы разбить строку на «потенциальные» операторы и проверить, действительно ли они являются операторами, и сформировать их целиком.
Процесс следующий:

-Если первые два токена представляют собой число, за которым следует оператор:

-Then,
-- Если оставшаяся часть представляет собой всего одну лексему и является числом:
--- Тогда это оператор.
--- В противном случае проверьте, образуют ли оставшиеся лексемы оператор ( рекурсивный вызов)

-Иначе, если первая лексема является круглой скобкой
-Затем, найдите соответствующую закрывающую круглую скобку и проверьте, является ли то, что находится внутри, выражением (рекурсией)
-- Также проверьте, есть ли что-то после закрывающей круглой скобки и образует ли она оператор, связанный со структурой скобок.


В чем проблема ?

Моя проблема в том, что я не могу найти подходящие скобки, когда есть вложенные структуры. Как я могу это сделать? Кроме того, как вы можете видеть, это не особенно общий и чистый алгоритм проверки грамматики. Есть ли у вас какие-либо идеи по улучшению этого шаблона?

Большое спасибо, что нашли время, чтобы прочитать все. Гаэль

(PS: Как вы, наверное, заметили, я не носитель английского языка! Извините за ошибки и все такое!)


person Gabriel S.    schedule 18.01.2011    source источник
comment
Вы можете попробовать этот инструмент: pegjs.majda.cz/online   -  person Bart Kiers    schedule 18.01.2011
comment
Это довольно круто, но (судя по тому, как это называется) он производит синтаксические анализаторы PEG (я полагаю, синтаксические анализаторы Packrat), которые на самом деле совершенно не нужны. Классический подход лексер + синтаксический анализатор предназначен для создания синтаксических анализаторов LL или LR для контекстно-свободных грамматик (или почти контекстно-свободных), а PEG описывает другой класс языка, чем CFG.   -  person Pointy    schedule 18.01.2011
comment
Да, и особенно следует отметить тот факт, что с парсером packrat для языка PEG вам вообще не нужен лексический анализатор :-)   -  person Pointy    schedule 18.01.2011
comment
@bart-kiers: отличный инструмент, спасибо. Пару лет назад я пытался сам создать парсер и сдался (к счастью, это было просто хобби, а не требование работы). Возможно, мне придется снять этот проект с полки.   -  person keithjgrant    schedule 18.01.2011
comment
@bart-kiers: Спасибо за ссылку, мне удалось создать из нее работающий парсер, но, поскольку я не знаю, как это работает и других юридических вещей, я не могу просто начать зарабатывать деньги на их спине!   -  person Gabriel S.    schedule 18.01.2011


Ответы (1)


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

  • Грамматика токенов — это набор шаблонов (обычно регулярных выражений), которые описывают токены для анализируемого языка. Регулярные выражения — это выражения над набором символов.

  • Языковая грамматика (или, я полагаю, целевая грамматика) — это грамматика языка, который вы хотите проанализировать. Эта грамматика выражается в терминах токенов.

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

Теперь, когда у вас есть регулярные выражения Javascript для работы, вы можете создать регулярное выражение, предназначенное для сопоставления строки токенов. Хитрость заключается в том, чтобы найти способ определить, какой токен был сопоставлен из списка возможных. Механизм регулярных выражений Javascript может вернуть вам массивы групп, поэтому, возможно, вы могли бы создать что-то поверх этого.

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

person Pointy    schedule 18.01.2011
comment
Действительно, иначе вы попадете в ловушку классического ответа на RegEx соответствует открытым тегам, кроме автономных тегов XHTML. - person Marcel Korpel; 18.01.2011
comment
@Pointy Спасибо за ваше время. Думая и бродя по парсерам и токенам, я наткнулся на эту статью от Д. Крокфорда, которую помнил, читал давным-давно, вообще ничего не понимая. Теперь это имеет гораздо больше смысла. Как вы думаете, стоит ли углубляться в это и на самом деле пытаться построить свой собственный парсер? javascript.crockford.com/tdop/tdop.html - person Gabriel S.; 18.01.2011
comment
@Marcel Korpel: Спасибо за этот совет! Это как бы заставляет меня спросить, как парсить XHTML, просто чтобы увидеть реакцию людей :D - person Gabriel S.; 18.01.2011
comment
@Gaël создание парсеров очень познавательно и очень весело; это хороший способ потренировать навыки разработки программ. Что касается этой конкретной техники синтаксического анализа, ну, я не так уж много о ней знаю. Я полагаю, что это так же полезно, как и все остальное. - person Pointy; 18.01.2011
comment
@Pointy Наконец-то мне удалось создать собственный парсер, используя ссылку, которую я разместил выше. Мысль о том, что я могу использовать это для разбора самого Javascript, просто ошеломляет и доставляет массу удовольствия! Спасибо ! - person Gabriel S.; 19.01.2011
comment
Круто, я рад, что все получилось. Вчера я потратил час или около того на сборку простой функции для генерации объекта лексического анализатора из списка регулярных выражений токенов. Это был забавный проект, хотя я не знаю, буду ли я когда-нибудь использовать код :-) - person Pointy; 19.01.2011
comment
@Gaël — вас может заинтересовать этот проект: github.com/aaditmshah/lexer Он создает динамический lexer из набора правил (шаблоны + действия). Он также поддерживает начальные условия, несколько возвращаемых значений и глобальные шаблоны. - person Aadit M Shah; 27.01.2013