zoukankan      html  css  js  c++  java
  • 大家来找错自己写个正则引擎(三)构建正则解析树及分词

      第一节介绍过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了。这部分代码应该可挑剔的地方不多吧,大家也再找找。

  • 相关阅读:
    Poj 2017 Speed Limit(水题)
    Poj 1316 Self Numbers(水题)
    Poj 1017 Packets(贪心策略)
    Poj 1017 Packets(贪心策略)
    Poj 2662,2909 Goldbach's Conjecture (素数判定)
    Poj 2662,2909 Goldbach's Conjecture (素数判定)
    poj 2388 Who's in the Middle(快速排序求中位数)
    poj 2388 Who's in the Middle(快速排序求中位数)
    poj 2000 Gold Coins(水题)
    poj 2000 Gold Coins(水题)
  • 原文地址:https://www.cnblogs.com/onlytiancai/p/1747554.html
Copyright © 2011-2022 走看看