Passed
Branch extract-store (f24e42)
by Konrad
04:37
created

SPARQLParser::xFunctionCall()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 6.6

Importance

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