【问题标题】:Recursive Descent Parser in ErlangErlang 中的递归下降解析器
【发布时间】:2016-03-26 22:28:03
【问题描述】:

我正在尝试在 Erlang 中构建 RDP。到目前为止,我已经阅读并处理了一个令牌文件,我将把它传递给函数,例如 [2,6,3,7,3,2,4,6,3,2,4,4,99](示例应该工作的输入),我需要确保可以通过从默认规则 [bexp0] 转换为一些匹配的终端列表来派生每个字符(或一组)。

get_terminal_list() ->
    [1,2,3,4,5,6,7,8,9,10,11,12,99].

get_prod_list() ->
[bexp0,bexp,bexp1,bterm].

get_sym_list(Prod) ->
    case Prod of
        bexp0 -> [[bexp,99]];
        bexp  -> [[bterm,bexp1]];
        bexp1 -> [[5,bexp,bexp1],[6,bexp,bexp1]];
        bterm -> [[3,bexp,4],[2],[8],[9],[2,10,1],[2,12,1],[2,11,1],[7,bterm]]
    end.

get_sym_list 显示了正在使用的语法 - 其中每个 int 代表一个终端字符,每个子列表是一个集合,即 bterm-> [[7,bterm]] 表示 bterm 可以变成终端 '7' 后跟 non-终端“bterm”。

现在我正在努力以某种方式实现这一点:

Check if first set of rule has some terminal
 if so, check which side, reduce list of tokens from same side until first occurrence of this token,  
     parse this new list (w/o token) with rest of the set of this rule (also without the matched terminal). 
       return {success|failure, list_of_tokens, list_of_rules}  
   if success -> check with new list_of_tokens, default_rule
   if failure, check with old list_of_tokens, and new list_of_rules.

如果规则列表为空,我假设将达到最终状态 - 因此我们已经用尽了所有可能的生产,因此无效,或者
标记列表为空,因此我们已将每个标记/标记集与某个规则匹配

【问题讨论】:

  • 支持,因为它在 erlang 中。
  • 您能添加一个您尝试解析的输入示例吗?什么是行不通的?您是否收到错误或算法似乎没有产生您期望的结果(在这种情况下,请说明您的期望)或其他什么?
  • @Amiramix 向问题添加了示例输入。目前我认为它会进入一个循环,或者一旦它到达一个列表的产品就会出错。
  • 谢谢,但看起来您的函数将两个列表作为初始参数,而不是一个:列表和 NT。您提供了一个列表作为示例输入。
  • @Amiramix 是的,第二个列表是我最初调用它时直接发送给函数的单个原子的列表,[bexp]

标签: parsing recursion erlang


【解决方案1】:

这可能会做你想要的:

-module(parse).
-export([parse1/0, parse1/1, parse2/0, parse2/1]).

parse1() ->
    parse([bexp], [2,6,3,7,3,2,4,6,3,2,4,4,99], fun get_sym_list1/1).

parse1(Input) ->
    parse([bexp], Input, fun get_sym_list1/1).


parse2() ->
    parse([bexp0], [2,6,3,7,3,2,4,6,3,2,4,4,99], fun get_sym_list2/1).

parse2(Input) ->
    parse([bexp0], Input, fun get_sym_list2/1).


parse([], [], _) ->
    true;
parse([], _, _) ->
    false;
parse([X | TX], [X | TY], Fun) ->
    io:format("+ Current:~w\tTokens:~w Input:~w~n", [X, TX, TY]),
    parse(TX, TY, Fun);
parse([X | TX], Input, Fun) ->
    io:format("  Current:~w\tTokens:~w Input:~w~n", [X, TX, Input]),
    case lists:member(X, get_terminal_list()) of
        true -> false;
        false -> lists:any(fun(T) -> parse(T ++ TX, Input, Fun) end, Fun(X))
    end.

get_terminal_list() ->
    [1,2,3,4,5,6,7,8,9,10,11,12,99].

get_sym_list1(Prod) ->
    case Prod of
        bexp    -> [[bexp1],[bterm],[bterm,bexp2]];
        bexp1   -> [[99]];
        bexp2   -> [[5,bterm,bexp2],[6,bterm,bexp2]];
        bterm   -> [[bfactor],[7,bterm]];
        bfactor -> [[3,bexp,4],[bconst],[2,10,1],[2,12,1],[2,11,1],[2]];
        bconst  -> [[8],[9]]
    end.

get_sym_list2(Prod) ->
    case Prod of
        bexp0 -> [[bterm,bexp1]];
        bexp  -> [[bterm,bexp1]];
        bexp1 -> [[5,u1],[6,bexp,bexp1],[99]];
        bterm -> [[u1,4],[2],[8],[9],[2,10,1],[2,12,1],[2,11,1],[7,bterm]];
        u1    -> [[3,bexp]]
    end.

但是,看起来语法或输入列表不正确,因为据我所知,旧语法和新语法都无法解析输入。它似乎工作正常,因为它会解析这样的输入:

