0

我正在尝试使用RIS 格式的内容编写语法

文件示例:

TY  - JOUR
KW  - foo
KW  - bar
ER  - 

文件*.ris总是以 tag 开始,以 tagTY结束ER。在两者之间可以有许多其他标签,如KW(关键字)。

规范说单个KW语句可以跨越多行。

所以这:

TY  - JOUR
KW  - foo
bar
baz
KW  - bat
ER  - 

相当于:

TY  - JOUR
KW  - foo bar baz
KW  - bat
ER  - 

我正在努力想出一个这样的语法:

关键字以开头,KW后跟 -以下之一:

  • 直到行尾的字母
  • 字母直到行尾和任何其他行直到下一个关键字

无论我尝试什么,最终都会“吞下”所有其他语句,例如,第一个多行关键字捕获它之后的所有其他内容。

你会怎么写这个规则?我不一定对特定的答案感兴趣。任何能触发我“啊哈”时刻的事情都可以!

4

1 回答 1

0

我绝对不是很擅长设计语法(你可能已经猜到了),但这触发了我的Aha时刻:

很多人向我指出,为 Nearley 编写语法很难。问题是,一般来说,写语法是非常困难的。某些与语法相关的问题可以证明是不可判定的,这无济于事。

https://nearley.js.org/docs/how-to-grammar-good

和:

使用分词器有很多好处。它……</p>

  • …通常会使您的解析器速度提高一个数量级以上。
  • …允许您编写更清晰、更易于维护的语法。
  • …在某些情况下有助于避免模棱两可的语法。[...]

https://nearley.js.org/docs/tokenizers

我知道建议使用

Nearley 支持并推荐 Moo,一个超快速的词法分析器。

https://nearley.js.org/docs/tokenizers

所以我四处搜索,在 YouTube 上找到了这个很棒的教程,它绝对让我畅通无阻。非常感谢@airportyh!

起初我认为这对于我的用例来说太复杂了,但事实证明,使用词法分析器实际上使事情变得既可能又更简单!


为了简单起见,我将提供一个带有截断 RIS 文件的解决方案:

样本.ris

KW  - foo
bar
baz
KW  - bat

这个文件应该['foo bar baz', 'bat']在解析后产生。

首先让我们安装一些东西

yarn add nearley
yarn add moo

现在让我们定义我们的词法分析器

词法分析器

const moo = require('moo');

const lexer =
  moo.compile
    ( { NL: {match: /[\n]/, lineBreaks: true}
      , KW: 'KW'
      , SEPARATOR: "  - "
      , CONTENT: /[a-z]+/
      }
    );

module.exports = lexer;

我们定义了四个令牌:

  1. 换行符NL
  2. 关键词KW……关键词!
  3. SEPARATOR标签与其内容之间的
  4. CONTENT标签的

接下来让我们定义我们的语法

语法网

@{% const lexer = require('./lexer.js'); %}
@lexer lexer
@builtin "whitespace.ne"

RECORD -> _ KW:+                {% ([, keywords]) => [].concat(...keywords) %}
KW     -> %KW %SEPARATOR LINE:+ {% ([,,lines])    => lines.join(' ')        %}
LINE   -> %CONTENT __           {% ([{value}])    => value                  %}

%注意:看看我们如何通过前缀!来引用词法分析器中定义的标记。

现在我们需要编译我们的语法

Nearley 附带一个编译器:

yarn -s nearleyc grammar.ne > grammar.js

您还可以compile在您的package.json:

{

  ...

  "scripts": {
    "compile": "nearleyc grammar.ne > grammar.js",
  }

  ...

}

最后让我们构建一个解析器并使用它!

const nearley = require('nearley');
const grammar = require('./grammar.js');

module.exports =
  str => {
    const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
    parser.feed(str);
    return parser.results[0];
  };

注意:这是需要编译的语法,即grammar.js

让我们给它写一些文字:

const parser = require('./parser.js');

parser(`
KW  - foo
bar
baz
KW  - bat
`);
//=> [ 'foo bar baz', 'bat' ]

最后提示:您还可以使用以下方法测试您的语法nearley-test

cat sample.ris | yarn -s nearley-test -- -q grammar.js
于 2020-06-24T20:32:30.843 回答