xref: /plugin/ebnf/ebnf.php (revision a345322f371276bcfd4aeaa4bdf2eab46577754e)
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?>