3 homepage: http://arc.semsol.org/
4 license: http://arc.semsol.org/license
6 class: ARC2 SPARQLScript Parser (SPARQL+ + functions)
7 author: Benjamin Nowack
8 version: 2008-09-22 (Addition: support for FunctionCall)
11 ARC2::inc('ARC2_SPARQLPlusParser');
13 class ARC2_SPARQLScriptParser extends ARC2_SPARQLPlusParser {
15 function __construct($a = '', &$caller) {
16 parent::__construct($a, $caller);
19 function ARC2_SPARQLScriptParser($a = '', &$caller) {
20 $this->__construct($a, $caller);
29 function parse($v, $src = '') {
30 $this->setDefaultPrefixes();
31 $this->base = $src ? $this->calcBase($src) : ARC2::getScriptURI();
32 $this->blocks = array();
33 $this->r = array('base' => '', 'vars' => array(), 'prefixes' => $this->prefixes);
36 if ((list($r, $v) = $this->xScriptBlock($v)) && $r) {
40 $this->unparsed_code = trim($v);
42 if (trim($this->unparsed_code) && !$this->getErrors()) {
43 $rest = preg_replace('/[\x0a|\x0d]/i', ' ', substr($this->unparsed_code, 0, 30));
44 $msg = trim($rest) ? 'Could not properly handle "' . $rest . '"' : 'Syntax Error';
45 $this->addError($msg);
49 function getScriptBlocks() {
50 return $this->v('blocks', array());
55 function xScriptBlock($v) {
57 while (preg_match('/^\s*(\#[^\xd\xa]*)(.*)$/si', $v, $m)) $v = $m[2];
59 if ((list($sub_r, $v) = $this->xBaseDecl($v)) && $sub_r) {
63 while ((list($r, $v) = $this->xPrefixDecl($v)) && $r) {
64 $this->prefixes[$r['prefix']] = $r['uri'];
67 if ((list($r, $v) = $this->xEndpointDecl($v)) && $r) {
71 if ((list($r, $v) = $this->xReturn($v)) && $r) {
75 if ((list($r, $v) = $this->xAssignment($v)) && $r) {
79 if ((list($r, $v) = $this->xIFBlock($v)) && $r) {
83 if ((list($r, $v) = $this->xFORBlock($v)) && $r) {
87 if ((list($r, $v) = $this->xString($v)) && $r) {
91 if ((list($r, $v) = $this->xFunctionCall($v)) && $r) {
92 return array($r, ltrim($v, ';'));
96 $this->r = array('base' => '', 'vars' => array(), 'prefixes' => $this->prefixes);
97 if ((list($r, $rest) = $this->xQuery($v)) && $r) {
98 $q = $rest ? trim(substr($v, 0, -strlen($rest))) : trim($v);
100 $r = array_merge($this->r, array(
102 'query_type' => $r['type'],
104 //'prefixes' => $this->prefixes,
105 'base' => $this->base,
108 return array($r, $v);
116 function xBlockSet($v) {
117 if (!$r = $this->x("\{", $v)) return array(0, $v);
120 while ((list($sub_r, $sub_v) = $this->xScriptBlock($sub_v)) && $sub_r) {
123 if (!$sub_r = $this->x("\}", $sub_v)) return array(0, $v);
125 return array(array('type' => 'block_set', 'blocks' => $blocks), $sub_v);
130 function xEndpointDecl($v) {
131 if ($r = $this->x("ENDPOINT\s+", $v)) {
132 if ((list($r, $sub_v) = $this->xIRI_REF($r[1])) && $r) {
133 $r = $this->calcURI($r, $this->base);
134 if ($sub_r = $this->x('\.', $sub_v)) {
138 array('type' => 'endpoint_decl', 'endpoint' => $r),
148 function xAssignment($v) {
150 list($r, $sub_v) = $this->xVar($v);
151 if (!$r) return array(0, $v);
154 if (!$sub_r = $this->x("\:?\=", $sub_v)) return array(0, $v);
157 list($r, $sub_v) = $this->xString($sub_v);
158 if ($r) return array(array('type' => 'assignment', 'var' => $var, 'sub_type' => 'string', 'string' => $r), ltrim($sub_v, '; '));
160 list($r, $sub_v) = $this->xVarMerge($sub_v);
161 if ($r) return array(array('type' => 'assignment', 'var' => $var, 'sub_type' => 'var_merge', 'var2' => $r[0], 'var3' => $r[1]), ltrim($sub_v, '; '));
163 list($r, $sub_v) = $this->xVar($sub_v);
164 if ($r) return array(array('type' => 'assignment', 'var' => $var, 'sub_type' => 'var', 'var2' => $r), ltrim($sub_v, '; '));
166 list($r, $sub_v) = $this->xFunctionCall($sub_v);
167 if ($r) return array(array('type' => 'assignment', 'var' => $var, 'sub_type' => 'function_call', 'function_call' => $r), ltrim($sub_v, '; '));
168 /* try Placeholder */
169 list($r, $sub_v) = $this->xPlaceholder($sub_v);
170 if ($r) return array(array('type' => 'assignment', 'var' => $var, 'sub_type' => 'placeholder', 'placeholder' => $r), ltrim($sub_v, '; '));
173 $this->r = array('base' => '', 'vars' => array(), 'prefixes' => $this->prefixes);
174 list($r, $rest) = $this->xQuery($sub_v);
180 $q = $rest ? trim(substr($sub_v, 0, -strlen($rest))) : trim($sub_v);
183 'type' => 'assignment',
185 'sub_type' => 'query',
186 'query' => array_merge($this->r, array(
188 'query_type' => $r['type'],
190 'base' => $this->base,
198 function xReturn($v) {
199 if ($r = $this->x("return\s+", $v)) {
200 /* fake assignment which accepts same right-hand values */
201 $sub_v = '$__return_value__ := ' . $r[1];
202 if ((list($r, $sub_v) = $this->xAssignment($sub_v)) && $r) {
203 $r['type'] = 'return';
204 return array($r, $sub_v);
210 /* s4 'IF' BrackettedExpression '{' Script '}' ( 'ELSE' '{' Script '}')? */
212 function xIFBlock($v) {
213 if ($r = $this->x("IF\s*", $v)) {
214 if ((list($sub_r, $sub_v) = $this->xBrackettedExpression($r[1])) && $sub_r) {
216 if ((list($sub_r, $sub_v) = $this->xBlockSet($sub_v)) && $sub_r) {
217 $blocks = $sub_r['blocks'];
219 $else_blocks = array();
221 if ($sub_r = $this->x("ELSE\s*", $sub_v)) {
222 if ((list($sub_r, $sub_v) = $this->xBlockSet($sub_r[1])) && $sub_r) {
223 $else_blocks = $sub_r['blocks'];
232 'condition' => $cond,
234 'else_blocks' => $else_blocks,
244 /* s5 'FOR' '(' Var 'IN' Var ')' '{' Script '}' */
246 function xFORBlock($v) {
247 if ($r = $this->x("FOR\s*\(\s*[\$\?]([^\s]+)\s+IN\s+[\$\?]([^\s]+)\s*\)", $v)) {/* @@todo split into sub-patterns? */
251 if ((list($sub_r, $sub_v) = $this->xBlockSet($sub_v)) && $sub_r) {
254 'type' => 'forblock',
256 'iterator' => $iterator,
257 'blocks' => $sub_r['blocks']
268 function xVarMerge($v) {
269 if ((list($sub_r, $sub_v) = $this->xVar($v)) && $sub_r) {
271 if ($sub_r = $this->x("\+", $sub_v)) {
273 if ((list($sub_r, $sub_v) = $this->xVar($sub_v)) && $sub_r) {
275 array($var1, $sub_r),