6

我正在尝试使用Superpower创建一个解析器。我已经查看了我在 repo 中找到的示例,但它们有点难以理解,至少对于像我这样的初学者来说:) 所以我带着这个小挑战来了。

我发明了一个非常基本的语法来学习。我想到了一种电梯,它遵循一系列指令来上升、下降和等待。

例子:

(UP 100),
(DOWN 200),
(DOWN 100),
(DOWN @1),
(UP @3),
(WAIT),
(UP 300)

如您所见,它由逗号分隔的要移动的动词列表组成,例如电梯。

  • 动词是UPDOWNWAIT
  • 每个动词都用括号括起来:(
  • UPDOWN需要一个绝对数或一个相对数,表示电梯应该移动到的楼层。相对楼层数字在数字前带有 @。
  • WAIT不接受任何号码,因为它会暂时停止电梯。

我真的很想学习如何为这个语法创建一个基于标记的解析器作为开始,以便了解如何使用 SuperPower。

4

2 回答 2

11

编写任何Superpower解析器的第 1 步是弄清楚令牌类型是什么。你有类似的东西:

// ECL - Elevator Control Language ;-)
enum EclToken {
    LParen,
    RParen,
    UpKeyword,
    DownKeyword,
    WaitKeyword,
    AtSymbol,
    Number,
    Comma
}

第 2 步,写一个Tokenizer<EclToken>. 这是 Superpower v1 的一项直接编程任务 - 没有多少助手可以依靠,您只需要像示例中那样编写代码即可。

分词器获取输入字符串,去掉空格,并计算出分词序列是什么。

对于您的示例输入,第一行将是:

// (UP 100),
LParen, UpKeyword, Number, RParen, Comma

对于Number包含内容的标记,与 关联的跨度Result<EclToken>将指向与标记对应的输入字符串部分。在这一行中,数字将是一个TextSpan覆盖100

第 3 步是弄清楚您要将输入解析. 对于具有嵌套表达式的编程语言,这通常是 AST。对于 ECL 示例,它非常简单,因此您可以将其缩减为:

struct ElevatorCommand {        
    public int Distance; // + or -
    public bool IsRelative;
}

第四步,解析器。这通常嵌入在静态类中。ElevatorCommand[]解析器的工作是从更简单的结果(数字、运动)构建更复杂的结果(此处为 an )。

这就是 Superpower 进行繁重工作的地方,尤其是在期望和错误方面。

static class EclParser 
{
    static TokenListParser<EclToken, int> Number =
        Token.EqualTo(EclToken.Number).Apply(Numerics.IntegerInt32);
}

我们要做的第一件事是定义数字解析器;这将内置应用TextParser<int>EclToken.Number跨度的内容。

您可以在此示例中看到更多解析机制。

还有一些线索可以帮助您找到方法(不检查语法,更不用说编译/测试了):

    static TokenListParser<EclToken, ElevatorCommand> Up =
        from _ in Token.EqualTo(EclToken.UpKeyword)
        from distance in Number
        select new ElevatorCommand {
            Distance = distance,
            IsRelative = false
        };

    static TokenListParser<EclToken, ElevatorCommand> Command =
        from lp in Token.EqualTo(EclToken.LParen)
        from command in Up // .Or(Down).Or(Wait)
        from rp in Token.EqualTo(EclToken.RParen)
        select command;

    static TokenListParser<EclToken, ElevatorCommand[]> Commands =
        Command.ManyDelimitedBy(Token.EqualTo(EclToken.Comma));
}

Commands是可以应用于输入的完整解析器。

最好逐步构建解析器,在每个较小的解析器上测试它们预期解析的输入块。

于 2017-12-10T21:47:40.710 回答
3

好的,我终于设法得到它。@Nicholas Blumhardt 的指导并没有那么困难 :)

在 GitHub 中创建了一个项目来说明该场景。由于课程对于帖子来说很大,我链接到文件:

于 2017-12-12T16:53:39.280 回答