Skip to content

Commit 498245d

Browse files
committed
Making slice a Utils function
1 parent 0ba2921 commit 498245d

File tree

5 files changed

+104
-88
lines changed

5 files changed

+104
-88
lines changed

src/FnDispatcher.php

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -236,79 +236,6 @@ private function fn_values(array $args)
236236
return array_values((array) $args[0]);
237237
}
238238

239-
private function fn_slice(array $args)
240-
{
241-
try {
242-
$this->validate('slice', $args, [
243-
['array', 'string'],
244-
['number', 'null'],
245-
['number', 'null'],
246-
['number', 'null']
247-
]);
248-
} catch (\Exception $e) {
249-
return null;
250-
}
251-
252-
return $this->sliceIndices($args[0], $args[1], $args[2], $args[3]);
253-
}
254-
255-
private function adjustEndpoint($length, $endpoint, $step)
256-
{
257-
if ($endpoint < 0) {
258-
$endpoint += $length;
259-
if ($endpoint < 0) {
260-
$endpoint = 0;
261-
}
262-
} elseif ($endpoint >= $length) {
263-
$endpoint = $step < 0 ? $length - 1 : $length;
264-
}
265-
266-
return $endpoint;
267-
}
268-
269-
private function adjustSlice($length, $start, $stop, $step)
270-
{
271-
if ($step === null) {
272-
$step = 1;
273-
} elseif ($step === 0) {
274-
throw new \RuntimeException('step cannot be 0');
275-
}
276-
277-
if ($start === null) {
278-
$start = $step < 0 ? $length - 1 : 0;
279-
} else {
280-
$start = $this->adjustEndpoint($length, $start, $step);
281-
}
282-
283-
if ($stop === null) {
284-
$stop = $step < 0 ? -1 : $length;
285-
} else {
286-
$stop = $this->adjustEndpoint($length, $stop, $step);
287-
}
288-
289-
return [$start, $stop, $step];
290-
}
291-
292-
private function sliceIndices($subject, $start, $stop, $step)
293-
{
294-
$type = gettype($subject);
295-
$len = $type == 'string' ? strlen($subject) : count($subject);
296-
list($start, $stop, $step) = $this->adjustSlice($len, $start, $stop, $step);
297-
298-
$result = [];
299-
if ($step > 0) {
300-
for ($i = $start; $i < $stop; $i += $step) {
301-
$result[] = $subject[$i];
302-
}
303-
} else {
304-
for ($i = $start; $i > $stop; $i += $step) {
305-
$result[] = $subject[$i];
306-
}
307-
}
308-
309-
return $type == 'string' ? implode($result, '') : $result;
310-
}
311-
312239
private function typeError($from, $msg)
313240
{
314241
if (strpos($from, ':')) {

src/TreeCompiler.php

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -254,15 +254,12 @@ private function visit_function(array $node)
254254
private function visit_slice(array $node)
255255
{
256256
return $this
257-
->write('$value = Fn::getInstance()->__invoke("slice", [')
258-
->indent()
259-
->write('$value, %s, %s, %s', [
260-
var_export($node['value'][0], true),
261-
var_export($node['value'][1], true),
262-
var_export($node['value'][2], true)
263-
])
264-
->outdent()
265-
->write(']);');
257+
->write('$value = !is_string($value) && !Utils::isArray($value)')
258+
->write(' ? null : Utils::slice($value, %s, %s, %s);', [
259+
var_export($node['value'][0], true),
260+
var_export($node['value'][1], true),
261+
var_export($node['value'][2], true)
262+
]);
266263
}
267264

268265
private function visit_current(array $node)

src/TreeInterpreter.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,13 @@ private function dispatch(array $node, $value)
186186
return $dispatcher($node['value'], $args);
187187

188188
case 'slice':
189-
return $dispatcher('slice', [
190-
$value,
191-
$node['value'][0],
192-
$node['value'][1],
193-
$node['value'][2],
194-
]);
189+
return is_string($value) || Utils::isArray($value)
190+
? Utils::slice(
191+
$value,
192+
$node['value'][0],
193+
$node['value'][1],
194+
$node['value'][2]
195+
) : null;
195196

196197
case 'expref':
197198
$apply = $node['children'][0];

src/Utils.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,81 @@ public static function stableSort(array $data, callable $sortFn)
128128
// Undecorate each item and return the resulting sorted array
129129
return array_map(function ($v) { return $v[0]; }, array_values($data));
130130
}
131+
132+
/**
133+
* Creates a Python-style slice of a string or array.
134+
*
135+
* @param array|string $value Value to slice
136+
* @param int|null $start Starting position
137+
* @param int|null $stop Stop position
138+
* @param int $step Step (1, 2, -1, -2, etc.)
139+
*
140+
* @return array|string
141+
* @throws \InvalidArgumentException
142+
*/
143+
public static function slice($value, $start = null, $stop = null, $step = 1)
144+
{
145+
if (!is_array($value) && !is_string($value)) {
146+
throw new \InvalidArgumentException('Expects string or array');
147+
}
148+
149+
return self::sliceIndices($value, $start, $stop, $step);
150+
}
151+
152+
private static function adjustEndpoint($length, $endpoint, $step)
153+
{
154+
if ($endpoint < 0) {
155+
$endpoint += $length;
156+
if ($endpoint < 0) {
157+
$endpoint = 0;
158+
}
159+
} elseif ($endpoint >= $length) {
160+
$endpoint = $step < 0 ? $length - 1 : $length;
161+
}
162+
163+
return $endpoint;
164+
}
165+
166+
private static function adjustSlice($length, $start, $stop, $step)
167+
{
168+
if ($step === null) {
169+
$step = 1;
170+
} elseif ($step === 0) {
171+
throw new \RuntimeException('step cannot be 0');
172+
}
173+
174+
if ($start === null) {
175+
$start = $step < 0 ? $length - 1 : 0;
176+
} else {
177+
$start = self::adjustEndpoint($length, $start, $step);
178+
}
179+
180+
if ($stop === null) {
181+
$stop = $step < 0 ? -1 : $length;
182+
} else {
183+
$stop = self::adjustEndpoint($length, $stop, $step);
184+
}
185+
186+
return [$start, $stop, $step];
187+
}
188+
189+
private static function sliceIndices($subject, $start, $stop, $step)
190+
{
191+
$type = gettype($subject);
192+
$len = $type == 'string' ? strlen($subject) : count($subject);
193+
list($start, $stop, $step) = self::adjustSlice($len, $start, $stop, $step);
194+
195+
$result = [];
196+
if ($step > 0) {
197+
for ($i = $start; $i < $stop; $i += $step) {
198+
$result[] = $subject[$i];
199+
}
200+
} else {
201+
for ($i = $start; $i > $stop; $i += $step) {
202+
$result[] = $subject[$i];
203+
}
204+
}
205+
206+
return $type == 'string' ? implode($result, '') : $result;
207+
}
131208
}

tests/UtilsTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ public function testHasStableSort()
9696
$this->assertEquals(2, $result[3]);
9797
$this->assertEquals(0, $result[4]);
9898
}
99+
100+
public function testSlicesArrays()
101+
{
102+
$this->assertEquals([3, 2, 1], Utils::slice([1, 2, 3], null, null, -1));
103+
$this->assertEquals([1, 3], Utils::slice([1, 2, 3], null, null, 2));
104+
$this->assertEquals([2, 3], Utils::slice([1, 2, 3], 1));
105+
}
106+
107+
public function testSlicesStrings()
108+
{
109+
$this->assertEquals('cba', Utils::slice('abc', null, null, -1));
110+
$this->assertEquals('ac', Utils::slice('abc', null, null, 2));
111+
$this->assertEquals('bc', Utils::slice('abc', 1));
112+
}
99113
}
100114

101115
class _TestClass implements \ArrayAccess

0 commit comments

Comments
 (0)