source: Dev/trunk/rdfapi/syntax/N3Parser.php @ 75

Last change on this file since 75 was 12, checked in by basvannuland, 14 years ago

Added RAP RDF API
Added RDF reader writer for save and load survey

File size: 30.9 KB
Line 
1<?php
2require_once RDFAPI_INCLUDE_DIR . 'util/Object.php';
3require_once RDFAPI_INCLUDE_DIR . 'model/Blanknode.php';
4require_once RDFAPI_INCLUDE_DIR . 'model/Resource.php';
5require_once RDFAPI_INCLUDE_DIR . 'model/Literal.php';
6require_once RDFAPI_INCLUDE_DIR . 'model/Statement.php';
7require_once RDFAPI_INCLUDE_DIR . 'model/MemModel.php';
8require_once RDFAPI_INCLUDE_DIR . 'constants.php';
9
10// ----------------------------------------------------------------------------------
11// Class: N3Parser
12// ----------------------------------------------------------------------------------
13
14
15/**
16 * PHP Notation3 Parser
17 *
18 * This parser can parse a subset of n3, reporting triples to a callback function
19 * or constructing a RAP Model ( http://www.wiwiss.fu-berlin.de/suhl/bizer/rdfapi )
20 *
21 * Supported N3 features:
22 * <ul>
23 *   <li>Standard things, repeated triples ( ; and , ), blank nodes using [ ], self-reference ('<>')</li>
24 *   <li>@prefix mappings</li>
25 *   <li>= maps to owl#sameAs</li>
26 *   <li>a maps to rdf-syntax-ns#type</li>
27 *   <li>Literal datytype- and xmlLanguageTag support
28 * </ul>
29 * Un-supported N3 Features include:
30 * <ul>
31 *   <li>Reification using { }</li>
32 *   <li>. and ^ operators for tree traversal</li>
33 *   <li>Any log operators, like log:forAll etc.</li>
34 * </ul>
35 *
36 * This parser is based on n3.py from Epp released 2nd March, 2002.
37 * by Sean B. Palmer
38 * ( http://infomesh.net/2002/eep/20020302-013802/n3.py )
39 *
40 * This parser is released under the GNU GPL license.
41 * ( http://www.gnu.org/licenses/gpl.txt )
42 *
43 *
44 *
45 * @author Sean B. Palmer <sean@mysterylights.com>
46 * @author Gunnar AA. Grimnes <ggrimnes@csd.abdn.ac.uk>
47 * @author Daniel Westphal <mail@d-westphal.de>
48 * @version $Id: N3Parser.php 569 2008-05-19 05:24:39Z p_frischmuth $
49 * @license GPL http://www.gnu.org/licenses/gpl.txt
50 * @package syntax
51 * @access public
52 **/
53
54class N3Parser extends Object {
55
56
57  /* ==================== Variables ==================== */
58
59  var $Tokens;
60  var $bNode;
61  var $RDF_NS, $DAML_NS, $OWL_NS;
62  var $debug;
63  var $parseError;
64  var $parsedNamespaces = array();
65
66  /* ==================== Public Methods ==================== */
67
68  /**
69   * Constructor
70   * @access public
71   **/
72  function N3Parser() {
73    //Regular expressions:
74     $Name     = '[A-Za-z0-9_@\.]+[^\.,;\[\]\s\) ]*';
75     $URI      = '<[^> ]*>';
76     $bNode    = '_:'.$Name;
77     $Univar   = '\?'.$Name;
78     $QName    = '(?:[A-Za-z][A-Za-z0-9_@\.]*)?:'.$Name;
79     $Literal  = '(?:'
80               . '"(\\\"|[^"])*"'
81               . '|'
82               . "'(\\\'|[^'])*'"
83               . ')';
84               # '"(?:\\"|[^"])*"'
85     $Number   = '[-+]?[0-9]+(\\.[0-9]+)?([eE][-+]?[0-9]+)?';
86     $Boolean  = '@(?:true|false)';
87//   $Literal  = '"[^"\\\\]*(?:\\.\\[^"\\]*)*"'; # '"(?:\\"|[^"])*"'
88     $LangTag  = '@[A-Za-z\-]*[^ \^\.\;\,]';
89     $Datatype = '(\^\^)[^ ,\.;)]+';
90     $Datatype_URI = '(\^\^)'.$URI;
91     //     $LLiteral = '"""[^"\\\\]*(?:(?:.|"(?!""))[^"\\\\]*)*"""';
92     $LLiteral = '(?:'
93                 . '"""[^"\\\\]*(?:(?:\\\\.|"(?!""))[^"\\\\]*)*"""'
94                 . '|'
95                 . "'''[^'\\\\]*(?:(?:\\\\.|'(?!''))[^\"\\\\]*)*'''"
96                 . ')';
97     //          '"""[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""'
98     $Comment    = '#.*$';
99     $Prefix     = '(?:[A-Za-z][A-Za-z0-9_]*)?:';
100     $PrefixDecl = '@prefix';
101     $WS         = '[ \t]';
102     $this->RDF_NS  = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; # for 'a' keyword
103     $this->DAML_NS = 'http://www.daml.org/2001/03/daml+oil#'; # for '=' keyword
104     $this->OWL_NS  = 'http://www.w3.org/2002/07/owl#';
105
106     //     $t = array( $LLiteral, $URI); //, $Literal, $PrefixDecl, $QName, $bNode, $Prefix,
107     //     $Univar, 'a', '{', '}', '\(', '\)', '\[', '\]', ',', ';', '\.', $WS, $Comment);
108     $t = array(
109            $Datatype_URI, $Datatype, $LLiteral, $URI, $Literal,
110            $PrefixDecl, $QName, $Number, $Boolean, $bNode,
111            $Prefix, $Univar, 'a','=',
112            '{', '}', '\(', '\)', '\[', '\]', ',', ';', '\.',
113            $WS, $Comment,$LangTag
114     );
115     $this->Tokens = "/(".join($t,"|").")/m";
116
117     $this->bNode      = 0;
118     $this->debug      = 0;
119     $this->bNodeMap   = array();
120     $this->FixBnodes  = FIX_BLANKNODES;
121     $this->parseError =false;
122  }
123
124
125  /**
126   * Sets, if BlankNode labels should be replaced by the generic label from the constants.php file
127   * default is "false" -> the used label in n3 is parsed to the model
128   * @param boolean
129   * @access public
130   **/
131  function setFixBnodes($set) {
132
133        if (($set===true) OR ($set===false)) $this->FixBnodes = $set;
134  }
135
136
137  /**
138   * This parses a N3 string and prints out the triples
139   * @param string $s
140   * @access public
141   **/
142  function parse($s) {
143    //   """Get a string, tokenize, create list, convert to Eep store."""
144    $stat=$this->n3tolist($s);
145    foreach ( $stat as $t) {
146
147      if (count($t)>3) {
148        $object=$t[2];
149
150        for ($i = 3; $i < 5; $i++){
151          if ($t[$i][0]=='@')$object.=$t[$i];
152          if (substr($t[$i],0,2)=='^^')$object.=$t[$i];
153        };
154      } else {$object=$t[2];};
155
156      print '('.$t[0].', '.$t[1].', '.$object.")\n";
157
158    }
159    //   return [[eep.Article(t[0]), eep.Article(t[1]), eep.Article(t[2])]
160    //              for t in n3tolist(s)]
161  }
162
163
164  /**
165   * This parses a N3 string and calls func($subject, $predicate, $object) with each triple
166   * @param string $s
167   * @param string $func
168   * @access public
169   **/
170  function uparse($s,$func) {
171    //   """Get a string, tokenize, create list, convert to Eep store."""
172    $stat=$this->n3tolist($s);
173    foreach ( $stat as $t) {
174
175        if (count($t)>3) {
176        $object=$t[2];
177
178        for ($i = 3; $i < 5; $i++){
179          if ($t[$i][0]=='@')$object.=$t[$i];
180          if (substr($t[$i],0,2)=='^^')$object.=$t[$i];
181        };
182      } else {$object=$t[2];};
183        //    print "(".$t[0].", ".$t[1].", ".$t[2].")";
184
185      $func($t[0],$t[1],$object);
186    }
187    //   return [[eep.Article(t[0]), eep.Article(t[1]), eep.Article(t[2])]
188    //              for t in n3tolist(s)]
189  }
190
191
192  /**
193   * This parses a N3 string and returns a memmodel
194   * @param string $s
195   * @access public
196   * @return object Model
197   **/
198
199  function parse2model($s,$model = false) {
200        if($model == false){
201            $m=new MemModel();
202        }else{
203                $m=$model;
204        }
205    //   """Get a string, tokenize, create list, convert to Eep store."""
206    $stat=$this->n3tolist($s);
207
208    foreach ( $stat as $t) {
209      $s=$this->toRDFNode($t[0],$t);
210      $p=$this->toRDFNode($t[1],$t);
211      $o=$this->toRDFNode($t[2],$t);
212
213       $new_statement= new Statement($s,$p,$o);
214
215      $m->add($new_statement);
216      //    print "(".$t[0].", ".$t[1].", ".$t[2].")";
217    }
218    //   return [[eep.Article(t[0]), eep.Article(t[1]), eep.Article(t[2])]
219    //              for t in n3tolist(s)]
220    $m->addParsedNamespaces($this->parsedNamespaces);
221    return $m;
222  }
223
224/**
225 * Generate a new MemModel from an URI or file.
226 *
227 * @access      public
228 * @param $path
229 * @throws PhpError
230 * @return object MemModel
231 */
232  function & generateModel($path,$dummy=false,$model=false) {
233
234    $handle = fopen($path,'r') or die("N3 Parser: Could not open File: '$path' - Stopped parsing.");
235        $done=false;
236        $input="";
237        while(!$done)
238        {
239          $input .= fread( $handle, 512 );
240          $done = feof($handle);
241
242        };
243
244
245    fclose($handle);
246
247    $m = $this->parse2model($input,$model);
248    return $m;
249  }
250 
251  function generateModelFromString($n3String, $model) {
252     
253      $m = $this->parse2model($n3String, $model);
254      $m->addModel($m);
255  }
256
257
258  /* ==================== Private Methods from here ==================== */
259
260  //  General list processing functions
261
262/**
263 * Returns FALSE if argument is a whitespace character
264 * @access private
265 * @param string $s
266 **/
267  function isWS($s) {
268    return !preg_match('/^(#.*|\s*)$/', $s);
269  }
270
271
272
273  /**
274   * Returns true if the string is not a comment
275   * @access private
276   * @param string $s
277   * @returns boolean
278   **/
279  function notComment($s) {
280    if ($s=="") return false;
281    $N3Comment = '^[ \t]*\#';
282
283    if (ereg($N3Comment,$s)) return false;
284    else return true;
285  }
286
287  /**
288   * Removes all whitespace tokens from list
289   * @access private
290   * @param array $list
291   **/
292  function filterWs($list) {
293    //    var_dump($list);
294    //  """Filter whitespace from a list."""
295
296    return array_filter($list, array($this,"isWS"));
297  }
298
299/**
300* converts a string to its unicode NFC form (e.g. \uHHHH or \UHHHHHHHH).
301*
302* @param String $str
303* @return String
304* @access private
305*
306*/
307function str2unicode_nfc($str=""){
308        $result="";
309        /* try to detect encoding */
310        $tmp=str_replace("?", "", $str);
311        if(strpos(utf8_decode($tmp), "?")===false){
312                $str=utf8_decode($str);
313        }
314        for($i=0,$i_max=strlen($str);$i<$i_max;$i++){
315                $nr=0;/* unicode dec nr */
316                /* char */
317                $char=$str[$i];
318                /* utf8 binary */
319                $utf8_char=utf8_encode($char);
320                $bytes=strlen($utf8_char);
321                if($bytes==1){
322                        /* 0####### (0-127) */
323                        $nr=ord($utf8_char);
324                }
325                elseif($bytes==2){
326                        /* 110##### 10###### = 192+x 128+x */
327                        $nr=((ord($utf8_char[0])-192)*64) + (ord($utf8_char[1])-128);
328                }
329                elseif($bytes==3){
330                        /* 1110#### 10###### 10###### = 224+x 128+x 128+x */
331                        $nr=((ord($utf8_char[0])-224)*4096) + ((ord($utf8_char[1])-128)*64) + (ord($utf8_char[2])-128);
332                }
333                elseif($bytes==4){
334                        /* 1111#### 10###### 10###### 10###### = 240+x 128+x 128+x 128+x */
335                        $nr=((ord($utf8_char[0])-240)*262144) + ((ord($utf8_char[1])-128)*4096) + ((ord($utf8_char[2])-128)*64) + (ord($utf8_char[3])-128);
336                }
337                /* result (see http://www.w3.org/TR/rdf-testcases/#ntrip_strings) */
338                if($nr<9){/* #x0-#x8 (0-8) */
339                        $result.="\\u".sprintf("%04X",$nr);
340                }
341                elseif($nr==9){/* #x9 (9) */
342                        $result.='\t';
343                }
344                elseif($nr==10){/* #xA (10) */
345                        $result.='\n';
346                }
347                elseif($nr<13){/* #xB-#xC (11-12) */
348                        $result.="\\u".sprintf("%04X",$nr);
349                }
350                elseif($nr==13){/* #xD (13) */
351                        $result.='\t';
352                }
353                elseif($nr<32){/* #xE-#x1F (14-31) */
354                        $result.="\\u".sprintf("%04X",$nr);
355                }
356                elseif($nr<34){/* #x20-#x21 (32-33) */
357                        $result.=$char;
358                }
359                elseif($nr==34){/* #x22 (34) */
360                        $result.='\"';
361                }
362                elseif($nr<92){/* #x23-#x5B (35-91) */
363                        $result.=$char;
364                }
365                elseif($nr==92){/* #x5C (92) */
366                        $result.='\\';
367                }
368                elseif($nr<127){/* #x5D-#x7E (93-126) */
369                        $result.=$char;
370                }
371                elseif($nr<65536){/* #x7F-#xFFFF (128-65535) */
372                        $result.="\\u".sprintf("%04X",$nr);
373                }
374                elseif($nr<1114112){/* #x10000-#x10FFFF (65536-1114111) */
375                        $result.="\\U".sprintf("%08X",$nr);
376                }
377                else{
378                        /* other chars are not defined => ignore */
379                }
380        }
381        return $result;
382}
383
384
385
386  /**
387   * Gets a slice of an array.
388   * Returns the wanted slice, as well as the remainder of the array.
389   * e.g. getSpan(['p', 'q', 'r'], 1, 2) gives (['q'], ['p', 'r'])
390   * @return array
391   * @access private
392   * @param array $list
393   * @param integer $start
394   * @param integer $end
395   **/
396  function getSpan($list, $start, $end) {
397
398    $pre=array_slice($list, 0, $start);
399    $post=array_slice($list, $end);
400
401    return array(array_slice($list, $start,$end-$start),$this->array_concat($pre,$post));
402  }
403
404
405  /**
406   * Concatenates two arrays
407   * @param array $a
408   * @param array $b
409   * @returns array
410   * @access private
411   **/
412  function array_concat($a, $b) {
413    array_splice($a,count($a),0,$b);
414    return $a;
415  }
416
417  /**
418   * Returns an array with all indexes where item appears in list
419   * @param array $list
420   * @param string $item
421   * @returns array
422   * @access private
423   **/
424  function posns($list, $item) {
425    $res=array();
426    $i=0;
427    foreach ( $list as $k=>$v) {
428      if ($v === $item ) $res[]=$i;
429      $i++;
430    }
431    $res[]=$i;
432    return $res;
433  }
434
435
436  /* More N3 specific functions */
437
438  /**
439   * Returns a list of tokens
440   * @param string $s
441   * @returns array
442   * @access private
443   **/
444  function toke($s) {
445
446    //    print "$s\n";
447    //   """Notation3 tokenizer. Takes in a string, returns a raw token list."""
448    if (strlen($s) == 0) die('Document has no content!');
449
450    $s=str_replace("\r\n","\n",$s);
451    $s=str_replace("\r","\n",$s);
452
453
454    //$lines=explode("\n",$s);
455
456    //$reallines=array_filter($lines, array($this, "notComment"));
457    //    print "LINES: ".join($reallines, " ")." :LINES\n";
458    //array_walk($reallines, array($this, "trimLine"));
459    //$res=array();
460
461    //    foreach ($reallines as $l) {
462    //preg_match_all($this->Tokens, $l, $newres);
463    //$res=$this->array_concat($res,$newres[0]);
464    //}
465
466    $res=array();
467    preg_match_all($this->Tokens, $s, $newres);
468    $res=$this->array_concat($res, array_map('trim', $newres[0]));
469//var_dump($newres[0]);
470    return $res;
471  }
472
473  /**
474   * Returns a list with the elements between start and end as one quoted string
475   * e.g. listify(["a","b","c","d"],1,2) => ["a","b c", "d"]
476   * @param array $list
477   * @param integer $start
478   * @param integer $end
479   * @returns array
480   * @access private
481   **/
482  function listify($list, $start, $end) {
483
484    //Re-form a list, merge elements start->end into one quoted element
485    //Start and end are offsets...
486
487    $l=$end-$start;
488
489    $s=array_slice($list, 0, $start);
490    $m=array_slice($list, $start,$l);
491    $e=array_slice($list, $end);
492
493    //  array_push($s,"\"".join($m," ")."\"");
494    array_push($s,$m);
495
496    return $this->array_concat($s,$e);
497  }
498
499  /**
500   * Returns an array with prefixes=>namespace mappings
501   * @param array $list
502   * @access private
503   * @returns array
504   **/
505  function getPrefixes($list) {
506
507    $prefixes=array();
508    $ns=1;
509    $name=2;
510    foreach ($list as $l) {
511      if ($l=='@prefix') {
512        //   while '@prefix' in list {
513
514        $pos=current($list);
515        //pos = list.index('@prefix')
516        $r = $this->getSpan($list, $pos, ($pos+4)); # processes the prefix tokens
517        $binding=$r[0];
518        $list=$r[1];
519        $prefixes[$binding[$ns]] = substr($binding[$name],1,-1);
520        $this->parsedNamespaces[substr($binding[$name],1,-1)] = substr($binding[$ns],0,-1);
521      }
522    }
523
524        if (count($prefixes)<1) $list= array_slice($list,0);
525
526    return array($prefixes, $list);
527  }
528
529  /**
530   * Callback function for replacing "a" elements with the right RDF uri.
531   * @param string $l
532   * @access private
533   **/
534  function replace_a_type(&$l,$p) {
535    if ($l=='a') $l='<'.$this->RDF_NS.'type>';
536  }
537
538  /**
539   * Callback function for replacing "=" elements with the right DAML+OIL uri.
540   * @param string $l
541   * @access private
542   **/
543  function replace_equal(&$l,$p) {
544    if ($l=='=') $l='<'.$this->OWL_NS.'sameAs>';
545  }
546
547  /**
548   * Callback function for replacing "this" elements with the right RDF uri.
549   * @param string $l
550   * @access private
551   **/
552  function replace_this($l,$p) {
553    if ($l=='this') $l='<urn:urn-n:this>';
554  }
555
556    /**
557    * Applies stuff :)
558    * Expands namespace prefixes etc.
559    * @param array $prefixes
560    * @param array $list
561    * @returns $list
562    * @access private
563    **/
564    function applyStuff($prefixes, $list)
565    {
566        array_walk($list, array($this, 'replace_a_type'));
567        array_walk($list, array($this, 'replace_equal'));
568        array_walk($list, array($this, 'replace_this'));
569
570        for ($i = 0; $i < count($list); $i++) {
571
572            if ($list[$i]=='<>') {
573                if (!isset($path)) {
574                    if (!isset($_SERVER['SERVER_ADDR'])) {
575                        $_SERVER['SERVER_ADDR'] = 'localhost';
576                    }
577                    if (!isset($_SERVER['REQUEST_URI'])) {
578                        $_SERVER['REQUEST_URI'] = '/rdfapi-php';
579                    }
580                    $list[$i] = '<http://'.$_SERVER['SERVER_ADDR'].$_SERVER['REQUEST_URI'].'#generate_timestamp_'.time().'>';
581                } else {
582                    $list[$i] = '<'.$path.'>';
583                };
584            };
585
586
587            if (preg_match('/^[-+]?[0-9]+$/', $list[$i])) {
588                //integer
589                $list[$i] = intval($list[$i]);
590            } else if (is_numeric($list[$i])) {
591                //float or decimal
592                // After conversion we cannot distinguish between both
593                $list[$i] = floatval($list[$i]);
594            } else if ((!strstr('<_"\'?.;,{}[]()@', $list[$i]{0}))
595             && (substr($list[$i],0,3) != '^^<')
596            ) {
597                //prefix or unknown
598                $_r   = explode(':', $list[$i]);
599                $ns   = $_r[0] . ':';
600                $name = $_r[1];
601
602                if (isset($prefixes[$ns])) {
603                    $list[$i] = '<'.$prefixes[$ns].$name.'>';
604                } else if (isset($prefixes[substr($ns, 2)])) {
605                    $list[$i] = '^^' . $prefixes[substr($ns, 2)] . $name . '';
606                } else {
607                    //die('Prefix not declared:'.$ns);
608                    $this->parseError = true;
609                    trigger_error('Prefix not declared: '.$ns, E_USER_ERROR);
610                    break;
611                }
612
613            } else {
614                if ($list[$i]{0} == '"') {
615                    $bLiteral = true;
616                    $chBase   = '"';
617                } else if ($list[$i]{0} == '\'') {
618                    $bLiteral = true;
619                    $chBase   = '\'';
620                } else {
621                    $bLiteral = false;
622                }
623                if ($bLiteral) {
624                    $tripleBase = $chBase . $chBase . $chBase;
625                    // Congratulations - it's a literal!
626                    if (substr($list[$i], 0, 3) == $tripleBase) {
627                        if (substr($list[$i],-3,3) == $tripleBase) {
628                            // A big literal...
629                            $lit = substr($list[$i],3,-3);
630                            //        print "++$lit++";
631                            $lit=str_replace('\n', '\\n',$lit);
632
633                            //$lit=ereg_replace("[^\\]" . $chBase, "\\" . $chBase, $lit);
634                            $lit = stripslashes($lit);
635
636                            $list[$i] = $chBase . $lit . $chBase;
637                        } else {
638                            die ('Incorrect string formatting: '.substr($list[$i],-3,3));
639                        }
640                    } else {
641                        if (strstr($list[$i],"\n")) {
642                            die('Newline in literal: ' . $list[$i]);
643                        }
644                        $list[$i] = stripslashes($list[$i]);
645                    }
646                }
647            }
648
649            if (substr($list[$i],0,2)=='^^') {
650                if ($list[$i][2]!='<') {
651                    $list[$i] = '^^<' . substr($list[$i], 2) . '>';
652                }
653            };
654
655        }//foreach list item
656
657        return $list;
658    }//function applyStuff($prefixes, $list)
659
660
661
662  /**
663   * Returns an array of triples extracted from the list of n3 tokens
664   * @param array $list
665   * @returns array
666   * @access private
667   **/
668  function getStatements($list) {
669
670
671    $statements = array();
672
673    while (in_array('.', $list)) {
674      //  for($i=0;$i<count($list); $i++) {
675      //    if ($list[$i]==".") {
676      //   while '.' in list {
677      $pos=array_search('.',$list);
678
679      $r=$this->getSpan($list, 0, $pos+1);
680
681      $statement=$r[0];
682      $list = $r[1];
683
684      array_pop($statement);
685      $statements[]=$statement;
686    }
687
688    return $statements;
689  }
690
691  /**
692   * Gets a list of triples with same subject
693   * e.g. :Gunnar :firstname "Gunnar" ; :lastname "Grimnes.
694   * @param array $list
695   * @returns array
696   * @acces private
697   **/
698  function getPovs($list) {
699    $povs = array();
700    while (in_array(';', $list)) {
701      $r=$this->posns($list,';');
702      $pos=array_slice($r,0,2);
703      $r = $this->getSpan($list, $pos[0], $pos[1]);
704      $pov=$r[0];
705      $list=$r[1];
706
707      // skip lone semicolons, e.g. "<a> <b> <c> ; ."
708      if (count($pov) == 1) continue;
709
710      $povs[]=array_slice($pov,1);
711    }
712
713    return array($list, $povs);
714  }
715
716  /**
717   * Gets a list of triples with same predicate
718   * e.g. :Gunnar :likes "Cheese", "Wine".
719   * @access private
720   * @param array $list
721   * @returns array
722   **/
723  function getObjs($list) {
724
725
726    $objs = array();
727    while (in_array(",",$list)) {
728      $pos=array_search(",",$list);
729      //  for($i=0;$i<count($list); $i++) {
730      //    if ($list[$i]==",") {
731      //   while ',' in list {
732
733
734      $get_array_fields=2;
735      if (isset ($list[$pos+2])) {
736        if (@$list[$pos+2][0]=='@') $get_array_fields++;
737        if (@$list[$pos+2][0]=='^') $get_array_fields++;
738      };
739      if (isset ($list[$pos+3])) { if (@$list[$pos+3][0]=='^') $get_array_fields++;};
740
741
742      $r=$this->getSpan($list, $pos, ($pos+$get_array_fields));
743
744      $obj=$r[0];
745      if (!isset($obj[2])) $obj[2]=' ';
746      if (!isset($obj[3])) $obj[3]=' ';
747
748      $list=$r[1];
749
750
751      $objs[]=$obj;
752
753
754    }
755
756    return array($list, $objs);
757  }
758
759  /**
760   * Does the real work, returns a list of subject, predicate, object triples.
761   * @param array $list
762   * @returns array
763   * @access private
764   **/
765  function statementize($list) {
766
767    if (count($list) == 1 && preg_match("/_".BNODE_PREFIX."[0-9]+_/",$list[0])) {
768        if ($this->debug) print "Ignored bNode exists statement. $list\n";
769        return array();
770    }
771
772
773
774    if (count($list) == 3) return array($list);
775    if (count($list) < 3) {
776        throw new Exception(
777            'N3 statement too short,'
778            . ' only ' . count($list) . ' elements instead of 3:' . "\n"
779            . implode("\n", $list)
780        );
781    }
782
783    //Get all ;
784    $r=$this->getPovs($list);
785    $spo=$r[0];
786    $po=$r[1];
787    $all=array();
788
789
790
791    //      (spo, po), all = getPovs(list), []
792    $subject = $spo[0];
793    foreach ($po as $pop) {
794      //  for pop in po {
795      $r=$this->getObjs($pop);
796
797      $myPo=$r[0];
798      $obj=$r[1];
799      //myPo, obj = getObjs(pop)
800
801      if (!isset($myPo[2])) $myPo[2]=' ';
802      if (!isset($myPo[3])) $myPo[3]=' ';
803
804
805      $predicate = $myPo[0];
806      $all[]=array($subject,$predicate,$myPo[1],$myPo[2],$myPo[3]);
807      //    all.append([subject, predicate, myPo[1]])
808
809
810
811      foreach ($obj as $o) $all[]=array($subject,$predicate, $o[1],$o[2],$o[3]);
812      //         for x in obj: all.append([subject, predicate, x])
813
814    }
815
816
817
818    $r = $this->getObjs($spo);
819    $spo=$r[0];
820
821    $objs=$r[1];
822
823    //spo, objs = getObjs(spo)
824    $subject=$spo[0];
825    $predicate=$spo[1];
826
827
828    if(!isset($spo[3])) $spo[3]=' ';
829    if(!isset($spo[4])) $spo[4]=' ';
830
831    $all[]=array($subject, $predicate, $spo[2],$spo[3],$spo[4]);
832
833    foreach ($objs as $obj) $all[]=array($subject, $predicate, $obj[1],$obj[2],$obj[3]);
834
835    return $all;
836  }
837
838  /**
839   * Makes lists of elements in list into a seperate array element.
840   * e.g. doLists(["a","b","[","c","]","d"], "[","]")=> ["a","b", ["c"], "d"]
841   * @param array $list
842   * @param string $schar
843   * @param string $echar
844   * @returns array
845   * @access private
846   **/
847  function doLists($list, $schar, $echar) {
848
849    while (in_array($schar, $list)) {
850      //   while schar in list {
851      $ndict        = array();
852      $nestingLevel = 0;
853      $biggest      = 0;
854      for ($i = 0; $i < count($list); $i++) {
855        if ($list[$i] == $schar) {
856            $nestingLevel += 1;
857            if (!in_array($nestingLevel, array_keys($ndict))) {
858              $ndict[$nestingLevel] = array(array($i));
859            } else {
860              $ndict[$nestingLevel][]=array($i);
861            }
862        }
863        if ($list[$i] == $echar) {
864          if (!in_array($nestingLevel, array_keys($ndict))) {
865            $ndict[$nestingLevel]=array(array($i));
866          } else {
867            $ndict[$nestingLevel][count($ndict[$nestingLevel])-1][]=$i;
868            $nestingLevel-= 1;
869# elif type(list[i]) == type([]) {
870#    list[i] = doLists(list[i], schar, echar)
871          }
872        }
873      }
874      foreach (array_keys($ndict) as $key) {
875        if ($key > $biggest)  $biggest = $key;
876      }
877
878      $tol  = $ndict[$biggest][0];
879      $list = $this->listify($list, $tol[0], ($tol[1]+1));
880    }
881    return $list;
882  }
883
884  /**
885   * Apply doLists for all different types of list.
886   * @param array
887   * @returns array
888   * @access private
889   **/
890  function listStuff($list) {
891# y, z = zip(['[', ']'], ['{', '}'], ['(', ')'])
892# return map(doLists, [list, list, list], y, z).pop()
893    $list = $this->doLists($list, '[', ']');
894    $list = $this->doLists($list, '{', '}');
895    return $this->doLists($list, '(', ')');
896  }
897
898  /**
899   * Generates a new node id.
900   * @access private
901   * @returns string
902   **/
903  function bnodeID() {
904
905    $this->bNode++;
906    return "_".BNODE_PREFIX.$this->bNode."_";
907  }
908
909  /**
910   * This makes bNodes out of variables like _:a etc.
911   * @access private
912   * @param array $list
913   * @returns array
914   **/
915  function fixAnon($list) {
916//    $map=array();
917    for($i=0;$i<count($list);$i++) {
918      $l=$list[$i];
919      if (substr($l,0,2)=="_:") {
920          if (!isset($this->bNodeMap[$l])) {
921          $a=$this->bnodeID();
922          $this->bNodeMap[$l]=$a;
923        } else $a=$this->bNodeMap[$l];
924        $list[$i]=$a;
925      }
926    }
927    return $list;
928  }
929
930  /**
931   * This makes [ ] lists into bnodes.
932   * @access private
933   * @param array $list
934   * @return array
935   **/
936  function expandLists($list) {
937
938    for($i=0;$i<count($list);$i++) {
939      if (is_array($list[$i])) {
940        if ( $list[$i][0]=='[' ) {
941          $bnode=$this->bnodeID();
942          $prop=$list[$i];
943          $list[$i]=$bnode;
944          $list[]=$bnode;
945          $list=$this->array_concat($list, array_slice($prop,1,-1));
946          $list[]='.';
947        }elseif($list[$i][0]=='(') {
948
949            $rdfNil = '<'. RDF_NAMESPACE_URI . RDF_NIL .'>';
950            $rdfFirst = '<'. RDF_NAMESPACE_URI . RDF_FIRST .'>';
951            $rdfRest = '<'. RDF_NAMESPACE_URI . RDF_REST .'>';
952
953            // local copy of list without "(" and ")"
954            $t_list = array_slice($list[$i], 1, -1);
955
956            //prepare bnodes
957            $fromBnode = $this->bnodeID();
958            $toBnode = $this->bnodeID();
959
960            //link first bnode into graph
961            $list[$i] = $fromBnode;
962
963            $count = count($t_list);
964
965            //loop through list, convert to RDF linked list
966            for ($idx = 0; $idx < $count; $idx++){
967
968                // set rdf:first
969                $list[] = $fromBnode;
970                $list[] = $rdfFirst;
971                $list[] = $t_list[$idx];
972                $list[] = '.';
973
974                // set rdf:rest (nil or next bnode)
975                if ($idx == $count - 1) {
976                    $list[] = $fromBnode;
977                    $list[] = $rdfRest;
978                    $list[] = $rdfNil;
979                    $list[] = '.';
980                }
981                else {
982                    $list[] = $fromBnode;
983                    $list[] = $rdfRest;
984                    $list[] = $toBnode;
985                    $list[] = '.';
986
987                    $fromBnode = $toBnode;
988                    $toBnode = $this->bnodeID();
989                }
990            }
991        }
992        else {
993            die('Only [ ] and () lists are supported!');
994        }
995    }
996
997
998    }
999    return $list;
1000  }
1001
1002  /**
1003   * Main work-horse function. This converts a N3 string to a list of statements
1004   * @param string $s
1005   * @returns array
1006   * @access private
1007   **/
1008  function n3tolist($s) {
1009
1010    //   """Convert an N3 string into a list of triples as strings."""
1011    $result = array();
1012
1013   $t = $this->filterWs($this->toke($s)); # tokenize the stream, and filter whitespace tokens
1014
1015    if ($this->debug) {
1016      print "Filter WS:\n";
1017      var_dump($t);
1018    }
1019    $r=$this->getPrefixes($t); # get the prefix directives, and add to a dict
1020    $prefixes=$r[0];
1021    $t=$r[1];
1022    if ($this->debug) {
1023      print "Prefixes:\n";
1024      var_dump($prefixes);
1025      print "***\n";
1026      var_dump($t);
1027    }
1028    $t=$this->applyStuff($prefixes, $t);#apply prefixes, keywords, and string formatting
1029    if ($this->debug) {
1030      print "Stuff applied:\n";
1031      var_dump($t);
1032    }
1033
1034    $t=$this->fixAnon($t); # fix _:a anons
1035    if ($this->debug) {
1036      print "Fix anon:\n";
1037      var_dump($t);
1038    }
1039
1040    $t = $this->listStuff($t); # apply list stuff: todo
1041    if ($this->debug) {
1042      print "Lists done:\n";
1043      var_dump($t);
1044    }
1045    $t=$this->expandLists($t);
1046    if ($this->debug) {
1047      print "Lists applied:\n";
1048      var_dump($t);
1049    }
1050    $t = $this->getStatements($t); # get all of the "statements" from the stream
1051
1052    foreach ($t as $stat) {
1053      $stats = $this->statementize($stat);
1054
1055      foreach ($stats as $y) {
1056        $result[]=$y;
1057      }
1058    }
1059
1060    //   for x in [statementize(stat) for stat in t] {
1061    //      for y in x: result.append(y)
1062    return $result;
1063  }
1064
1065
1066
1067    /**
1068     * Constructs a RAP RDFNode from URI/Literal/Bnode
1069     * @access private
1070     * @param string $s
1071     * @returns object RDFNode
1072     **/
1073    function toRDFNode($s, $state)
1074    {
1075        $ins = substr($s, 1, -1);
1076        if ($s{0} == '"' || $s{0} == '\'') {
1077            $lang = NULL;
1078
1079            if (count($state)>3) {
1080                for ($i = 3; $i < count($state); $i++) {
1081                    if ($state[$i][0]=='@') {
1082                        $lang = substr($state[3], 1);
1083                    }
1084                    if (substr($state[$i],0,2) == '^^') {
1085                        $dtype = substr($state[$i],2);
1086                        if ($dtype[0]=='<') {
1087                            $dtype = substr($dtype,1,-1);
1088                        }
1089                    }
1090                }
1091            }
1092
1093
1094            if (UNIC_RDF) {
1095                $ins = $this->str2unicode_nfc($ins);
1096            }
1097            $new_Literal = new Literal($ins, $lang);
1098            if (isset($dtype)) {
1099                $new_Literal->setDatatype($dtype);
1100            }
1101            return  $new_Literal;
1102        } else if (is_int($s)) {
1103            $value = new Literal($s);
1104            $value->setDatatype(XML_SCHEMA . 'integer');
1105            return $value;
1106        } else if (is_float($s)) {
1107            $value = new Literal($s);
1108            $value->setDatatype(XML_SCHEMA . 'double');
1109            return $value;
1110        } else if ($s == '@true') {
1111            $value = new Literal(true);
1112            $value->setDatatype(XML_SCHEMA . 'boolean');
1113            return $value;
1114        } else if ($s == '@false') {
1115            $value = new Literal(false);
1116            $value->setDatatype(XML_SCHEMA . 'boolean');
1117            return $value;
1118        }
1119
1120        if (strstr($s, '_' . BNODE_PREFIX)) {
1121            if (($this->FixBnodes) || (!array_search($s,$this->bNodeMap))) {
1122                return new BlankNode($ins);
1123            } else {
1124                return new BlankNode(
1125                    trim(
1126                        substr(
1127                            array_search($s, $this->bNodeMap),
1128                            2
1129                        )
1130                    )
1131                );
1132            };
1133        }
1134
1135        return new Resource($ins);
1136    }//function toRDFNode($s, $state)
1137
1138
1139
1140
1141} //end: N3Parser
1142
1143?>
Note: See TracBrowser for help on using the repository browser.