使用Superpower解析简单的文本语法

时间:2017-12-10 16:37:07

标签: c# .net parsing superpower

我正在尝试使用Superpower创建解析器。我已经看过我在回购中发现的样品,但是它们有点难以理解,至少对于像我这样的初学者来说:)所以我带来了这个小挑战。

我发明了一个非常基本的语法,只是为了学习。我想到了一个电梯,它遵循一系列指令,上下,等待。

示例:

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

如您所见,它包含一个逗号分隔的动词列表,例如电梯。

  • 动词 UP DOWN 等待
  • 每个动词都用括号括起来:()
  • UP DOWN 需要绝对数字相对数字,表示要向的地板电梯应该移动。相对楼层号码在号码前加上@。
  • WAIT 不接受任何号码,因为它会暂停电梯一段时间。

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

2 个答案:

答案 0 :(得分:9)

编写任何Superpower解析器的

第1步是要弄清楚令牌种类是什么。你有类似的东西:

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

第2步,撰写Tokenizer<EclToken>。这是Superpower v1的直接编程任务 - 没有多少帮助者依赖,你只需要像the examples那样编写代码。

标记生成器获取输入字符串,去掉空格,并确定标记序列是什么。

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

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

对于包含内容的Number之类的令牌,与Result<EclToken>关联的范围将指向与令牌对应的输入字符串部分。在此行中,该数字将为TextSpan,涵盖100

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

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

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

这是超级大国的重任,特别是在期望和错误方面。

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

我们要做的第一件事是为数字定义解析器;这个内置的TextParser<int>适用于EclToken.Number范围的内容。

您可以在this example中看到更多解析机制。

帮助您找到方法的更多线索(未检查语法,更不用说编译/测试):

    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是您可以应用于输入的已完成解析器。

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

答案 1 :(得分:1)

好的,我终于成功了。这并不困难 @Nicholas Blumhardt的指导:)

我已创建a project in GitHub来说明该方案。由于课程对于帖子来说很重要,我会链接到文件: