Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
///////////////////////////////
4
/// Utility regex constants ///
5
///////////////////////////////
6
 
7
const LIB = '(?(DEFINE)
8
    (?<singleQuotedString>\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')
9
    (?<doubleQuotedString>"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+")
10
    (?<string>(?&singleQuotedString)|(?&doubleQuotedString))
11
    (?<comment>/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/)
12
    (?<code>\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+})
13
)';
14
 
15
const PARAMS = '\[(?<params>[^[\]]*+(?:\[(?&params)\][^[\]]*+)*+)\]';
16
const ARGS   = '\((?<args>[^()]*+(?:\((?&args)\)[^()]*+)*+)\)';
17
 
18
///////////////////////////////
19
/// Preprocessing functions ///
20
///////////////////////////////
21
 
22
function preprocessGrammar($code) {
23
    $code = resolveNodes($code);
24
    $code = resolveMacros($code);
25
    $code = resolveStackAccess($code);
26
 
27
    return $code;
28
}
29
 
30
function resolveNodes($code) {
31
    return preg_replace_callback(
32
        '~\b(?<name>[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~',
33
        function($matches) {
34
            // recurse
35
            $matches['params'] = resolveNodes($matches['params']);
36
 
37
            $params = magicSplit(
38
                '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
39
                $matches['params']
40
            );
41
 
42
            $paramCode = '';
43
            foreach ($params as $param) {
44
                $paramCode .= $param . ', ';
45
            }
46
 
47
            return 'new ' . $matches['name'] . '(' . $paramCode . 'attributes())';
48
        },
49
        $code
50
    );
51
}
52
 
53
function resolveMacros($code) {
54
    return preg_replace_callback(
55
        '~\b(?<!::|->)(?!array\()(?<name>[a-z][A-Za-z]++)' . ARGS . '~',
56
        function($matches) {
57
            // recurse
58
            $matches['args'] = resolveMacros($matches['args']);
59
 
60
            $name = $matches['name'];
61
            $args = magicSplit(
62
                '(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
63
                $matches['args']
64
            );
65
 
66
            if ('attributes' === $name) {
67
                assertArgs(0, $args, $name);
68
                return '$this->startAttributeStack[#1] + $this->endAttributes';
69
            }
70
 
71
            if ('stackAttributes' === $name) {
72
                assertArgs(1, $args, $name);
73
                return '$this->startAttributeStack[' . $args[0] . ']'
74
                       . ' + $this->endAttributeStack[' . $args[0] . ']';
75
            }
76
 
77
            if ('init' === $name) {
78
                return '$$ = array(' . implode(', ', $args) . ')';
79
            }
80
 
81
            if ('push' === $name) {
82
                assertArgs(2, $args, $name);
83
 
84
                return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0];
85
            }
86
 
87
            if ('pushNormalizing' === $name) {
88
                assertArgs(2, $args, $name);
89
 
90
                return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }'
91
                       . ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
92
            }
93
 
94
            if ('toArray' == $name) {
95
                assertArgs(1, $args, $name);
96
 
97
                return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')';
98
            }
99
 
100
            if ('parseVar' === $name) {
101
                assertArgs(1, $args, $name);
102
 
103
                return 'substr(' . $args[0] . ', 1)';
104
            }
105
 
106
            if ('parseEncapsed' === $name) {
107
                assertArgs(3, $args, $name);
108
 
109
                return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
110
                       . ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }';
111
            }
112
 
113
            if ('makeNop' === $name) {
114
                assertArgs(3, $args, $name);
115
 
116
                return '$startAttributes = ' . $args[1] . ';'
117
                       . ' if (isset($startAttributes[\'comments\']))'
118
                       . ' { ' . $args[0] . ' = new Stmt\Nop($startAttributes + ' . $args[2] . '); }'
119
                       . ' else { ' . $args[0] . ' = null; }';
120
            }
121
 
122
            if ('makeZeroLengthNop' == $name) {
123
                assertArgs(2, $args, $name);
124
 
125
                return '$startAttributes = ' . $args[1] . ';'
126
                       . ' if (isset($startAttributes[\'comments\']))'
127
                       . ' { ' . $args[0] . ' = new Stmt\Nop($this->createCommentNopAttributes($startAttributes[\'comments\'])); }'
128
                       . ' else { ' . $args[0] . ' = null; }';
129
            }
130
 
131
            if ('prependLeadingComments' === $name) {
132
                assertArgs(1, $args, $name);
133
 
134
                return '$attrs = $this->startAttributeStack[#1]; $stmts = ' . $args[0] . '; '
135
                       . 'if (!empty($attrs[\'comments\'])) {'
136
                       . '$stmts[0]->setAttribute(\'comments\', '
137
                       . 'array_merge($attrs[\'comments\'], $stmts[0]->getAttribute(\'comments\', []))); }';
138
            }
139
 
140
            return $matches[0];
141
        },
142
        $code
143
    );
144
}
145
 
146
function assertArgs($num, $args, $name) {
147
    if ($num != count($args)) {
148
        die('Wrong argument count for ' . $name . '().');
149
    }
150
}
151
 
152
function resolveStackAccess($code) {
153
    $code = preg_replace('/\$\d+/', '$this->semStack[$0]', $code);
154
    $code = preg_replace('/#(\d+)/', '$$1', $code);
155
    return $code;
156
}
157
 
158
function removeTrailingWhitespace($code) {
159
    $lines = explode("\n", $code);
160
    $lines = array_map('rtrim', $lines);
161
    return implode("\n", $lines);
162
}
163
 
164
//////////////////////////////
165
/// Regex helper functions ///
166
//////////////////////////////
167
 
168
function regex($regex) {
169
    return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~';
170
}
171
 
172
function magicSplit($regex, $string) {
173
    $pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string);
174
 
175
    foreach ($pieces as &$piece) {
176
        $piece = trim($piece);
177
    }
178
 
179
    if ($pieces === ['']) {
180
        return [];
181
    }
182
 
183
    return $pieces;
184
}