ebnf:index
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
ebnf:index [2009/01/19 13:17] – Vincent Tscherter | ebnf:index [2023/09/28 07:51] (aktuell) – angelegt Vincent Tscherter | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
+ | ====== EBNF Parser & Syntax Diagram Renderer ====== | ||
+ | The //EBNF Parser & Syntax Diagram Renderer// can be used stand-alone or as Dokuwiki-Plugin. | ||
+ | |||
+ | ===== Download and Install===== | ||
+ | * https:// | ||
+ | * Requirements: | ||
+ | ===== How to use ===== | ||
+ | |||
+ | < | ||
+ | syntax | ||
+ | production = identifier " | ||
+ | expression = term { " | ||
+ | term = factor { factor } . | ||
+ | factor | ||
+ | | literal | ||
+ | | " | ||
+ | | " | ||
+ | | " | ||
+ | identifier = character { character } . | ||
+ | title = literal . | ||
+ | comment | ||
+ | literal | ||
+ | | '"' | ||
+ | }</ | ||
+ | |||
+ | |||
+ | < | ||
+ | syntax | ||
+ | production = identifier " | ||
+ | expression = term { " | ||
+ | term = factor { factor } . | ||
+ | factor | ||
+ | | literal | ||
+ | | " | ||
+ | | " | ||
+ | | " | ||
+ | identifier = character { character } . | ||
+ | title = literal . | ||
+ | comment | ||
+ | literal | ||
+ | | '"' | ||
+ | } </ | ||
+ | |||
+ | The script is also available as stand-alone version under: http:// | ||
+ | http:// | ||
+ | Note: The stand-alone script supports also XML output | ||
+ | http:// | ||
+ | |||
+ | ===== Syntax ===== | ||
+ | * See examples: [[examples]] | ||
+ | * [[wp> | ||
+ | * [[wp> | ||
+ | |||
+ | ===== Todo & Ideas ===== | ||
+ | * Ellipse ... for enumerations. E.g. '' | ||
+ | * temp hack in v0.2: '' | ||
+ | else { | ||
+ | if ($text!=" | ||
+ | rr($im, UNIT, 0, $w-UNIT-1, $h-1, UNIT/2, $black); | ||
+ | imagestring($im, | ||
+ | $text, $text!=" | ||
+ | } ...</ | ||
+ | * Other fonts | ||
+ | * Configurable dimensions | ||
+ | * escape-chars and or unicode in terminals | ||
+ | * Adding //Regexp// beside terminal and identifiers | ||
+ | * using a lightweight syntaxtree instead DOM | ||
+ | * using some standard parsing mechanism | ||
+ | * support for comments & special-sequences ''? | ||
+ | * http:// | ||
+ | * replace antialias function | ||
+ | |||
+ | ===== Code ===== | ||
+ | <code php syntax.php> | ||
+ | <?php | ||
+ | /** | ||
+ | * Dokuwiki Plugin EBNF: Displays Syntax Diagrams | ||
+ | * | ||
+ | * Syntax: < | ||
+ | * | ||
+ | * @license | ||
+ | * @author | ||
+ | * @version | ||
+ | */ | ||
+ | |||
+ | if(!defined(' | ||
+ | if(!defined(' | ||
+ | require_once(DOKU_PLUGIN.' | ||
+ | |||
+ | class syntax_plugin_ebnf extends DokuWiki_Syntax_Plugin { | ||
+ | |||
+ | function getType(){ | ||
+ | return ' | ||
+ | } | ||
+ | |||
+ | function getSort(){ | ||
+ | return 999; | ||
+ | } | ||
+ | |||
+ | function connectTo($mode) { | ||
+ | $this-> | ||
+ | } | ||
+ | |||
+ | function handle($match, | ||
+ | | ||
+ | case DOKU_LEXER_ENTER : | ||
+ | break; | ||
+ | case DOKU_LEXER_MATCHED : | ||
+ | break; | ||
+ | case DOKU_LEXER_UNMATCHED : | ||
+ | break; | ||
+ | case DOKU_LEXER_EXIT : | ||
+ | break; | ||
+ | case DOKU_LEXER_SPECIAL : | ||
+ | break; | ||
+ | } | ||
+ | return array($match); | ||
+ | } | ||
+ | |||
+ | function render($mode, | ||
+ | if($mode == ' | ||
+ | try { | ||
+ | $text = substr($data[0], | ||
+ | $text = preg_replace( "/ | ||
+ | $text = preg_replace( "/ | ||
+ | $text = urlencode($text); | ||
+ | | ||
+ | } catch (Exception $e) { | ||
+ | $renderer-> | ||
+ | } | ||
+ | return true; | ||
+ | } | ||
+ | return false; | ||
+ | } | ||
+ | } | ||
+ | ?> | ||
+ | </ | ||
+ | <code php ebnf.php> | ||
+ | <?php | ||
+ | /* | ||
+ | This program is free software: you can redistribute it and/or modify | ||
+ | it under the terms of the GNU General Public License as published by | ||
+ | the Free Software Foundation, either version 3 of the License, or | ||
+ | (at your option) any later version. | ||
+ | |||
+ | This program is distributed in the hope that it will be useful, | ||
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
+ | GNU General Public License for more details. | ||
+ | |||
+ | You should have received a copy of the GNU General Public License | ||
+ | along with this program. | ||
+ | |||
+ | Vincent Tscherter, tscherter@karmin.ch, | ||
+ | |||
+ | 2009-01-18 version 0.1 first release | ||
+ | 2009-01-02 version 0.2 | ||
+ | - title und comment literal added | ||
+ | - ";" | ||
+ | */ | ||
+ | error_reporting(E_ALL|E_STRICT); | ||
+ | |||
+ | // | ||
+ | define(' | ||
+ | |||
+ | // parser | ||
+ | define(' | ||
+ | define(' | ||
+ | define(' | ||
+ | define(' | ||
+ | |||
+ | // rendering | ||
+ | define(' | ||
+ | define(' | ||
+ | define(' | ||
+ | |||
+ | // lexemes | ||
+ | $ebnf_lexemes[] = array( ' | ||
+ | $ebnf_lexemes[] = array( ' | ||
+ | $ebnf_lexemes[] = array( ' | ||
+ | $ebnf_lexemes[] = array( ' | ||
+ | $ebnf_lexemes[] = array( ' | ||
+ | |||
+ | // input example | ||
+ | $input = <<< | ||
+ | "EBNF defined in itself" | ||
+ | syntax | ||
+ | rule = identifier " | ||
+ | expression = term { " | ||
+ | term = factor { factor } . | ||
+ | factor | ||
+ | | literal | ||
+ | | " | ||
+ | | " | ||
+ | | " | ||
+ | identifier = character { character } . | ||
+ | title = literal . | ||
+ | comment | ||
+ | literal | ||
+ | | '"' | ||
+ | } | ||
+ | EOD; | ||
+ | |||
+ | if (isset($_GET[' | ||
+ | $input = $_GET[' | ||
+ | $input = stripslashes($input); | ||
+ | } | ||
+ | |||
+ | $format = " | ||
+ | if (isset($_GET[' | ||
+ | |||
+ | try { | ||
+ | $tokens = ebnf_scan($input, | ||
+ | $dom = ebnf_parse_syntax($tokens); | ||
+ | if ($format == ' | ||
+ | header(' | ||
+ | echo $dom-> | ||
+ | } else { | ||
+ | render_node($dom-> | ||
+ | } | ||
+ | } catch (Exception $e) { | ||
+ | header(' | ||
+ | $dom = new DOMDocument(); | ||
+ | $syntax = $dom-> | ||
+ | $syntax-> | ||
+ | $syntax-> | ||
+ | $dom-> | ||
+ | render_node($dom-> | ||
+ | } | ||
+ | |||
+ | function rr($im, $x1, $y1, $x2, $y2, $r, $black){ | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | imagearc($im, | ||
+ | imagearc($im, | ||
+ | imagearc($im, | ||
+ | imagearc($im, | ||
+ | } | ||
+ | |||
+ | function create_image($w, | ||
+ | global $white, $black, $blue, $red, $green, $silver; | ||
+ | $im = imagecreatetruecolor($w, | ||
+ | imageantialias($im, | ||
+ | $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, | ||
+ | return $im; | ||
+ | } | ||
+ | |||
+ | function arrow($image, | ||
+ | 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, | ||
+ | } | ||
+ | |||
+ | |||
+ | function render_node($node, | ||
+ | global $white, $black, $blue, $red, $green, $silver; | ||
+ | if ($node-> | ||
+ | $text = $node-> | ||
+ | $w = imagefontwidth(FONT)*(strlen($text)) + 4*UNIT; | ||
+ | $h = 2*UNIT; | ||
+ | $im = create_image($w, | ||
+ | |||
+ | if ($node-> | ||
+ | imagerectangle($im, | ||
+ | imagestring($im, | ||
+ | } else { | ||
+ | if ($text!=" | ||
+ | rr($im, UNIT, 0, $w-UNIT-1, $h-1, UNIT/2, $black); | ||
+ | imagestring($im, | ||
+ | $text, $text!=" | ||
+ | } | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | return $im; | ||
+ | } else if ($node-> | ||
+ | if ($node-> | ||
+ | $lefttoright = ! $lefttoright; | ||
+ | $inner = render_node($node-> | ||
+ | $w = imagesx($inner)+6*UNIT; | ||
+ | $h = imagesy($inner)+2*UNIT; | ||
+ | $im = create_image($w, | ||
+ | imagecopy($im, | ||
+ | imageline($im, | ||
+ | arrow($im, $w/ | ||
+ | arrow($im, 3*UNIT, 3*UNIT, $lefttoright); | ||
+ | arrow($im, $w-2*UNIT, 3*UNIT, $lefttoright); | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | return $im; | ||
+ | } else if ($node-> | ||
+ | $inner = render_childs($node, | ||
+ | if (!$lefttoright) | ||
+ | $inner = array_reverse($inner); | ||
+ | $w = count($inner)*UNIT-UNIT; | ||
+ | for ($i = 0; $i< | ||
+ | $w += imagesx($inner[$i]); | ||
+ | $h = max($h, imagesy($inner[$i])); | ||
+ | } $im = create_image($w, | ||
+ | imagecopy($im, | ||
+ | $x = imagesx($inner[0])+UNIT; | ||
+ | for ($i = 1; $i< | ||
+ | imageline($im, | ||
+ | arrow($im, $x, UNIT, $lefttoright); | ||
+ | imagecopy($im, | ||
+ | $x += imagesx($inner[$i])+UNIT; | ||
+ | } return $im; | ||
+ | } else if ($node-> | ||
+ | $inner = render_childs($node, | ||
+ | $h = (count($inner)-1)*UNIT; | ||
+ | for ($i = 0; $i< | ||
+ | $h += imagesy($inner[$i]); | ||
+ | $w = max($w, imagesx($inner[$i])); | ||
+ | } $w += 6*UNIT; $im = create_image($w, | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | for ($i = 0; $i< | ||
+ | imageline($im, | ||
+ | imagecopy($im, | ||
+ | arrow($im, 3*UNIT, $y+UNIT, $lefttoright); | ||
+ | arrow($im, $w-2*UNIT, $y+UNIT, $lefttoright); | ||
+ | $top = $y + UNIT; | ||
+ | $y += imagesy($inner[$i])+UNIT; | ||
+ | } | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | return $im; | ||
+ | } else if ($node-> | ||
+ | $title = $node-> | ||
+ | $meta = $node-> | ||
+ | $node = $node-> | ||
+ | $names = array(); | ||
+ | $images = array(); | ||
+ | while ($node!=null) { | ||
+ | | ||
+ | $im = render_node($node-> | ||
+ | | ||
+ | $node = $node-> | ||
+ | } $wn = 0; $wr = 0; $h = 5*UNIT; | ||
+ | for ($i = 0; $i< | ||
+ | $wn = max($wn, imagefontwidth(FONT)*strlen($names[$i])); | ||
+ | $wr = max($wr, imagesx($images[$i])); | ||
+ | $h += imagesy($images[$i])+2*UNIT; | ||
+ | } | ||
+ | if ($title=='' | ||
+ | if ($meta=='' | ||
+ | $w = max($wr+$wn+3*UNIT, | ||
+ | $im = create_image($w, | ||
+ | $y = 2*UNIT; | ||
+ | if ($title!='' | ||
+ | imagestring($im, | ||
+ | $title, $green); | ||
+ | imageline($im, | ||
+ | $y += 2*UNIT; | ||
+ | } | ||
+ | for ($i = 0; $i< | ||
+ | imagestring($im, | ||
+ | imagecopy($im, | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | imageline($im, | ||
+ | $y += 2*UNIT + imagesy($images[$i]); | ||
+ | } | ||
+ | imagestring($im, | ||
+ | $meta, $silver); | ||
+ | rr($im, 0,0,$w-1, $h-1, UNIT/2, $green); | ||
+ | header(' | ||
+ | imagepng($im); | ||
+ | return $im; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function render_childs($node, | ||
+ | | ||
+ | $node = $node-> | ||
+ | while ($node!=null) { | ||
+ | | ||
+ | $node = $node-> | ||
+ | } return $childs; | ||
+ | } | ||
+ | |||
+ | function ebnf_scan(& | ||
+ | global $ebnf_lexemes; | ||
+ | $i = 0; $n = strlen($input); | ||
+ | while ($i < $n) { | ||
+ | $j = 0; | ||
+ | while ($j < $m && | ||
+ | preg_match("/ | ||
+ | if ($j<$m) { | ||
+ | if ($ebnf_lexemes[$j][' | ||
+ | $tokens[] = array(' | ||
+ | ' | ||
+ | $i += strlen($matches[0]); | ||
+ | } else | ||
+ | throw new Exception(" | ||
+ | } return $tokens; | ||
+ | } | ||
+ | |||
+ | |||
+ | function ebnf_check_token($token, | ||
+ | return $token[' | ||
+ | } | ||
+ | |||
+ | function ebnf_parse_syntax(& | ||
+ | $dom = new DOMDocument(); | ||
+ | $syntax = $dom-> | ||
+ | $syntax-> | ||
+ | $dom-> | ||
+ | $i = 0; $token = $tokens[$i++]; | ||
+ | if ($token[' | ||
+ | $syntax-> | ||
+ | stripcslashes(substr($token[' | ||
+ | $token = $tokens[$i++]; | ||
+ | } | ||
+ | if (!ebnf_check_token($token, | ||
+ | throw new Exception(" | ||
+ | $token = $tokens[$i]; | ||
+ | while ($i < count($tokens) && $token[' | ||
+ | $syntax-> | ||
+ | if ($i< | ||
+ | } $i++; if (!ebnf_check_token($token, | ||
+ | throw new Exception(" | ||
+ | if ($i< | ||
+ | $token = $tokens[$i]; | ||
+ | if ($token[' | ||
+ | $syntax-> | ||
+ | stripcslashes(substr($token[' | ||
+ | } | ||
+ | } | ||
+ | return $dom; | ||
+ | } | ||
+ | |||
+ | function ebnf_parse_production(& | ||
+ | $token = $tokens[$i++]; | ||
+ | if ($token[' | ||
+ | throw new Exception(" | ||
+ | $production = $dom-> | ||
+ | $production-> | ||
+ | $token = $tokens[$i++]; | ||
+ | if (!ebnf_check_token($token, | ||
+ | throw new Exception(" | ||
+ | $production-> | ||
+ | $token = $tokens[$i++]; | ||
+ | if (!ebnf_check_token($token, | ||
+ | && !ebnf_check_token($token, | ||
+ | throw new Exception(" | ||
+ | return $production; | ||
+ | } | ||
+ | |||
+ | function ebnf_parse_expression(& | ||
+ | $choise = $dom-> | ||
+ | $choise-> | ||
+ | $token=$tokens[$i]; | ||
+ | while (ebnf_check_token($token, | ||
+ | $i++; | ||
+ | $choise-> | ||
+ | $token=$tokens[$i]; | ||
+ | } return $mul ? $choise : $choise-> | ||
+ | } | ||
+ | |||
+ | function ebnf_parse_term(& | ||
+ | $sequence = $dom-> | ||
+ | $factor = ebnf_parse_factor($dom, | ||
+ | $sequence-> | ||
+ | $token=$tokens[$i]; | ||
+ | while ($token[' | ||
+ | && $token[' | ||
+ | $sequence-> | ||
+ | $token=$tokens[$i]; | ||
+ | } return $mul ? $sequence: $sequence-> | ||
+ | } | ||
+ | |||
+ | function ebnf_parse_factor(& | ||
+ | $token = $tokens[$i++]; | ||
+ | if ($token[' | ||
+ | $identifier = $dom-> | ||
+ | $identifier-> | ||
+ | return $identifier; | ||
+ | } if ($token[' | ||
+ | $literal = $dom-> | ||
+ | $literal-> | ||
+ | return $literal; | ||
+ | } if (ebnf_check_token($token, | ||
+ | $expression = ebnf_parse_expression($dom, | ||
+ | $token = $tokens[$i++]; | ||
+ | if (!ebnf_check_token($token, | ||
+ | throw new Exception(" | ||
+ | return $expression; | ||
+ | } if (ebnf_check_token($token, | ||
+ | $option = $dom-> | ||
+ | $option-> | ||
+ | $token = $tokens[$i++]; | ||
+ | if (!ebnf_check_token($token, | ||
+ | throw new Exception(" | ||
+ | return $option; | ||
+ | } if (ebnf_check_token($token, | ||
+ | $loop = $dom-> | ||
+ | $loop-> | ||
+ | $token = $tokens[$i++]; | ||
+ | if (!ebnf_check_token($token, | ||
+ | throw new Exception(" | ||
+ | return $loop; | ||
+ | } | ||
+ | throw new Exception(" | ||
+ | } | ||
+ | |||
+ | ?></ | ||