Грамматика - как сопоставить необязательные и обязательные пробелы до и после слов?
Я использую Nearley и moo, чтобы придумать довольно сложную грамматику. Кажется, он работает нормально, ЗА ИСКЛЮЧЕНИЕМ моих требований к пробелам. Мне нужно требовать пробелы, когда это необходимо, и разрешать их, когда нет, сохраняя при этом однозначную грамматику.
Например:
After dinner, I went to bed.
Мне нужно требовать пробелы между словами, но разрешать его вокруг запятой. Таким образом, справедливо и следующее:
After dinner , I went to bed.
After dinner,I went to bed.
Ниже приведена небольшая грамматика, которая пытается это сделать. Если вы не понимаете синтаксиса, разобраться в нем довольно легко.
// Required whitespace
rws : [ \t]+
// Optional whitespace
ows : [ \t]*
sentence -> words %ows "," sentence
| words
words -> word %rws words
-> word
word -> [a-zA-Z]
В грамматике могут быть проблемы, но идея та же. Это становится двусмысленной грамматикой. Как я могу определить однозначную грамматику, ожидая дополнительных и обязательных пробелов?
2 ответа
Я не знаком с Nearly или Moo, но регулярное выражение может быть
whitespace : ([ \t]*,[ \t]*|[ \t])
и ваша грамматика станет
word %whitespace word
Надеюсь, это имеет смысл, и я не испортил язык полностью.
Я считаю, что использование moo-lexer упрощает мою грамматику, и в результате я обычно трачу меньше времени на исправление двусмысленных грамматик.
Я не специалист по грамматике, но вот что я бы сделал:
lexer.js
word
будет соответствовать последовательности символовcomma
будет соответствовать" , "
," ,"
,", "
и","
.space
будет соответствовать одному пространству" "
period
будет соответствовать одному периоду"."
nl
будет соответствовать одному или нескольким символам новой строки.
const moo = require('moo');
const lexer =
moo.compile
( { word: /[a-zA-Z]+/
, comma:/ ?, ?/
, space: / /
, period: /\./
, nl: {match: /\n+/, lineBreaks: true}
}
);
module.exports = lexer;
grammar.ne
Здесь мы говорим:
- Текст состоит из одного или нескольких предложений
- Новые строки могут находиться до и после каждого предложения
- Предложение может начинаться с последовательности
%word
за которым следует либо%comma
или%space
и должен закончить%word
за которым следует%period.
Все правила постобработки - сглаживание списка токенов и извлечение
.value
из токенов, чтобы мы получили списки слов.
@{% const lexer = require("./lexer.js"); %}
@lexer lexer
text
-> %nl sentence:+ {% ([_, sentences]) => sentences %}
sentence
-> seq:* %word %period %nl {% ([seq, w, p, n]) => [...seq, w.value] %}
seq
-> (%word %space) {% ([[w]]) => w.value %}
| (%word %comma) {% ([[w]]) => w.value %}
Эта грамматика позволяет разобрать этот текст:
After breakfast, I went to work.
After lunch , I went to my desk.
After the pub,I went home.
sleep.
Пример:
const nearley = require('nearley');
const grammar = require('./grammar.js');
const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
parser.feed(`
After breakfast, I went to work.
After lunch , I went to my desk.
After the pub,I went home.
sleep.
`);
if (parser.results.length > 1) throw new Error('grammar is ambiguous');
JSON.stringify(parser.results[0], null, 2);
Вывод:
[
[
"After",
"breakfast",
"I",
"went",
"to",
"work"
],
[
"After",
"lunch",
"I",
"went",
"to",
"my",
"desk"
],
[
"After",
"the",
"pub",
"I",
"went",
"home"
],
[
"sleep"
]
]