1a345322fSVincent.Tscherter<?php 2a345322fSVincent.Tscherter/* 3a345322fSVincent.Tscherter This program is free software: you can redistribute it and/or modify 4a345322fSVincent.Tscherter it under the terms of the GNU General Public License as published by 5a345322fSVincent.Tscherter the Free Software Foundation, either version 3 of the License, or 6a345322fSVincent.Tscherter (at your option) any later version. 7a345322fSVincent.Tscherter 8a345322fSVincent.Tscherter This program is distributed in the hope that it will be useful, 9a345322fSVincent.Tscherter but WITHOUT ANY WARRANTY; without even the implied warranty of 10a345322fSVincent.Tscherter MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11a345322fSVincent.Tscherter GNU General Public License for more details. 12a345322fSVincent.Tscherter 13a345322fSVincent.Tscherter You should have received a copy of the GNU General Public License 14a345322fSVincent.Tscherter along with this program. If not, see <http://www.gnu.org/licenses/>. 15a345322fSVincent.Tscherter 16*3dfddad1SVincent Tscherter Vincent Tscherter, tscherter@tscherter.net, Solothurn, 2009-01-18 17a345322fSVincent.Tscherter 18a345322fSVincent.Tscherter 2009-01-18 version 0.1 first release 19a345322fSVincent.Tscherter 2009-01-02 version 0.2 20a345322fSVincent.Tscherter - title und comment literal added 21a345322fSVincent.Tscherter - ";" als terminator-symbol added 223d5b504aSVincent Tscherter 2023-09-28 version 0.3 prefixed all constants 23*3dfddad1SVincent Tscherter 2025-09-05 version 0.4 PHP 8 compatibility and other fixes 24a345322fSVincent.Tscherter*/ 25a345322fSVincent.Tscherter 2677a47d83SVincent Tscherterdefine('META', 'https://www.dokuwiki.org/plugin:ebnf'); 27a345322fSVincent.Tscherter 28a345322fSVincent.Tscherter// parser 29a345322fSVincent.Tscherterdefine('EBNF_OPERATOR_TOKEN', 1); 30a345322fSVincent.Tscherterdefine('EBNF_LITERAL_TOKEN', 2); 31a345322fSVincent.Tscherterdefine('EBNF_WHITESPACE_TOKEN', 3); 32a345322fSVincent.Tscherterdefine('EBNF_IDENTIFIER_TOKEN', 4); 33a345322fSVincent.Tscherter 34a345322fSVincent.Tscherter// rendering 3577a47d83SVincent Tscherterdefine('EBNF_FONT', 2); 3677a47d83SVincent Tscherterdefine('EBNF_U', 10); 3777a47d83SVincent Tscherterdefine('EBNF_AW', 3); 38a345322fSVincent.Tscherter 39a345322fSVincent.Tscherter// lexemes 40a345322fSVincent.Tscherter$ebnf_lexemes[] = array( 'type' => EBNF_OPERATOR_TOKEN, 'expr' => '[={}()|.;[\]]' ); 41a345322fSVincent.Tscherter$ebnf_lexemes[] = array( 'type' => EBNF_LITERAL_TOKEN, 'expr' => "\"[^\"]*\"" ); 42a345322fSVincent.Tscherter$ebnf_lexemes[] = array( 'type' => EBNF_LITERAL_TOKEN, 'expr' => "'[^']*'" ); 43a345322fSVincent.Tscherter$ebnf_lexemes[] = array( 'type' => EBNF_IDENTIFIER_TOKEN, 'expr' => "[a-zA-Z0-9_-]+" ); 44a345322fSVincent.Tscherter$ebnf_lexemes[] = array( 'type' => EBNF_WHITESPACE_TOKEN, 'expr' => "\\s+" ); 45a345322fSVincent.Tscherter 46a345322fSVincent.Tscherter// input example 47a345322fSVincent.Tscherter$input = <<<EOD 48a345322fSVincent.Tscherter"EBNF defined in itself" { 49a345322fSVincent.Tscherter syntax = [ title ] "{" { rule } "}" [ comment ]. 50a345322fSVincent.Tscherter rule = identifier "=" expression ( "." | ";" ) . 51a345322fSVincent.Tscherter expression = term { "|" term } . 52a345322fSVincent.Tscherter term = factor { factor } . 53a345322fSVincent.Tscherter factor = identifier 54a345322fSVincent.Tscherter | literal 55a345322fSVincent.Tscherter | "[" expression "]" 56a345322fSVincent.Tscherter | "(" expression ")" 57a345322fSVincent.Tscherter | "{" expression "}" . 58a345322fSVincent.Tscherter identifier = character { character } . 59a345322fSVincent.Tscherter title = literal . 60a345322fSVincent.Tscherter comment = literal . 61a345322fSVincent.Tscherter literal = "'" character { character } "'" 62a345322fSVincent.Tscherter | '"' character { character } '"' . 63a345322fSVincent.Tscherter} 64a345322fSVincent.TscherterEOD; 65a345322fSVincent.Tscherter 66a345322fSVincent.Tscherterif (isset($_GET['syntax'])) { 67a345322fSVincent.Tscherter $input = $_GET['syntax']; 68a345322fSVincent.Tscherter $input = stripslashes($input); 69a345322fSVincent.Tscherter} 70a345322fSVincent.Tscherter 71a345322fSVincent.Tscherter$format = "png"; 72a345322fSVincent.Tscherterif (isset($_GET['format'])) $format = $_GET['format']; 73a345322fSVincent.Tscherter 74a345322fSVincent.Tschertertry { 75a345322fSVincent.Tscherter $tokens = ebnf_scan($input, true); 76a345322fSVincent.Tscherter $dom = ebnf_parse_syntax($tokens); 77a345322fSVincent.Tscherter if ($format == 'xml') { 78a345322fSVincent.Tscherter header('Content-Type: application/xml'); 79a345322fSVincent.Tscherter echo $dom->saveXML(); 80a345322fSVincent.Tscherter } else { 81a345322fSVincent.Tscherter render_node($dom->firstChild, true); 82a345322fSVincent.Tscherter } 835a779085SDamien Regad} catch (EbnfException $e) { 84a345322fSVincent.Tscherter header('Content-Type: text/plain'); 85a345322fSVincent.Tscherter $dom = new DOMDocument(); 86a345322fSVincent.Tscherter $syntax = $dom->createElement("syntax"); 87a345322fSVincent.Tscherter $syntax->setAttribute('title', 'EBNF - Syntax Error'); 885a779085SDamien Regad $syntax->setAttribute('meta', 895a779085SDamien Regad $e->getMessage() 905a779085SDamien Regad . " - '" . substr($input, $e->getPos(), 30) . "...'" 915a779085SDamien Regad ); 92a345322fSVincent.Tscherter $dom->appendChild($syntax); 93a345322fSVincent.Tscherter render_node($dom->firstChild, true); 94a345322fSVincent.Tscherter} 95a345322fSVincent.Tscherter 96a345322fSVincent.Tscherterfunction rr($im, $x1, $y1, $x2, $y2, $r, $black){ 97a345322fSVincent.Tscherter imageline($im, $x1+$r, $y1, $x2-$r, $y1, $black); 98a345322fSVincent.Tscherter imageline($im, $x1+$r, $y2, $x2-$r, $y2, $black); 99a345322fSVincent.Tscherter imageline($im, $x1, $y1+$r, $x1, $y2-$r, $black); 100a345322fSVincent.Tscherter imageline($im, $x2, $y1+$r, $x2, $y2-$r, $black); 101a345322fSVincent.Tscherter imagearc($im, $x1+$r, $y1+$r, 2*$r, 2*$r, 180, 270, $black); 102a345322fSVincent.Tscherter imagearc($im, $x2-$r, $y1+$r, 2*$r, 2*$r, 270, 360, $black); 103a345322fSVincent.Tscherter imagearc($im, $x1+$r, $y2-$r, 2*$r, 2*$r, 90, 180, $black); 104a345322fSVincent.Tscherter imagearc($im, $x2-$r, $y2-$r, 2*$r, 2*$r, 0, 90, $black); 105a345322fSVincent.Tscherter} 106a345322fSVincent.Tscherter 107a345322fSVincent.Tscherterfunction create_image($w, $h) { 108a345322fSVincent.Tscherter global $white, $black, $blue, $red, $green, $silver; 109a345322fSVincent.Tscherter $im = imagecreatetruecolor($w, $h) or die("no img"); 110a345322fSVincent.Tscherter imageantialias($im, true); 111a345322fSVincent.Tscherter $white = imagecolorallocate ($im, 255, 255, 255); 112a345322fSVincent.Tscherter $black = imagecolorallocate ($im, 0, 0, 0); 113a345322fSVincent.Tscherter $blue = imagecolorallocate ($im, 0, 0, 255); 114a345322fSVincent.Tscherter $red = imagecolorallocate ($im, 255, 0, 0); 115a345322fSVincent.Tscherter $green = imagecolorallocate ($im, 0, 200, 0); 116a345322fSVincent.Tscherter $silver = imagecolorallocate ($im, 127, 127, 127); 117a345322fSVincent.Tscherter imagefilledrectangle($im, 0,0,$w,$h,$white); 118a345322fSVincent.Tscherter return $im; 119a345322fSVincent.Tscherter} 120a345322fSVincent.Tscherter 121a345322fSVincent.Tscherterfunction arrow($image, $x, $y, $lefttoright) { 122a345322fSVincent.Tscherter global $white, $black; 12370e79227SDamien Regad if (!$lefttoright) { 12470e79227SDamien Regad $points = array($x, $y - EBNF_U / 3, $x - EBNF_U, $y, $x, $y + EBNF_U / 3); 12570e79227SDamien Regad } else { 12670e79227SDamien Regad $points = array($x - EBNF_U, $y - EBNF_U / 3, $x, $y, $x - EBNF_U, $y + EBNF_U / 3); 12770e79227SDamien Regad } 12870e79227SDamien Regad if (PHP_VERSION_ID >= 80000 ) { 12970e79227SDamien Regad imagefilledpolygon($image, $points, $black); 13070e79227SDamien Regad } else { 13170e79227SDamien Regad imagefilledpolygon($image, $points, 3, $black); 13270e79227SDamien Regad } 133a345322fSVincent.Tscherter} 134a345322fSVincent.Tscherter 135a345322fSVincent.Tscherter 136a345322fSVincent.Tscherterfunction render_node($node, $lefttoright) { 137a345322fSVincent.Tscherter global $white, $black, $blue, $red, $green, $silver; 138a345322fSVincent.Tscherter if ($node->nodeName=='identifier' || $node->nodeName=='terminal') { 139b099bbf8SRalf Müller $text = html_entity_decode($node->getAttribute('value')); 14077a47d83SVincent Tscherter $w = imagefontwidth(EBNF_FONT)*(strlen($text)) + 4*EBNF_U; 14177a47d83SVincent Tscherter $h = 2*EBNF_U; 142a345322fSVincent.Tscherter $im = create_image($w, $h); 143a345322fSVincent.Tscherter 144a345322fSVincent.Tscherter if ($node->nodeName!='terminal') { 14577a47d83SVincent Tscherter imagerectangle($im, EBNF_U, 0, $w-EBNF_U-1, $h-1, $black); 14608455648SRalf Müller imagestring($im, EBNF_FONT, intval(2*EBNF_U), intval(($h-imagefontheight(EBNF_FONT))/2), $text, $red); 147a345322fSVincent.Tscherter } else { 148a345322fSVincent.Tscherter if ($text!="...") 14977a47d83SVincent Tscherter rr($im, EBNF_U, 0, $w-EBNF_U-1, $h-1, EBNF_U/2, $black); 15008455648SRalf Müller imagestring($im, EBNF_FONT, intval(2*EBNF_U), intval(($h-imagefontheight(EBNF_FONT))/2), 151a345322fSVincent.Tscherter $text, $text!="..."?$blue:$black); 152a345322fSVincent.Tscherter } 15377a47d83SVincent Tscherter imageline($im,0,EBNF_U, EBNF_U, EBNF_U, $black); 15477a47d83SVincent Tscherter imageline($im,$w-EBNF_U,EBNF_U, $w+1, EBNF_U, $black); 155a345322fSVincent.Tscherter return $im; 156a345322fSVincent.Tscherter } else if ($node->nodeName=='option' || $node->nodeName=='loop') { 157a345322fSVincent.Tscherter if ($node->nodeName=='loop') 158a345322fSVincent.Tscherter $lefttoright = ! $lefttoright; 159a345322fSVincent.Tscherter $inner = render_node($node->firstChild, $lefttoright); 16077a47d83SVincent Tscherter $w = imagesx($inner)+6*EBNF_U; 16177a47d83SVincent Tscherter $h = imagesy($inner)+2*EBNF_U; 162a345322fSVincent.Tscherter $im = create_image($w, $h); 16377a47d83SVincent Tscherter imagecopy($im, $inner, 3*EBNF_U, 2*EBNF_U, 0,0, imagesx($inner), imagesy($inner)); 16477a47d83SVincent Tscherter imageline($im,0,EBNF_U, $w, EBNF_U, $black); 16577a47d83SVincent Tscherter arrow($im, $w/2+EBNF_U/2, EBNF_U, $node->nodeName=='loop'?!$lefttoright:$lefttoright); 16677a47d83SVincent Tscherter arrow($im, 3*EBNF_U, 3*EBNF_U, $lefttoright); 16777a47d83SVincent Tscherter arrow($im, $w-2*EBNF_U, 3*EBNF_U, $lefttoright); 16877a47d83SVincent Tscherter imageline($im,EBNF_U,EBNF_U, EBNF_U, 3*EBNF_U, $black); 16977a47d83SVincent Tscherter imageline($im,EBNF_U,3*EBNF_U, 2*EBNF_U, 3*EBNF_U, $black); 17077a47d83SVincent Tscherter imageline($im,$w-EBNF_U,EBNF_U, $w-EBNF_U, 3*EBNF_U, $black); 17177a47d83SVincent Tscherter imageline($im,$w-3*EBNF_U-1,3*EBNF_U, $w-EBNF_U, 3*EBNF_U, $black); 172a345322fSVincent.Tscherter return $im; 173a345322fSVincent.Tscherter } else if ($node->nodeName=='sequence') { 174a345322fSVincent.Tscherter $inner = render_childs($node, $lefttoright); 175a345322fSVincent.Tscherter if (!$lefttoright) 176a345322fSVincent.Tscherter $inner = array_reverse($inner); 17777a47d83SVincent Tscherter $w = count($inner)*EBNF_U-EBNF_U; $h = 0; 178a345322fSVincent.Tscherter for ($i = 0; $i<count($inner); $i++) { 179a345322fSVincent.Tscherter $w += imagesx($inner[$i]); 180a345322fSVincent.Tscherter $h = max($h, imagesy($inner[$i])); 181a345322fSVincent.Tscherter } $im = create_image($w, $h); 182a345322fSVincent.Tscherter imagecopy($im, $inner[0], 0, 0, 0,0, imagesx($inner[0]), imagesy($inner[0])); 18377a47d83SVincent Tscherter $x = imagesx($inner[0])+EBNF_U; 184a345322fSVincent.Tscherter for ($i = 1; $i<count($inner); $i++) { 18577a47d83SVincent Tscherter imageline($im, $x-EBNF_U-1, EBNF_U, $x, EBNF_U, $black); 18677a47d83SVincent Tscherter arrow($im, $x, EBNF_U, $lefttoright); 187a345322fSVincent.Tscherter imagecopy($im, $inner[$i], $x, 0, 0,0, imagesx($inner[$i]), imagesy($inner[$i])); 18877a47d83SVincent Tscherter $x += imagesx($inner[$i])+EBNF_U; 189a345322fSVincent.Tscherter } return $im; 190a345322fSVincent.Tscherter } else if ($node->nodeName=='choise') { 191a345322fSVincent.Tscherter $inner = render_childs($node, $lefttoright); 19277a47d83SVincent Tscherter $h = (count($inner)-1)*EBNF_U; $w = 0; 193a345322fSVincent.Tscherter for ($i = 0; $i<count($inner); $i++) { 194a345322fSVincent.Tscherter $h += imagesy($inner[$i]); 195a345322fSVincent.Tscherter $w = max($w, imagesx($inner[$i])); 19677a47d83SVincent Tscherter } $w += 6*EBNF_U; $im = create_image($w, $h); $y = 0; 19777a47d83SVincent Tscherter imageline($im, 0, EBNF_U, EBNF_U, EBNF_U, $black); 19877a47d83SVincent Tscherter imageline($im, $w-EBNF_U, EBNF_U, $w, EBNF_U, $black); 199a345322fSVincent.Tscherter for ($i = 0; $i<count($inner); $i++) { 20077a47d83SVincent Tscherter imageline($im, EBNF_U, $y+EBNF_U, $w-EBNF_U, $y+EBNF_U, $black); 20177a47d83SVincent Tscherter imagecopy($im, $inner[$i], 3*EBNF_U, $y, 0,0, imagesx($inner[$i]), imagesy($inner[$i])); 20277a47d83SVincent Tscherter arrow($im, 3*EBNF_U, $y+EBNF_U, $lefttoright); 20377a47d83SVincent Tscherter arrow($im, $w-2*EBNF_U, $y+EBNF_U, $lefttoright); 20477a47d83SVincent Tscherter $top = $y + EBNF_U; 20577a47d83SVincent Tscherter $y += imagesy($inner[$i])+EBNF_U; 206a345322fSVincent.Tscherter } 20777a47d83SVincent Tscherter imageline($im, EBNF_U, EBNF_U, EBNF_U, $top, $black); 20877a47d83SVincent Tscherter imageline($im, $w-EBNF_U, EBNF_U, $w-EBNF_U, $top, $black); 209a345322fSVincent.Tscherter return $im; 210a345322fSVincent.Tscherter } else if ($node->nodeName=='syntax') { 211a345322fSVincent.Tscherter $title = $node->getAttribute('title'); 212a345322fSVincent.Tscherter $meta = $node->getAttribute('meta'); 213a345322fSVincent.Tscherter $node = $node->firstChild; 214a345322fSVincent.Tscherter $names = array(); 215a345322fSVincent.Tscherter $images = array(); 216a345322fSVincent.Tscherter while ($node!=null) { 217a345322fSVincent.Tscherter $names[] = $node->getAttribute('name'); 218a345322fSVincent.Tscherter $im = render_node($node->firstChild, $lefttoright); 219a345322fSVincent.Tscherter $images[] = $im; 220a345322fSVincent.Tscherter $node = $node->nextSibling; 22177a47d83SVincent Tscherter } $wn = 0; $wr = 0; $h = 5*EBNF_U; 222a345322fSVincent.Tscherter for ($i = 0; $i<count($images); $i++) { 22377a47d83SVincent Tscherter $wn = max($wn, imagefontwidth(EBNF_FONT)*strlen($names[$i])); 224a345322fSVincent.Tscherter $wr = max($wr, imagesx($images[$i])); 22577a47d83SVincent Tscherter $h += imagesy($images[$i])+2*EBNF_U; 226a345322fSVincent.Tscherter } 22777a47d83SVincent Tscherter if ($title=='') $h -= 2*EBNF_U; 22877a47d83SVincent Tscherter if ($meta=='') $h -= 2*EBNF_U; 22977a47d83SVincent Tscherter $w = max($wr+$wn+3*EBNF_U, imagefontwidth(1)*strlen($meta)+2*EBNF_U); 230a345322fSVincent.Tscherter $im = create_image($w, $h); 23177a47d83SVincent Tscherter $y = 2*EBNF_U; 232a345322fSVincent.Tscherter if ($title!='') { 23308455648SRalf Müller imagestring($im, EBNF_FONT, EBNF_U, intval((2*EBNF_U-imagefontheight(EBNF_FONT))/2), 234a345322fSVincent.Tscherter $title, $green); 23577a47d83SVincent Tscherter imageline($im, 0, 2*EBNF_U, $w, 2*EBNF_U, $green); 23677a47d83SVincent Tscherter $y += 2*EBNF_U; 237a345322fSVincent.Tscherter } 238a345322fSVincent.Tscherter for ($i = 0; $i<count($images); $i++) { 23908455648SRalf Müller imagestring($im, EBNF_FONT, EBNF_U, intval($y-EBNF_U+(2*EBNF_U-imagefontheight(EBNF_FONT))/2), $names[$i], $red); 24077a47d83SVincent Tscherter imagecopy($im, $images[$i], $wn+2*EBNF_U, $y, 0,0, imagesx($images[$i]) , imagesy($images[$i])); 24177a47d83SVincent Tscherter imageline($im, EBNF_U, $y+EBNF_U, $wn+2*EBNF_U, $y+EBNF_U, $black); 24277a47d83SVincent Tscherter imageline($im, $wn+2*EBNF_U+imagesx($images[$i])-1, $y+EBNF_U, $w-EBNF_U, $y+EBNF_U, $black); 24377a47d83SVincent Tscherter imageline($im, $w-EBNF_U, $y+EBNF_U/2, $w-EBNF_U ,$y+1.5*EBNF_U, $black); 24477a47d83SVincent Tscherter $y += 2*EBNF_U + imagesy($images[$i]); 245a345322fSVincent.Tscherter } 24677a47d83SVincent Tscherter imagestring($im, 1, EBNF_U, $h-2*EBNF_U+(2*EBNF_U-imagefontheight(1))/2, 247a345322fSVincent.Tscherter $meta, $silver); 24877a47d83SVincent Tscherter rr($im, 0,0,$w-1, $h-1, EBNF_U/2, $green); 249a345322fSVincent.Tscherter header('Content-Type: image/png'); 250a345322fSVincent.Tscherter imagepng($im); 251a345322fSVincent.Tscherter return $im; 252a345322fSVincent.Tscherter } 253a345322fSVincent.Tscherter} 254a345322fSVincent.Tscherter 255a345322fSVincent.Tscherterfunction render_childs($node, $lefttoright) { 256a345322fSVincent.Tscherter $childs = array(); 257a345322fSVincent.Tscherter $node = $node->firstChild; 258a345322fSVincent.Tscherter while ($node!=null) { 259a345322fSVincent.Tscherter $childs[] = render_node($node, $lefttoright); 260a345322fSVincent.Tscherter $node = $node->nextSibling; 261a345322fSVincent.Tscherter } return $childs; 262a345322fSVincent.Tscherter} 263a345322fSVincent.Tscherter 264a345322fSVincent.Tscherterfunction ebnf_scan(&$input) { 265a345322fSVincent.Tscherter global $ebnf_lexemes; 266a345322fSVincent.Tscherter $i = 0; $n = strlen($input); $m = count($ebnf_lexemes); $tokens = array(); 267a345322fSVincent.Tscherter while ($i < $n) { 268a345322fSVincent.Tscherter $j = 0; 269a345322fSVincent.Tscherter while ($j < $m && 270a345322fSVincent.Tscherter preg_match("/^{$ebnf_lexemes[$j]['expr']}/", substr($input,$i), $matches)==0) $j++; 271a345322fSVincent.Tscherter if ($j<$m) { 272a345322fSVincent.Tscherter if ($ebnf_lexemes[$j]['type']!=EBNF_WHITESPACE_TOKEN) 273a345322fSVincent.Tscherter $tokens[] = array('type' => $ebnf_lexemes[$j]['type'], 274a345322fSVincent.Tscherter 'value' => $matches[0], 'pos' => $i); 275a345322fSVincent.Tscherter $i += strlen($matches[0]); 276a345322fSVincent.Tscherter } else 2775a779085SDamien Regad throw new EbnfException("Invalid token at position", $i); 278a345322fSVincent.Tscherter } return $tokens; 279a345322fSVincent.Tscherter} 280a345322fSVincent.Tscherter 281a345322fSVincent.Tscherter 282a345322fSVincent.Tscherterfunction ebnf_check_token($token, $type, $value) { 283a345322fSVincent.Tscherter return $token['type']==$type && $token['value']==$value; 284a345322fSVincent.Tscherter} 285a345322fSVincent.Tscherter 286a345322fSVincent.Tscherterfunction ebnf_parse_syntax(&$tokens) { 287a345322fSVincent.Tscherter $dom = new DOMDocument(); 288a345322fSVincent.Tscherter $syntax = $dom->createElement("syntax"); 289a345322fSVincent.Tscherter $syntax->setAttribute('meta', META); 290a345322fSVincent.Tscherter $dom->appendChild($syntax); 291a345322fSVincent.Tscherter $i = 0; $token = $tokens[$i++]; 292a345322fSVincent.Tscherter if ($token['type'] == EBNF_LITERAL_TOKEN) { 293a345322fSVincent.Tscherter $syntax->setAttribute('title', 294a345322fSVincent.Tscherter stripcslashes(substr($token['value'], 1, strlen($token['value'])-2 ))); 295a345322fSVincent.Tscherter $token = $tokens[$i++]; 296a345322fSVincent.Tscherter } 297a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '{') ) 2985a779085SDamien Regad throw new EbnfException("Syntax must start with '{'", $token['pos']); 299a345322fSVincent.Tscherter $token = $tokens[$i]; 300a345322fSVincent.Tscherter while ($i < count($tokens) && $token['type'] == EBNF_IDENTIFIER_TOKEN) { 301a345322fSVincent.Tscherter $syntax->appendChild(ebnf_parse_production($dom, $tokens, $i)); 302a345322fSVincent.Tscherter if ($i<count($tokens)) $token = $tokens[$i]; 303a345322fSVincent.Tscherter } $i++; if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '}')) 3045a779085SDamien Regad throw new EbnfException("Syntax must end with '}'", $tokens[count($tokens)-1]['pos']); 305a345322fSVincent.Tscherter if ($i<count($tokens)) { 306a345322fSVincent.Tscherter $token = $tokens[$i]; 307a345322fSVincent.Tscherter if ($token['type'] == EBNF_LITERAL_TOKEN) { 308a345322fSVincent.Tscherter $syntax->setAttribute('meta', 309a345322fSVincent.Tscherter stripcslashes(substr($token['value'], 1, strlen($token['value'])-2 ))); 310a345322fSVincent.Tscherter } 311a345322fSVincent.Tscherter } 312a345322fSVincent.Tscherter return $dom; 313a345322fSVincent.Tscherter} 314a345322fSVincent.Tscherter 315a345322fSVincent.Tscherterfunction ebnf_parse_production(&$dom, &$tokens, &$i) { 316a345322fSVincent.Tscherter $token = $tokens[$i++]; 317a345322fSVincent.Tscherter if ($token['type']!=EBNF_IDENTIFIER_TOKEN) 3185a779085SDamien Regad throw new EbnfException("Production must start with an identifier'{'", $token['pos']); 319a345322fSVincent.Tscherter $production = $dom->createElement("rule"); 320a345322fSVincent.Tscherter $production->setAttribute('name', $token['value']); 321a345322fSVincent.Tscherter $token = $tokens[$i++]; 322a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, "=")) 3235a779085SDamien Regad throw new EbnfException("Identifier must be followed by '='", $token['pos']); 324a345322fSVincent.Tscherter $production->appendChild( ebnf_parse_expression($dom, $tokens, $i)); 325a345322fSVincent.Tscherter $token = $tokens[$i++]; 326a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '.') 327a345322fSVincent.Tscherter && !ebnf_check_token($token, EBNF_OPERATOR_TOKEN, ';')) 3285a779085SDamien Regad throw new EbnfException("Rule must end with '.' or ';'", $token['pos']); 329a345322fSVincent.Tscherter return $production; 330a345322fSVincent.Tscherter} 331a345322fSVincent.Tscherter 332a345322fSVincent.Tscherterfunction ebnf_parse_expression(&$dom, &$tokens, &$i) { 333a345322fSVincent.Tscherter $choise = $dom->createElement("choise"); 334a345322fSVincent.Tscherter $choise->appendChild(ebnf_parse_term($dom, $tokens, $i)); 335a345322fSVincent.Tscherter $token=$tokens[$i]; $mul = false; 336a345322fSVincent.Tscherter while (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '|')) { 337a345322fSVincent.Tscherter $i++; 338a345322fSVincent.Tscherter $choise->appendChild(ebnf_parse_term($dom, $tokens, $i)); 339a345322fSVincent.Tscherter $token=$tokens[$i]; $mul = true; 340a345322fSVincent.Tscherter } return $mul ? $choise : $choise->removeChild($choise->firstChild); 341a345322fSVincent.Tscherter} 342a345322fSVincent.Tscherter 343a345322fSVincent.Tscherterfunction ebnf_parse_term(&$dom, &$tokens, &$i) { 344a345322fSVincent.Tscherter $sequence = $dom->createElement("sequence"); 345a345322fSVincent.Tscherter $factor = ebnf_parse_factor($dom, $tokens, $i); 346a345322fSVincent.Tscherter $sequence->appendChild($factor); 347a345322fSVincent.Tscherter $token=$tokens[$i]; $mul = false; 348a345322fSVincent.Tscherter while ($token['value']!='.' && $token['value']!='=' && $token['value']!='|' 349a345322fSVincent.Tscherter && $token['value']!=')' && $token['value']!=']' && $token['value']!='}') { 350a345322fSVincent.Tscherter $sequence->appendChild(ebnf_parse_factor($dom, $tokens, $i)); 351a345322fSVincent.Tscherter $token=$tokens[$i]; $mul = true; 352a345322fSVincent.Tscherter } return $mul ? $sequence: $sequence->removeChild($sequence->firstChild); 353a345322fSVincent.Tscherter} 354a345322fSVincent.Tscherter 355a345322fSVincent.Tscherterfunction ebnf_parse_factor(&$dom, &$tokens, &$i) { 356a345322fSVincent.Tscherter $token = $tokens[$i++]; 357a345322fSVincent.Tscherter if ($token['type']==EBNF_IDENTIFIER_TOKEN) { 358a345322fSVincent.Tscherter $identifier = $dom->createElement("identifier"); 359a345322fSVincent.Tscherter $identifier->setAttribute('value', $token['value']); 360a345322fSVincent.Tscherter return $identifier; 361a345322fSVincent.Tscherter } if ($token['type']==EBNF_LITERAL_TOKEN){ 362a345322fSVincent.Tscherter $literal = $dom->createElement("terminal"); 363a345322fSVincent.Tscherter $literal->setAttribute('value', stripcslashes(substr($token['value'], 1, strlen($token['value'])-2 ))); 364a345322fSVincent.Tscherter return $literal; 365a345322fSVincent.Tscherter } if (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '(')) { 366a345322fSVincent.Tscherter $expression = ebnf_parse_expression($dom, $tokens, $i); 367a345322fSVincent.Tscherter $token = $tokens[$i++]; 368a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, ')')) 3695a779085SDamien Regad throw new EbnfException("Group must end with ')'", $token['pos']); 370a345322fSVincent.Tscherter return $expression; 371a345322fSVincent.Tscherter } if (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '[')) { 372a345322fSVincent.Tscherter $option = $dom->createElement("option"); 373a345322fSVincent.Tscherter $option->appendChild(ebnf_parse_expression($dom, $tokens, $i)); 374a345322fSVincent.Tscherter $token = $tokens[$i++]; 375a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, ']')) 3765a779085SDamien Regad throw new EbnfException("Option must end with ']'", $token['pos']); 377a345322fSVincent.Tscherter return $option; 378a345322fSVincent.Tscherter } if (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '{')) { 379a345322fSVincent.Tscherter $loop = $dom->createElement("loop"); 380a345322fSVincent.Tscherter $loop->appendChild(ebnf_parse_expression($dom, $tokens, $i)); 381a345322fSVincent.Tscherter $token = $tokens[$i++]; 382a345322fSVincent.Tscherter if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '}')) 3835a779085SDamien Regad throw new EbnfException("Loop must end with '}'", $token['pos']); 384a345322fSVincent.Tscherter return $loop; 385a345322fSVincent.Tscherter } 3865a779085SDamien Regad throw new EbnfException("Factor expected", $token['pos']); 3875a779085SDamien Regad} 3885a779085SDamien Regad 3895a779085SDamien Regadclass EbnfException extends Exception { 3905a779085SDamien Regad protected int $pos; 3915a779085SDamien Regad 3925a779085SDamien Regad public function __construct($message, $pos) { 3935a779085SDamien Regad $this->pos = $pos; 3945a779085SDamien Regad parent::__construct($message . ": $pos"); 3955a779085SDamien Regad } 3965a779085SDamien Regad 3975a779085SDamien Regad public function getPos() { 3985a779085SDamien Regad return $this->pos; 3995a779085SDamien Regad } 400a345322fSVincent.Tscherter} 401