Completed
Push — master ( d94322...a3e983 )
by Josh
02:31
created

CoalesceOptionalStrings::runPass()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 8
cts 8
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 1
crap 3
1
<?php
2
3
/**
4
* @package   s9e\RegexpBuilder
5
* @copyright Copyright (c) 2016 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\RegexpBuilder\Passes;
9
10
/**
11
* Replaces (?:ab?|b)? with a?b?
12
*/
13
class CoalesceOptionalStrings extends AbstractPass
14
{
15
	/**
16
	* {@inheritdoc}
17
	*/
18 7
	protected function canRun(array $strings)
19
	{
20 7
		return ($this->isOptional && count($strings) > 1);
21
	}
22
23
	/**
24
	* {@inheritdoc}
25
	*/
26 7
	protected function runPass(array $strings)
27
	{
28 7
		foreach ($this->getPrefixGroups($strings) as $suffix => $prefixStrings)
29
		{
30 7
			$suffix        = unserialize($suffix);
31 7
			$suffixStrings = array_diff_key($strings, $prefixStrings);
32 7
			if ($suffix === $this->buildSuffix($suffixStrings))
33
			{
34 4
				$this->isOptional = false;
35
36 7
				return $this->buildCoalescedStrings($prefixStrings, $suffix);
37
			}
38
		}
39
40 7
		return $strings;
41
	}
42
43
	/**
44
	* Build the final list of coalesced strings
45
	*
46
	* @param  array[] $prefixStrings
47
	* @param  array   $suffix
48
	* @return array[]
49
	*/
50 4
	protected function buildCoalescedStrings(array $prefixStrings, array $suffix)
51
	{
52 4
		$strings = $this->runPass($this->buildPrefix($prefixStrings));
53 4
		if (count($strings) === 1 && $strings[0][0][0] === [])
54
		{
55
			// If the prefix has been remerged into a list of strings which contains only one string
56
			// of which the first element is an optional alternations, we only need to append the
57
			// suffix
58 2
			$strings[0][] = $suffix;
59
		}
60
		else
61
		{
62
			// Put the current list of strings that form the prefix into a new list of strings, of
63
			// which the only string is composed of our optional prefix followed by the suffix
64 4
			array_unshift($strings, []);
65 4
			$strings = [[$strings, $suffix]];
66
		}
67
68 4
		return $strings;
69
	}
70
71
	/**
72
	* Build the list of strings used as prefix
73
	*
74
	* @param  array[] $strings
75
	* @return array[]
76
	*/
77 4
	protected function buildPrefix(array $strings)
78
	{
79 4
		$prefix = [];
80 4
		foreach ($strings as $string)
81
		{
82
			// Remove the last element (suffix) of each string before adding it
83 4
			array_pop($string);
84 4
			$prefix[] = $string;
85
		}
86
87 4
		return $prefix;
88
	}
89
90
	/**
91
	* Build a list of strings that matches any given strings or nothing
92
	*
93
	* Will unpack groups of single characters
94
	*
95
	* @param  array[] $strings
96
	* @return array[]
97
	*/
98 7
	protected function buildSuffix(array $strings)
99
	{
100 7
		$suffix = [[]];
101 7
		foreach ($strings as $string)
102
		{
103 7
			if ($this->isCharacterClassString($string))
104
			{
105 1
				foreach ($string[0] as $element)
106
				{
107 1
					$suffix[] = $element;
108
				}
109
			}
110
			else
111
			{
112 7
				$suffix[] = $string;
113
			}
114
		}
115
116 7
		return $suffix;
117
	}
118
119
	/**
120
	* Get the list of potential prefix strings grouped by identical suffix
121
	*
122
	* @param  array[] $strings
123
	* @return array
124
	*/
125 7
	protected function getPrefixGroups(array $strings)
126
	{
127 7
		$groups = [];
128 7
		foreach ($strings as $k => $string)
129
		{
130 7
			if ($this->hasOptionalSuffix($string))
131
			{
132 7
				$groups[serialize(end($string))][$k] = $string;
133
			}
134
		}
135
136 7
		return $groups;
137
	}
138
}