41> parse:parse2([2,6,8,5,3,bterm,5,3,9,99,99]).
  Current:bexp0 Tokens:[] Input:[2,6,8,5,3,bterm,5,3,9,99,99]
  Current:bterm Tokens:[bexp1] Input:[2,6,8,5,3,bterm,5,3,9,99,99]
  Current:u1    Tokens:[4,bexp1] Input:[2,6,8,5,3,bterm,5,3,9,99,99]
  Current:3     Tokens:[bexp,4,bexp1] Input:[2,6,8,5,3,bterm,5,3,9,99,99]
+ Current:2     Tokens:[bexp1] Input:[6,8,5,3,bterm,5,3,9,99,99]
  Current:bexp1 Tokens:[] Input:[6,8,5,3,bterm,5,3,9,99,99]
  Current:5     Tokens:[u1] Input:[6,8,5,3,bterm,5,3,9,99,99]
+ Current:6     Tokens:[bexp,bexp1] Input:[8,5,3,bterm,5,3,9,99,99]
  Current:bexp  Tokens:[bexp1] Input:[8,5,3,bterm,5,3,9,99,99]
  Current:bterm Tokens:[bexp1,bexp1] Input:[8,5,3,bterm,5,3,9,99,99]
  Current:u1    Tokens:[4,bexp1,bexp1] Input:[8,5,3,bterm,5,3,9,99,99]
  Current:3     Tokens:[bexp,4,bexp1,bexp1] Input:[8,5,3,bterm,5,3,9,99,99]
  Current:2     Tokens:[bexp1,bexp1] Input:[8,5,3,bterm,5,3,9,99,99]
+ Current:8     Tokens:[bexp1,bexp1] Input:[5,3,bterm,5,3,9,99,99]
  Current:bexp1 Tokens:[bexp1] Input:[5,3,bterm,5,3,9,99,99]
+ Current:5     Tokens:[u1,bexp1] Input:[3,bterm,5,3,9,99,99]
  Current:u1    Tokens:[bexp1] Input:[3,bterm,5,3,9,99,99]
+ Current:3     Tokens:[bexp,bexp1] Input:[bterm,5,3,9,99,99]
  Current:bexp  Tokens:[bexp1] Input:[bterm,5,3,9,99,99]
+ Current:bterm Tokens:[bexp1,bexp1] Input:[5,3,9,99,99]
  Current:bexp1 Tokens:[bexp1] Input:[5,3,9,99,99]
+ Current:5     Tokens:[u1,bexp1] Input:[3,9,99,99]
  Current:u1    Tokens:[bexp1] Input:[3,9,99,99]
+ Current:3     Tokens:[bexp,bexp1] Input:[9,99,99]
  Current:bexp  Tokens:[bexp1] Input:[9,99,99]
  Current:bterm Tokens:[bexp1,bexp1] Input:[9,99,99]
  Current:u1    Tokens:[4,bexp1,bexp1] Input:[9,99,99]
  Current:3     Tokens:[bexp,4,bexp1,bexp1] Input:[9,99,99]
  Current:2     Tokens:[bexp1,bexp1] Input:[9,99,99]
  Current:8     Tokens:[bexp1,bexp1] Input:[9,99,99]
+ Current:9     Tokens:[bexp1,bexp1] Input:[99,99]
  Current:bexp1 Tokens:[bexp1] Input:[99,99]
  Current:5     Tokens:[u1,bexp1] Input:[99,99]
  Current:6     Tokens:[bexp,bexp1,bexp1] Input:[99,99]
+ Current:99    Tokens:[bexp1] Input:[99]
  Current:bexp1 Tokens:[] Input:[99]
  Current:5     Tokens:[u1] Input:[99]
  Current:6     Tokens:[bexp,bexp1] Input:[99]
+ Current:99    Tokens:[] Input:[]
true

顺便说一句,true 表示输入已被解析,false 表示尚未解析。

【讨论】:

  • 那是个漂亮的人,一个大概 7 行的解析器。我正在重新修改语法,也许我可以一次正确,但示例输入是正确的。
  • 是的,Erlang 中的代码可以非常简洁,这是我喜欢的。我很高兴它终于奏效了。顺便说一句,它可能与您手头的项目无关,但通常 Erlang 支持正确的解析器和带有 yecc 和 leex 的标记器,因此您可能会在某个时候研究一下。从 Elixir 的角度来看,这是一个很好的描述如何使用它,但仍然值得一读:andrealeopardi.com/posts/…
  • 也许导致错误的原因是 6 必须以 bterm 开头,所以如果案例失败,也许我们必须尝试一起检查这对?我也更新了规则,但除了确保 99 必须位于输入流的末尾之外,没有什么特别的改变
  • 我建议您从根符号开始,然后将标记替换为生产,并确保在每次停止时输入正确解析。如果语法是自上而下解析的话,从下往上调试解析出来的语法是相当困难的。
猜你喜欢
  • 2015-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多