SPARQLParser::xGraphPatternNotTriples()   B
last analyzed

Complexity

Conditions 11
Paths 11

Size

Total Lines 28
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 11.121

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 20
c 1
b 0
f 0
nc 11
nop 1
dl 0
loc 28
ccs 18
cts 20
cp 0.9
crap 11.121
rs 7.3166

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