nut-parser v1.0.9
nut-parser
Установка
npm install nut-parser
Использование
Подключение
let {Lexer, Parser} = require('nut-parser');
Принцип работы лексера
Лексер перебирает символы из входной строки, и для каждого символа поочерёдно применяет все добавленные правила (о добавлении правил - ниже). Если символ соответствует правилу - правило сохраняется, если нет - правило отбрасывается и не применяется для следующих символов. Как только список применяемых правил окажется пуст, лексер вернётся на один символ назад. Затем добавит в результирующий массив лексему с именем последнего актуального правила и контентом - строкой от начала разбора и до текущей позиции. Дальнейший разбор продолжится с текущей позиции с применением всех правил.
Для примера разберём строку "43.3 ы"
, c применением 2х правил. Первое правило для пробелов (любое количество подряд):
function(char, state){
if(state === 1 && char === ' ') return 1;
return 0;
};
Второе правило для чисел (С точкой или без):
function(char, state){
if(state === 1 && /[0-9]/.test(char)) return 2;
if(state === 2 && /[0-9]/.test(char)) {
if(/[0-9]/.test(char)) return 2;
if(/[.]/.test(char)) return -3;
}
if(state === -3 && /[0-9]/.test(char)) return 2;
return 0;
};
Первый символ, который поступает в лексер - "4". В это время state
(состояние) всех правил равно 1. Первое правило получает char = "4" state = 1 и возвращает 0
(поэтому для последующих символов это правило применяться не будет. Т.е. данная лексема уже точно не пробел). Второе правило получает char = "4" state = 1 и возвращает 2
.
Далее лексер проверит второй символ - "3". Первое правило проверяться не будет, так как ранее функция вернула 0
. Второе правило получит char = "3" state = 2 (на прошлом символе функция вернула состояние 2
).
Далее третий символ - ".". Первая функция по-прежнему не применяется. А вторая применяется с аргументами char = "." state = 2, и возвращает -3
(отрицательное число означает, что для следующих символов это правило применяться будет, но в качестве конечного символа не рассматривается. В данном случае это признак того, что число не может заканчиваться точкой).
Четвёртый символ - "3". Второе правило примет char = "3" state = -3, вернёт 2
.
Следующий символ " ". Первое правило по-прежнему не работает. Второе примет char = " " state = 2, и вернёт 0
. На этом этапе у нас нет работающих правил (все вернули 0
), поэтому лексер вернётся на один символ назад и создаст лексему из единственного работающего правила для этого символа (символ "3", второе правило). В результирующий массив будет записана лексема:
{name: "имя правила для чисел", content: "43.3", begin: 0, end: 4}
,
begin
и end
- позиция лексемы в исходной строке.
Далее лексер будет рассматривать остаток строки - " ы". Все правила снова получат состояние 1. Первый символ - " ". Первое правило получает char = " " state = 1 и возвращает 1
. Второе правило получает char = " " state = 1 и возвращает 0
. Для второго символа "ы" теперь актуально только первое правило, которое получает char = "ы" state = 1 и возвращает 0
. Теперь актуальных правил нет, лексер возвращается на один символ назад и добавляет лексему в результирующий массив:
{name: "имя правила для пробелов", content: " ", begin: 4, end: 5}
Далее лексер рассмотрит остаток строки - "ы". Оба правила вернут 0
для первого символа, и лексер создаст лексему из этого символа с именем "unknown".
Задание правил для лексера
Задание правил, по которым будут создаваться лексемы происходит через метод лексера addRule
объекта Lexer
.
Lexer.addRule(lang, name, cb)
lang
(строка) - язык для которого будет действовать правило;name
(строка) - имя правила. Будет помещено в лексему в свойство name;cb
- Функция обработки одного символа (подробно рассмотрена в предыдущем разделе), принимает два параметраchar
- текущий (проверяемый) символ,state
- состояние, которое функция вернула в прошлый раз (для первого символа - 1). Возвращает новое состояние (0 - символ не соответствует правилам; отрицательное число - символ соответствует правилу, но не может быть последним; положительное число - символ соответствует правилу).
Пример для десятичных целых чисел языка JavaScript:
Lexer.addRule('js', 'number', function(char, state){
if(state === 1 && /[0-9]/.test(char)) return 1;
return 0;
});
Запуск лексера
let lexemes = Lexer.run(text, lang);
lang
(строка) - язык текста. Из тех языков, которые добавлялись для лексера через методaddRule
.
В lexemes
попадёт массив лексем, который можно передать парсеру, или false
, если нет правил разбора для данного языка.
Задание правил для парсера
Задание правил, по которым будет осуществляться разбор лексем происходит через метод лексера addRule
объекта Parser
.
Parser.addRule(lang, state, cb)
lang
(строка) - язык для которого будет действовать правило;state
(строка) - текущее состояние парсера;cb
- Функция обработки лексемы, принимает два параметраlex
- текущая лексема,memory
- глобальный объект который вернёт парсер после окончания работы. Возврашает имя нового состояния, или объект со свойствомparseError
- если имеет место синтаксическая ошибка (в таком случае парсер завершит свою работу и вернёт этот объект, добавив в него информацию о лексеме в свойствоerrorLexeme
).
Запуск парсера
let result = Parser.run(lexemes, lang);
lexemes
(массив) - лексемы полученные от лексераlang
(строка) - язык парсинга. Из тех языков, которые добавлялись для парсера через методaddRule
.
В result
попадёт объект memory
, который модифицировался через addRule
, или объект ошибки со свойством parseError
.