Esempio n. 1
0
func compile(tree *ast.Node, sep []rune) (m match.Matcher, err error) {
	switch tree.Kind {
	case ast.KindAnyOf:
		// todo this could be faster on pattern_alternatives_combine_lite (see glob_test.go)
		if n := minimizeTree(tree); n != nil {
			return compile(n, sep)
		}
		matchers, err := compileTreeChildren(tree, sep)
		if err != nil {
			return nil, err
		}
		return match.NewAnyOf(matchers...), nil

	case ast.KindPattern:
		if len(tree.Children) == 0 {
			return match.NewNothing(), nil
		}
		matchers, err := compileTreeChildren(tree, sep)
		if err != nil {
			return nil, err
		}
		m, err = compileMatchers(minimizeMatchers(matchers))
		if err != nil {
			return nil, err
		}

	case ast.KindAny:
		m = match.NewAny(sep)

	case ast.KindSuper:
		m = match.NewSuper()

	case ast.KindSingle:
		m = match.NewSingle(sep)

	case ast.KindNothing:
		m = match.NewNothing()

	case ast.KindList:
		l := tree.Value.(ast.List)
		m = match.NewList([]rune(l.Chars), l.Not)

	case ast.KindRange:
		r := tree.Value.(ast.Range)
		m = match.NewRange(r.Lo, r.Hi, r.Not)

	case ast.KindText:
		t := tree.Value.(ast.Text)
		m = match.NewText(t.Text)

	default:
		return nil, fmt.Errorf("could not compile tree: unknown node type")
	}

	return optimizeMatcher(m), nil
}
Esempio n. 2
0
func TestGlueMatchers(t *testing.T) {
	for id, test := range []struct {
		in  []match.Matcher
		exp match.Matcher
	}{
		{
			[]match.Matcher{
				match.NewSuper(),
				match.NewSingle(nil),
			},
			match.NewMin(1),
		},
		{
			[]match.Matcher{
				match.NewAny(separators),
				match.NewSingle(separators),
			},
			match.EveryOf{match.Matchers{
				match.NewMin(1),
				match.NewContains(string(separators), true),
			}},
		},
		{
			[]match.Matcher{
				match.NewSingle(nil),
				match.NewSingle(nil),
				match.NewSingle(nil),
			},
			match.EveryOf{match.Matchers{
				match.NewMin(3),
				match.NewMax(3),
			}},
		},
		{
			[]match.Matcher{
				match.NewList([]rune{'a'}, true),
				match.NewAny([]rune{'a'}),
			},
			match.EveryOf{match.Matchers{
				match.NewMin(1),
				match.NewContains("a", true),
			}},
		},
	} {
		act, err := compileMatchers(test.in)
		if err != nil {
			t.Errorf("#%d convert matchers error: %s", id, err)
			continue
		}

		if !reflect.DeepEqual(act, test.exp) {
			t.Errorf("#%d unexpected convert matchers result:\nact: %#v;\nexp: %#v", id, act, test.exp)
			continue
		}
	}
}
Esempio n. 3
0
func do2(node node, s []rune) ([]match.Matcher, error) {
	var result []match.Matcher

	switch n := node.(type) {

	case *nodePattern:
		ways := [][]match.Matcher{[]match.Matcher{}}

		for _, desc := range node.children() {
			variants, err := do2(desc, s)
			if err != nil {
				return nil, err
			}

			fmt.Println("variants pat", variants)

			for i, l := 0, len(ways); i < l; i++ {
				for i := 0; i < len(variants); i++ {
					o := optimize(variants[i])
					if i == len(variants)-1 {
						ways[i] = append(ways[i], o)
					} else {
						var w []match.Matcher
						copy(w, ways[i])
						ways = append(ways, append(w, o))
					}
				}
			}

			fmt.Println("ways pat", ways)
		}

		for _, matchers := range ways {
			c, err := compileMatchers(minimizeMatchers(matchers))
			if err != nil {
				return nil, err
			}
			result = append(result, c)
		}

	case *nodeAnyOf:
		ways := make([][]match.Matcher, len(node.children()))
		for _, desc := range node.children() {
			variants, err := do2(desc, s)
			if err != nil {
				return nil, err
			}

			fmt.Println("variants any", variants)

			for x, l := 0, len(ways); x < l; x++ {
				for i := 0; i < len(variants); i++ {
					o := optimize(variants[i])
					if i == len(variants)-1 {
						ways[x] = append(ways[x], o)
					} else {
						var w []match.Matcher
						copy(w, ways[x])
						ways = append(ways, append(w, o))
					}
				}
			}

			fmt.Println("ways any", ways)
		}

		for _, matchers := range ways {
			c, err := compileMatchers(minimizeMatchers(matchers))
			if err != nil {
				return nil, err
			}
			result = append(result, c)
		}

	case *nodeList:
		result = append(result, match.NewList([]rune(n.chars), n.not))

	case *nodeRange:
		result = append(result, match.NewRange(n.lo, n.hi, n.not))

	case *nodeAny:
		result = append(result, match.NewAny(s))

	case *nodeSuper:
		result = append(result, match.NewSuper())

	case *nodeSingle:
		result = append(result, match.NewSingle(s))

	case *nodeText:
		result = append(result, match.NewText(n.text))

	default:
		return nil, fmt.Errorf("could not compile tree: unknown node type")
	}

	for i, m := range result {
		result[i] = optimize(m)
	}

	return result, nil
}
Esempio n. 4
0
func do(leaf node, s []rune) (m match.Matcher, err error) {
	switch n := leaf.(type) {

	case *nodeAnyOf:
		// todo this could be faster on pattern_alternatives_combine_lite
		if n := minimizeAnyOf(n.children()); n != nil {
			return do(n, s)
		}

		var matchers []match.Matcher
		for _, desc := range n.children() {
			if desc == nil {
				matchers = append(matchers, match.NewNothing())
				continue
			}

			m, err := do(desc, s)
			if err != nil {
				return nil, err
			}
			matchers = append(matchers, optimize(m))
		}

		return match.NewAnyOf(matchers...), nil

	case *nodePattern:
		nodes := leaf.children()
		if len(nodes) == 0 {
			return match.NewNothing(), nil
		}

		var matchers []match.Matcher
		for _, desc := range nodes {
			m, err := do(desc, s)
			if err != nil {
				return nil, err
			}
			matchers = append(matchers, optimize(m))
		}

		m, err = compileMatchers(minimizeMatchers(matchers))
		if err != nil {
			return nil, err
		}

	case *nodeList:
		m = match.NewList([]rune(n.chars), n.not)

	case *nodeRange:
		m = match.NewRange(n.lo, n.hi, n.not)

	case *nodeAny:
		m = match.NewAny(s)

	case *nodeSuper:
		m = match.NewSuper()

	case *nodeSingle:
		m = match.NewSingle(s)

	case *nodeText:
		m = match.NewText(n.text)

	default:
		return nil, fmt.Errorf("could not compile tree: unknown node type")
	}

	return optimize(m), nil
}
Esempio n. 5
0
func glueAsEvery(matchers []match.Matcher) match.Matcher {
	if len(matchers) <= 1 {
		return nil
	}

	var (
		hasAny    bool
		hasSuper  bool
		hasSingle bool
		min       int
		separator []rune
	)

	for i, matcher := range matchers {
		var sep []rune

		switch m := matcher.(type) {
		case match.Super:
			sep = []rune{}
			hasSuper = true

		case match.Any:
			sep = m.Separators
			hasAny = true

		case match.Single:
			sep = m.Separators
			hasSingle = true
			min++

		case match.List:
			if !m.Not {
				return nil
			}
			sep = m.List
			hasSingle = true
			min++

		default:
			return nil
		}

		// initialize
		if i == 0 {
			separator = sep
		}

		if runes.Equal(sep, separator) {
			continue
		}

		return nil
	}

	if hasSuper && !hasAny && !hasSingle {
		return match.NewSuper()
	}

	if hasAny && !hasSuper && !hasSingle {
		return match.NewAny(separator)
	}

	if (hasAny || hasSuper) && min > 0 && len(separator) == 0 {
		return match.NewMin(min)
	}

	every := match.NewEveryOf()

	if min > 0 {
		every.Add(match.NewMin(min))

		if !hasAny && !hasSuper {
			every.Add(match.NewMax(min))
		}
	}

	if len(separator) > 0 {
		every.Add(match.NewContains(string(separator), true))
	}

	return every
}
Esempio n. 6
0
func optimize(matcher match.Matcher) match.Matcher {
	switch m := matcher.(type) {

	case match.Any:
		if len(m.Separators) == 0 {
			return match.NewSuper()
		}

	case match.AnyOf:
		if len(m.Matchers) == 1 {
			return m.Matchers[0]
		}

		return m

	case match.List:
		if m.Not == false && len(m.List) == 1 {
			return match.NewText(string(m.List))
		}

		return m

	case match.BTree:
		m.Left = optimize(m.Left)
		m.Right = optimize(m.Right)

		r, ok := m.Value.(match.Text)
		if !ok {
			return m
		}

		leftNil := m.Left == nil
		rightNil := m.Right == nil

		if leftNil && rightNil {
			return match.NewText(r.Str)
		}

		_, leftSuper := m.Left.(match.Super)
		lp, leftPrefix := m.Left.(match.Prefix)

		_, rightSuper := m.Right.(match.Super)
		rs, rightSuffix := m.Right.(match.Suffix)

		if leftSuper && rightSuper {
			return match.NewContains(r.Str, false)
		}

		if leftSuper && rightNil {
			return match.NewSuffix(r.Str)
		}

		if rightSuper && leftNil {
			return match.NewPrefix(r.Str)
		}

		if leftNil && rightSuffix {
			return match.NewPrefixSuffix(r.Str, rs.Suffix)
		}

		if rightNil && leftPrefix {
			return match.NewPrefixSuffix(lp.Prefix, r.Str)
		}

		return m
	}

	return matcher
}
Esempio n. 7
0
func TestCompileMatchers(t *testing.T) {
	for id, test := range []struct {
		in  []match.Matcher
		exp match.Matcher
	}{
		{
			[]match.Matcher{
				match.NewSuper(),
				match.NewSingle(separators),
				match.NewText("c"),
			},
			match.NewBTree(
				match.NewText("c"),
				match.NewBTree(
					match.NewSingle(separators),
					match.NewSuper(),
					nil,
				),
				nil,
			),
		},
		{
			[]match.Matcher{
				match.NewAny(nil),
				match.NewText("c"),
				match.NewAny(nil),
			},
			match.NewBTree(
				match.NewText("c"),
				match.NewAny(nil),
				match.NewAny(nil),
			),
		},
		{
			[]match.Matcher{
				match.NewRange('a', 'c', true),
				match.NewList([]rune{'z', 't', 'e'}, false),
				match.NewText("c"),
				match.NewSingle(nil),
			},
			match.NewRow(
				4,
				match.Matchers{
					match.NewRange('a', 'c', true),
					match.NewList([]rune{'z', 't', 'e'}, false),
					match.NewText("c"),
					match.NewSingle(nil),
				}...,
			),
		},
	} {
		act, err := compileMatchers(test.in)
		if err != nil {
			t.Errorf("#%d convert matchers error: %s", id, err)
			continue
		}

		if !reflect.DeepEqual(act, test.exp) {
			t.Errorf("#%d unexpected convert matchers result:\nact: %#v\nexp: %#v", id, act, test.exp)
			continue
		}
	}
}
Esempio n. 8
0
func TestCompiler(t *testing.T) {
	for id, test := range []struct {
		ast    *nodePattern
		result Glob
		sep    []rune
	}{
		{
			ast:    pattern(&nodeText{text: "abc"}),
			result: match.NewText("abc"),
		},
		{
			ast:    pattern(&nodeAny{}),
			sep:    separators,
			result: match.NewAny(separators),
		},
		{
			ast:    pattern(&nodeAny{}),
			result: match.NewSuper(),
		},
		{
			ast:    pattern(&nodeSuper{}),
			result: match.NewSuper(),
		},
		{
			ast:    pattern(&nodeSingle{}),
			sep:    separators,
			result: match.NewSingle(separators),
		},
		{
			ast: pattern(&nodeRange{
				lo:  'a',
				hi:  'z',
				not: true,
			}),
			result: match.NewRange('a', 'z', true),
		},
		{
			ast: pattern(&nodeList{
				chars: "abc",
				not:   true,
			}),
			result: match.NewList([]rune{'a', 'b', 'c'}, true),
		},
		{
			ast: pattern(&nodeAny{}, &nodeSingle{}, &nodeSingle{}, &nodeSingle{}),
			sep: separators,
			result: match.EveryOf{Matchers: match.Matchers{
				match.NewMin(3),
				match.NewContains(string(separators), true),
			}},
		},
		{
			ast:    pattern(&nodeAny{}, &nodeSingle{}, &nodeSingle{}, &nodeSingle{}),
			result: match.NewMin(3),
		},
		{
			ast: pattern(&nodeAny{}, &nodeText{text: "abc"}, &nodeSingle{}),
			sep: separators,
			result: match.NewBTree(
				match.NewRow(
					4,
					match.Matchers{
						match.NewText("abc"),
						match.NewSingle(separators),
					}...,
				),
				match.NewAny(separators),
				nil,
			),
		},
		{
			ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSingle{}),
			sep: separators,
			result: match.NewBTree(
				match.NewRow(
					5,
					match.Matchers{
						match.NewSingle(separators),
						match.NewText("abc"),
						match.NewSingle(separators),
					}...,
				),
				match.NewSuper(),
				nil,
			),
		},
		{
			ast:    pattern(&nodeAny{}, &nodeText{text: "abc"}),
			result: match.NewSuffix("abc"),
		},
		{
			ast:    pattern(&nodeText{text: "abc"}, &nodeAny{}),
			result: match.NewPrefix("abc"),
		},
		{
			ast:    pattern(&nodeText{text: "abc"}, &nodeAny{}, &nodeText{text: "def"}),
			result: match.NewPrefixSuffix("abc", "def"),
		},
		{
			ast:    pattern(&nodeAny{}, &nodeAny{}, &nodeAny{}, &nodeText{text: "abc"}, &nodeAny{}, &nodeAny{}),
			result: match.NewContains("abc", false),
		},
		{
			ast: pattern(&nodeAny{}, &nodeAny{}, &nodeAny{}, &nodeText{text: "abc"}, &nodeAny{}, &nodeAny{}),
			sep: separators,
			result: match.NewBTree(
				match.NewText("abc"),
				match.NewAny(separators),
				match.NewAny(separators),
			),
		},
		{
			ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSuper{}, &nodeSingle{}),
			result: match.NewBTree(
				match.NewText("abc"),
				match.NewMin(1),
				match.NewMin(1),
			),
		},
		{
			ast:    pattern(anyOf(&nodeText{text: "abc"})),
			result: match.NewText("abc"),
		},
		{
			ast:    pattern(anyOf(pattern(anyOf(pattern(&nodeText{text: "abc"}))))),
			result: match.NewText("abc"),
		},
		{
			ast: pattern(anyOf(
				pattern(
					&nodeText{text: "abc"},
					&nodeSingle{},
				),
				pattern(
					&nodeText{text: "abc"},
					&nodeList{chars: "def"},
				),
				pattern(
					&nodeText{text: "abc"},
				),
				pattern(
					&nodeText{text: "abc"},
				),
			)),
			result: match.NewBTree(
				match.NewText("abc"),
				nil,
				match.AnyOf{Matchers: match.Matchers{
					match.NewSingle(nil),
					match.NewList([]rune{'d', 'e', 'f'}, false),
					match.NewNothing(),
				}},
			),
		},
		{
			ast: pattern(
				&nodeRange{lo: 'a', hi: 'z'},
				&nodeRange{lo: 'a', hi: 'x', not: true},
				&nodeAny{},
			),
			result: match.NewBTree(
				match.NewRow(
					2,
					match.Matchers{
						match.NewRange('a', 'z', false),
						match.NewRange('a', 'x', true),
					}...,
				),
				nil,
				match.NewSuper(),
			),
		},
		{
			ast: pattern(anyOf(
				pattern(
					&nodeText{text: "abc"},
					&nodeList{chars: "abc"},
					&nodeText{text: "ghi"},
				),
				pattern(
					&nodeText{text: "abc"},
					&nodeList{chars: "def"},
					&nodeText{text: "ghi"},
				),
			)),
			result: match.NewRow(
				7,
				match.Matchers{
					match.NewText("abc"),
					match.AnyOf{Matchers: match.Matchers{
						match.NewList([]rune{'a', 'b', 'c'}, false),
						match.NewList([]rune{'d', 'e', 'f'}, false),
					}},
					match.NewText("ghi"),
				}...,
			),
		},
		//				{
		//			ast: pattern(
		//				anyOf(&nodeText{text: "a"}, &nodeText{text: "b"}),
		//				anyOf(&nodeText{text: "c"}, &nodeText{text: "d"}),
		//			),
		//			result: match.AnyOf{Matchers: match.Matchers{
		//				match.NewRow(Matchers: match.Matchers{match.Raw{"a"}, match.Raw{"c", 1}}),
		//				match.NewRow(Matchers: match.Matchers{match.Raw{"a"}, match.Raw{"d"}}),
		//				match.NewRow(Matchers: match.Matchers{match.Raw{"b"}, match.Raw{"c", 1}}),
		//				match.NewRow(Matchers: match.Matchers{match.Raw{"b"}, match.Raw{"d"}}),
		//			}},
		//		},
	} {
		m, err := compile(test.ast, test.sep)
		if err != nil {
			t.Errorf("compilation error: %s", err)
			continue
		}

		if !reflect.DeepEqual(m, test.result) {
			t.Errorf("#%d results are not equal:\nexp: %#v\nact: %#v", id, test.result, m)
			continue
		}
	}
}
Esempio n. 9
0
func TestCompiler(t *testing.T) {
	for id, test := range []struct {
		ast    *ast.Node
		result match.Matcher
		sep    []rune
	}{
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindText, ast.Text{"abc"}),
			),
			result: match.NewText("abc"),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAny, nil),
			),
			sep:    separators,
			result: match.NewAny(separators),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAny, nil),
			),
			result: match.NewSuper(),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindSuper, nil),
			),
			result: match.NewSuper(),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindSingle, nil),
			),
			sep:    separators,
			result: match.NewSingle(separators),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindRange, ast.Range{
					Lo:  'a',
					Hi:  'z',
					Not: true,
				}),
			),
			result: match.NewRange('a', 'z', true),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindList, ast.List{
					Chars: "abc",
					Not:   true,
				}),
			),
			result: match.NewList([]rune{'a', 'b', 'c'}, true),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindSingle, nil),
				ast.NewNode(ast.KindSingle, nil),
				ast.NewNode(ast.KindSingle, nil),
			),
			sep: separators,
			result: match.EveryOf{Matchers: match.Matchers{
				match.NewMin(3),
				match.NewContains(string(separators), true),
			}},
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindSingle, nil),
				ast.NewNode(ast.KindSingle, nil),
				ast.NewNode(ast.KindSingle, nil),
			),
			result: match.NewMin(3),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindText, ast.Text{"abc"}),
				ast.NewNode(ast.KindSingle, nil),
			),
			sep: separators,
			result: match.NewBTree(
				match.NewRow(
					4,
					match.Matchers{
						match.NewText("abc"),
						match.NewSingle(separators),
					}...,
				),
				match.NewAny(separators),
				nil,
			),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindText, ast.Text{"/"}),
				ast.NewNode(ast.KindAnyOf, nil,
					ast.NewNode(ast.KindText, ast.Text{"z"}),
					ast.NewNode(ast.KindText, ast.Text{"ab"}),
				),
				ast.NewNode(ast.KindSuper, nil),
			),
			sep: separators,
			result: match.NewBTree(
				match.NewText("/"),
				nil,
				match.NewBTree(
					match.NewAnyOf(match.NewText("z"), match.NewText("ab")),
					nil,
					match.NewSuper(),
				),
			),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindSuper, nil),
				ast.NewNode(ast.KindSingle, nil),
				ast.NewNode(ast.KindText, ast.Text{"abc"}),
				ast.NewNode(ast.KindSingle, nil),
			),
			sep: separators,
			result: match.NewBTree(
				match.NewRow(
					5,
					match.Matchers{
						match.NewSingle(separators),
						match.NewText("abc"),
						match.NewSingle(separators),
					}...,
				),
				match.NewSuper(),
				nil,
			),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindText, ast.Text{"abc"}),
			),
			result: match.NewSuffix("abc"),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindText, ast.Text{"abc"}),
				ast.NewNode(ast.KindAny, nil),
			),
			result: match.NewPrefix("abc"),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindText, ast.Text{"abc"}),
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindText, ast.Text{"def"}),
			),
			result: match.NewPrefixSuffix("abc", "def"),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindText, ast.Text{"abc"}),
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindAny, nil),
			),
			result: match.NewContains("abc", false),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindText, ast.Text{"abc"}),
				ast.NewNode(ast.KindAny, nil),
				ast.NewNode(ast.KindAny, nil),
			),
			sep: separators,
			result: match.NewBTree(
				match.NewText("abc"),
				match.NewAny(separators),
				match.NewAny(separators),
			),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindSuper, nil),
				ast.NewNode(ast.KindSingle, nil),
				ast.NewNode(ast.KindText, ast.Text{"abc"}),
				ast.NewNode(ast.KindSuper, nil),
				ast.NewNode(ast.KindSingle, nil),
			),
			result: match.NewBTree(
				match.NewText("abc"),
				match.NewMin(1),
				match.NewMin(1),
			),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindText, ast.Text{"abc"}),
			),
			result: match.NewText("abc"),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAnyOf, nil,
					ast.NewNode(ast.KindPattern, nil,
						ast.NewNode(ast.KindAnyOf, nil,
							ast.NewNode(ast.KindPattern, nil,
								ast.NewNode(ast.KindText, ast.Text{"abc"}),
							),
						),
					),
				),
			),
			result: match.NewText("abc"),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAnyOf, nil,
					ast.NewNode(ast.KindPattern, nil,
						ast.NewNode(ast.KindText, ast.Text{"abc"}),
						ast.NewNode(ast.KindSingle, nil),
					),
					ast.NewNode(ast.KindPattern, nil,
						ast.NewNode(ast.KindText, ast.Text{"abc"}),
						ast.NewNode(ast.KindList, ast.List{Chars: "def"}),
					),
					ast.NewNode(ast.KindPattern, nil,
						ast.NewNode(ast.KindText, ast.Text{"abc"}),
					),
					ast.NewNode(ast.KindPattern, nil,
						ast.NewNode(ast.KindText, ast.Text{"abc"}),
					),
				),
			),
			result: match.NewBTree(
				match.NewText("abc"),
				nil,
				match.AnyOf{Matchers: match.Matchers{
					match.NewSingle(nil),
					match.NewList([]rune{'d', 'e', 'f'}, false),
					match.NewNothing(),
				}},
			),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindRange, ast.Range{Lo: 'a', Hi: 'z'}),
				ast.NewNode(ast.KindRange, ast.Range{Lo: 'a', Hi: 'x', Not: true}),
				ast.NewNode(ast.KindAny, nil),
			),
			result: match.NewBTree(
				match.NewRow(
					2,
					match.Matchers{
						match.NewRange('a', 'z', false),
						match.NewRange('a', 'x', true),
					}...,
				),
				nil,
				match.NewSuper(),
			),
		},
		{
			ast: ast.NewNode(ast.KindPattern, nil,
				ast.NewNode(ast.KindAnyOf, nil,
					ast.NewNode(ast.KindPattern, nil,
						ast.NewNode(ast.KindText, ast.Text{"abc"}),
						ast.NewNode(ast.KindList, ast.List{Chars: "abc"}),
						ast.NewNode(ast.KindText, ast.Text{"ghi"}),
					),
					ast.NewNode(ast.KindPattern, nil,
						ast.NewNode(ast.KindText, ast.Text{"abc"}),
						ast.NewNode(ast.KindList, ast.List{Chars: "def"}),
						ast.NewNode(ast.KindText, ast.Text{"ghi"}),
					),
				),
			),
			result: match.NewRow(
				7,
				match.Matchers{
					match.NewText("abc"),
					match.AnyOf{Matchers: match.Matchers{
						match.NewList([]rune{'a', 'b', 'c'}, false),
						match.NewList([]rune{'d', 'e', 'f'}, false),
					}},
					match.NewText("ghi"),
				}...,
			),
		},
	} {
		m, err := Compile(test.ast, test.sep)
		if err != nil {
			t.Errorf("compilation error: %s", err)
			continue
		}

		if !reflect.DeepEqual(m, test.result) {
			t.Errorf("[%d] Compile():\nexp: %#v\nact: %#v\n\ngraphviz:\nexp:\n%s\nact:\n%s\n", id, test.result, m, debug.Graphviz("", test.result.(match.Matcher)), debug.Graphviz("", m.(match.Matcher)))
			continue
		}
	}
}