exp/regexp/syntax: syntax data structures, parser

Parser is a work in progress but can populate most of the
interesting parts of the data structure, so a good checkpoint.
All the complicated Perl syntax is missing, as are various
important optimizations made during parsing to the
syntax tree.

The plan is that exp/regexp's API will mimic regexp,
and exp/regexp/syntax provides the parser directly
for programs that need it (and for implementing exp/regexp).

Once finished, exp/regexp will replace regexp.

R=r, sam.thorogood, kevlar, edsrzf
CC=golang-dev
https://golang.org/cl/4538123
diff --git a/src/pkg/exp/regexp/syntax/parse_test.go b/src/pkg/exp/regexp/syntax/parse_test.go
new file mode 100644
index 0000000..4ae184c
--- /dev/null
+++ b/src/pkg/exp/regexp/syntax/parse_test.go
@@ -0,0 +1,266 @@
+// Copyright 2011 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+	"bytes"
+	"fmt"
+	"testing"
+	"unicode"
+)
+
+var parseTests = []struct {
+	Regexp string
+	Dump   string
+}{
+	// Base cases
+	{"a", "lit{a}"},
+	{"a.", "cat{lit{a}dot{}}"},
+	{"a.b", "cat{lit{a}dot{}lit{b}}"},
+	//	{ "ab", "str{ab}" },
+	{"ab", "cat{lit{a}lit{b}}"},
+	{"a.b.c", "cat{lit{a}dot{}lit{b}dot{}lit{c}}"},
+	//	{ "abc", "str{abc}" },
+	{"abc", "cat{lit{a}lit{b}lit{c}}"},
+	{"a|^", "alt{lit{a}bol{}}"},
+	//	{ "a|b", "cc{0x61-0x62}" },
+	{"a|b", "alt{lit{a}lit{b}}"},
+	{"(a)", "cap{lit{a}}"},
+	{"(a)|b", "alt{cap{lit{a}}lit{b}}"},
+	{"a*", "star{lit{a}}"},
+	{"a+", "plus{lit{a}}"},
+	{"a?", "que{lit{a}}"},
+	//	{ "a{2}", "rep{2,2 lit{a}}" },
+	//	{ "a{2,3}", "rep{2,3 lit{a}}" },
+	//	{ "a{2,}", "rep{2,-1 lit{a}}" },
+	//	{ "a*?", "nstar{lit{a}}" },
+	//	{ "a+?", "nplus{lit{a}}" },
+	//	{ "a??", "nque{lit{a}}" },
+	//	{ "a{2}?", "nrep{2,2 lit{a}}" },
+	//	{ "a{2,3}?", "nrep{2,3 lit{a}}" },
+	//	{ "a{2,}?", "nrep{2,-1 lit{a}}" },
+	{"", "emp{}"},
+	//	{ "|", "emp{}" },  // alt{emp{}emp{}} but got factored
+	//	{ "|", "alt{emp{}emp{}}" },
+	{"|x|", "alt{emp{}lit{x}emp{}}"},
+	{".", "dot{}"},
+	{"^", "bol{}"},
+	{"$", "eol{}"},
+	//	{ "\\|", "lit{|}" },
+	//	{ "\\(", "lit{(}" },
+	//	{ "\\)", "lit{)}" },
+	//	{ "\\*", "lit{*}" },
+	//	{ "\\+", "lit{+}" },
+	//	{ "\\?", "lit{?}" },
+	//	{ "{", "lit{{}" },
+	{"}", "lit{}}"},
+	//	{ "\\.", "lit{.}" },
+	//	{ "\\^", "lit{^}" },
+	//	{ "\\$", "lit{$}" },
+	//	{ "\\\\", "lit{\\}" },
+	{"[ace]", "cc{0x61 0x63 0x65}"},
+	{"[abc]", "cc{0x61-0x63}"},
+	{"[a-z]", "cc{0x61-0x7a}"},
+	//	{ "[a]", "lit{a}" },
+	{"[a]", "cc{0x61}"},
+	//	{ "\\-", "lit{-}" },
+	{"-", "lit{-}"},
+	//	{ "\\_", "lit{_}" },
+
+	// Posix and Perl extensions
+	//	{ "[[:lower:]]", "cc{0x61-0x7a}" },
+	//	{ "[a-z]", "cc{0x61-0x7a}" },
+	//	{ "[^[:lower:]]", "cc{0x0-0x60 0x7b-0x10ffff}" },
+	//	{ "[[:^lower:]]", "cc{0x0-0x60 0x7b-0x10ffff}" },
+	//	{ "(?i)[[:lower:]]", "cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}" },
+	//	{ "(?i)[a-z]", "cc{0x41-0x5a 0x61-0x7a 0x17f 0x212a}" },
+	//	{ "(?i)[^[:lower:]]", "cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}" },
+	//	{ "(?i)[[:^lower:]]", "cc{0x0-0x40 0x5b-0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}" },
+	//	{ "\\d", "cc{0x30-0x39}" },
+	//	{ "\\D", "cc{0x0-0x2f 0x3a-0x10ffff}" },
+	//	{ "\\s", "cc{0x9-0xa 0xc-0xd 0x20}" },
+	//	{ "\\S", "cc{0x0-0x8 0xb 0xe-0x1f 0x21-0x10ffff}" },
+	//	{ "\\w", "cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a}" },
+	//	{ "\\W", "cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x10ffff}" },
+	//	{ "(?i)\\w", "cc{0x30-0x39 0x41-0x5a 0x5f 0x61-0x7a 0x17f 0x212a}" },
+	//	{ "(?i)\\W", "cc{0x0-0x2f 0x3a-0x40 0x5b-0x5e 0x60 0x7b-0x17e 0x180-0x2129 0x212b-0x10ffff}" },
+	//	{ "[^\\\\]", "cc{0x0-0x5b 0x5d-0x10ffff}" },
+	//	{ "\\C", "byte{}" },
+
+	// Unicode, negatives, and a double negative.
+	//	{ "\\p{Braille}", "cc{0x2800-0x28ff}" },
+	//	{ "\\P{Braille}", "cc{0x0-0x27ff 0x2900-0x10ffff}" },
+	//	{ "\\p{^Braille}", "cc{0x0-0x27ff 0x2900-0x10ffff}" },
+	//	{ "\\P{^Braille}", "cc{0x2800-0x28ff}" },
+
+	// More interesting regular expressions.
+	//	{ "a{,2}", "str{a{,2}}" },
+	//	{ "\\.\\^\\$\\\\", "str{.^$\\}" },
+	{"[a-zABC]", "cc{0x41-0x43 0x61-0x7a}"},
+	{"[^a]", "cc{0x0-0x60 0x62-0x10ffff}"},
+	{"[\xce\xb1-\xce\xb5\xe2\x98\xba]", "cc{0x3b1-0x3b5 0x263a}"}, // utf-8
+	//	{ "a*{", "cat{star{lit{a}}lit{{}}" },
+
+	// Test precedences
+	//	{ "(?:ab)*", "star{str{ab}}" },
+	//	{ "(ab)*", "star{cap{str{ab}}}" },
+	//	{ "ab|cd", "alt{str{ab}str{cd}}" },
+	//	{ "a(b|c)d", "cat{lit{a}cap{cc{0x62-0x63}}lit{d}}" },
+	{"(?:ab)*", "star{cat{lit{a}lit{b}}}"},
+	{"(ab)*", "star{cap{cat{lit{a}lit{b}}}}"},
+	{"ab|cd", "alt{cat{lit{a}lit{b}}cat{lit{c}lit{d}}}"},
+	{"a(b|c)d", "cat{lit{a}cap{alt{lit{b}lit{c}}}lit{d}}"},
+
+	// Test flattening.
+	//	{ "(?:a)", "lit{a}" },
+	//	{ "(?:ab)(?:cd)", "str{abcd}" },
+	//	{ "(?:a|b)|(?:c|d)", "cc{0x61-0x64}" },
+	//	{ "a|.", "dot{}" },
+	//	{ ".|a", "dot{}" },
+
+	// Test Perl quoted literals
+	//	{ "\\Q+|*?{[\\E", "str{+|*?{[}" },
+	//	{ "\\Q+\\E+", "plus{lit{+}}" },
+	//	{ "\\Q\\\\E", "lit{\\}" },
+	//	{ "\\Q\\\\\\E", "str{\\\\}" },
+
+	// Test Perl \A and \z
+	//	{ "(?m)^", "bol{}" },
+	//	{ "(?m)$", "eol{}" },
+	//	{ "(?-m)^", "bot{}" },
+	//	{ "(?-m)$", "eot{}" },
+	//	{ "(?m)\\A", "bot{}" },
+	//	{ "(?m)\\z", "eot{\\z}" },
+	//	{ "(?-m)\\A", "bot{}" },
+	//	{ "(?-m)\\z", "eot{\\z}" },
+
+	// Test named captures
+	//	{ "(?P<name>a)", "cap{name:lit{a}}" },
+
+	// Case-folded literals
+	//	{ "[Aa]", "litfold{a}" },
+
+	// Strings
+	//	{ "abcde", "str{abcde}" },
+	//	{ "[Aa][Bb]cd", "cat{strfold{ab}str{cd}}" },
+}
+
+const testFlags = MatchNL | PerlX | UnicodeGroups
+
+// Test Parse -> Dump.
+func TestParseDump(t *testing.T) {
+	for _, tt := range parseTests {
+		re, err := Parse(tt.Regexp, testFlags)
+		if err != nil {
+			t.Errorf("Parse(%#q): %v", tt.Regexp, err)
+			continue
+		}
+		d := dump(re)
+		if d != tt.Dump {
+			t.Errorf("Parse(%#q).Dump() = %#q want %#q", tt.Regexp, d, tt.Dump)
+		}
+	}
+}
+
+// dump prints a string representation of the regexp showing
+// the structure explicitly.
+func dump(re *Regexp) string {
+	var b bytes.Buffer
+	dumpRegexp(&b, re)
+	return b.String()
+}
+
+var opNames = []string{
+	OpNoMatch:        "no",
+	OpEmptyMatch:     "emp",
+	OpLiteral:        "lit",
+	OpCharClass:      "cc",
+	OpAnyCharNotNL:   "dnl",
+	OpAnyChar:        "dot",
+	OpBeginLine:      "bol",
+	OpEndLine:        "eol",
+	OpBeginText:      "bot",
+	OpEndText:        "eot",
+	OpWordBoundary:   "wb",
+	OpNoWordBoundary: "nwb",
+	OpCapture:        "cap",
+	OpStar:           "star",
+	OpPlus:           "plus",
+	OpQuest:          "que",
+	OpRepeat:         "rep",
+	OpConcat:         "cat",
+	OpAlternate:      "alt",
+}
+
+// dumpRegexp writes an encoding of the syntax tree for the regexp re to b.
+// It is used during testing to distinguish between parses that might print
+// the same using re's String method.
+func dumpRegexp(b *bytes.Buffer, re *Regexp) {
+	if int(re.Op) >= len(opNames) || opNames[re.Op] == "" {
+		fmt.Fprintf(b, "op%d", re.Op)
+	} else {
+		switch re.Op {
+		default:
+			b.WriteString(opNames[re.Op])
+		case OpStar, OpPlus, OpQuest, OpRepeat:
+			if re.Flags&NonGreedy != 0 {
+				b.WriteByte('n')
+			}
+			b.WriteString(opNames[re.Op])
+		case OpLiteral:
+			if len(re.Rune) > 1 {
+				b.WriteString("str")
+			} else {
+				b.WriteString("lit")
+			}
+			if re.Flags&FoldCase != 0 {
+				for _, r := range re.Rune {
+					if unicode.ToUpper(r) != r {
+						b.WriteString("fold")
+					}
+				}
+			}
+		}
+	}
+	b.WriteByte('{')
+	switch re.Op {
+	case OpEndText:
+		if re.Flags&WasDollar == 0 {
+			b.WriteString(`\z`)
+		}
+	case OpLiteral:
+		for _, r := range re.Rune {
+			b.WriteRune(r)
+		}
+	case OpConcat, OpAlternate:
+		for _, sub := range re.Sub {
+			dumpRegexp(b, sub)
+		}
+	case OpStar, OpPlus, OpQuest:
+		dumpRegexp(b, re.Sub[0])
+	case OpRepeat:
+		fmt.Fprintf(b, "%d,%d ", re.Min, re.Max)
+		dumpRegexp(b, re.Sub[0])
+	case OpCapture:
+		if re.Name != "" {
+			b.WriteString(re.Name)
+			b.WriteByte(':')
+		}
+		dumpRegexp(b, re.Sub[0])
+	case OpCharClass:
+		sep := ""
+		for i := 0; i < len(re.Rune); i += 2 {
+			b.WriteString(sep)
+			sep = " "
+			lo, hi := re.Rune[i], re.Rune[i+1]
+			if lo == hi {
+				fmt.Fprintf(b, "%#x", lo)
+			} else {
+				fmt.Fprintf(b, "%#x-%#x", lo, hi)
+			}
+		}
+	}
+	b.WriteByte('}')
+}