Passed
Push — master ( 4e8b13...a3a48f )
by Konrad
04:16
created

SPARQLParser::xSelectQuery()   C

Complexity

Conditions 17
Paths 113

Size

Total Lines 55
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 17.0118

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 17
eloc 33
c 1
b 0
f 0
nc 113
nop 1
dl 0
loc 55
ccs 28
cts 29
cp 0.9655
crap 17.0118
rs 5.1083

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the sweetrdf/InMemoryStoreSqlite package and licensed under
5
 * the terms of the GPL-3 license.
6
 *
7
 * (c) Konrad Abicht <[email protected]>
8
 * (c) Benjamin Nowack
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace sweetrdf\InMemoryStoreSqlite\Parser;
15
16
use function sweetrdf\InMemoryStoreSqlite\calcBase;
17
use sweetrdf\InMemoryStoreSqlite\Log\Logger;
18
use sweetrdf\InMemoryStoreSqlite\NamespaceHelper;
19
use sweetrdf\InMemoryStoreSqlite\StringReader;
20
21
class SPARQLParser extends TurtleParser
22
{
23 94
    public function __construct(Logger $logger, NamespaceHelper $namespaceHelper, StringReader $stringReader)
24
    {
25 94
        parent::__construct($logger, $namespaceHelper, $stringReader);
26
27 94
        $this->bnode_prefix = 'arc'.substr(md5(uniqid(rand())), 0, 4).'b';
0 ignored issues
show
Bug Best Practice introduced by
The property bnode_prefix does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
28 94
        $this->bnode_id = 0;
29 94
        $this->bnode_pattern_index = ['patterns' => [], 'bnodes' => []];
0 ignored issues
show
Bug Best Practice introduced by
The property bnode_pattern_index does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
30 94
    }
31
32 94
    public function parse(string $q, string $path = ''): void
33
    {
34 94
        $this->base = $path ? calcBase($path) : NamespaceHelper::BASE_NAMESPACE;
35 94
        $this->r = [
36
            'base' => '',
37
            'vars' => [],
38
            'prefixes' => [],
39
        ];
40 94
        $this->unparsed_code = $q;
0 ignored issues
show
Bug Best Practice introduced by
The property unparsed_code does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
41 94
        list($r, $v) = $this->xQuery($q);
42 94
        if ($r) {
43 93
            $this->r['query'] = $r;
44 93
            $this->unparsed_code = trim($v);
45 4
        } elseif (!$this->logger->hasEntries('error') && !$this->unparsed_code) {
46
            $this->logger->error('Query not properly closed');
47
        }
48 94
        $this->r['prefixes'] = $this->namespaceHelper->getNamespaces();
49 94
        $this->r['base'] = $this->base;
50
        /* remove trailing comments */
51 94
        while (preg_match('/^\s*(\#[^\xd\xa]*)(.*)$/si', $this->unparsed_code, $m)) {
52
            $this->unparsed_code = $m[2];
53
        }
54 94
        if ($this->unparsed_code && !$this->logger->hasEntries('error')) {
55
            $rest = preg_replace('/[\x0a|\x0d]/i', ' ', substr($this->unparsed_code, 0, 30));
56
            $msg = trim($rest) ? 'Could not properly handle "'.$rest.'"' : 'Syntax error, probably an incomplete pattern';
57
            $this->logger->error($msg);
58
        }
59 94
    }
60
61
    /* 1 */
62
63
    protected function xQuery($v)
64
    {
65
        list($r, $v) = $this->xPrologue($v);
66
        foreach (['Select', 'Construct', 'Describe', 'Ask'] as $type) {
67
            $m = 'x'.$type.'Query';
68
            if ((list($r, $v) = $this->$m($v)) && $r) {
69
                return [$r, $v];
70
            }
71
        }
72
73
        return [0, $v];
74
    }
75
76
    /* 2 */
77
78 94
    protected function xPrologue($v)
79
    {
80 94
        $r = 0;
81 94
        if ((list($sub_r, $v) = $this->xBaseDecl($v)) && $sub_r) {
82
            $this->base = $sub_r;
83
            $r = 1;
84
        }
85 94
        while ((list($sub_r, $v) = $this->xPrefixDecl($v)) && $sub_r) {
86 19
            $this->namespaceHelper->setPrefix($sub_r['prefix'], $sub_r['uri']);
87 19
            $r = 1;
88
        }
89
90 94
        return [$r, $v];
91
    }
92
93
    /* 5.. */
94
95 94
    protected function xSelectQuery($v)
96
    {
97 94
        if ($sub_r = $this->x('SELECT\s+', $v)) {
98 87
            $r = [
99
                'type' => 'select',
100
                'result_vars' => [],
101
                'dataset' => [],
102
            ];
103 87
            $all_vars = 0;
104 87
            $sub_v = $sub_r[1];
105
            /* distinct, reduced */
106 87
            if ($sub_r = $this->x('(DISTINCT|REDUCED)\s+', $sub_v)) {
107 1
                $r[strtolower($sub_r[1])] = 1;
108 1
                $sub_v = $sub_r[2];
109
            }
110
            /* result vars */
111 87
            if ($sub_r = $this->x('\*\s+', $sub_v)) {
112 44
                $all_vars = 1;
113 44
                $sub_v = $sub_r[1];
114
            } else {
115 44
                while ((list($sub_r, $sub_v) = $this->xResultVar($sub_v)) && $sub_r) {
116 42
                    $r['result_vars'][] = $sub_r;
117
                }
118
            }
119 87
            if (!$all_vars && !\count($r['result_vars'])) {
120 2
                $this->logger->error('No result bindings specified.');
121
            }
122
            /* dataset */
123 87
            while ((list($sub_r, $sub_v) = $this->xDatasetClause($sub_v)) && $sub_r) {
124 27
                $r['dataset'][] = $sub_r;
125
            }
126
            /* where */
127 87
            if ((list($sub_r, $sub_v) = $this->xWhereClause($sub_v)) && $sub_r) {
128 84
                $r['pattern'] = $sub_r;
129
            } else {
130 3
                return [0, $v];
131
            }
132
            /* solution modifier */
133 84
            if ((list($sub_r, $sub_v) = $this->xSolutionModifier($sub_v)) && $sub_r) {
134 14
                $r = array_merge($r, $sub_r);
135
            }
136
            /* all vars */
137 84
            if ($all_vars) {
138 43
                foreach ($this->r['vars'] as $var) {
139 43
                    $r['result_vars'][] = ['var' => $var, 'aggregate' => 0, 'alias' => ''];
140
                }
141 43
                if (!$r['result_vars']) {
142
                    $r['result_vars'][] = '*';
143
                }
144
            }
145
146 84
            return [$r, $sub_v];
147
        }
148
149 90
        return [0, $v];
150
    }
151
152
    protected function xResultVar($v)
153
    {
154
        return $this->xVar($v);
155
    }
156
157
    /* 6.. */
158
159 90
    protected function xConstructQuery($v)
160
    {
161 90
        if ($sub_r = $this->x('CONSTRUCT\s*', $v)) {
162 3
            $r = [
163
                'type' => 'construct',
164
                'dataset' => [],
165
            ];
166 3
            $sub_v = $sub_r[1];
167
            /* construct template */
168 3
            if ((list($sub_r, $sub_v) = $this->xConstructTemplate($sub_v)) && \is_array($sub_r)) {
169 3
                $r['construct_triples'] = $sub_r;
170
            } else {
171
                $this->logger->error('Construct Template not found');
172
173
                return [0, $v];
174
            }
175
            /* dataset */
176 3
            while ((list($sub_r, $sub_v) = $this->xDatasetClause($sub_v)) && $sub_r) {
177
                $r['dataset'][] = $sub_r;
178
            }
179
            /* where */
180 3
            if ((list($sub_r, $sub_v) = $this->xWhereClause($sub_v)) && $sub_r) {
181 3
                $r['pattern'] = $sub_r;
182
            } else {
183
                return [0, $v];
184
            }
185
            /* solution modifier */
186 3
            if ((list($sub_r, $sub_v) = $this->xSolutionModifier($sub_v)) && $sub_r) {
187
                $r = array_merge($r, $sub_r);
188
            }
189
190 3
            return [$r, $sub_v];
191
        }
192
193 90
        return [0, $v];
194
    }
195
196
    /* 7.. */
197
198 90
    protected function xDescribeQuery($v)
199
    {
200 90
        if ($sub_r = $this->x('DESCRIBE\s+', $v)) {
201 3
            $r = [
202
                'type' => 'describe',
203
                'result_vars' => [],
204
                'result_uris' => [],
205
                'dataset' => [],
206
            ];
207 3
            $sub_v = $sub_r[1];
208 3
            $all_vars = 0;
209
            /* result vars/uris */
210 3
            if ($sub_r = $this->x('\*\s+', $sub_v)) {
211 1
                $all_vars = 1;
212 1
                $sub_v = $sub_r[1];
213
            } else {
214
                do {
215 2
                    $proceed = 0;
216 2
                    if ((list($sub_r, $sub_v) = $this->xResultVar($sub_v)) && $sub_r) {
217 1
                        $r['result_vars'][] = $sub_r;
218 1
                        $proceed = 1;
219
                    }
220 2
                    if ((list($sub_r, $sub_v) = $this->xIRIref($sub_v)) && $sub_r) {
221 1
                        $r['result_uris'][] = $sub_r;
222 1
                        $proceed = 1;
223
                    }
224 2
                } while ($proceed);
225
            }
226 3
            if (!$all_vars && !\count($r['result_vars']) && !\count($r['result_uris'])) {
227
                $this->logger->error('No result bindings specified.');
228
            }
229
            /* dataset */
230 3
            while ((list($sub_r, $sub_v) = $this->xDatasetClause($sub_v)) && $sub_r) {
231
                $r['dataset'][] = $sub_r;
232
            }
233
            /* where */
234 3
            if ((list($sub_r, $sub_v) = $this->xWhereClause($sub_v)) && $sub_r) {
235 2
                $r['pattern'] = $sub_r;
236
            }
237
            /* solution modifier */
238 3
            if ((list($sub_r, $sub_v) = $this->xSolutionModifier($sub_v)) && $sub_r) {
239
                $r = array_merge($r, $sub_r);
240
            }
241
            /* all vars */
242 3
            if ($all_vars) {
243 1
                foreach ($this->r['vars'] as $var) {
244 1
                    $r['result_vars'][] = ['var' => $var, 'aggregate' => 0, 'alias' => ''];
245
                }
246
            }
247
248 3
            return [$r, $sub_v];
249
        }
250
251 90
        return [0, $v];
252
    }
253
254
    /* 8.. */
255
256 90
    protected function xAskQuery($v)
257
    {
258 90
        if ($sub_r = $this->x('ASK\s+', $v)) {
259 3
            $r = [
260
                'type' => 'ask',
261
                'dataset' => [],
262
            ];
263 3
            $sub_v = $sub_r[1];
264
            /* dataset */
265 3
            while ((list($sub_r, $sub_v) = $this->xDatasetClause($sub_v)) && $sub_r) {
266 2
                $r['dataset'][] = $sub_r;
267
            }
268
            /* where */
269 3
            if ((list($sub_r, $sub_v) = $this->xWhereClause($sub_v)) && $sub_r) {
270 3
                $r['pattern'] = $sub_r;
271
272 3
                return [$r, $sub_v];
273
            } else {
274
                $this->logger->error('Missing or invalid WHERE clause.');
275
            }
276
        }
277
278 90
        return [0, $v];
279
    }
280
281
    /* 9, 10, 11, 12 */
282
283 93
    protected function xDatasetClause($v)
284
    {
285 93
        if ($r = $this->x('FROM(\s+NAMED)?\s+', $v)) {
286 29
            $named = $r[1] ? 1 : 0;
287 29
            if ((list($r, $sub_v) = $this->xIRIref($r[2])) && $r) {
288 29
                return [['graph' => $r, 'named' => $named], $sub_v];
289
            }
290
        }
291
292 93
        return [0, $v];
293
    }
294
295
    /* 13 */
296
297 93
    protected function xWhereClause($v)
298
    {
299 93
        if ($r = $this->x('(WHERE)?', $v)) {
300 93
            $v = $r[2];
301
        }
302 93
        if ((list($r, $v) = $this->xGroupGraphPattern($v)) && $r) {
303 90
            return [$r, $v];
304
        }
305
306 89
        return [0, $v];
307
    }
308
309
    /* 14, 15 */
310
311
    protected function xSolutionModifier($v)
312
    {
313
        $r = [];
314
        if ((list($sub_r, $sub_v) = $this->xOrderClause($v)) && $sub_r) {
315
            $r['order_infos'] = $sub_r;
316
        }
317
        while ((list($sub_r, $sub_v) = $this->xLimitOrOffsetClause($sub_v)) && $sub_r) {
318
            $r = array_merge($r, $sub_r);
319
        }
320
321
        return ($v == $sub_v) ? [0, $v] : [$r, $sub_v];
322
    }
323
324
    /* 18, 19 */
325
326 93
    protected function xLimitOrOffsetClause($v)
327
    {
328 93
        if ($sub_r = $this->x('(LIMIT|OFFSET)', $v)) {
329 3
            $key = strtolower($sub_r[1]);
330 3
            $sub_v = $sub_r[2];
331 3
            if ((list($sub_r, $sub_v) = $this->xINTEGER($sub_v)) && (false !== $sub_r)) {
332 3
                return [[$key => $sub_r], $sub_v];
333
            }
334
            if ((list($sub_r, $sub_v) = $this->xPlaceholder($sub_v)) && (false !== $sub_r)) {
335
                return [[$key => $sub_r], $sub_v];
336
            }
337
        }
338
339 93
        return [0, $v];
340
    }
341
342
    /* 16 */
343
344 93
    protected function xOrderClause($v)
345
    {
346 93
        if ($sub_r = $this->x('ORDER BY\s+', $v)) {
347 5
            $sub_v = $sub_r[1];
348 5
            $r = [];
349 5
            while ((list($sub_r, $sub_v) = $this->xOrderCondition($sub_v)) && $sub_r) {
350 4
                $r[] = $sub_r;
351
            }
352 5
            if (\count($r)) {
353 4
                return [$r, $sub_v];
354
            } else {
355 1
                $this->logger->error('No order conditions specified.');
356
            }
357
        }
358
359 93
        return [0, $v];
360
    }
361
362
    /* 17, 27 */
363
364 5
    protected function xOrderCondition($v)
365
    {
366 5
        if ($sub_r = $this->x('(ASC|DESC)', $v)) {
367 4
            $dir = strtolower($sub_r[1]);
368 4
            $sub_v = $sub_r[2];
369 4
            if ((list($sub_r, $sub_v) = $this->xBrackettedExpression($sub_v)) && $sub_r) {
370 4
                $sub_r['direction'] = $dir;
371
372 4
                return [$sub_r, $sub_v];
373
            }
374 5
        } elseif ((list($sub_r, $sub_v) = $this->xVar($v)) && $sub_r) {
375
            $sub_r['direction'] = 'asc';
376
377
            return [$sub_r, $sub_v];
378 5
        } elseif ((list($sub_r, $sub_v) = $this->xBrackettedExpression($v)) && $sub_r) {
379
            return [$sub_r, $sub_v];
380 5
        } elseif ((list($sub_r, $sub_v) = $this->xBuiltInCall($v)) && $sub_r) {
381
            $sub_r['direction'] = 'asc';
382
383
            return [$sub_r, $sub_v];
384 5
        } elseif ((list($sub_r, $sub_v) = $this->xFunctionCall($v)) && $sub_r) {
385
            $sub_r['direction'] = 'asc';
386
387
            return [$sub_r, $sub_v];
388
        }
389
390 5
        return [0, $v];
391
    }
392
393
    /* 20 */
394
395 93
    protected function xGroupGraphPattern($v)
396
    {
397 93
        $pattern_id = substr(md5(uniqid(rand())), 0, 4);
398 93
        if ($sub_r = $this->x('\{', $v)) {
399 91
            $r = ['type' => 'group', 'patterns' => []];
400 91
            $sub_v = $sub_r[1];
401 91
            if ((list($sub_r, $sub_v) = $this->xTriplesBlock($sub_v)) && $sub_r) {
402 90
                $this->indexBnodes($sub_r, $pattern_id);
403 90
                $r['patterns'][] = ['type' => 'triples', 'patterns' => $sub_r];
404
            }
405
            do {
406 91
                $proceed = 0;
407 91
                if ((list($sub_r, $sub_v) = $this->xGraphPatternNotTriples($sub_v)) && $sub_r) {
408 3
                    $r['patterns'][] = $sub_r;
409 3
                    $pattern_id = substr(md5(uniqid(rand())), 0, 4);
410 3
                    $proceed = 1;
411 91
                } elseif ((list($sub_r, $sub_v) = $this->xFilter($sub_v)) && $sub_r) {
412 23
                    $r['patterns'][] = ['type' => 'filter', 'constraint' => $sub_r];
413 23
                    $proceed = 1;
414
                }
415 91
                if ($sub_r = $this->x('\.', $sub_v)) {
416
                    $sub_v = $sub_r[1];
417
                }
418 91
                if ((list($sub_r, $sub_v) = $this->xTriplesBlock($sub_v)) && $sub_r) {
419
                    $this->indexBnodes($sub_r, $pattern_id);
420
                    $r['patterns'][] = ['type' => 'triples', 'patterns' => $sub_r];
421
                    $proceed = 1;
422
                }
423 91
                if ((list($sub_r, $sub_v) = $this->xPlaceholder($sub_v)) && $sub_r) {
424
                    $r['patterns'][] = $sub_r;
425
                    $proceed = 1;
426
                }
427 91
            } while ($proceed);
428 91
            if ($sub_r = $this->x('\}', $sub_v)) {
429 90
                $sub_v = $sub_r[1];
430
431 90
                return [$r, $sub_v];
432
            }
433 1
            $rest = preg_replace('/[\x0a|\x0d]/i', ' ', substr($sub_v, 0, 30));
434 1
            $this->logger->error('Incomplete or invalid Group Graph pattern. Could not handle "'.$rest.'"');
435
        }
436
437 93
        return [0, $v];
438
    }
439
440 90
    protected function indexBnodes($triples, $pattern_id)
441
    {
442 90
        $index_id = \count($this->bnode_pattern_index['patterns']);
0 ignored issues
show
Unused Code introduced by
The assignment to $index_id is dead and can be removed.
Loading history...
443 90
        $index_id = $pattern_id;
444 90
        $this->bnode_pattern_index['patterns'][] = $triples;
0 ignored issues
show
Bug Best Practice introduced by
The property bnode_pattern_index does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
445 90
        foreach ($triples as $t) {
446 90
            foreach (['s', 'p', 'o'] as $term) {
447 90
                if ('bnode' == $t[$term.'_type']) {
448
                    $val = $t[$term];
449
                    if (isset($this->bnode_pattern_index['bnodes'][$val]) && ($this->bnode_pattern_index['bnodes'][$val] != $index_id)) {
450
                        $this->logger->error('Re-used bnode label "'.$val.'" across graph patterns');
451
                    } else {
452
                        $this->bnode_pattern_index['bnodes'][$val] = $index_id;
453
                    }
454
                }
455
            }
456
        }
457 90
    }
458
459
    /* 22.., 25.. */
460
461 91
    protected function xGraphPatternNotTriples($v)
462
    {
463 91
        if ((list($sub_r, $sub_v) = $this->xOptionalGraphPattern($v)) && $sub_r) {
464 2
            return [$sub_r, $sub_v];
465
        }
466 91
        if ((list($sub_r, $sub_v) = $this->xGraphGraphPattern($v)) && $sub_r) {
467
            return [$sub_r, $sub_v];
468
        }
469 91
        $r = ['type' => 'union', 'patterns' => []];
470 91
        $sub_v = $v;
471
        do {
472 91
            $proceed = 0;
473 91
            if ((list($sub_r, $sub_v) = $this->xGroupGraphPattern($sub_v)) && $sub_r) {
474 1
                $r['patterns'][] = $sub_r;
475 1
                if ($sub_r = $this->x('UNION', $sub_v)) {
476 1
                    $sub_v = $sub_r[1];
477 1
                    $proceed = 1;
478
                }
479
            }
480 91
        } while ($proceed);
481 91
        $pc = \count($r['patterns']);
482 91
        if (1 == $pc) {
483
            return [$r['patterns'][0], $sub_v];
484 91
        } elseif ($pc > 1) {
485 1
            return [$r, $sub_v];
486
        }
487
488 91
        return [0, $v];
489
    }
490
491
    /* 23 */
492
493 91
    protected function xOptionalGraphPattern($v)
494
    {
495 91
        if ($sub_r = $this->x('OPTIONAL', $v)) {
496 2
            $sub_v = $sub_r[1];
497 2
            if ((list($sub_r, $sub_v) = $this->xGroupGraphPattern($sub_v)) && $sub_r) {
498 2
                return [['type' => 'optional', 'patterns' => $sub_r['patterns']], $sub_v];
499
            }
500
            $this->logger->error('Missing or invalid Group Graph Pattern after OPTIONAL');
501
        }
502
503 91
        return [0, $v];
504
    }
505
506
    /* 24.. */
507
508 91
    protected function xGraphGraphPattern($v)
509
    {
510 91
        if ($sub_r = $this->x('GRAPH', $v)) {
511
            $sub_v = $sub_r[1];
512
            $r = ['type' => 'graph', 'var' => '', 'uri' => '', 'patterns' => []];
513
            if ((list($sub_r, $sub_v) = $this->xVar($sub_v)) && $sub_r) {
514
                $r['var'] = $sub_r;
515
            } elseif ((list($sub_r, $sub_v) = $this->xIRIref($sub_v)) && $sub_r) {
516
                $r['uri'] = $sub_r;
517
            }
518
            if ($r['var'] || $r['uri']) {
519
                if ((list($sub_r, $sub_v) = $this->xGroupGraphPattern($sub_v)) && $sub_r) {
520
                    $r['patterns'][] = $sub_r;
521
522
                    return [$r, $sub_v];
523
                }
524
                $this->logger->error('Missing or invalid Graph Pattern');
525
            }
526
        }
527
528 91
        return [0, $v];
529
    }
530
531
    /* 26.., 27.. */
532
533 91
    protected function xFilter($v)
534
    {
535 91
        if ($r = $this->x('FILTER', $v)) {
536 23
            $sub_v = $r[1];
537 23
            if ((list($r, $sub_v) = $this->xBrackettedExpression($sub_v)) && $r) {
538 19
                return [$r, $sub_v];
539
            }
540 4
            if ((list($r, $sub_v) = $this->xBuiltInCall($sub_v)) && $r) {
541 4
                return [$r, $sub_v];
542
            }
543
            if ((list($r, $sub_v) = $this->xFunctionCall($sub_v)) && $r) {
544
                return [$r, $sub_v];
545
            }
546
            $this->logger->error('Incomplete FILTER');
547
        }
548
549 91
        return [0, $v];
550
    }
551
552
    /* 28.. */
553
554 5
    protected function xFunctionCall($v)
555
    {
556 5
        if ((list($r, $sub_v) = $this->xIRIref($v)) && $r) {
557
            if ((list($sub_r, $sub_v) = $this->xArgList($sub_v)) && $sub_r) {
558
                return [['type' => 'function_call', 'uri' => $r, 'args' => $sub_r], $sub_v];
559
            }
560
        }
561
562 5
        return [0, $v];
563
    }
564
565
    /* 29 */
566
567 18
    protected function xArgList($v)
568
    {
569 18
        $r = [];
570 18
        $sub_v = $v;
571 18
        $closed = 0;
572 18
        if ($sub_r = $this->x('\(', $sub_v)) {
573 18
            $sub_v = $sub_r[1];
574
            do {
575 18
                $proceed = 0;
576 18
                if ((list($sub_r, $sub_v) = $this->xExpression($sub_v)) && $sub_r) {
577 18
                    $r[] = $sub_r;
578 18
                    if ($sub_r = $this->x('\,', $sub_v)) {
579 4
                        $sub_v = $sub_r[1];
580 4
                        $proceed = 1;
581
                    }
582
                }
583 18
                if ($sub_r = $this->x('\)', $sub_v)) {
584 18
                    $sub_v = $sub_r[1];
585 18
                    $closed = 1;
586 18
                    $proceed = 0;
587
                }
588 18
            } while ($proceed);
589
        }
590
591 18
        return $closed ? [$r, $sub_v] : [0, $v];
592
    }
593
594
    /* 30, 31 */
595
596 90
    protected function xConstructTemplate($v)
597
    {
598 90
        if ($sub_r = $this->x('\{', $v)) {
599 90
            $r = [];
600 90
            if ((list($sub_r, $sub_v) = $this->xTriplesBlock($sub_r[1])) && \is_array($sub_r)) {
601 90
                $r = $sub_r;
602
            }
603 90
            if ($sub_r = $this->x('\}', $sub_v)) {
604 89
                return [$r, $sub_r[1]];
605
            }
606
        }
607
608 5
        return [0, $v];
609
    }
610
611
    /* 46, 47 */
612
613 27
    protected function xExpression($v)
614
    {
615 27
        if ((list($sub_r, $sub_v) = $this->xConditionalAndExpression($v)) && $sub_r) {
616 27
            $r = ['type' => 'expression', 'sub_type' => 'or', 'patterns' => [$sub_r]];
617
            do {
618 27
                $proceed = 0;
619 27
                if ($sub_r = $this->x('\|\|', $sub_v)) {
620
                    $sub_v = $sub_r[1];
621
                    if ((list($sub_r, $sub_v) = $this->xConditionalAndExpression($sub_v)) && $sub_r) {
622
                        $r['patterns'][] = $sub_r;
623
                        $proceed = 1;
624
                    }
625
                }
626 27
            } while ($proceed);
627
628 27
            return 1 == \count($r['patterns']) ? [$r['patterns'][0], $sub_v] : [$r, $sub_v];
629
        }
630
631
        return [0, $v];
632
    }
633
634
    /* 48.., 49.. */
635
636 27
    protected function xConditionalAndExpression($v)
637
    {
638 27
        if ((list($sub_r, $sub_v) = $this->xRelationalExpression($v)) && $sub_r) {
639 27
            $r = ['type' => 'expression', 'sub_type' => 'and', 'patterns' => [$sub_r]];
640
            do {
641 27
                $proceed = 0;
642 27
                if ($sub_r = $this->x('\&\&', $sub_v)) {
643 1
                    $sub_v = $sub_r[1];
644 1
                    if ((list($sub_r, $sub_v) = $this->xRelationalExpression($sub_v)) && $sub_r) {
645 1
                        $r['patterns'][] = $sub_r;
646 1
                        $proceed = 1;
647
                    }
648
                }
649 27
            } while ($proceed);
650
651 27
            return 1 == \count($r['patterns']) ? [$r['patterns'][0], $sub_v] : [$r, $sub_v];
652
        }
653
654
        return [0, $v];
655
    }
656
657
    /* 50, 51 */
658
659 27
    protected function xRelationalExpression($v)
660
    {
661 27
        if ((list($sub_r, $sub_v) = $this->xAdditiveExpression($v)) && $sub_r) {
662 27
            $r = ['type' => 'expression', 'sub_type' => 'relational', 'patterns' => [$sub_r]];
663
            do {
664 27
                $proceed = 0;
665
                /* don't mistake '<' + uriref with '<'-operator ("longest token" rule) */
666 27
                if ((list($sub_r, $sub_v) = $this->xIRI_REF($sub_v)) && $sub_r) {
667
                    $this->logger->error('Expected operator, found IRIref: "'.$sub_r.'".');
668
                }
669 27
                if ($sub_r = $this->x('(\!\=|\=\=|\=|\<\=|\>\=|\<|\>)', $sub_v)) {
670 9
                    $op = $sub_r[1];
671 9
                    $sub_v = $sub_r[2];
672 9
                    $r['operator'] = $op;
673 9
                    if ((list($sub_r, $sub_v) = $this->xAdditiveExpression($sub_v)) && $sub_r) {
674
                        //$sub_r['operator'] = $op;
675 9
                        $r['patterns'][] = $sub_r;
676 9
                        $proceed = 1;
677
                    }
678
                }
679 27
            } while ($proceed);
680
681 27
            return 1 == \count($r['patterns']) ? [$r['patterns'][0], $sub_v] : [$r, $sub_v];
682
        }
683
684
        return [0, $v];
685
    }
686
687
    /* 52 */
688
689 27
    protected function xAdditiveExpression($v)
690
    {
691 27
        if ((list($sub_r, $sub_v) = $this->xMultiplicativeExpression($v)) && $sub_r) {
692 27
            $r = ['type' => 'expression', 'sub_type' => 'additive', 'patterns' => [$sub_r]];
693
            do {
694 27
                $proceed = 0;
695 27
                if ($sub_r = $this->x('(\+|\-)', $sub_v)) {
696
                    $op = $sub_r[1];
697
                    $sub_v = $sub_r[2];
698
                    if ((list($sub_r, $sub_v) = $this->xMultiplicativeExpression($sub_v)) && $sub_r) {
699
                        $sub_r['operator'] = $op;
700
                        $r['patterns'][] = $sub_r;
701
                        $proceed = 1;
702
                    } elseif ((list($sub_r, $sub_v) = $this->xNumericLiteral($sub_v)) && $sub_r) {
703
                        $r['patterns'][] = ['type' => 'numeric', 'operator' => $op, 'value' => $sub_r];
704
                        $proceed = 1;
705
                    }
706
                }
707 27
            } while ($proceed);
708
            //return array($r, $sub_v);
709 27
            return 1 == \count($r['patterns']) ? [$r['patterns'][0], $sub_v] : [$r, $sub_v];
710
        }
711
712
        return [0, $v];
713
    }
714
715
    /* 53 */
716
717 27
    protected function xMultiplicativeExpression($v)
718
    {
719 27
        if ((list($sub_r, $sub_v) = $this->xUnaryExpression($v)) && $sub_r) {
720 27
            $r = ['type' => 'expression', 'sub_type' => 'multiplicative', 'patterns' => [$sub_r]];
721
            do {
722 27
                $proceed = 0;
723 27
                if ($sub_r = $this->x('(\*|\/)', $sub_v)) {
724
                    $op = $sub_r[1];
725
                    $sub_v = $sub_r[2];
726
                    if ((list($sub_r, $sub_v) = $this->xUnaryExpression($sub_v)) && $sub_r) {
727
                        $sub_r['operator'] = $op;
728
                        $r['patterns'][] = $sub_r;
729
                        $proceed = 1;
730
                    }
731
                }
732 27
            } while ($proceed);
733
734 27
            return 1 == \count($r['patterns']) ? [$r['patterns'][0], $sub_v] : [$r, $sub_v];
735
        }
736
737
        return [0, $v];
738
    }
739
740
    /* 54 */
741
742 27
    protected function xUnaryExpression($v)
743
    {
744 27
        $sub_v = $v;
745 27
        $op = '';
746 27
        if ($sub_r = $this->x('(\!|\+|\-)', $sub_v)) {
747
            $op = $sub_r[1];
748
            $sub_v = $sub_r[2];
749
        }
750 27
        if ((list($sub_r, $sub_v) = $this->xPrimaryExpression($sub_v)) && $sub_r) {
751 27
            if (!\is_array($sub_r)) {
752
                $sub_r = ['type' => 'unary', 'expression' => $sub_r];
753 27
            } elseif ($sub_op = $sub_r['operator'] ?? '') {
754
                $ops = ['!!' => '', '++' => '+', '--' => '+', '+-' => '-', '-+' => '-'];
755
                $op = isset($ops[$op.$sub_op]) ? $ops[$op.$sub_op] : $op.$sub_op;
756
            }
757 27
            $sub_r['operator'] = $op;
758
759 27
            return [$sub_r, $sub_v];
760
        }
761
762
        return [0, $v];
763
    }
764
765
    /* 55 */
766
767 27
    protected function xPrimaryExpression($v)
768
    {
769 27
        foreach (['BrackettedExpression', 'BuiltInCall', 'IRIrefOrFunction', 'RDFLiteral', 'NumericLiteral', 'BooleanLiteral', 'Var', 'Placeholder'] as $type) {
770 27
            $m = 'x'.$type;
771 27
            if ((list($sub_r, $sub_v) = $this->$m($v)) && $sub_r) {
772 27
                return [$sub_r, $sub_v];
773
            }
774
        }
775
776
        return [0, $v];
777
    }
778
779
    /* 56 */
780
781 28
    protected function xBrackettedExpression($v)
782
    {
783 28
        if ($r = $this->x('\(', $v)) {
784 23
            if ((list($r, $sub_v) = $this->xExpression($r[1])) && $r) {
785 23
                if ($sub_r = $this->x('\)', $sub_v)) {
786 23
                    return [$r, $sub_r[1]];
787
                }
788
            }
789
        }
790
791 28
        return [0, $v];
792
    }
793
794
    /* 57.., 58.. */
795
796 28
    protected function xBuiltInCall($v)
797
    {
798 28
        if ($sub_r = $this->x('(str|lang|langmatches|datatype|bound|sameterm|isiri|isuri|isblank|isliteral|regex)\s*\(', $v)) {
799 18
            $r = ['type' => 'built_in_call', 'call' => strtolower($sub_r[1])];
800 18
            if ((list($sub_r, $sub_v) = $this->xArgList('('.$sub_r[2])) && \is_array($sub_r)) {
801 18
                $r['args'] = $sub_r;
802
803 18
                return [$r, $sub_v];
804
            }
805
        }
806
807 28
        return [0, $v];
808
    }
809
810
    /* 59.. */
811
812 27
    protected function xIRIrefOrFunction($v)
813
    {
814 27
        if ((list($r, $v) = $this->xIRIref($v)) && $r) {
815 1
            if ((list($sub_r, $sub_v) = $this->xArgList($v)) && \is_array($sub_r)) {
816
                return [['type' => 'function', 'uri' => $r, 'args' => $sub_r], $sub_v];
817
            }
818
819 1
            return [['type' => 'uri', 'uri' => $r], $sub_v];
820
        }
821 27
    }
822
823
    /* 70.. @@sync with TurtleParser */
824
825 94
    protected function xIRI_REF($v)
826
    {
827 94
        if (($r = $this->x('\<(\$\{[^\>]*\})\>', $v)) && ($sub_r = $this->xPlaceholder($r[1]))) {
0 ignored issues
show
Unused Code introduced by
The assignment to $sub_r is dead and can be removed.
Loading history...
828
            return [$r[1], $r[2]];
829 94
        } elseif ($r = $this->x('\<([^\<\>\s\"\|\^`]*)\>', $v)) {
830 93
            return [$r[1] ? $r[1] : true, $r[2]];
831
        }
832
        /* allow reserved chars in obvious IRIs */
833 94
        elseif ($r = $this->x('\<(https?\:[^\s][^\<\>]*)\>', $v)) {
834
            return [$r[1] ? $r[1] : true, $r[2]];
835
        }
836
837 94
        return [0, $v];
838
    }
839
}
840