我目前正在维护一个用C#编写的应用程序。有一个我需要编码的新功能,我已经碰壁了。
我从数据库中提取用VB6编写的不同应用程序的数据。有一个字段保存树列表的数据。我需要在我的应用程序中重新创建这个树。数据如下所示:
{
{
table1~col_b^table1~colc^=
}|
{
{
table1~col_b^table2~col_b^=
}|{
table2~col_a^table3~cola^=
}|AND
}|OR
}
我甚至不知道从哪里开始。我需要完成的是这个。 {}表示表达式,|单独的表达。基本上这棵树应该是这样的:
OR
-- table1~col_b^table1~colc^=
-- AND
---- table1~col_b^table2~col_b^=
---- table2~col_a^table3~cola^=
解决这个问题的任何帮助或方向都会很棒!
谢谢,
道格
答案 0 :(得分:3)
当您定义语法时,我建议您查看Irony.net。它允许非常容易地编写解析器。 Sample
这对你的代码来说是粗略的语法:
[Language("ExpressionEvaluator", "1.0", "Multi-line expression evaluator")]
public class ExpressionEvaluatorGrammar : Grammar
{
public ExpressionEvaluatorGrammar()
{
// 1. Terminals
var identifier = new RegexBasedTerminal("identifier", "[a-z\\d_^~]+");
// 2. Non-terminals
var root = new NonTerminal("root");
var block = new NonTerminal("block");
var expression = new NonTerminal("expression");
var expressions = new NonTerminal("expressions");
var prop = new NonTerminal("prop");
var op = new NonTerminal("op");
// 3. BNF rules
op.Rule = ToTerm("OR") | "AND";
prop.Rule = identifier + "=" ;
expression.Rule = "{" + (prop | block) + "}" + "|" ;
expressions.Rule = MakeStarRule(expressions, expression);
block.Rule = expressions + op;
root.Rule = "{" + block +"}";
Root = root;
//automatically add NewLine before EOF so that our BNF rules work correctly when there's no final line break in source
this.LanguageFlags = LanguageFlags.NewLineBeforeEOF;
}
}
} //namespace
它解析好了,你需要做的就是添加AST树并使用它。
答案 1 :(得分:1)
没有任何额外的库,在纯C#代码中将它解析为表达式树并不太难:
class TreeNode
{
private enum ParseState
{
Operator,
Expression
}
public static TreeNode ParseTree(string treeData)
{
Stack<TreeNode> parsed = new Stack<TreeNode>();
StringBuilder nodeData = new StringBuilder();
ParseState state = ParseState.Operator;
for (int charIndex = 0; charIndex < treeData.Length; charIndex++)
{
switch (treeData[charIndex])
{
case '{':
nodeData.Clear();
state = ParseState.Expression;
break;
case '\t':
case ' ':
case '\r':
case '\n':
case '|':
// ignore whitespace and |
break;
case '}':
{
if (state == ParseState.Expression)
{
state = ParseState.Operator;
parsed.Push(new TreeNodeData(nodeData.ToString()));
}
else // Operator
{
TreeNodeOperators op = (TreeNodeOperators)(Enum.Parse(typeof(TreeNodeOperators), nodeData.ToString()));
TreeNodeExpression exp = new TreeNodeExpression();
exp.Operator = op;
exp.Right = parsed.Pop();
exp.Left = parsed.Pop();
parsed.Push(exp);
}
nodeData.Clear();
}
break;
default:
nodeData.Append(treeData[charIndex]);
break;
}
}
return parsed.Pop();
}
}
enum TreeNodeOperators
{
AND,
OR
}
class TreeNodeExpression : TreeNode
{
public TreeNodeOperators Operator {get; set;}
public TreeNode Left { get; set; }
public TreeNode Right { get; set; }
}
class TreeNodeData : TreeNode
{
public string Data {get; set;}
public TreeNodeData(string data)
{
Data = data;
}
}
答案 2 :(得分:0)
您可以使用regexp进行标记化,并使用递归方式解析堆栈,如此
internal class Node
{
public string Terminal { get; set; }
public List<Node> Operands { get; set; }
}
internal static readonly Regex TokensPattern = new Regex(@"(?<ws>\s+)|{\s*(?<value>[^\s}]+)\s*}|(?<token>OR|AND|.)", RegexOptions.Compiled);
static Node parseData(string str)
{
// init stack
var stack = new Stack<Node>();
stack.Push(new Node() { Operands = new List<Node>() });
// define parser
var parser = new Dictionary<string, Action<string>>();
parser.Add("{", _ => stack.Push(new Node() { Operands = new List<Node>() }));
parser.Add("}", _ => { var top = stack.Pop(); stack.Peek().Operands.Add(top); });
parser.Add("|", _ => { });
parser.Add("AND", _ => stack.Peek().Terminal = "AND");
parser.Add("OR", _ => stack.Peek().Terminal = "OR");
parser.Add("", value => stack.Peek().Operands.Add(new Node { Terminal = value }));
// execute parser
TokensPattern.Matches(str).Cast<Match>()
.Where(m => string.IsNullOrEmpty(m.Groups["ws"].Value))
.Count(m => { parser[m.Groups["token"].Value](m.Groups["value"].Value); return false; });
// return top of the tree
return stack.Peek().Operands[0];
}
static void Main(string[] args)
{
const string str = @"{
{
table1~col_b^table1~colc^=
}|
{
{
table1~col_b^table2~col_b^=
}|{
table2~col_a^table3~cola^=
}|{cccc}|AND
}|OR
}";
// print tree function
Action<int, Node> dump = null;
dump = new Action<int, Node>((level, node) =>
{
Console.WriteLine("{0}{1}", new string(' ', level * 2), node.Terminal);
if (node.Operands != null)
node.Operands.ForEach(el => dump(level + 1, el));
});
dump(0, parseData(str));
}