PageRenderTime 311ms CodeModel.GetById 80ms app.highlight 141ms RepoModel.GetById 80ms app.codeStats 1ms

/arc/parsers/ARC2_TurtleParser.php

https://github.com/damz/foafssl-drupal
PHP | 874 lines | 747 code | 80 blank | 47 comment | 170 complexity | a5353276d2e984fd2ab7d1fb84d11a8a MD5 | raw file
  1<?php
  2/*
  3homepage: http://arc.semsol.org/
  4license:  http://arc.semsol.org/license
  5
  6class:    ARC2 SPARQL-enhanced Turtle Parser
  7author:   Benjamin Nowack
  8version:  2009-08-04
  9*/
 10
 11ARC2::inc('RDFParser');
 12
 13class ARC2_TurtleParser extends ARC2_RDFParser {
 14
 15  function __construct($a = '', &$caller) {
 16    parent::__construct($a, $caller);
 17  }
 18  
 19  function ARC2_TurtleParser($a = '', &$caller) {
 20    $this->__construct($a, $caller);
 21  }
 22
 23  function __init() {/* reader */
 24    parent::__init();
 25    $this->state = 0;
 26    $this->xml = 'http://www.w3.org/XML/1998/namespace';
 27    $this->rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
 28    $this->xsd = 'http://www.w3.org/2001/XMLSchema#';
 29    $this->nsp = array($this->xml => 'xml', $this->rdf => 'rdf', $this->xsd => 'xsd');
 30    $this->unparsed_code = '';
 31  }
 32  
 33  /*  */
 34  
 35  function x($re, $v, $options = 'si') {
 36    $v = preg_replace('/^[\xA0\xC2]+/', ' ', $v);
 37    while (preg_match('/^\s*(\#[^\xd\xa]*)(.*)$/si', $v, $m)) {/* comment removal */
 38      $v = $m[2];
 39    }
 40    $this->unparsed_code = (strlen($this->unparsed_code) > strlen($v)) ? $v : $this->unparsed_code;
 41    return ARC2::x($re, $v, $options);
 42  }
 43
 44  function createBnodeID(){
 45    $this->bnode_id++;
 46    return '_:' . $this->bnode_prefix . $this->bnode_id;
 47  }
 48
 49  /*  */
 50  
 51  function addT($t) {
 52    if ($this->skip_dupes) {
 53      $h = md5(serialize($t));
 54      if (!isset($this->added_triples[$h])) {
 55        $this->triples[$this->t_count] = $t;
 56        $this->t_count++;
 57        $this->added_triples[$h] = true;
 58      }
 59    }
 60    else {
 61      $this->triples[$this->t_count] = $t;
 62      $this->t_count++;
 63    }
 64  }
 65
 66  /*  */
 67
 68  function getTriples() {
 69    return $this->v('triples', array());
 70  }
 71  
 72  function countTriples() {
 73    return $this->t_count;
 74  }
 75  
 76  /*  */
 77  
 78  function getUnparsedCode() {
 79    return $this->v('unparsed_code', '');
 80  }
 81  
 82  /*  */
 83  
 84  function setDefaultPrefixes() {
 85    $this->prefixes = array(
 86      'rdf:' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
 87      'rdfs:' => 'http://www.w3.org/2000/01/rdf-schema#',
 88      'owl:' => 'http://www.w3.org/2002/07/owl#',
 89      'xsd:' => 'http://www.w3.org/2001/XMLSchema#',
 90    );
 91    if ($ns = $this->v('ns', array(), $this->a)) {
 92      foreach ($ns as $p => $u) $this->prefixes[$p . ':'] = $u;
 93    }
 94  }
 95  
 96
 97  function parse($path, $data = '', $iso_fallback = false) {
 98    $this->setDefaultPrefixes();
 99    /* reader */
100    if (!$this->v('reader')) {
101      ARC2::inc('Reader');
102      $this->reader = & new ARC2_Reader($this->a, $this);
103    }
104    $this->reader->setAcceptHeader('Accept: application/x-turtle; q=0.9, */*; q=0.1');
105    $this->reader->activate($path, $data);
106    $this->base = $this->v1('base', $this->reader->base, $this->a);
107    $this->r = array('vars' => array());
108    /* parse */
109    $buffer = '';
110    $more_triples = array();
111    $sub_v = '';
112    $sub_v2 = '';
113    $loops = 0;
114    $prologue_done = 0;
115    while ($d = $this->reader->readStream(0)) {
116      $buffer .= $d;
117      $sub_v = $buffer;
118      do {
119        $proceed = 0;
120        if (!$prologue_done) {
121          $proceed = 1;
122          if ((list($sub_r, $sub_v) = $this->xPrologue($sub_v)) && $sub_r) {
123            $loops = 0;
124            $sub_v .= $this->reader->readStream(0, 128);
125            /* we might have missed the final DOT in the previous prologue loop */
126            if ($sub_r = $this->x('\.', $sub_v)) $sub_v = $sub_r[1];
127            if ($this->x("\@?(base|prefix)", $sub_v)) {/* more prologue to come, use outer loop */
128              $proceed = 0;
129            }
130          }
131          else {
132            $prologue_done = 1;
133          }
134        }
135        if ($prologue_done && (list($sub_r, $sub_v, $more_triples, $sub_v2) = $this->xTriplesBlock($sub_v)) && is_array($sub_r)) {
136          $proceed = 1;
137          $loops = 0;
138          foreach ($sub_r as $t) {
139            $this->addT($t);
140          }
141        }
142      } while ($proceed);
143      $loops++;
144      $buffer = $sub_v;
145      if ($loops > 100) {/* most probably a parser or code bug, might also be a huge object value, though */
146        $this->addError('too many loops: ' . $loops);
147        break;
148      }
149    }
150    foreach ($more_triples as $t) {
151      $this->addT($t);
152    }
153    $sub_v = count($more_triples) ? $sub_v2 : $sub_v;
154    $buffer = $sub_v;
155    $this->reader->closeStream();
156    unset($this->reader);
157    return $this->done();
158  }
159
160  function xPrologue($v) {
161    $r = 0;
162    if (!$this->t_count) {
163      if ((list($sub_r, $v) = $this->xBaseDecl($v)) && $sub_r) {
164        $this->base = $sub_r;
165        $r = 1;
166      }
167      while ((list($sub_r, $v) = $this->xPrefixDecl($v)) && $sub_r) {
168        $this->prefixes[$sub_r['prefix']] = $sub_r['uri'];
169        $r = 1;
170      }
171    }
172    return array($r, $v);
173  }
174  
175  /* 3 */
176
177  function xBaseDecl($v) {
178    if ($r = $this->x("\@?base\s+", $v)) {
179      if ((list($r, $sub_v) = $this->xIRI_REF($r[1])) && $r) {
180        if ($sub_r = $this->x('\.', $sub_v)) {
181          $sub_v = $sub_r[1];
182        }
183        return array($r, $sub_v);
184      }
185    }
186    return array(0, $v);
187  }
188  
189  /* 4 */
190  
191  function xPrefixDecl($v) {
192    if ($r = $this->x("\@?prefix\s+", $v)) {
193      if ((list($r, $sub_v) = $this->xPNAME_NS($r[1])) && $r) {
194        $prefix = $r;
195        if((list($r, $sub_v) = $this->xIRI_REF($sub_v)) && $r) {
196          $uri = $this->calcURI($r, $this->base);
197          if ($sub_r = $this->x('\.', $sub_v)) {
198            $sub_v = $sub_r[1];
199          }
200          return array(array('prefix' => $prefix, 'uri_ref' => $r, 'uri' => $uri), $sub_v);
201        }
202      }
203    }
204    return array(0, $v);
205  }
206
207  /* 21.., 32.. */
208  
209  function xTriplesBlock($v) {
210    $pre_r = array();
211    $r = array();
212    $state = 1;
213    $sub_v = $v;
214    $buffer = $sub_v;
215    do {
216      $proceed = 0;
217      if ($state == 1) {/* expecting subject */
218        $t = array('type' => 'triple', 's' => '', 'p' => '', 'o' => '', 's_type' => '', 'p_type' => '', 'o_type' => '', 'o_datatype' => '', 'o_lang' => '');
219        if ((list($sub_r, $sub_v) = $this->xVarOrTerm($sub_v)) && $sub_r) {
220          $t['s'] = $sub_r['value'];
221          $t['s_type'] = $sub_r['type'];
222          $state = 2;
223          $proceed = 1;
224          if ($sub_r = $this->x('(\}|\.)', $sub_v)) {
225            if ($t['s_type'] == 'placeholder') {
226              $state = 4;
227            }
228            else {
229              $this->addError('"' . $sub_r[1]. '" after subject found.');
230            }
231          }
232        }
233        elseif ((list($sub_r, $sub_v) = $this->xCollection($sub_v)) && $sub_r) {
234          $t['s'] = $sub_r['id'];
235          $t['s_type'] = $sub_r['type'];
236          $pre_r = array_merge($pre_r, $sub_r['triples']);
237          $state = 2;
238          $proceed = 1;
239          if ($sub_r = $this->x('\.', $sub_v)) {
240            $this->addError('DOT after subject found.');
241          }
242        }
243        elseif ((list($sub_r, $sub_v) = $this->xBlankNodePropertyList($sub_v)) && $sub_r) {
244          $t['s'] = $sub_r['id'];
245          $t['s_type'] = $sub_r['type'];
246          $pre_r = array_merge($pre_r, $sub_r['triples']);
247          $state = 2;
248          $proceed = 1;
249        }
250        elseif ($sub_r = $this->x('\.', $sub_v)) {
251          $this->addError('Subject expected, DOT found.' . $sub_v);
252        }
253      }
254      if ($state == 2) {/* expecting predicate */
255        if ($sub_r = $this->x('a\s+', $sub_v)) {
256          $sub_v = $sub_r[1];
257          $t['p'] = $this->rdf . 'type';
258          $t['p_type'] = 'uri';
259          $state = 3;
260          $proceed = 1;
261        }
262        elseif ((list($sub_r, $sub_v) = $this->xVarOrTerm($sub_v)) && $sub_r) {
263          if ($sub_r['type'] == 'bnode') {
264            $this->addError('Blank node used as triple predicate');
265          }
266          $t['p'] = $sub_r['value'];
267          $t['p_type'] = $sub_r['type'];
268          $state = 3;
269          $proceed = 1;
270        }
271        elseif ($sub_r = $this->x('\.', $sub_v)) {
272          $state = 4;          
273        }
274        elseif ($sub_r = $this->x('\}', $sub_v)) {
275          $buffer = $sub_v;
276          $r = array_merge($r, $pre_r);
277          $pre_r = array();
278          $proceed = 0;
279        }
280      }
281      if ($state == 3) {/* expecting object */
282        if ((list($sub_r, $sub_v) = $this->xVarOrTerm($sub_v)) && $sub_r) {
283          $t['o'] = $sub_r['value'];
284          $t['o_type'] = $sub_r['type'];
285          $t['o_lang'] = $this->v('lang', '', $sub_r);
286          $t['o_datatype'] = $this->v('datatype', '', $sub_r);
287          $pre_r[] = $t;
288          $state = 4;
289          $proceed = 1;
290        }
291        elseif ((list($sub_r, $sub_v) = $this->xCollection($sub_v)) && $sub_r) {
292          $t['o'] = $sub_r['id'];
293          $t['o_type'] = $sub_r['type'];
294          $pre_r = array_merge($pre_r, array($t), $sub_r['triples']);
295          $state = 4;
296          $proceed = 1;
297        }
298        elseif ((list($sub_r, $sub_v) = $this->xBlankNodePropertyList($sub_v)) && $sub_r) {
299          $t['o'] = $sub_r['id'];
300          $t['o_type'] = $sub_r['type'];
301          $pre_r = array_merge($pre_r, array($t), $sub_r['triples']);
302          $state = 4;
303          $proceed = 1;
304        }
305      }
306      if ($state == 4) {/* expecting . or ; or , or } */
307        if ($sub_r = $this->x('\.', $sub_v)) {
308          $sub_v = $sub_r[1];
309          $buffer = $sub_v;
310          $r = array_merge($r, $pre_r);
311          $pre_r = array();
312          $state = 1;
313          $proceed = 1;
314        }
315        elseif ($sub_r = $this->x('\;', $sub_v)) {
316          $sub_v = $sub_r[1];
317          $state = 2;
318          $proceed = 1;
319        }
320        elseif ($sub_r = $this->x('\,', $sub_v)) {
321          $sub_v = $sub_r[1];
322          $state = 3;
323          $proceed = 1;
324          if ($sub_r = $this->x('\}', $sub_v)) {
325            $this->addError('Object expected, } found.');
326          }
327        }
328        if ($sub_r = $this->x('(\}|\{|OPTIONAL|FILTER|GRAPH)', $sub_v)) {
329          $buffer = $sub_v;
330          $r = array_merge($r, $pre_r);
331          $pre_r = array();
332          $proceed = 0;
333        }
334      }
335    } while ($proceed);
336    return count($r) ? array($r, $buffer, $pre_r, $sub_v) : array(0, $buffer, $pre_r, $sub_v);
337  }
338  
339  /* 39.. */
340  
341  function xBlankNodePropertyList($v) {
342    if ($sub_r = $this->x('\[', $v)) {
343      $sub_v = $sub_r[1];
344      $s = $this->createBnodeID();
345      $r = array('id' => $s, 'type' => 'bnode', 'triples' => array());
346      $t = array('type' => 'triple', 's' => $s, 'p' => '', 'o' => '', 's_type' => 'bnode', 'p_type' => '', 'o_type' => '', 'o_datatype' => '', 'o_lang' => '');
347      $state = 2;
348      $closed = 0;
349      do {
350        $proceed = 0;
351        if ($state == 2) {/* expecting predicate */
352          if ($sub_r = $this->x('a\s+', $sub_v)) {
353            $sub_v = $sub_r[1];
354            $t['p'] = $this->rdf . 'type';
355            $t['p_type'] = 'uri';
356            $state = 3;
357            $proceed = 1;
358          }
359          elseif ((list($sub_r, $sub_v) = $this->xVarOrTerm($sub_v)) && $sub_r) {
360            $t['p'] = $sub_r['value'];
361            $t['p_type'] = $sub_r['type'];
362            $state = 3;
363            $proceed = 1;
364          }
365        }
366        if ($state == 3) {/* expecting object */
367          if ((list($sub_r, $sub_v) = $this->xVarOrTerm($sub_v)) && $sub_r) {
368            $t['o'] = $sub_r['value'];
369            $t['o_type'] = $sub_r['type'];
370            $t['o_lang'] = $this->v('lang', '', $sub_r);
371            $t['o_datatype'] = $this->v('datatype', '', $sub_r);
372            $r['triples'][] = $t;
373            $state = 4;
374            $proceed = 1;
375          }
376          elseif ((list($sub_r, $sub_v) = $this->xCollection($sub_v)) && $sub_r) {
377            $t['o'] = $sub_r['id'];
378            $t['o_type'] = $sub_r['type'];
379            $r['triples'] = array_merge($r['triples'], array($t), $sub_r['triples']);
380            $state = 4;
381            $proceed = 1;
382          }
383          elseif((list($sub_r, $sub_v) = $this->xBlankNodePropertyList($sub_v)) && $sub_r) {
384            $t['o'] = $sub_r['id'];
385            $t['o_type'] = $sub_r['type'];
386            $r['triples'] = array_merge($r['triples'], array($t), $sub_r['triples']);
387            $state = 4;
388            $proceed = 1;
389          }
390        }
391        if ($state == 4) {/* expecting . or ; or , or ] */
392          if ($sub_r = $this->x('\.', $sub_v)) {
393            $sub_v = $sub_r[1];
394            $state = 1;
395            $proceed = 1;
396          }
397          if ($sub_r = $this->x('\;', $sub_v)) {
398            $sub_v = $sub_r[1];
399            $state = 2;
400            $proceed = 1;
401          }
402          if ($sub_r = $this->x('\,', $sub_v)) {
403            $sub_v = $sub_r[1];
404            $state = 3;
405            $proceed = 1;
406          }
407          if ($sub_r = $this->x('\]', $sub_v)) {
408            $sub_v = $sub_r[1];
409            $proceed = 0;
410            $closed = 1;
411          }
412        }
413      } while ($proceed);
414      if ($closed) {
415        return array($r, $sub_v);
416      }
417      return array(0, $v);
418    }
419    return array(0, $v);
420  }
421  
422  /* 40.. */
423  
424  function xCollection($v) {
425    if ($sub_r = $this->x('\(', $v)) {
426      $sub_v = $sub_r[1];
427      $s = $this->createBnodeID();
428      $r = array('id' => $s, 'type' => 'bnode', 'triples' => array());
429      $closed = 0;
430      do {
431        $proceed = 0;
432        if ((list($sub_r, $sub_v) = $this->xVarOrTerm($sub_v)) && $sub_r) {
433          $r['triples'][] = array('type' => 'triple', 's' => $s, 'p' => $this->rdf . 'first', 'o' => $sub_r['value'], 's_type' => 'bnode', 'p_type' => 'uri', 'o_type' => $sub_r['type'], 'o_lang' => $this->v('lang', '', $sub_r), 'o_datatype' => $this->v('datatype', '', $sub_r));
434          $proceed = 1;
435        }
436        elseif ((list($sub_r, $sub_v) = $this->xCollection($sub_v)) && $sub_r) {
437          $r['triples'][] = array('type' => 'triple', 's' => $s, 'p' => $this->rdf . 'first', 'o' => $sub_r['id'], 's_type' => 'bnode', 'p_type' => 'uri', 'o_type' => $sub_r['type'], 'o_lang' => '', 'o_datatype' => '');
438          $r['triples'] = array_merge($r['triples'], $sub_r['triples']);
439          $proceed = 1;
440        }
441        elseif((list($sub_r, $sub_v) = $this->xBlankNodePropertyList($sub_v)) && $sub_r) {
442          $r['triples'][] = array('type' => 'triple', 's' => $s, 'p' => $this->rdf . 'first', 'o' => $sub_r['id'], 's_type' => 'bnode', 'p_type' => 'uri', 'o_type' => $sub_r['type'], 'o_lang' => '', 'o_datatype' => '');
443          $r['triples'] = array_merge($r['triples'], $sub_r['triples']);
444          $proceed = 1;
445        }
446        if ($proceed) {
447          if ($sub_r = $this->x('\)', $sub_v)) {
448            $sub_v = $sub_r[1];
449            $r['triples'][] = array('type' => 'triple', 's' => $s, 'p' => $this->rdf . 'rest', 'o' => $this->rdf . 'nil', 's_type' => 'bnode', 'p_type' => 'uri', 'o_type' => 'uri', 'o_lang' => '', 'o_datatype' => '');
450            $closed = 1;
451            $proceed = 0;
452          }
453          else {
454            $next_s = $this->createBnodeID();
455            $r['triples'][] = array('type' => 'triple', 's' => $s, 'p' => $this->rdf . 'rest', 'o' => $next_s, 's_type' => 'bnode', 'p_type' => 'uri', 'o_type' => 'bnode', 'o_lang' => '', 'o_datatype' => '');
456            $s = $next_s;
457          }
458        }
459      } while ($proceed);
460      if ($closed) {
461        return array($r, $sub_v);
462      }
463    }
464    return array (0, $v);
465  }
466  
467  /* 42 */
468  
469  function xVarOrTerm($v) {
470    if ((list($sub_r, $sub_v) = $this->xVar($v)) && $sub_r) {
471      return array($sub_r, $sub_v);
472    }
473    elseif ((list($sub_r, $sub_v) = $this->xGraphTerm($v)) && $sub_r) {
474      return array($sub_r, $sub_v);
475    }
476    return array(0, $v);
477  }
478  
479  /* 44, 74.., 75.. */
480  
481  function xVar($v) {
482    if ($r = $this->x('(\?|\$)([^\s]+)', $v)) {
483      if ((list($sub_r, $sub_v) = $this->xVARNAME($r[2])) && $sub_r) {
484        if (!in_array($sub_r, $this->r['vars'])) {
485          $this->r['vars'][] = $sub_r;
486        }
487        return array(array('value' => $sub_r, 'type' => 'var'), $sub_v . $r[3]);
488      }
489    }
490    return array(0, $v);
491  }
492
493  /* 45 */
494  
495  function xGraphTerm($v) {
496    foreach (array(
497      'IRIref' => 'uri', 
498      'RDFLiteral' => 'literal', 
499      'NumericLiteral' => 'literal', 
500      'BooleanLiteral' => 'literal', 
501      'BlankNode' => 'bnode', 
502      'NIL' => 'uri',
503      'Placeholder' => 'placeholder'
504    ) as $term => $type) {
505      $m = 'x' . $term;
506      if ((list($sub_r, $sub_v) = $this->$m($v)) && $sub_r) {
507        if (!is_array($sub_r)) {
508          $sub_r = array('value' => $sub_r);
509        }
510        $sub_r['type'] = $this->v1('type', $type, $sub_r);
511        return array($sub_r, $sub_v);
512      }
513    }
514    return array(0, $v);
515  }
516
517  /* 60 */
518  
519  function xRDFLiteral($v) {
520    if ((list($sub_r, $sub_v) = $this->xString($v)) && $sub_r) {
521      $sub_r['value'] = $this->unescapeNtripleUTF($sub_r['value']);
522      $r = $sub_r;
523      if ((list($sub_r, $sub_v) = $this->xLANGTAG($sub_v)) && $sub_r) {
524        $r['lang'] = $sub_r;
525      }
526      elseif (!$this->x('\s', $sub_v) && ($sub_r = $this->x('\^\^', $sub_v)) && (list($sub_r, $sub_v) = $this->xIRIref($sub_r[1])) && $sub_r[1]) {
527        $r['datatype'] = $sub_r;
528      }
529      return array($r, $sub_v);
530    }
531    return array(0, $v);
532  }
533
534  /* 61.., 62.., 63.., 64.. */  
535  
536  function xNumericLiteral($v) {
537    $sub_r = $this->x('(\-|\+)?', $v);
538    $prefix = $sub_r[1];
539    $sub_v = $sub_r[2];
540    foreach (array('DOUBLE' => 'double', 'DECIMAL' => 'decimal', 'INTEGER' => 'integer') as $type => $xsd) {
541      $m = 'x' . $type;
542      if ((list($sub_r, $sub_v) = $this->$m($sub_v)) && ($sub_r !== false)) {
543        $r = array('value' => $prefix . $sub_r, 'type' => 'literal', 'datatype' => $this->xsd . $xsd);
544        return array($r, $sub_v);
545      }
546    }
547    return array(0, $v);
548  }
549  
550  /* 65.. */
551  
552  function xBooleanLiteral($v) {
553    if ($r = $this->x('(true|false)', $v)) {
554      return array($r[1], $r[2]);
555    }
556    return array(0, $v);
557  }
558
559  /* 66.., 87.., 88.., 89.., 90.., 91.. */
560  
561  function xString($v) {/* largely simplified, may need some tweaks in following revisions */
562    $sub_v = $v;
563    if (!preg_match('/^\s*([\']{3}|\'|[\"]{3}|\")(.*)$/s', $sub_v, $m)) return array(0, $v);
564    $delim = $m[1];
565    $rest = $m[2];
566    $sub_types = array("'''" => 'literal_long1', '"""' => 'literal_long2', "'" => 'literal1', '"' => 'literal2');
567    $sub_type = $sub_types[$delim];
568    $pos = 0;
569    $r = false;
570    do {
571      $proceed = 0;
572      $delim_pos = strpos($rest, $delim, $pos);
573      if ($delim_pos === false) break;
574      $new_rest = substr($rest, $delim_pos + strlen($delim));
575      $r = substr($rest, 0, $delim_pos);
576      if (!preg_match('/([\x5c]+)$/s', $r, $m) || !(strlen($m[1]) % 2)) {
577        $rest = $new_rest;
578      }
579      else {
580        $r = false;
581        $pos = $delim_pos + 1;
582        $proceed = 1;
583      }
584    } while ($proceed);
585    if ($r !== false) {
586      return array(array('value' => $this->toUTF8($r) , 'type' => 'literal', 'sub_type' => $sub_type), $rest);
587    }
588    return array(0, $v);
589  }
590  
591  /* 67 */
592  
593  function xIRIref($v) {
594    if ((list($r, $v) = $this->xIRI_REF($v)) && $r) {
595      return array($this->calcURI($r, $this->base), $v);
596    }
597    elseif ((list($r, $v) = $this->xPrefixedName($v)) && $r) {
598      return array($r, $v);
599    }
600    return array(0, $v);
601  }
602  
603  /* 68 */
604  
605  function xPrefixedName($v) {
606    if ((list($r, $v) = $this->xPNAME_LN($v)) && $r) {
607      return array($r, $v);
608    }
609    elseif ((list($r, $sub_v) = $this->xPNAME_NS($v)) && $r) {
610      return isset($this->prefixes[$r]) ? array($this->prefixes[$r], $sub_v) : array(0, $v);
611    }
612    return array(0, $v);
613  }
614  
615  /* 69.., 73.., 93, 94..  */
616  
617  function xBlankNode($v) {
618    if (($r = $this->x('\_\:', $v)) && (list($r, $sub_v) = $this->xPN_LOCAL($r[1])) && $r) {
619      return array(array('type' => 'bnode', 'value' => '_:' . $r), $sub_v);
620    }
621    if ($r = $this->x('\[[\x20\x9\xd\xa]*\]', $v)) {
622      return array(array('type' => 'bnode', 'value' => $this->createBnodeID()), $r[1]);
623    }
624    return array(0, $v);
625  }
626
627  /* 70.. */
628  
629  function xIRI_REF($v) {
630    //if ($r = $this->x('\<([^\<\>\"\{\}\|\^\'[:space:]]*)\>', $v)) {
631    if (($r = $this->x('\<(\$\{[^\>]*\})\>', $v)) && ($sub_r = $this->xPlaceholder($r[1]))) {
632      return array($r[1], $r[2]);
633    }
634    elseif ($r = $this->x('\<([^\<\>\s]*)\>', $v)) {
635      return array($r[1] ? $r[1] : true, $r[2]);
636    }
637    return array(0, $v);
638  }
639  
640  /* 71 */
641  
642  function xPNAME_NS($v) {
643    list($r, $sub_v) = $this->xPN_PREFIX($v);
644    $prefix = $r ? $r : '';
645    return ($r = $this->x("\:", $sub_v)) ? array($prefix . ':', $r[1]) : array(0, $v);
646  }
647
648  /* 72 */
649  
650  function xPNAME_LN($v) {
651    if ((list($r, $sub_v) = $this->xPNAME_NS($v)) && $r) {
652      if (!$this->x('\s', $sub_v) && (list($sub_r, $sub_v) = $this->xPN_LOCAL($sub_v)) && $sub_r) {
653        if (!isset($this->prefixes[$r])) {
654          return array(0, $v);
655        }
656        return array($this->prefixes[$r] . $sub_r, $sub_v);
657      }
658    }
659    return array(0, $v);
660  }
661  
662  /* 76 */
663  
664  function xLANGTAG($v) {
665    if (!$this->x('\s', $v) && ($r = $this->x('\@([a-z]+(\-[a-z0-9]+)*)', $v))) {
666      return array($r[1], $r[3]);
667    }
668    return array(0, $v);
669  }
670  
671  /* 77.. */
672  
673  function xINTEGER($v) {
674    if ($r = $this->x('([0-9]+)', $v)) {
675      return array($r[1], $r[2]);
676    }
677    return array(false, $v);
678  }
679
680  /* 78.. */
681
682  function xDECIMAL($v) {
683    if ($r = $this->x('([0-9]+\.[0-9]*)', $v)) {
684      return array($r[1], $r[2]);
685    }
686    if ($r = $this->x('(\.[0-9]+)', $v)) {
687      return array($r[1], $r[2]);
688    }
689    return array(false, $v);
690  }
691
692  /* 79.., 86.. */
693
694  function xDOUBLE($v) {
695    if ($r = $this->x('([0-9]+\.[0-9]*E[\+\-]?[0-9]+)', $v)) {
696      return array($r[1], $r[2]);
697    }
698    if ($r = $this->x('(\.[0-9]+E[\+\-]?[0-9]+)', $v)) {
699      return array($r[1], $r[2]);
700    }
701    if ($r = $this->x('([0-9]+E[\+\-]?[0-9]+)', $v)) {
702      return array($r[1], $r[2]);
703    }
704    return array(false, $v);
705  }
706  
707  /* 92 */
708  
709  function xNIL($v) {
710    if ($r = $this->x('\([\x20\x9\xd\xa]*\)', $v)) {
711      return array(array('type' => 'uri', 'value' => $this->rdf . 'nil'), $r[1]);
712    }
713    return array(0, $v);
714  }
715
716  /* 95.. */
717  
718  function xPN_CHARS_BASE($v) {
719    if ($r = $this->x("([a-z]+|\\\u[0-9a-f]{1,4})", $v)) {
720      return array($r[1], $r[2]);
721    }
722    return array(0, $v);
723  }
724
725  /* 96 */
726  
727  function xPN_CHARS_U($v) {
728    if ((list($r, $sub_v) = $this->xPN_CHARS_BASE($v)) && $r) {
729      return array($r, $sub_v);
730    }
731    elseif ($r = $this->x("(_)", $v)) {
732      return array($r[1], $r[2]);
733    }
734    return array(0, $v);
735  }
736
737  /* 97.. */
738  
739  function xVARNAME($v) {
740    $r = '';
741    do {
742      $proceed = 0;
743      if ($sub_r = $this->x('([0-9]+)', $v)) {
744        $r .= $sub_r[1];
745        $v = $sub_r[2];
746        $proceed = 1;
747      }
748      elseif ((list($sub_r, $sub_v) = $this->xPN_CHARS_U($v)) && $sub_r) {
749        $r .= $sub_r;
750        $v = $sub_v;
751        $proceed = 1;
752      }
753      elseif ($r && ($sub_r = $this->x('([\xb7\x300-\x36f]+)', $v))) {
754        $r .= $sub_r[1];
755        $v = $sub_r[2];
756        $proceed = 1;
757      }
758    } while ($proceed);
759    return array($r, $v);
760  }
761
762  /* 98.. */
763  
764  function xPN_CHARS($v) {
765    if ((list($r, $sub_v) = $this->xPN_CHARS_U($v)) && $r) {
766      return array($r, $sub_v);
767    }
768    elseif ($r = $this->x('([\-0-9\xb7\x300-\x36f])', $v)) {
769      return array($r[1], $r[2]);
770    }
771    return array(false, $v);
772  }
773
774  /* 99 */
775  
776  function xPN_PREFIX($v) {
777    if ($sub_r = $this->x("([^\s\:\(\)\{\}\;\,]+)", $v, 's')) {/* accelerator */
778      return array($sub_r[1], $sub_r[2]);/* @@testing */
779    }
780    if ((list($r, $sub_v) = $this->xPN_CHARS_BASE($v)) && $r) {
781      do {
782        $proceed = 0;
783        list($sub_r, $sub_v) = $this->xPN_CHARS($sub_v);
784        if ($sub_r !== false) {
785          $r .= $sub_r;
786          $proceed = 1;
787        }
788        elseif ($sub_r = $this->x("\.", $sub_v)) {
789          $r .= '.';
790          $sub_v = $sub_r[1];
791          $proceed = 1;
792        }
793      } while ($proceed);
794      list($sub_r, $sub_v) = $this->xPN_CHARS($sub_v);
795      $r .= $sub_r ? $sub_r : '';
796    }
797    return array($r, $sub_v);
798  }
799  
800  /* 100 */
801  
802  function xPN_LOCAL($v) {
803    if (($sub_r = $this->x("([^\s\(\)\{\}\;\,\.]+)", $v, 's')) && !preg_match('/^\./', $sub_r[2])) {/* accelerator */
804      return array($sub_r[1], $sub_r[2]);/* @@testing */
805    }
806    $r = '';
807    $sub_v = $v;
808    do {
809      $proceed = 0;
810      if ($this->x('\s', $sub_v)) {
811        return array($r, $sub_v);
812      }
813      if ($sub_r = $this->x('([0-9])', $sub_v)) {
814        $r .= $sub_r[1];
815        $sub_v = $sub_r[2];
816        $proceed = 1;
817      }
818      elseif ((list($sub_r, $sub_v) = $this->xPN_CHARS_U($sub_v)) && $sub_r) {
819        $r .= $sub_r;
820        $proceed = 1;
821      }
822      elseif ($r) {
823        if (($sub_r = $this->x('(\.)', $sub_v)) && !preg_match('/^\s/s', $sub_r[2])) {
824          $r .= $sub_r[1];
825          $sub_v = $sub_r[2];
826        }
827        if ((list($sub_r, $sub_v) = $this->xPN_CHARS($sub_v)) && $sub_r) {
828          $r .= $sub_r;
829          $proceed = 1;
830        }
831      }
832    } while ($proceed);
833    return array($r, $sub_v);
834  }
835  
836  /*  */
837  
838  function unescapeNtripleUTF($v) {
839    if (strpos($v, '\\') === false) return $v;
840    $mappings = array('t' => "\t", 'n' => "\n", 'r' => "\r", '\"' => '"', '\'' => "'");
841    foreach ($mappings as $in => $out) {
842      $v = preg_replace('/\x5c([' . $in . '])/', $out, $v);
843    }
844    if (strpos(strtolower($v), '\u') === false) return $v;
845    while (preg_match('/\\\(U)([0-9A-F]{8})/', $v, $m) || preg_match('/\\\(u)([0-9A-F]{4})/', $v, $m)) {
846      $no = hexdec($m[2]);
847  		if ($no < 128) $char = chr($no);
848      else if ($no < 2048) $char = chr(($no >> 6) + 192) . chr(($no & 63) + 128);
849      else if ($no < 65536) $char = chr(($no >> 12) + 224) . chr((($no >> 6) & 63) + 128) . chr(($no & 63) + 128);
850  		else if ($no < 2097152) $char = chr(($no >> 18) + 240) . chr((($no >> 12) & 63) + 128) . chr((($no >> 6) & 63) + 128) . chr(($no & 63) + 128);
851      else $char= '';
852      $v = str_replace('\\' . $m[1] . $m[2], $char, $v);
853    }
854    return $v;
855  }
856  
857  /*  */
858  
859  function xPlaceholder($v) {
860    //if ($r = $this->x('(\?|\$)\{([^\}]+)\}', $v)) {
861    if ($r = $this->x('(\?|\$)', $v)) {
862      if (preg_match('/(\{(?:[^{}]+|(?R))*\})/', $r[2], $m) && strpos(trim($r[2]), $m[1]) === 0) {
863        $ph = substr($m[1], 1, -1);
864        $rest = substr(trim($r[2]), strlen($m[1]));
865        if (!isset($this->r['placeholders'])) $this->r['placeholders'] = array();
866        if (!in_array($ph, $this->r['placeholders'])) $this->r['placeholders'][] = $ph;
867        return array(array('value' => $ph, 'type' => 'placeholder'), $rest);
868      }
869    }
870    return array(0, $v);
871  }
872  
873  /*  */
874}