-
Notifications
You must be signed in to change notification settings - Fork 0
/
Compiler.php
110 lines (93 loc) · 2.61 KB
/
Compiler.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<?php
/**
* This file is part of FFI package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace FFI\Preprocessor\Directive\FunctionLikeDirective;
use FFI\Preprocessor\Directive\FunctionLikeDirective;
/**
* @internal Refers to {@see FunctionLikeDirective}
*/
final class Compiler
{
/**
* @var string
*/
private const T_STRINGIZE = 'stringize';
/**
* @var string
*/
private const T_CONCAT_LEFT = 'concat_left';
/**
* @var string
*/
private const T_CONCAT_RIGHT = 'concat_right';
/**
* @var string
*/
private const T_NATIVE = 'native';
/**
* @var string
*/
private const TPL_PATTERN = '/' .
'#\h*%1$s(*MARK:' . self::T_STRINGIZE . ')|' .
'##\h*%1$s(?:\h*##)?(*MARK:' . self::T_CONCAT_LEFT . ')|' .
'%1$s\h*##(*MARK:' . self::T_CONCAT_RIGHT . ')|' .
'(?<![a-z0-9_])%1$s(?![a-z0-9_])(*MARK:' . self::T_NATIVE . ')' .
'/Ssum'
;
/**
* @psalm-param array<array-key, string> $arguments
* @psalm-return \Closure(mixed ...$args): string
*
* @param string $body
* @param array $arguments
* @return \Closure
*/
public static function compile(string $body, array $arguments): \Closure
{
$template = self::build($body, $arguments);
return static function (...$args) use ($template): string {
$from = \array_map(static fn (int $i): string => "\0$i\0", \array_keys($args));
return \str_replace($from, $args, $template);
};
}
/**
* @param string $body
* @param array $arguments
* @return string
*/
private static function build(string $body, array $arguments): string
{
foreach ($arguments as $i => $name) {
$body = self::replace($body, $i, $name);
}
return $body;
}
/**
* @param string $body
* @param int $i
* @param string $name
* @return string
*/
private static function replace(string $body, int $i, string $name): string
{
$pattern = \sprintf(self::TPL_PATTERN, \preg_quote($name, '/'));
return (string)\preg_replace_callback($pattern, self::callback($i), $body);
}
/**
* @param int $i
* @return \Closure
*/
private static function callback(int $i): \Closure
{
return static function ($match) use ($i): string {
return $match['MARK'] === self::T_STRINGIZE
? "\"\0$i\0\""
: "\0$i\0";
};
}
}