1.0.9 • Published 8 years ago

nut-parser v1.0.9

Weekly downloads
2
License
ISC
Repository
github
Last release
8 years ago

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.

Проекты на основе nut-parser