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

SPARQLParser::xConditionalAndExpression()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 8.0368

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 12
c 1
b 0
f 0
nc 7
nop 1
dl 0
loc 19
ccs 11
cts 12
cp 0.9167
crap 8.0368
rs 8.4444
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