SuperPower 解析器 运行 时间异常:值不能为空。参数名称:解析器
SuperPower parser run time exception: Value cannot be null. Parameter name: parser
我被要求为由 3 种类型的运算符(AND、AND_NOT、OR)、分组括号和数据值组成的逻辑语句创建一个 Superpower 解析器。数据值是整数格式,但是是标识符。这些更像是规则,而不是数学陈述。
示例 1:20 AND NOT(29 OR 30)
示例 2:27 AND (9 OR (24 AND 25 AND 27))
目前我在 运行 时间收到异常,内部消息为 {"Value cannot be null.\r\nParameter name: parser"}.堆栈跟踪表明它出现在我调用主解析器的 TryParse 方法的那一行。有人可以给我一些关于调试的提示吗?
这是我在控制台应用程序中的内容。
Program.cs
class Program
{
static void Main(string[] args)
{
//SAMPLE DATA
string input = "20 AND 12";
//string input = "86 AND NOT 18";
//string input = "89 AND NOT (87 OR 88)";";
//string input = "91 AND NOT (94 OR 98 OR 99) AND 12";
var tokenizer = new TokenizerBuilder<Tokens>()
.Ignore(Character.EqualTo(' '))
.Match(Character.EqualTo('('), Tokens.LParen)
.Match(Character.EqualTo(')'), Tokens.RParen)
.Match(Span.EqualTo("AND NOT"), Tokens.AndNot)
.Match(Span.EqualTo("AND"), Tokens.And)
.Match(Span.EqualTo("OR"), Tokens.Or)
.Match(Numerics.Integer, Tokens.ItemNumber)
.Build();
var tokens = tokenizer.Tokenize(input); //.Where(x => !x.Kind.Equals(Tokens.Whitespace))
var result = Parsers.Expression.TryParse(tokens);
if (result.HasValue)
{
// input is valid
var expression = (Expression)result.Value;
// do what you need with it here, i.e. loop through the nodes, output the text, etc.
}
else
{
// not valid
}
}
}
Tokens.cs
public enum Tokens
{
None,
Whitespace,
[Token(Category = "logic", Example = "AND NOT")]
AndNot,
[Token(Category = "logic", Example = "AND")]
And,
[Token(Category = "logic", Example = "OR")]
Or,
[Token(Category = "parenthesis", Example = "(")]
LParen,
[Token(Category = "parenthesis", Example = ")")]
RParen,
[Token(Category = "data", Example = "XX")]
ItemNumber
}
Parsers.cs
public static class Parsers
{
public readonly static TokenListParser<Tokens, Node> Expression =
ItemNumber.Try().Or(ExpressionNoParens).Try().Or(ExpressionWithParens);
private static readonly TokenListParser<Tokens, Node> LParen =
Token.EqualTo(Tokens.LParen)
.Select(i => (Node) new Parenthesis());
private static readonly TokenListParser<Tokens, Node> RParen =
Token.EqualTo(Tokens.RParen)
.Select(i => (Node)new Parenthesis());
private static readonly TokenListParser<Tokens, Node> ItemNumber =
ItemNumberNoParens.Try().Or(ItemNumberWithParens);
private static readonly TokenListParser<Tokens, Node> ItemNumberNoParens =
Token.EqualTo(Tokens.ItemNumber)
.Apply(Numerics.IntegerInt32)
.Select(i => (Node)new ItemNumber(i));
private static readonly TokenListParser<Tokens, Node> ItemNumberWithParens =
Token.EqualTo(Tokens.ItemNumber)
.Between(LParen, RParen)
.Apply(Numerics.IntegerInt32)
.Select(i => (Node)new ItemNumber(i));
private static readonly TokenListParser<Tokens, Node> AndNot =
Token.EqualTo(Tokens.AndNot)
.Select(i => (Node)new AndNotOperator());
private static readonly TokenListParser<Tokens, Node> And =
Token.EqualTo(Tokens.And)
.Select(i => (Node)new AndOperator());
private static readonly TokenListParser<Tokens, Node> Or =
Token.EqualTo(Tokens.Or)
.Select(i => (Node)new OrOperator());
private readonly static TokenListParser<Tokens, Node> Operator =
AndNot.Try().Or(And).Try().Or(Or);
private readonly static TokenListParser<Tokens, Node> OperatorExpression =
from op in Operator
from ex in Expression
select (Node) new OperatorExpression(op as Operator, ex as Expression);
private readonly static TokenListParser<Tokens, Node> ExpressionNoParens =
from a in Expression
from op in OperatorExpression.Many()
select (Node)new ComplexExpression(a as Expression, op as OperatorExpression[]);
private readonly static TokenListParser<Tokens, Node> ExpressionWithParens =
from a in Expression
from op in OperatorExpression.Between(LParen, RParen).Many()
select (Node)new ComplexExpression(a as Expression, op as OperatorExpression[]);
}
TokenNodes.cs
public abstract class Node
{
}
public class Parenthesis : Node
{
}
public abstract class Operator: Node
{
}
public class AndNotOperator : Operator
{
}
public class AndOperator : Operator
{
}
public class OrOperator : Operator
{
}
public abstract class Expression : Node
{
}
public class ItemNumber : Expression
{
public ItemNumber(int value)
{
Value = value;
}
public int Value { get; set; }
}
public class ComplexExpression : Expression
{
public ComplexExpression(Expression _exp, OperatorExpression[] _ops)
{
Prime = _exp;
OperatorExpressions = _ops;
}
public Expression Prime { get; set; }
public OperatorExpression[] OperatorExpressions { get; set; }
}
public class OperatorExpression : Node
{
public OperatorExpression(Operator _op, Expression _exp)
{
Operator = _op;
Expression = _exp;
}
public Operator Operator { get; set; }
public Expression Expression { get; set; }
}
所以我的问题是“我该如何调试它?!?!”,我找到了答案。
我注释掉了我的每个解析器 类 并开始一次将它们添加回去,从最简单的没有依赖关系的开始。
我被要求为由 3 种类型的运算符(AND、AND_NOT、OR)、分组括号和数据值组成的逻辑语句创建一个 Superpower 解析器。数据值是整数格式,但是是标识符。这些更像是规则,而不是数学陈述。
示例 1:20 AND NOT(29 OR 30) 示例 2:27 AND (9 OR (24 AND 25 AND 27))
目前我在 运行 时间收到异常,内部消息为 {"Value cannot be null.\r\nParameter name: parser"}.堆栈跟踪表明它出现在我调用主解析器的 TryParse 方法的那一行。有人可以给我一些关于调试的提示吗?
这是我在控制台应用程序中的内容。
Program.cs
class Program
{
static void Main(string[] args)
{
//SAMPLE DATA
string input = "20 AND 12";
//string input = "86 AND NOT 18";
//string input = "89 AND NOT (87 OR 88)";";
//string input = "91 AND NOT (94 OR 98 OR 99) AND 12";
var tokenizer = new TokenizerBuilder<Tokens>()
.Ignore(Character.EqualTo(' '))
.Match(Character.EqualTo('('), Tokens.LParen)
.Match(Character.EqualTo(')'), Tokens.RParen)
.Match(Span.EqualTo("AND NOT"), Tokens.AndNot)
.Match(Span.EqualTo("AND"), Tokens.And)
.Match(Span.EqualTo("OR"), Tokens.Or)
.Match(Numerics.Integer, Tokens.ItemNumber)
.Build();
var tokens = tokenizer.Tokenize(input); //.Where(x => !x.Kind.Equals(Tokens.Whitespace))
var result = Parsers.Expression.TryParse(tokens);
if (result.HasValue)
{
// input is valid
var expression = (Expression)result.Value;
// do what you need with it here, i.e. loop through the nodes, output the text, etc.
}
else
{
// not valid
}
}
}
Tokens.cs
public enum Tokens
{
None,
Whitespace,
[Token(Category = "logic", Example = "AND NOT")]
AndNot,
[Token(Category = "logic", Example = "AND")]
And,
[Token(Category = "logic", Example = "OR")]
Or,
[Token(Category = "parenthesis", Example = "(")]
LParen,
[Token(Category = "parenthesis", Example = ")")]
RParen,
[Token(Category = "data", Example = "XX")]
ItemNumber
}
Parsers.cs
public static class Parsers
{
public readonly static TokenListParser<Tokens, Node> Expression =
ItemNumber.Try().Or(ExpressionNoParens).Try().Or(ExpressionWithParens);
private static readonly TokenListParser<Tokens, Node> LParen =
Token.EqualTo(Tokens.LParen)
.Select(i => (Node) new Parenthesis());
private static readonly TokenListParser<Tokens, Node> RParen =
Token.EqualTo(Tokens.RParen)
.Select(i => (Node)new Parenthesis());
private static readonly TokenListParser<Tokens, Node> ItemNumber =
ItemNumberNoParens.Try().Or(ItemNumberWithParens);
private static readonly TokenListParser<Tokens, Node> ItemNumberNoParens =
Token.EqualTo(Tokens.ItemNumber)
.Apply(Numerics.IntegerInt32)
.Select(i => (Node)new ItemNumber(i));
private static readonly TokenListParser<Tokens, Node> ItemNumberWithParens =
Token.EqualTo(Tokens.ItemNumber)
.Between(LParen, RParen)
.Apply(Numerics.IntegerInt32)
.Select(i => (Node)new ItemNumber(i));
private static readonly TokenListParser<Tokens, Node> AndNot =
Token.EqualTo(Tokens.AndNot)
.Select(i => (Node)new AndNotOperator());
private static readonly TokenListParser<Tokens, Node> And =
Token.EqualTo(Tokens.And)
.Select(i => (Node)new AndOperator());
private static readonly TokenListParser<Tokens, Node> Or =
Token.EqualTo(Tokens.Or)
.Select(i => (Node)new OrOperator());
private readonly static TokenListParser<Tokens, Node> Operator =
AndNot.Try().Or(And).Try().Or(Or);
private readonly static TokenListParser<Tokens, Node> OperatorExpression =
from op in Operator
from ex in Expression
select (Node) new OperatorExpression(op as Operator, ex as Expression);
private readonly static TokenListParser<Tokens, Node> ExpressionNoParens =
from a in Expression
from op in OperatorExpression.Many()
select (Node)new ComplexExpression(a as Expression, op as OperatorExpression[]);
private readonly static TokenListParser<Tokens, Node> ExpressionWithParens =
from a in Expression
from op in OperatorExpression.Between(LParen, RParen).Many()
select (Node)new ComplexExpression(a as Expression, op as OperatorExpression[]);
}
TokenNodes.cs
public abstract class Node
{
}
public class Parenthesis : Node
{
}
public abstract class Operator: Node
{
}
public class AndNotOperator : Operator
{
}
public class AndOperator : Operator
{
}
public class OrOperator : Operator
{
}
public abstract class Expression : Node
{
}
public class ItemNumber : Expression
{
public ItemNumber(int value)
{
Value = value;
}
public int Value { get; set; }
}
public class ComplexExpression : Expression
{
public ComplexExpression(Expression _exp, OperatorExpression[] _ops)
{
Prime = _exp;
OperatorExpressions = _ops;
}
public Expression Prime { get; set; }
public OperatorExpression[] OperatorExpressions { get; set; }
}
public class OperatorExpression : Node
{
public OperatorExpression(Operator _op, Expression _exp)
{
Operator = _op;
Expression = _exp;
}
public Operator Operator { get; set; }
public Expression Expression { get; set; }
}
所以我的问题是“我该如何调试它?!?!”,我找到了答案。
我注释掉了我的每个解析器 类 并开始一次将它们添加回去,从最简单的没有依赖关系的开始。