Compare commits
3 Commits
08e01a1c81
...
1db61108e4
| Author | SHA1 | Date | |
|---|---|---|---|
| 1db61108e4 | |||
| 8feaefeeb8 | |||
| a259f0ceab |
31
compile.go
31
compile.go
@@ -666,10 +666,7 @@ func thompson(re []postfixNode) (Reg, error) {
|
||||
// nothing), the output of shuntingYard() (and the input to thompson()) ends up being empty.
|
||||
// In these cases, we will return an NFA with 1 state, with an assertion that is always true.
|
||||
if len(re) == 0 {
|
||||
start := newState()
|
||||
start.content = newContents(EPSILON)
|
||||
start.isEmpty = true
|
||||
start.assert = ALWAYS_TRUE
|
||||
start := zeroLengthMatchState()
|
||||
nfa = append(nfa, &start)
|
||||
}
|
||||
|
||||
@@ -852,8 +849,30 @@ func thompson(re []postfixNode) (Reg, error) {
|
||||
s2 := question(s1)
|
||||
nfa = append(nfa, s2)
|
||||
case PIPE:
|
||||
s1 := mustPop(&nfa)
|
||||
s2 := mustPop(&nfa)
|
||||
// A pipe operator doesn't actually need either operand to be present. If an operand isn't present,
|
||||
// it is replaced with an implicit 'matchZeroLength' state (this is the same thing that we add at the top if our
|
||||
// input has zero postfixNodes).
|
||||
// Things to think about:
|
||||
// 'a|'
|
||||
// '|a'
|
||||
// '^a|'
|
||||
// '^|a'
|
||||
s1, err1 := pop(&nfa)
|
||||
s2, err2 := pop(&nfa)
|
||||
if err2 != nil || (s2.groupBegin && len(s2.transitions) == 0) { // Doesn't exist, or its just an LPAREN
|
||||
if err2 == nil { // Roundabout way of saying that this node existed, but it was an LPAREN, so we append it back
|
||||
nfa = append(nfa, s2)
|
||||
}
|
||||
tmp := zeroLengthMatchState()
|
||||
s2 = &tmp
|
||||
}
|
||||
if err1 != nil || (s1.groupBegin && len(s1.transitions) == 0) { // Doesn't exist, or its just an LPAREN
|
||||
if err1 == nil { // See above for explanation
|
||||
nfa = append(nfa, s1)
|
||||
}
|
||||
tmp := zeroLengthMatchState()
|
||||
s1 = &tmp
|
||||
}
|
||||
s3 := alternate(s1, s2)
|
||||
nfa = append(nfa, s3)
|
||||
}
|
||||
|
||||
9
nfa.go
9
nfa.go
@@ -333,3 +333,12 @@ func newState() State {
|
||||
ret.output = append(ret.output, &ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Creates and returns a state that _always_ has a zero-length match.
|
||||
func zeroLengthMatchState() State {
|
||||
start := newState()
|
||||
start.content = newContents(EPSILON)
|
||||
start.isEmpty = true
|
||||
start.assert = ALWAYS_TRUE
|
||||
return start
|
||||
}
|
||||
|
||||
26
re_test.go
26
re_test.go
@@ -265,6 +265,21 @@ var reTests = []struct {
|
||||
{`\By\B`, nil, `xyz`, []Group{{1, 2}}},
|
||||
{`ab|cd`, nil, `abc`, []Group{{0, 2}}},
|
||||
{`ab|cd`, nil, `abcd`, []Group{{0, 2}, {2, 4}}},
|
||||
{`$b`, nil, `b`, []Group{}},
|
||||
{`a\(b`, nil, `a(b`, []Group{{0, 3}}},
|
||||
{`a\(*b`, nil, `ab`, []Group{{0, 2}}},
|
||||
{`a\(*b`, nil, `a((b`, []Group{{0, 4}}},
|
||||
{`a\\b`, nil, `a\b`, []Group{{0, 3}}},
|
||||
{`a+b+c`, nil, `aabbabc`, []Group{{4, 7}}},
|
||||
{`)(`, nil, `-`, nil},
|
||||
{`[^ab]*`, nil, `cde`, []Group{{0, 3}, {3, 3}}},
|
||||
{`abc`, nil, ``, []Group{}},
|
||||
{`a*`, nil, ``, []Group{{0, 0}}},
|
||||
{`a|b|c|d|e`, nil, `e`, []Group{{0, 1}}},
|
||||
{`abcd*efg`, nil, `abcdefg`, []Group{{0, 7}}},
|
||||
{`ab*`, nil, `xabyabbbz`, []Group{{1, 3}, {4, 8}}},
|
||||
{`ab*`, nil, `xayabbbz`, []Group{{1, 2}, {3, 7}}},
|
||||
{`[abhgefdc]ij`, nil, `hij`, []Group{{0, 3}}},
|
||||
|
||||
// Todo - add numeric range tests
|
||||
}
|
||||
@@ -294,10 +309,21 @@ var groupTests = []struct {
|
||||
{"(a?)a?", nil, "ab", []Match{[]Group{{0, 1}, {0, 1}}, []Group{{1, 1}, {1, 1}}, []Group{{2, 2}, {2, 2}}}},
|
||||
{"(a?)a?", nil, "aa", []Match{[]Group{{0, 2}, {0, 1}}, []Group{{2, 2}, {2, 2}}}},
|
||||
{"a((b.d){3})", nil, "abfdbhdbid", []Match{[]Group{{0, 10}, {1, 10}, {7, 10}}}},
|
||||
|
||||
// Test cases from Python's RE test suite
|
||||
{`(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\071`, nil, `abcdefghijkl9`, []Match{[]Group{{0, 13}, {0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 7}, {7, 8}, {8, 9}, {9, 10}, {10, 11}, {11, 12}}}},
|
||||
{`()ef`, nil, `def`, []Match{[]Group{{1, 3}, {1, 1}}}},
|
||||
{`(?:)ef`, nil, `def`, []Match{[]Group{{1, 3}}}},
|
||||
{`(?:)`, nil, `def`, []Match{[]Group{{0, 0}}, []Group{{1, 1}}, []Group{{2, 2}}, []Group{{3, 3}}}},
|
||||
{`((a))`, nil, `abc`, []Match{[]Group{{0, 1}, {0, 1}, {0, 1}}}},
|
||||
{`(a)b(c)`, nil, `abc`, []Match{[]Group{{0, 3}, {0, 1}, {2, 3}}}},
|
||||
{`(a+|b)*`, nil, `ab`, []Match{[]Group{{0, 2}, {1, 2}}, []Group{{2, 2}}}},
|
||||
{`(a+|b)+`, nil, `ab`, []Match{[]Group{{0, 2}, {1, 2}}}},
|
||||
{`(a+|b)?`, nil, `ab`, []Match{[]Group{{0, 1}, {0, 1}}, []Group{{1, 2}, {1, 2}}, []Group{{2, 2}}}},
|
||||
{`(a|b|c|d|e)f`, nil, `ef`, []Match{[]Group{{0, 2}, {0, 1}}}},
|
||||
{`(ab|cd)e`, nil, `abcde`, []Match{[]Group{{2, 5}, {2, 4}}}},
|
||||
{`^(ab|cd)e`, nil, `abcde`, []Match{}},
|
||||
{`(abc|)ef`, nil, `abcdef`, []Match{[]Group{{4, 6}, {4, 4}}}},
|
||||
}
|
||||
|
||||
func TestFindAllMatches(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user