1*a345322fSVincent.Tscherter<?php 2*a345322fSVincent.Tscherter/* 3*a345322fSVincent.Tscherter This program is free software: you can redistribute it and/or modify 4*a345322fSVincent.Tscherter it under the terms of the GNU General Public License as published by 5*a345322fSVincent.Tscherter the Free Software Foundation, either version 3 of the License, or 6*a345322fSVincent.Tscherter (at your option) any later version. 7*a345322fSVincent.Tscherter 8*a345322fSVincent.Tscherter This program is distributed in the hope that it will be useful, 9*a345322fSVincent.Tscherter but WITHOUT ANY WARRANTY; without even the implied warranty of 10*a345322fSVincent.Tscherter MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11*a345322fSVincent.Tscherter GNU General Public License for more details. 12*a345322fSVincent.Tscherter 13*a345322fSVincent.Tscherter You should have received a copy of the GNU General Public License 14*a345322fSVincent.Tscherter along with this program. If not, see <http://www.gnu.org/licenses/>. 15*a345322fSVincent.Tscherter 16*a345322fSVincent.Tscherter Vincent Tscherter, tscherter@karmin.ch, Solothurn, 2009-01-18 17*a345322fSVincent.Tscherter 18*a345322fSVincent.Tscherter 2009-01-18 version 0.1 first release 19*a345322fSVincent.Tscherter 2009-01-02 version 0.2 20*a345322fSVincent.Tscherter - title und comment literal added 21*a345322fSVincent.Tscherter - ";" als terminator-symbol added 22*a345322fSVincent.Tscherter*/ 23*a345322fSVincent.Tschertererror_reporting(E_ALL|E_STRICT); 24*a345322fSVincent.Tscherter 25*a345322fSVincent.Tscherter// 26*a345322fSVincent.Tscherterdefine('META', 'xis/ebnf v0.2 http://wiki.karmin.ch/ebnf/ gpl3'); 27*a345322fSVincent.Tscherter 28*a345322fSVincent.Tscherter// parser 29*a345322fSVincent.Tscherterdefine('EBNF_OPERATOR_TOKEN', 1); 30*a345322fSVincent.Tscherterdefine('EBNF_LITERAL_TOKEN', 2); 31*a345322fSVincent.Tscherterdefine('EBNF_WHITESPACE_TOKEN', 3); 32*a345322fSVincent.Tscherterdefine('EBNF_IDENTIFIER_TOKEN', 4); 33*a345322fSVincent.Tscherter 34*a345322fSVincent.Tscherter// rendering 35*a345322fSVincent.Tscherterdefine('FONT', 3); 36*a345322fSVincent.Tscherterdefine('UNIT', 10); 37*a345322fSVincent.Tscherterdefine('AW', 3); 38*a345322fSVincent.Tscherter 39*a345322fSVincent.Tscherter// lexemes 40*a345322fSVincent.Tscherter$ebnf_lexemes[] = array( 'type' => EBNF_OPERATOR_TOKEN, 'expr' => '[={}()|.;[\]]' ); 41*a345322fSVincent.Tscherter$ebnf_lexemes[] = array( 'type' => EBNF_LITERAL_TOKEN, 'expr' => "\"[^\"]*\"" ); 42*a345322fSVincent.Tscherter$ebnf_lexemes[] = array( 'type' => EBNF_LITERAL_TOKEN, 'expr' => "'[^']*'" ); 43*a345322fSVincent.Tscherter$ebnf_lexemes[] = array( 'type' => EBNF_IDENTIFIER_TOKEN, 'expr' => "[a-zA-Z0-9_-]+" ); 44*a345322fSVincent.Tscherter$ebnf_lexemes[] = array( 'type' => EBNF_WHITESPACE_TOKEN, 'expr' => "\\s+" ); 45*a345322fSVincent.Tscherter 46*a345322fSVincent.Tscherter// input example 47*a345322fSVincent.Tscherter$input = <<<EOD 48*a345322fSVincent.Tscherter"EBNF defined in itself" { 49*a345322fSVincent.Tscherter syntax = [ title ] "{" { rule } "}" [ comment ]. 50*a345322fSVincent.Tscherter rule = identifier "=" expression ( "." | ";" ) . 51*a345322fSVincent.Tscherter expression = term { "|" term } . 52*a345322fSVincent.Tscherter term = factor { factor } . 53*a345322fSVincent.Tscherter factor = identifier 54*a345322fSVincent.Tscherter | literal 55*a345322fSVincent.Tscherter | "[" expression "]" 56*a345322fSVincent.Tscherter | "(" expression ")" 57*a345322fSVincent.Tscherter | "{" expression "}" . 58*a345322fSVincent.Tscherter identifier = character { character } . 59*a345322fSVincent.Tscherter title = literal . 60*a345322fSVincent.Tscherter comment = literal . 61*a345322fSVincent.Tscherter literal = "'" character { character } "'" 62*a345322fSVincent.Tscherter | '"' character { character } '"' . 63*a345322fSVincent.Tscherter} 64*a345322fSVincent.TscherterEOD; 65*a345322fSVincent.Tscherter 66*a345322fSVincent.Tscherterif (isset($_GET['syntax'])) { 67*a345322fSVincent.Tscherter $input = $_GET['syntax']; 68*a345322fSVincent.Tscherter $input = stripslashes($input); 69*a345322fSVincent.Tscherter} 70*a345322fSVincent.Tscherter 71*a345322fSVincent.Tscherter$format = "png"; 72*a345322fSVincent.Tscherterif (isset($_GET['format'])) $format = $_GET['format']; 73*a345322fSVincent.Tscherter 74*a345322fSVincent.Tschertertry { 75*a345322fSVincent.Tscherter $tokens = ebnf_scan($input, true); 76*a345322fSVincent.Tscherter $dom = ebnf_parse_syntax($tokens); 77*a345322fSVincent.Tscherter if ($format == 'xml') { 78*a345322fSVincent.Tscherter header('Content-Type: application/xml'); 79*a345322fSVincent.Tscherter echo $dom->saveXML(); 80*a345322fSVincent.Tscherter } else { 81*a345322fSVincent.Tscherter render_node($dom->firstChild, true); 82*a345322fSVincent.Tscherter } 83*a345322fSVincent.Tscherter} catch (Exception $e) { 84*a345322fSVincent.Tscherter header('Content-Type: text/plain'); 85*a345322fSVincent.Tscherter $dom = new DOMDocument(); 86*a345322fSVincent.Tscherter $syntax = $dom->createElement("syntax"); 87*a345322fSVincent.Tscherter $syntax->setAttribute('title', 'EBNF - Syntax Error'); 88*a345322fSVincent.Tscherter $syntax->setAttribute('meta', $e->getMessage()); 89*a345322fSVincent.Tscherter $dom->appendChild($syntax); 90*a345322fSVincent.Tscherter render_node($dom->firstChild, true); 91*a345322fSVincent.Tscherter} 92*a345322fSVincent.Tscherter 93*a345322fSVincent.Tscherterfunction rr($im, $x1, $y1, $x2, $y2, $r, $black){ 94*a345322fSVincent.Tscherter imageline($im, $x1+$r, $y1, $x2-$r, $y1, $black); 95*a345322fSVincent.Tscherter imageline($im, $x1+$r, $y2, $x2-$r, $y2, $black); 96*a345322fSVincent.Tscherter imageline($im, $x1, $y1+$r, $x1, $y2-$r, $black); 97*a345322fSVincent.Tscherter imageline($im, $x2, $y1+$r, $x2, $y2-$r, $black); 98*a345322fSVincent.Tscherter imagearc($im, $x1+$r, $y1+$r, 2*$r, 2*$r, 180, 270, $black); 99*a345322fSVincent.Tscherter imagearc($im, $x2-$r, $y1+$r, 2*$r, 2*$r, 270, 360, $black); 100*a345322fSVincent.Tscherter imagearc($im, $x1+$r, $y2-$r, 2*$r, 2*$r, 90, 180, $black); 101*a345322fSVincent.Tscherter imagearc($im, $x2-$r, $y2-$r, 2*$r, 2*$r, 0, 90, $black); 102*a345322fSVincent.Tscherter} 103*a345322fSVincent.Tscherter 104*a345322fSVincent.Tscherterfunction create_image($w, $h) { 105*a345322fSVincent.Tscherter global $white, $black, $blue, $red, $green, $silver; 106*a345322fSVincent.Tscherter $im = imagecreatetruecolor($w, $h) or die("no img"); 107*a345322fSVincent.Tscherter imageantialias($im, true); 108*a345322fSVincent.Tscherter $white = imagecolorallocate ($im, 255, 255, 255); 109*a345322fSVincent.Tscherter $black = imagecolorallocate ($im, 0, 0, 0); 110*a345322fSVincent.Tscherter $blue = imagecolorallocate ($im, 0, 0, 255); 111*a345322fSVincent.Tscherter $red = imagecolorallocate ($im, 255, 0, 0); 112*a345322fSVincent.Tscherter $green = imagecolorallocate ($im, 0, 200, 0); 113*a345322fSVincent.Tscherter $silver = imagecolorallocate ($im, 127, 127, 127); 114*a345322fSVincent.Tscherter imagefilledrectangle($im, 0,0,$w,$h,$white); 115*a345322fSVincent.Tscherter return $im; 116*a345322fSVincent.Tscherter} 117*a345322fSVincent.Tscherter 118*a345322fSVincent.Tscherterfunction arrow($image, $x, $y, $lefttoright) { 119*a345322fSVincent.Tscherter global $white, $black; 120*a345322fSVincent.Tscherter if (!$lefttoright) 121*a345322fSVincent.Tscherter imagefilledpolygon($image, 122*a345322fSVincent.Tscherter array($x, $y-UNIT/3, $x-UNIT, $y, $x, $y+UNIT/3), 3, $black); 123*a345322fSVincent.Tscherter else 124*a345322fSVincent.Tscherter imagefilledpolygon($image, 125*a345322fSVincent.Tscherter array($x-UNIT, $y-UNIT/3, $x, $y, $x-UNIT, $y+UNIT/3), 3, $black); 126*a345322fSVincent.Tscherter} 127*a345322fSVincent.Tscherter 128*a345322fSVincent.Tscherter 129*a345322fSVincent.Tscherterfunction render_node($node, $lefttoright) { 130*a345322fSVincent.Tscherter global $white, $black, $blue, $red, $green, $silver; 131*a345322fSVincent.Tscherter if ($node->nodeName=='identifier' || $node->nodeName=='terminal') { 132*a345322fSVincent.Tscherter $text = $node->getAttribute('value'); 133*a345322fSVincent.Tscherter $w = imagefontwidth(FONT)*(strlen($text)) + 4*UNIT; 134*a345322fSVincent.Tscherter $h = 2*UNIT; 135*a345322fSVincent.Tscherter $im = create_image($w, $h); 136*a345322fSVincent.Tscherter 137*a345322fSVincent.Tscherter if ($node->nodeName!='terminal') { 138*a345322fSVincent.Tscherter imagerectangle($im, UNIT, 0, $w-UNIT-1, $h-1, $black); 139*a345322fSVincent.Tscherter imagestring($im, FONT, 2*UNIT, ($h-imagefontheight(FONT))/2, $text, $red); 140*a345322fSVincent.Tscherter } else { 141*a345322fSVincent.Tscherter if ($text!="...") 142*a345322fSVincent.Tscherter rr($im, UNIT, 0, $w-UNIT-1, $h-1, UNIT/2, $black); 143*a345322fSVincent.Tscherter imagestring($im, FONT, 2*UNIT, ($h-imagefontheight(FONT))/2, 144*a345322fSVincent.Tscherter $text, $text!="..."?$blue:$black); 145*a345322fSVincent.Tscherter } 146*a345322fSVincent.Tscherter imageline($im,0,UNIT, UNIT, UNIT, $black); 147*a345322fSVincent.Tscherter imageline($im,$w-UNIT,UNIT, $w+1, UNIT, $black); 148*a345322fSVincent.Tscherter return $im; 149*a345322fSVincent.Tscherter } else if ($node->nodeName=='option' || $node->nodeName=='loop') { 150*a345322fSVincent.Tscherter if ($node->nodeName=='loop') 151*a345322fSVincent.Tscherter $lefttoright = ! $lefttoright; 152*a345322fSVincent.Tscherter $inner = render_node($node->firstChild, $lefttoright); 153*a345322fSVincent.Tscherter $w = imagesx($inner)+6*UNIT; 154*a345322fSVincent.Tscherter $h = imagesy($inner)+2*UNIT; 155*a345322fSVincent.Tscherter $im = create_image($w, $h); 156*a345322fSVincent.Tscherter imagecopy($im, $inner, 3*UNIT, 2*UNIT, 0,0, imagesx($inner), imagesy($inner)); 157*a345322fSVincent.Tscherter imageline($im,0,UNIT, $w, UNIT, $black); 158*a345322fSVincent.Tscherter arrow($im, $w/2+UNIT/2, UNIT, $node->nodeName=='loop'?!$lefttoright:$lefttoright); 159*a345322fSVincent.Tscherter arrow($im, 3*UNIT, 3*UNIT, $lefttoright); 160*a345322fSVincent.Tscherter arrow($im, $w-2*UNIT, 3*UNIT, $lefttoright); 161*a345322fSVincent.Tscherter imageline($im,UNIT,UNIT, UNIT, 3*UNIT, $black); 162*a345322fSVincent.Tscherter imageline($im,UNIT,3*UNIT, 2*UNIT, 3*UNIT, $black); 163*a345322fSVincent.Tscherter imageline($im,$w-UNIT,UNIT, $w-UNIT, 3*UNIT, $black); 164*a345322fSVincent.Tscherter imageline($im,$w-3*UNIT-1,3*UNIT, $w-UNIT, 3*UNIT, $black); 165*a345322fSVincent.Tscherter return $im; 166*a345322fSVincent.Tscherter } else if ($node->nodeName=='sequence') { 167*a345322fSVincent.Tscherter $inner = render_childs($node, $lefttoright); 168*a345322fSVincent.Tscherter if (!$lefttoright) 169*a345322fSVincent.Tscherter $inner = array_reverse($inner); 170*a345322fSVincent.Tscherter $w = count($inner)*UNIT-UNIT; $h = 0; 171*a345322fSVincent.Tscherter for ($i = 0; $i<count($inner); $i++) { 172*a345322fSVincent.Tscherter $w += imagesx($inner[$i]); 173*a345322fSVincent.Tscherter $h = max($h, imagesy($inner[$i])); 174*a345322fSVincent.Tscherter } $im = create_image($w, $h); 175*a345322fSVincent.Tscherter imagecopy($im, $inner[0], 0, 0, 0,0, imagesx($inner[0]), imagesy($inner[0])); 176*a345322fSVincent.Tscherter $x = imagesx($inner[0])+UNIT; 177*a345322fSVincent.Tscherter for ($i = 1; $i<count($inner); $i++) { 178*a345322fSVincent.Tscherter imageline($im, $x-UNIT-1, UNIT, $x, UNIT, $black); 179*a345322fSVincent.Tscherter arrow($im, $x, UNIT, $lefttoright); 180*a345322fSVincent.Tscherter imagecopy($im, $inner[$i], $x, 0, 0,0, imagesx($inner[$i]), imagesy($inner[$i])); 181*a345322fSVincent.Tscherter $x += imagesx($inner[$i])+UNIT; 182*a345322fSVincent.Tscherter } return $im; 183*a345322fSVincent.Tscherter } else if ($node->nodeName=='choise') { 184*a345322fSVincent.Tscherter $inner = render_childs($node, $lefttoright); 185*a345322fSVincent.Tscherter $h = (count($inner)-1)*UNIT; $w = 0; 186*a345322fSVincent.Tscherter for ($i = 0; $i<count($inner); $i++) { 187*a345322fSVincent.Tscherter $h += imagesy($inner[$i]); 188*a345322fSVincent.Tscherter $w = max($w, imagesx($inner[$i])); 189*a345322fSVincent.Tscherter } $w += 6*UNIT; $im = create_image($w, $h); $y = 0; 190*a345322fSVincent.Tscherter imageline($im, 0, UNIT, UNIT, UNIT, $black); 191*a345322fSVincent.Tscherter imageline($im, $w-UNIT, UNIT, $w, UNIT, $black); 192*a345322fSVincent.Tscherter for ($i = 0; $i<count($inner); $i++) { 193*a345322fSVincent.Tscherter imageline($im, UNIT, $y+UNIT, $w-UNIT, $y+UNIT, $black); 194*a345322fSVincent.Tscherter imagecopy($im, $inner[$i], 3*UNIT, $y, 0,0, imagesx($inner[$i]), imagesy($inner[$i])); 195*a345322fSVincent.Tscherter arrow($im, 3*UNIT, $y+UNIT, $lefttoright); 196*a345322fSVincent.Tscherter arrow($im, $w-2*UNIT, $y+UNIT, $lefttoright); 197*a345322fSVincent.Tscherter $top = $y + UNIT; 198*a345322fSVincent.Tscherter $y += imagesy($inner[$i])+UNIT; 199*a345322fSVincent.Tscherter } 200*a345322fSVincent.Tscherter imageline($im, UNIT, UNIT, UNIT, $top, $black); 201*a345322fSVincent.Tscherter imageline($im, $w-UNIT, UNIT, $w-UNIT, $top, $black); 202*a345322fSVincent.Tscherter return $im; 203*a345322fSVincent.Tscherter } else if ($node->nodeName=='syntax') { 204*a345322fSVincent.Tscherter $title = $node->getAttribute('title'); 205*a345322fSVincent.Tscherter $meta = $node->getAttribute('meta'); 206*a345322fSVincent.Tscherter $node = $node->firstChild; 207*a345322fSVincent.Tscherter $names = array(); 208*a345322fSVincent.Tscherter $images = array(); 209*a345322fSVincent.Tscherter while ($node!=null) { 210*a345322fSVincent.Tscherter $names[] = $node->getAttribute('name'); 211*a345322fSVincent.Tscherter $im = render_node($node->firstChild, $lefttoright); 212*a345322fSVincent.Tscherter $images[] = $im; 213*a345322fSVincent.Tscherter $node = $node->nextSibling; 214*a345322fSVincent.Tscherter } $wn = 0; $wr = 0; $h = 5*UNIT; 215*a345322fSVincent.Tscherter for ($i = 0; $i<count($images); $i++) { 216*a345322fSVincent.Tscherter $wn = max($wn, imagefontwidth(FONT)*strlen($names[$i])); 217*a345322fSVincent.Tscherter $wr = max($wr, imagesx($images[$i])); 218*a345322fSVincent.Tscherter $h += imagesy($images[$i])+2*UNIT; 219*a345322fSVincent.Tscherter } 220*a345322fSVincent.Tscherter if ($title=='') $h -= 2*UNIT; 221*a345322fSVincent.Tscherter if ($meta=='') $h -= 2*UNIT; 222*a345322fSVincent.Tscherter $w = max($wr+$wn+3*UNIT, imagefontwidth(1)*strlen($meta)+2*UNIT); 223*a345322fSVincent.Tscherter $im = create_image($w, $h); 224*a345322fSVincent.Tscherter $y = 2*UNIT; 225*a345322fSVincent.Tscherter if ($title!='') { 226*a345322fSVincent.Tscherter imagestring($im, FONT, UNIT, (2*UNIT-imagefontheight(FONT))/2, 227*a345322fSVincent.Tscherter $title, $green); 228*a345322fSVincent.Tscherter imageline($im, 0, 2*UNIT, $w, 2*UNIT, $green); 229*a345322fSVincent.Tscherter $y += 2*UNIT; 230*a345322fSVincent.Tscherter } 231*a345322fSVincent.Tscherter for ($i = 0; $i<count($images); $i++) { 232*a345322fSVincent.Tscherter imagestring($im, FONT, UNIT, $y-UNIT+(2*UNIT-imagefontheight(FONT))/2, $names[$i], $red); 233*a345322fSVincent.Tscherter imagecopy($im, $images[$i], $wn+2*UNIT, $y, 0,0, imagesx($images[$i]) , imagesy($images[$i])); 234*a345322fSVincent.Tscherter imageline($im, UNIT, $y+UNIT, $wn+2*UNIT, $y+UNIT, $black); 235*a345322fSVincent.Tscherter imageline($im, $wn+2*UNIT+imagesx($images[$i])-1, $y+UNIT, $w-UNIT, $y+UNIT, $black); 236*a345322fSVincent.Tscherter imageline($im, $w-UNIT, $y+UNIT/2, $w-UNIT ,$y+1.5*UNIT, $black); 237*a345322fSVincent.Tscherter $y += 2*UNIT + imagesy($images[$i]); 238*a345322fSVincent.Tscherter } 239*a345322fSVincent.Tscherter imagestring($im, 1, UNIT, $h-2*UNIT+(2*UNIT-imagefontheight(1))/2, 240*a345322fSVincent.Tscherter $meta, $silver); 241*a345322fSVincent.Tscherter rr($im, 0,0,$w-1, $h-1, UNIT/2, $green); 242*a345322fSVincent.Tscherter header('Content-Type: image/png'); 243*a345322fSVincent.Tscherter imagepng($im); 244*a345322fSVincent.Tscherter return $im; 245*a345322fSVincent.Tscherter } 246*a345322fSVincent.Tscherter} 247*a345322fSVincent.Tscherter 248*a345322fSVincent.Tscherterfunction render_childs($node, $lefttoright) { 249*a345322fSVincent.Tscherter $childs = array(); 250*a345322fSVincent.Tscherter $node = $node->firstChild; 251*a345322fSVincent.Tscherter while ($node!=null) { 252*a345322fSVincent.Tscherter $childs[] = render_node($node, $lefttoright); 253*a345322fSVincent.Tscherter $node = $node->nextSibling; 254*a345322fSVincent.Tscherter } return $childs; 255*a345322fSVincent.Tscherter} 256*a345322fSVincent.Tscherter 257*a345322fSVincent.Tscherterfunction ebnf_scan(&$input) { 258*a345322fSVincent.Tscherter global $ebnf_lexemes; 259*a345322fSVincent.Tscherter $i = 0; $n = strlen($input); $m = count($ebnf_lexemes); $tokens = array(); 260*a345322fSVincent.Tscherter while ($i < $n) { 261*a345322fSVincent.Tscherter $j = 0; 262*a345322fSVincent.Tscherter while ($j < $m && 263*a345322fSVincent.Tscherter preg_match("/^{$ebnf_lexemes[$j]['expr']}/", substr($input,$i), $matches)==0) $j++; 264*a345322fSVincent.Tscherter if ($j<$m) { 265*a345322fSVincent.Tscherter if ($ebnf_lexemes[$j]['type']!=EBNF_WHITESPACE_TOKEN) 266*a345322fSVincent.Tscherter $tokens[] = array('type' => $ebnf_lexemes[$j]['type'], 267*a345322fSVincent.Tscherter 'value' => $matches[0], 'pos' => $i); 268*a345322fSVincent.Tscherter $i += strlen($matches[0]); 269*a345322fSVincent.Tscherter } else 270*a345322fSVincent.Tscherter throw new Exception("Invalid token at position: $i"); 271*a345322fSVincent.Tscherter } return $tokens; 272*a345322fSVincent.Tscherter} 273*a345322fSVincent.Tscherter 274*a345322fSVincent.Tscherter 275*a345322fSVincent.Tscherterfunction ebnf_check_token($token, $type, $value) { 276*a345322fSVincent.Tscherter return $token['type']==$type && $token['value']==$value; 277*a345322fSVincent.Tscherter} 278*a345322fSVincent.Tscherter 279*a345322fSVincent.Tscherterfunction ebnf_parse_syntax(&$tokens) { 280*a345322fSVincent.Tscherter $dom = new DOMDocument(); 281*a345322fSVincent.Tscherter $syntax = $dom->createElement("syntax"); 282*a345322fSVincent.Tscherter $syntax->setAttribute('meta', META); 283*a345322fSVincent.Tscherter $dom->appendChild($syntax); 284*a345322fSVincent.Tscherter $i = 0; $token = $tokens[$i++]; 285*a345322fSVincent.Tscherter if ($token['type'] == EBNF_LITERAL_TOKEN) { 286*a345322fSVincent.Tscherter $syntax->setAttribute('title', 287*a345322fSVincent.Tscherter stripcslashes(substr($token['value'], 1, strlen($token['value'])-2 ))); 288*a345322fSVincent.Tscherter $token = $tokens[$i++]; 289*a345322fSVincent.Tscherter } 290*a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '{') ) 291*a345322fSVincent.Tscherter throw new Exception("Syntax must start with '{': {$token['pos']}"); 292*a345322fSVincent.Tscherter $token = $tokens[$i]; 293*a345322fSVincent.Tscherter while ($i < count($tokens) && $token['type'] == EBNF_IDENTIFIER_TOKEN) { 294*a345322fSVincent.Tscherter $syntax->appendChild(ebnf_parse_production($dom, $tokens, $i)); 295*a345322fSVincent.Tscherter if ($i<count($tokens)) $token = $tokens[$i]; 296*a345322fSVincent.Tscherter } $i++; if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '}')) 297*a345322fSVincent.Tscherter throw new Exception("Syntax must end with '}': ".$tokens[count($tokens)-1]['pos']); 298*a345322fSVincent.Tscherter if ($i<count($tokens)) { 299*a345322fSVincent.Tscherter $token = $tokens[$i]; 300*a345322fSVincent.Tscherter if ($token['type'] == EBNF_LITERAL_TOKEN) { 301*a345322fSVincent.Tscherter $syntax->setAttribute('meta', 302*a345322fSVincent.Tscherter stripcslashes(substr($token['value'], 1, strlen($token['value'])-2 ))); 303*a345322fSVincent.Tscherter } 304*a345322fSVincent.Tscherter } 305*a345322fSVincent.Tscherter return $dom; 306*a345322fSVincent.Tscherter} 307*a345322fSVincent.Tscherter 308*a345322fSVincent.Tscherterfunction ebnf_parse_production(&$dom, &$tokens, &$i) { 309*a345322fSVincent.Tscherter $token = $tokens[$i++]; 310*a345322fSVincent.Tscherter if ($token['type']!=EBNF_IDENTIFIER_TOKEN) 311*a345322fSVincent.Tscherter throw new Exception("Production must start with an identifier'{': {$token['pos']}"); 312*a345322fSVincent.Tscherter $production = $dom->createElement("rule"); 313*a345322fSVincent.Tscherter $production->setAttribute('name', $token['value']); 314*a345322fSVincent.Tscherter $token = $tokens[$i++]; 315*a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, "=")) 316*a345322fSVincent.Tscherter throw new Exception("Identifier must be followed by '=': {$token['pos']}"); 317*a345322fSVincent.Tscherter $production->appendChild( ebnf_parse_expression($dom, $tokens, $i)); 318*a345322fSVincent.Tscherter $token = $tokens[$i++]; 319*a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '.') 320*a345322fSVincent.Tscherter && !ebnf_check_token($token, EBNF_OPERATOR_TOKEN, ';')) 321*a345322fSVincent.Tscherter throw new Exception("Rule must end with '.' or ';' : {$token['pos']}"); 322*a345322fSVincent.Tscherter return $production; 323*a345322fSVincent.Tscherter} 324*a345322fSVincent.Tscherter 325*a345322fSVincent.Tscherterfunction ebnf_parse_expression(&$dom, &$tokens, &$i) { 326*a345322fSVincent.Tscherter $choise = $dom->createElement("choise"); 327*a345322fSVincent.Tscherter $choise->appendChild(ebnf_parse_term($dom, $tokens, $i)); 328*a345322fSVincent.Tscherter $token=$tokens[$i]; $mul = false; 329*a345322fSVincent.Tscherter while (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '|')) { 330*a345322fSVincent.Tscherter $i++; 331*a345322fSVincent.Tscherter $choise->appendChild(ebnf_parse_term($dom, $tokens, $i)); 332*a345322fSVincent.Tscherter $token=$tokens[$i]; $mul = true; 333*a345322fSVincent.Tscherter } return $mul ? $choise : $choise->removeChild($choise->firstChild); 334*a345322fSVincent.Tscherter} 335*a345322fSVincent.Tscherter 336*a345322fSVincent.Tscherterfunction ebnf_parse_term(&$dom, &$tokens, &$i) { 337*a345322fSVincent.Tscherter $sequence = $dom->createElement("sequence"); 338*a345322fSVincent.Tscherter $factor = ebnf_parse_factor($dom, $tokens, $i); 339*a345322fSVincent.Tscherter $sequence->appendChild($factor); 340*a345322fSVincent.Tscherter $token=$tokens[$i]; $mul = false; 341*a345322fSVincent.Tscherter while ($token['value']!='.' && $token['value']!='=' && $token['value']!='|' 342*a345322fSVincent.Tscherter && $token['value']!=')' && $token['value']!=']' && $token['value']!='}') { 343*a345322fSVincent.Tscherter $sequence->appendChild(ebnf_parse_factor($dom, $tokens, $i)); 344*a345322fSVincent.Tscherter $token=$tokens[$i]; $mul = true; 345*a345322fSVincent.Tscherter } return $mul ? $sequence: $sequence->removeChild($sequence->firstChild); 346*a345322fSVincent.Tscherter} 347*a345322fSVincent.Tscherter 348*a345322fSVincent.Tscherterfunction ebnf_parse_factor(&$dom, &$tokens, &$i) { 349*a345322fSVincent.Tscherter $token = $tokens[$i++]; 350*a345322fSVincent.Tscherter if ($token['type']==EBNF_IDENTIFIER_TOKEN) { 351*a345322fSVincent.Tscherter $identifier = $dom->createElement("identifier"); 352*a345322fSVincent.Tscherter $identifier->setAttribute('value', $token['value']); 353*a345322fSVincent.Tscherter return $identifier; 354*a345322fSVincent.Tscherter } if ($token['type']==EBNF_LITERAL_TOKEN){ 355*a345322fSVincent.Tscherter $literal = $dom->createElement("terminal"); 356*a345322fSVincent.Tscherter $literal->setAttribute('value', stripcslashes(substr($token['value'], 1, strlen($token['value'])-2 ))); 357*a345322fSVincent.Tscherter return $literal; 358*a345322fSVincent.Tscherter } if (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '(')) { 359*a345322fSVincent.Tscherter $expression = ebnf_parse_expression($dom, $tokens, $i); 360*a345322fSVincent.Tscherter $token = $tokens[$i++]; 361*a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, ')')) 362*a345322fSVincent.Tscherter throw new Exception("Group must end with ')': {$token['pos']}"); 363*a345322fSVincent.Tscherter return $expression; 364*a345322fSVincent.Tscherter } if (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '[')) { 365*a345322fSVincent.Tscherter $option = $dom->createElement("option"); 366*a345322fSVincent.Tscherter $option->appendChild(ebnf_parse_expression($dom, $tokens, $i)); 367*a345322fSVincent.Tscherter $token = $tokens[$i++]; 368*a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, ']')) 369*a345322fSVincent.Tscherter throw new Exception("Option must end with ']': {$token['pos']}"); 370*a345322fSVincent.Tscherter return $option; 371*a345322fSVincent.Tscherter } if (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '{')) { 372*a345322fSVincent.Tscherter $loop = $dom->createElement("loop"); 373*a345322fSVincent.Tscherter $loop->appendChild(ebnf_parse_expression($dom, $tokens, $i)); 374*a345322fSVincent.Tscherter $token = $tokens[$i++]; 375*a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '}')) 376*a345322fSVincent.Tscherter throw new Exception("Loop must end with '}': {$token['pos']}"); 377*a345322fSVincent.Tscherter return $loop; 378*a345322fSVincent.Tscherter } 379*a345322fSVincent.Tscherter throw new Exception("Factor expected: {$token['pos']}"); 380*a345322fSVincent.Tscherter} 381*a345322fSVincent.Tscherter 382*a345322fSVincent.Tscherter?>