大家来找错自己写个正则引擎(三)构建正则解析树及分词

  第一节介绍过RegexNode类,它的SplitWords用来分词,大致逻辑就是遍历要分词的字符串,不断的调用自身的Parse属性指向的委托,Parse属性指向一个bool ParseFunc(string input, int index, out string output)类型的委托,其中input表示要解析的字符串,index表示其实的解析位置,返回值表示否则匹配成功,output表示匹配成功是匹配到的字符串。如果本次匹配没有匹配到输出,就把当前字符当成一个独立的匹配结果,然后扫描指针向后移动,继续遍历扫描解析。

  具体代码灰常简单,如下。

public delegate bool ParseFunc(string input, int index, out string output);
public class RegexNode {
    public RegexNode() {
        Nodes = new List<RegexNode>();
    }
    public RegexNode(ParseFunc func) {
        Parse = func;
        Nodes = new List<RegexNode>();
    }
    public List<RegexNode> Nodes { get; set; }
    public ParseFunc Parse { get; set; }
    public Releation Releation { get; set; }
    public string Text { get; set; }

    public string[] SplitWords(string input) {
        List<string> result = new List<string>();
        int index = 0;

        while (index < input.Length) {
            string output = null;
            if(Parse(input, index, out output))
            {
                index += output.Length;
                result.Add(output);
            }
            else
            {
                if (index < input.Length) {
                    result.Add(input.Substring(index, 1));
                }
                index ++;
            }
        }
        return result.ToArray();
    }

}

  其实把抽象模式树转换成正则解析树的代码也很简单,因为都是树与数的转换,用一个简单的递归调用对源树进行深度优先遍历就行了,遍历的过程中根据模式树中提供的信息,调用解析方法工厂ParseFuncFactory的相关方法得到该子模式的解析函数,并赋值给目标树节点的Parse属性。代码中要考虑当前节点没有子节点的情况,如果是匹配一个或多个就用OneOrMoreMaxMatch,否则用MaxMatch,如果有子节点,要考察当前节点的Releation,然后进一步确定调用ParseFuncFactory的哪个方法,代码很短,如下。

public static RegexNode GetParseNode(PatternNode rootPatt) {
    RegexNode root = new RegexNode();
    root.Releation = rootPatt.Releation;
    root.Text = rootPatt.Text;
    if (rootPatt.Nodes.Count == 0) {
        if (rootPatt.OneOrMore) {
            string toMatch = rootPatt.Text.TrimEnd(new[]{'*',')'});
            toMatch = toMatch.TrimStart('(');
            root.Parse = ParseFuncFactory.OneOrMoreMaxMatch(toMatch);
        }
        else {
            root.Parse = ParseFuncFactory.MaxMatch(rootPatt.Text);
        }
        return root;
    }

    foreach (var pt in rootPatt.Nodes) {
        var node = GetParseNode(pt);
        root.Nodes.Add(node);
    }

    if (rootPatt.Releation == Releation.And)
    {
        if (rootPatt.OneOrMore) {
            root.Parse = ParseFuncFactory.MatchOneOrMoreWithAnd(root.Nodes);
        }
        else {
            root.Parse = ParseFuncFactory.MatchAnd(root.Nodes);
        }
    }
    else if (rootPatt.Releation == Releation.Or) {
        if (rootPatt.OneOrMore) {
            root.Parse = ParseFuncFactory.MatchOneOrMoreWithOr(root.Nodes);
        }
        else {
            root.Parse = ParseFuncFactory.MatchOr(root.Nodes);
        }
    }
    return root;
}

这部分代码比较简洁,比我预想的还简单,可见高阶函数的应用确实能使某些问题的解决变得简单,应该抽空仔细看看SICP了。这部分代码应该可挑剔的地方不多吧,大家也再找找。

原文地址:https://www.cnblogs.com/onlytiancai/p/1747554.html