使用Elix / Erlang中的Yecc解析器附加项目进行映射

时间:2017-06-13 20:51:50

标签: erlang elixir leex yecc

我正在尝试使用Elixir中的Leex / Yecc解析特定的日志文件。经过几个小时,我得到了最简单的方案。但是我想进入下一步,但我无法弄清楚如何这样做。

首先,这是一个日志格式的例子:

[!] plugin error detected
 |  check the version of the plugin

我的简单尝试只是第一行,但是它们的多个条目,如下:

[!] plugin error detected
[!] plugin error 2 detected
[!] plugin error 3 detected

这工作并给了我一个很好的地图,其中包含文字和日志行类型(警告):

iex(20)> LogParser.parse("[!] a big warning\n[!] another warning")
[%{text: "a big warning", type: :warning},
 %{text: "another warning", type: :warning}]

这很完美。但如上所示,日志行可以在下一行继续,用管道符|表示。我的词法分析器具有管道字符,解析器可以理解它,但我想要的是下一行要附加到我的地图的text值。现在它只是作为字符串附加在地图中。所以而不是:

[%{text: "a big warning ", type: :warning}, " continues on next line"]

我需要:

[%{text: "a big warning continues on next line", type: :warning}]

我在网上查看了一些示例,但大多数都有非常明确的'结束'标记,例如结束标记或结束标记,然后我仍然不清楚如何添加属性以便最终AST是正确的。

为了完整起见,这是我的词法分析员:

Definitions.

Char          = [a-zA-Z0-9\.\s\,\[\]]
Word          = [^\t\s\.#"=]+
Space         = [\s\t]
New_Line      = [\n]
%New_Line      = \n|\r\n|\r
Type_Regular  = \[\s\]\s
Type_Warning  = \[!\]\s
Pipe          = \|

Rules.

{Type_Regular}  : {token, {type_regular,  TokenLine}}.
{Type_Warning}  : {token, {type_warning,  TokenLine}}.
{Char}          : {token, {char, TokenLine, TokenChars}}.
{Space}         : skip_token.
{Pipe}          : {token, {pipe, TokenLine}}.
{New_Line}      : skip_token.

Erlang code.

我的解析器:

Nonterminals lines line line_content chars.
Terminals type_regular type_warning char pipe.
Rootsymbol lines.

lines -> line lines : ['$1'|['$2']].
lines -> line : '$1'.

line -> pipe line_content : '$2'.
line -> type_regular line_content : #{type => regular, text => '$2'}.
line -> type_warning line_content : #{type => warning, text => '$2'}.

line_content -> chars : '$1'.
line_content -> pipe chars : '$1'.

chars -> char chars : unicode:characters_to_binary([get_value('$1')] ++ '$2').
chars -> char : unicode:characters_to_binary([get_value('$1')]).

Erlang code.

get_value({_, _, Value}) -> Value.

如果你到目前为止,谢谢已经!如果有人能帮忙,甚至更大的谢意!

1 个答案:

答案 0 :(得分:1)

我建议添加line_content规则来处理由管道分隔的多行,并删除规则line -> pipe line_content : '$2'.

[]子句中的'$2'周围也有一个不必要的lines,单行子句应该返回一个列表,以便与前一个子句的返回值一致,所以你不要以不正确的名单结束。

通过这四项变更,

-lines -> line lines : ['$1'|['$2']].
+lines -> line lines : ['$1'|'$2'].
-lines -> line : '$1'.
+lines -> line : ['$1'].

-line -> pipe line_content : '$2'.
 line -> type_regular line_content : #{type => regular, text => '$2'}.
 line -> type_warning line_content : #{type => warning, text => '$2'}.

 line_content -> chars : '$1'.
-line_content -> pipe chars : '$1'.
+line_content -> line_content pipe chars : <<'$1'/binary, '$3'/binary>>.

我可以很好地解析多行文字:

Belino.parse("[!] Look at the error")
Belino.parse("[!] plugin error detected
 | check the version of the plugin")
Belino.parse("[!] a
 | warning
 [ ] a
 | regular
 [ ] another
 | regular
 [!] and another
 | warning")

输出:

[%{text: "Look at the error", type: :warning}]
[%{text: "plugin error detected  check the version of the plugin",
   type: :warning}]
[%{text: "a  warning ", type: :warning}, %{text: "a  regular ", type: :regular},
 %{text: "another  regular ", type: :regular},
 %{text: "and another  warning", type: :warning}]