. Vincent Tscherter, tscherter@karmin.ch, Solothurn, 2009-01-18 2009-01-18 version 0.1 first release 2009-01-02 version 0.2 - title und comment literal added - ";" als terminator-symbol added */ error_reporting(E_ALL|E_STRICT); // define('META', 'xis/ebnf v0.2 https://www.dokuwiki.org/plugin:ebnf gpl3'); // parser define('EBNF_OPERATOR_TOKEN', 1); define('EBNF_LITERAL_TOKEN', 2); define('EBNF_WHITESPACE_TOKEN', 3); define('EBNF_IDENTIFIER_TOKEN', 4); // rendering define('FONT', 3); define('UNIT', 10); define('AW', 3); // lexemes $ebnf_lexemes[] = array( 'type' => EBNF_OPERATOR_TOKEN, 'expr' => '[={}()|.;[\]]' ); $ebnf_lexemes[] = array( 'type' => EBNF_LITERAL_TOKEN, 'expr' => "\"[^\"]*\"" ); $ebnf_lexemes[] = array( 'type' => EBNF_LITERAL_TOKEN, 'expr' => "'[^']*'" ); $ebnf_lexemes[] = array( 'type' => EBNF_IDENTIFIER_TOKEN, 'expr' => "[a-zA-Z0-9_-]+" ); $ebnf_lexemes[] = array( 'type' => EBNF_WHITESPACE_TOKEN, 'expr' => "\\s+" ); // input example $input = <<saveXML(); } else { render_node($dom->firstChild, true); } } catch (Exception $e) { header('Content-Type: text/plain'); $dom = new DOMDocument(); $syntax = $dom->createElement("syntax"); $syntax->setAttribute('title', 'EBNF - Syntax Error'); $syntax->setAttribute('meta', $e->getMessage()); $dom->appendChild($syntax); render_node($dom->firstChild, true); } function rr($im, $x1, $y1, $x2, $y2, $r, $black){ imageline($im, $x1+$r, $y1, $x2-$r, $y1, $black); imageline($im, $x1+$r, $y2, $x2-$r, $y2, $black); imageline($im, $x1, $y1+$r, $x1, $y2-$r, $black); imageline($im, $x2, $y1+$r, $x2, $y2-$r, $black); imagearc($im, $x1+$r, $y1+$r, 2*$r, 2*$r, 180, 270, $black); imagearc($im, $x2-$r, $y1+$r, 2*$r, 2*$r, 270, 360, $black); imagearc($im, $x1+$r, $y2-$r, 2*$r, 2*$r, 90, 180, $black); imagearc($im, $x2-$r, $y2-$r, 2*$r, 2*$r, 0, 90, $black); } function create_image($w, $h) { global $white, $black, $blue, $red, $green, $silver; $im = imagecreatetruecolor($w, $h) or die("no img"); imageantialias($im, true); $white = imagecolorallocate ($im, 255, 255, 255); $black = imagecolorallocate ($im, 0, 0, 0); $blue = imagecolorallocate ($im, 0, 0, 255); $red = imagecolorallocate ($im, 255, 0, 0); $green = imagecolorallocate ($im, 0, 200, 0); $silver = imagecolorallocate ($im, 127, 127, 127); imagefilledrectangle($im, 0,0,$w,$h,$white); return $im; } function arrow($image, $x, $y, $lefttoright) { global $white, $black; if (!$lefttoright) imagefilledpolygon($image, array($x, $y-UNIT/3, $x-UNIT, $y, $x, $y+UNIT/3), 3, $black); else imagefilledpolygon($image, array($x-UNIT, $y-UNIT/3, $x, $y, $x-UNIT, $y+UNIT/3), 3, $black); } function render_node($node, $lefttoright) { global $white, $black, $blue, $red, $green, $silver; if ($node->nodeName=='identifier' || $node->nodeName=='terminal') { $text = $node->getAttribute('value'); $w = imagefontwidth(FONT)*(strlen($text)) + 4*UNIT; $h = 2*UNIT; $im = create_image($w, $h); if ($node->nodeName!='terminal') { imagerectangle($im, UNIT, 0, $w-UNIT-1, $h-1, $black); imagestring($im, FONT, 2*UNIT, ($h-imagefontheight(FONT))/2, $text, $red); } else { if ($text!="...") rr($im, UNIT, 0, $w-UNIT-1, $h-1, UNIT/2, $black); imagestring($im, FONT, 2*UNIT, ($h-imagefontheight(FONT))/2, $text, $text!="..."?$blue:$black); } imageline($im,0,UNIT, UNIT, UNIT, $black); imageline($im,$w-UNIT,UNIT, $w+1, UNIT, $black); return $im; } else if ($node->nodeName=='option' || $node->nodeName=='loop') { if ($node->nodeName=='loop') $lefttoright = ! $lefttoright; $inner = render_node($node->firstChild, $lefttoright); $w = imagesx($inner)+6*UNIT; $h = imagesy($inner)+2*UNIT; $im = create_image($w, $h); imagecopy($im, $inner, 3*UNIT, 2*UNIT, 0,0, imagesx($inner), imagesy($inner)); imageline($im,0,UNIT, $w, UNIT, $black); arrow($im, $w/2+UNIT/2, UNIT, $node->nodeName=='loop'?!$lefttoright:$lefttoright); arrow($im, 3*UNIT, 3*UNIT, $lefttoright); arrow($im, $w-2*UNIT, 3*UNIT, $lefttoright); imageline($im,UNIT,UNIT, UNIT, 3*UNIT, $black); imageline($im,UNIT,3*UNIT, 2*UNIT, 3*UNIT, $black); imageline($im,$w-UNIT,UNIT, $w-UNIT, 3*UNIT, $black); imageline($im,$w-3*UNIT-1,3*UNIT, $w-UNIT, 3*UNIT, $black); return $im; } else if ($node->nodeName=='sequence') { $inner = render_childs($node, $lefttoright); if (!$lefttoright) $inner = array_reverse($inner); $w = count($inner)*UNIT-UNIT; $h = 0; for ($i = 0; $inodeName=='choise') { $inner = render_childs($node, $lefttoright); $h = (count($inner)-1)*UNIT; $w = 0; for ($i = 0; $inodeName=='syntax') { $title = $node->getAttribute('title'); $meta = $node->getAttribute('meta'); $node = $node->firstChild; $names = array(); $images = array(); while ($node!=null) { $names[] = $node->getAttribute('name'); $im = render_node($node->firstChild, $lefttoright); $images[] = $im; $node = $node->nextSibling; } $wn = 0; $wr = 0; $h = 5*UNIT; for ($i = 0; $ifirstChild; while ($node!=null) { $childs[] = render_node($node, $lefttoright); $node = $node->nextSibling; } return $childs; } function ebnf_scan(&$input) { global $ebnf_lexemes; $i = 0; $n = strlen($input); $m = count($ebnf_lexemes); $tokens = array(); while ($i < $n) { $j = 0; while ($j < $m && preg_match("/^{$ebnf_lexemes[$j]['expr']}/", substr($input,$i), $matches)==0) $j++; if ($j<$m) { if ($ebnf_lexemes[$j]['type']!=EBNF_WHITESPACE_TOKEN) $tokens[] = array('type' => $ebnf_lexemes[$j]['type'], 'value' => $matches[0], 'pos' => $i); $i += strlen($matches[0]); } else throw new Exception("Invalid token at position: $i"); } return $tokens; } function ebnf_check_token($token, $type, $value) { return $token['type']==$type && $token['value']==$value; } function ebnf_parse_syntax(&$tokens) { $dom = new DOMDocument(); $syntax = $dom->createElement("syntax"); $syntax->setAttribute('meta', META); $dom->appendChild($syntax); $i = 0; $token = $tokens[$i++]; if ($token['type'] == EBNF_LITERAL_TOKEN) { $syntax->setAttribute('title', stripcslashes(substr($token['value'], 1, strlen($token['value'])-2 ))); $token = $tokens[$i++]; } if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '{') ) throw new Exception("Syntax must start with '{': {$token['pos']}"); $token = $tokens[$i]; while ($i < count($tokens) && $token['type'] == EBNF_IDENTIFIER_TOKEN) { $syntax->appendChild(ebnf_parse_production($dom, $tokens, $i)); if ($isetAttribute('meta', stripcslashes(substr($token['value'], 1, strlen($token['value'])-2 ))); } } return $dom; } function ebnf_parse_production(&$dom, &$tokens, &$i) { $token = $tokens[$i++]; if ($token['type']!=EBNF_IDENTIFIER_TOKEN) throw new Exception("Production must start with an identifier'{': {$token['pos']}"); $production = $dom->createElement("rule"); $production->setAttribute('name', $token['value']); $token = $tokens[$i++]; if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, "=")) throw new Exception("Identifier must be followed by '=': {$token['pos']}"); $production->appendChild( ebnf_parse_expression($dom, $tokens, $i)); $token = $tokens[$i++]; if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '.') && !ebnf_check_token($token, EBNF_OPERATOR_TOKEN, ';')) throw new Exception("Rule must end with '.' or ';' : {$token['pos']}"); return $production; } function ebnf_parse_expression(&$dom, &$tokens, &$i) { $choise = $dom->createElement("choise"); $choise->appendChild(ebnf_parse_term($dom, $tokens, $i)); $token=$tokens[$i]; $mul = false; while (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '|')) { $i++; $choise->appendChild(ebnf_parse_term($dom, $tokens, $i)); $token=$tokens[$i]; $mul = true; } return $mul ? $choise : $choise->removeChild($choise->firstChild); } function ebnf_parse_term(&$dom, &$tokens, &$i) { $sequence = $dom->createElement("sequence"); $factor = ebnf_parse_factor($dom, $tokens, $i); $sequence->appendChild($factor); $token=$tokens[$i]; $mul = false; while ($token['value']!='.' && $token['value']!='=' && $token['value']!='|' && $token['value']!=')' && $token['value']!=']' && $token['value']!='}') { $sequence->appendChild(ebnf_parse_factor($dom, $tokens, $i)); $token=$tokens[$i]; $mul = true; } return $mul ? $sequence: $sequence->removeChild($sequence->firstChild); } function ebnf_parse_factor(&$dom, &$tokens, &$i) { $token = $tokens[$i++]; if ($token['type']==EBNF_IDENTIFIER_TOKEN) { $identifier = $dom->createElement("identifier"); $identifier->setAttribute('value', $token['value']); return $identifier; } if ($token['type']==EBNF_LITERAL_TOKEN){ $literal = $dom->createElement("terminal"); $literal->setAttribute('value', stripcslashes(substr($token['value'], 1, strlen($token['value'])-2 ))); return $literal; } if (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '(')) { $expression = ebnf_parse_expression($dom, $tokens, $i); $token = $tokens[$i++]; if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, ')')) throw new Exception("Group must end with ')': {$token['pos']}"); return $expression; } if (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '[')) { $option = $dom->createElement("option"); $option->appendChild(ebnf_parse_expression($dom, $tokens, $i)); $token = $tokens[$i++]; if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, ']')) throw new Exception("Option must end with ']': {$token['pos']}"); return $option; } if (ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '{')) { $loop = $dom->createElement("loop"); $loop->appendChild(ebnf_parse_expression($dom, $tokens, $i)); $token = $tokens[$i++]; if (!ebnf_check_token($token, EBNF_OPERATOR_TOKEN, '}')) throw new Exception("Loop must end with '}': {$token['pos']}"); return $loop; } throw new Exception("Factor expected: {$token['pos']}"); } ?>