Грамматика - как сопоставить необязательные и обязательные пробелы до и после слов?

Я использую 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

Здесь мы говорим:

  1. Текст состоит из одного или нескольких предложений
  2. Новые строки могут находиться до и после каждого предложения
  3. Предложение может начинаться с последовательности %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"
  ]
]
Другие вопросы по тегам