Added first PHP parser version (doesn't work correct actually)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / PHPParser.java
1 package net.sourceforge.phpeclipse.phpeditor;
2
3 import java.util.HashMap;
4
5 import net.sourceforge.phpeclipse.phpeditor.php.PHPKeywords;
6
7 /**********************************************************************
8 Copyright (c) 2000, 2002 IBM Corp. and others.
9 All rights reserved. This program and the accompanying materials
10 are made available under the terms of the Common Public License v1.0
11 which accompanies this distribution, and is available at
12 http://www.eclipse.org/legal/cpl-v10.html
13
14 Contributors:
15     IBM Corporation - Initial implementation
16     Klaus Hartlage - www.eclipseproject.de
17 **********************************************************************/
18
19 public class PHPParser extends PHPKeywords {
20
21   private static HashMap keywordMap = null;
22   private String str;
23
24   // current character
25   char ch;
26   // current token
27   int token;
28
29   // row counter for syntax errors:
30   int rowCount;
31   // column counter for syntax errors:
32   int columnCount;
33
34   int chIndx;
35
36   // current identifier
37   String identifier;
38
39   Long longNumber;
40   Double doubleNumber;
41
42   final static int TT_EOF = 0;
43   final static int TT_UNDEFINED = 1;
44
45   final static int TT_NOT = 31;
46   final static int TT_DOT = 32;
47   final static int TT_POW = 33;
48   final static int TT_DIVIDE = 34;
49   final static int TT_MULTIPLY = 35;
50   final static int TT_SUBTRACT = 36;
51   final static int TT_ADD = 37;
52   final static int TT_EQUAL = 38;
53   final static int TT_UNEQUAL = 39;
54   final static int TT_GREATER = 40;
55   final static int TT_GREATEREQUAL = 41;
56   final static int TT_LESS = 42;
57   final static int TT_LESSEQUAL = 43;
58   final static int TT_AND = 44;
59   final static int TT_OR = 45;
60   final static int TT_HASH = 46;
61   final static int TT_DDOT = 47;
62   final static int TT_DOTASSIGN = 48;
63
64   final static int TT_SET = 49;
65
66   final static int TT_FOREACH = 51;
67   final static int TT_ARGOPEN = 128;
68   final static int TT_ARGCLOSE = 129;
69   final static int TT_LISTOPEN = 130;
70   final static int TT_LISTCLOSE = 131;
71   final static int TT_PARTOPEN = 132;
72   final static int TT_PARTCLOSE = 133;
73   final static int TT_COMMA = 134;
74   final static int TT_PERCENT = 135;
75   final static int TT_STRING = 136;
76   final static int TT_IDENTIFIER = 138;
77   final static int TT_DIGIT = 139;
78   final static int TT_SEMICOLON = 140;
79   final static int TT_SLOT = 141;
80   final static int TT_SLOTSEQUENCE = 142;
81   final static int TT_DECREMENT = 144;
82   final static int TT_INCREMENT = 145;
83   final static int TT_ADDTO = 146;
84   final static int TT_DIVIDEBY = 147;
85   final static int TT_SUBTRACTFROM = 148;
86   final static int TT_TIMESBY = 149;
87   final static int TT_VARIABLE = 150;
88   final static int TT_INT_NUMBER = 151;
89   final static int TT_DOUBLE_NUMBER = 152;
90   final static int TT_INTERPOLATED_STRING = 153;
91   final static int TT_STRING_CONSTANT = 154;
92   //  final static int TT_AT = 153; // @
93   /**
94    *  Class Constructor.
95    *
96    *@param  s
97    *@param  sess  Description of Parameter
98    *@see
99    */
100   public PHPParser(String s, int rowCount) {
101     if (keywordMap == null) {
102       keywordMap = new HashMap();
103       for (int i = 0; i < PHP_KEYWORS.length; i++) {
104         keywordMap.put(PHP_KEYWORS[i], new Integer(PHP_KEYWORD_TOKEN[i]));
105       }
106     }
107     this.str = s;
108     this.token = TT_EOF;
109     this.chIndx = 0;
110     this.rowCount = rowCount;
111     this.columnCount = 0;
112
113     getNextToken();
114   }
115
116   private void throwSyntaxError(String error) {
117
118     if (str.length() < chIndx) {
119       chIndx--;
120     }
121     // read until end-of-line
122     int eol = chIndx;
123     while (str.length() > eol) {
124       ch = str.charAt(eol++);
125       if (ch == '\n') {
126         eol--;
127         break;
128       }
129     }
130     throw new SyntaxError(rowCount, chIndx - columnCount, str.substring(columnCount + 1, eol), error);
131   }
132
133   /**
134    *  Method Declaration.
135    *
136    *@see
137    */
138   void getChar() {
139     if (str.length() > chIndx) {
140       ch = str.charAt(chIndx++);
141
142       return;
143     }
144
145     chIndx = str.length() + 1;
146     ch = ' ';
147     token = TT_EOF;
148   }
149
150   /**
151    * gets the next token from input
152    */
153   void getNextToken() {
154     while (str.length() > chIndx) {
155       ch = str.charAt(chIndx++);
156       token = TT_UNDEFINED;
157       if (ch == '\n') {
158         rowCount++;
159         columnCount = chIndx;
160         continue; // while loop
161       }
162
163       if (!Character.isWhitespace(ch)) {
164         if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == '_') || (ch == '$') || (ch == '@')) {
165           getIdentifier();
166           return;
167         }
168         if (ch >= '0' && ch <= '9') {
169           getNumber();
170           return;
171         }
172         if (ch == '/') {
173           if (str.length() > chIndx) {
174             if (str.charAt(chIndx) == '/') {
175               chIndx++;
176               // read comment until end of line:
177               while ((str.length() > chIndx) && (str.charAt(chIndx) != '\n')) {
178                 chIndx++;
179               }
180               continue;
181             } else if (str.charAt(chIndx) == '*') {
182               chIndx++;
183               // multi line comment:
184               while (str.length() > chIndx) {
185                 if  (str.charAt(chIndx) == '*' &&
186                       (str.length() > (chIndx+1) ) && 
187                       str.charAt(chIndx+1) == '/') {
188                   chIndx += 2;
189                   break;
190                 }
191                 chIndx++;
192               }
193               continue;
194             }
195           }
196         } else if (ch == '#') {
197           // read comment until end of line:
198           while ((str.length() > chIndx) && (str.charAt(chIndx) != '\n')) {
199             chIndx++;
200           }
201           continue;
202         } else if (ch == '"') {
203           // read string until end
204           while ((str.length() > chIndx) && (str.charAt(chIndx++) != '"')) {
205             if (str.charAt(chIndx) == '\\') {
206               if (str.length() > chIndx) {
207                 chIndx++;
208               }
209               if (str.length() > chIndx) {
210                 chIndx++;
211               }
212             } else {
213               if (str.charAt(chIndx) == '\n') {
214                 rowCount++;
215                 columnCount = chIndx;
216               }
217             }
218           }
219           if (str.length() > chIndx) {
220             chIndx++;
221           }
222           token = TT_INTERPOLATED_STRING;
223           return;
224         } else if (ch == '\'') {
225           // read string until end
226           while ((str.length() > chIndx) && (str.charAt(chIndx++) != '\'')) {
227             if (str.charAt(chIndx) == '\\') {
228               if (str.length() > chIndx) {
229                 chIndx++;
230               }
231               if (str.length() > chIndx) {
232                 chIndx++;
233               }
234             }
235           }
236           if (str.length() > chIndx) {
237             chIndx++;
238           }
239           token = TT_STRING_CONSTANT;
240           return;
241         }
242
243         switch (ch) {
244
245           case '(' :
246             token = TT_ARGOPEN;
247
248             break;
249           case ')' :
250             token = TT_ARGCLOSE;
251
252             break;
253           case '{' :
254             token = TT_LISTOPEN;
255
256             break;
257           case '}' :
258             token = TT_LISTCLOSE;
259
260             break;
261           case '[' :
262             token = TT_PARTOPEN;
263
264             break;
265           case ']' :
266             token = TT_PARTCLOSE;
267
268             break;
269           case ',' :
270             token = TT_COMMA;
271
272             break;
273
274           case '.' :
275             token = TT_DOT;
276             if (str.length() > chIndx) {
277               if (str.charAt(chIndx) == '=') {
278                 chIndx++;
279                 token = TT_DOTASSIGN;
280
281                 break;
282               }
283             }
284
285             break;
286           case '"' :
287             token = TT_STRING;
288
289             break;
290           case '%' :
291             token = TT_PERCENT;
292
293             break;
294           case ';' :
295             token = TT_SEMICOLON;
296
297             break;
298           case '^' :
299             token = TT_POW;
300
301             break;
302           case '/' :
303             token = TT_DIVIDE;
304
305             if (str.length() > chIndx) {
306               if (str.charAt(chIndx) == '=') {
307                 chIndx++;
308                 token = TT_DIVIDEBY;
309
310                 break;
311               }
312             }
313
314             break;
315           case '*' :
316             token = TT_MULTIPLY;
317             if (str.length() > chIndx) {
318               if (str.charAt(chIndx) == '*') {
319                 chIndx++;
320                 token = TT_POW;
321
322                 break;
323               }
324               if (str.charAt(chIndx) == '=') {
325                 chIndx++;
326                 token = TT_TIMESBY;
327
328                 break;
329               }
330             }
331
332             break;
333           case '+' :
334             token = TT_ADD;
335             if (str.length() > chIndx) {
336               if (str.charAt(chIndx) == '+') {
337                 chIndx++;
338                 token = TT_INCREMENT;
339
340                 break;
341               }
342               if (str.charAt(chIndx) == '=') {
343                 chIndx++;
344                 token = TT_ADDTO;
345
346                 break;
347               }
348             }
349             break;
350           case '-' :
351             token = TT_SUBTRACT;
352             if (str.length() > chIndx) {
353               if (str.charAt(chIndx) == '-') {
354                 chIndx++;
355                 token = TT_DECREMENT;
356
357                 break;
358               }
359               if (str.charAt(chIndx) == '=') {
360                 chIndx++;
361                 token = TT_SUBTRACTFROM;
362
363                 break;
364               }
365             }
366
367             break;
368           case '=' :
369             token = TT_SET;
370
371             if (str.length() > chIndx) {
372               ch = str.charAt(chIndx);
373
374               if (ch == '=') {
375                 chIndx++;
376                 token = TT_EQUAL;
377
378                 break;
379               }
380               if (ch == '>') {
381                 chIndx++;
382                 token = TT_FOREACH;
383
384                 break;
385               }
386             }
387
388             break;
389           case '!' :
390             token = TT_NOT;
391
392             if (str.length() > chIndx) {
393               if (str.charAt(chIndx) == '=') {
394                 chIndx++;
395                 token = TT_UNEQUAL;
396
397                 break;
398               }
399             }
400
401             break;
402           case '>' :
403             token = TT_GREATER;
404
405             if (str.length() > chIndx) {
406               if (str.charAt(chIndx) == '=') {
407                 chIndx++;
408                 token = TT_GREATEREQUAL;
409
410                 break;
411               }
412             }
413
414             break;
415           case '<' :
416             token = TT_LESS;
417
418             if (str.length() > chIndx) {
419               if (str.charAt(chIndx) == '=') {
420                 chIndx++;
421                 token = TT_LESSEQUAL;
422
423                 break;
424               }
425             }
426
427             break;
428
429           case '|' :
430             if (str.length() > chIndx) {
431               if (str.charAt(chIndx++) == '|') {
432                 token = TT_OR;
433
434                 break;
435               }
436             }
437
438             break;
439           case '&' :
440             if (str.length() > chIndx) {
441               if (str.charAt(chIndx++) == '&') {
442                 token = TT_AND;
443
444                 break;
445               }
446             }
447
448             break;
449           case ':' :
450             token = TT_DDOT;
451
452             break;
453           case '#' :
454             token = TT_HASH;
455
456             break;
457             //          case '@' :
458             //            token = TT_AT;
459             //
460             //            break;
461           default :
462             throwSyntaxError("unexpected character: '" + ch + "'");
463         }
464
465         if (token == TT_UNDEFINED) {
466           throwSyntaxError("token not found");
467         }
468
469         return;
470       }
471     }
472
473     chIndx = str.length() + 1;
474     ch = ' ';
475     token = TT_EOF;
476   }
477
478   void getIdentifier() {
479     StringBuffer ident = new StringBuffer();
480     ident.append(ch);
481
482     ident.append(ch);
483     if (ch == '$') {
484       token = TT_VARIABLE;
485     } else {
486       token = TT_IDENTIFIER;
487     }
488     getChar();
489     while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || (ch >= '_')) {
490       ident.append(ch);
491       getChar();
492     }
493     identifier = ident.toString();
494
495     Integer i = (Integer) keywordMap.get(identifier);
496     if (i != null) {
497       token = i.intValue();
498     }
499   }
500
501   void getNumber() {
502     StringBuffer inum = new StringBuffer();
503     char dFlag = ' ';
504     int numFormat = 10;
505
506     // save first digit
507     char firstCh = ch;
508     inum.append(ch);
509
510     getChar();
511     // determine number conversions:
512     if (firstCh == '0') {
513       switch (ch) {
514         case 'b' :
515           numFormat = 2;
516           getChar();
517           break;
518         case 'B' :
519           numFormat = 2;
520           getChar();
521           break;
522         case 'o' :
523           numFormat = 8;
524           getChar();
525           break;
526         case 'O' :
527           numFormat = 8;
528           getChar();
529           break;
530         case 'x' :
531           numFormat = 16;
532           getChar();
533           break;
534         case 'X' :
535           numFormat = 16;
536           getChar();
537           break;
538       }
539     }
540
541     if (numFormat == 16) {
542       while ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) {
543         inum.append(ch);
544         getChar();
545       }
546     } else {
547       while ((ch >= '0' && ch <= '9') || (ch == '.') || (ch == 'E') || (ch == 'e')) {
548         if ((ch == '.') || (ch == 'E') || (ch == 'e')) {
549           if (ch == '.' && dFlag != ' ') {
550             break;
551           }
552           if ((dFlag == 'E') || (dFlag == 'e')) {
553             break;
554           }
555           dFlag = ch;
556           inum.append(ch);
557           getChar();
558           if ((ch == '-') || (ch == '+')) {
559             inum.append(ch);
560             getChar();
561           }
562         } else {
563           inum.append(ch);
564           getChar();
565         }
566       }
567     }
568
569     // token = TT_INT_NUMBER;
570
571     try {
572       if (dFlag != ' ') {
573         doubleNumber = new Double(inum.toString());
574         token = TT_DOUBLE_NUMBER;
575         return;
576       } else {
577         longNumber = Long.valueOf(inum.toString(), numFormat);
578         token = TT_INT_NUMBER;
579         return;
580       }
581
582     } catch (Throwable e) {
583       throwSyntaxError("Number format error: " + inum.toString());
584     }
585   }
586
587   public void start() throws SyntaxError {
588     statementList();
589     if (token != TT_EOF) {
590       if (token == TT_ARGCLOSE) {
591         throwSyntaxError("too many closing ')'; end-of-file not reached");
592       }
593       if (token == TT_LISTCLOSE) {
594         throwSyntaxError("too many closing '}'; end-of-file not reached");
595       }
596       if (token == TT_PARTCLOSE) {
597         throwSyntaxError("too many closing ']'; end-of-file not reached");
598       }
599
600       throwSyntaxError("end-of-file not reached");
601     }
602
603   }
604
605   public void statementList() {
606     statement();
607   }
608
609   public void statement() {
610     while (token != TT_UNDEFINED) {
611       if (token > TT_KEYWORD) {
612         if (token == TT_case) {
613           getNextToken();
614           constant();
615           if (token == TT_DDOT) {
616             getNextToken();
617             statement();
618           } else {
619             throwSyntaxError("':' character after 'case' constant expected.");
620           }
621           return;
622         } else if (token == TT_default) {
623           getNextToken();
624           if (token == TT_DDOT) {
625             getNextToken();
626             statement();
627           } else {
628             throwSyntaxError("':' character after 'default' expected.");
629           }
630           return;
631         } else if (token == TT_include || token == TT_include_once) {
632           getNextToken();
633           expression();
634           if (token == TT_SEMICOLON) {
635             getNextToken();
636           } else {
637             throwSyntaxError("';' character after 'include' or 'include_once' expected.");
638           }
639           return;
640         } else if (token == TT_require || token == TT_require_once) {
641           getNextToken();
642           constant();
643           if (token == TT_SEMICOLON) {
644             getNextToken();
645           } else {
646             throwSyntaxError("';' character after 'require' or 'require_once' expected.");
647           }
648           return;
649         } else if (token == TT_if) {
650           getNextToken();
651           if (token == TT_ARGOPEN) {
652             getNextToken();
653           } else {
654             throwSyntaxError("'(' expected after 'if' keyword.");
655           }
656           expression();
657           if (token == TT_ARGCLOSE) {
658             getNextToken();
659           } else {
660             throwSyntaxError("')' expected after 'if' condition.");
661           }
662           ifStatement();
663           return;
664
665         } else if (token == TT_switch) {
666           getNextToken();
667           if (token == TT_ARGOPEN) {
668             getNextToken();
669           } else {
670             throwSyntaxError("'(' expected after 'switch' keyword.");
671           }
672           expression();
673           if (token == TT_ARGCLOSE) {
674             getNextToken();
675           } else {
676             throwSyntaxError("')' expected after 'switch' condition.");
677           }
678           switchStatement();
679           return;
680         } else if (token == TT_for) {
681           getNextToken();
682           if (token == TT_ARGOPEN) {
683             getNextToken();
684           } else {
685             throwSyntaxError("'(' expected after 'for' keyword.");
686           }
687           if (token == TT_SEMICOLON) {
688             getNextToken();
689           } else {
690             expression();
691             if (token == TT_SEMICOLON) {
692               getNextToken();
693             } else {
694               throwSyntaxError("';' character after 'for' expected.");
695             }
696           }
697           if (token == TT_SEMICOLON) {
698             getNextToken();
699           } else {
700             expression();
701             if (token == TT_SEMICOLON) {
702               getNextToken();
703             } else {
704               throwSyntaxError("';' character after 'for' expected.");
705             }
706           }
707           if (token == TT_ARGCLOSE) {
708             getNextToken();
709           } else {
710             expression();
711             if (token == TT_ARGCLOSE) {
712               getNextToken();
713             } else {
714               throwSyntaxError("')' expected after 'for' condition.");
715             }
716           }
717           forStatement();
718           return;
719         } else if (token == TT_while) {
720           getNextToken();
721           if (token == TT_ARGOPEN) {
722             getNextToken();
723           } else {
724             throwSyntaxError("'(' expected after 'while' keyword.");
725           }
726           expression();
727           if (token == TT_ARGCLOSE) {
728             getNextToken();
729           } else {
730             throwSyntaxError("')' expected after 'while' condition.");
731           }
732           whileStatement();
733           return;
734         } else if (token == TT_foreach) {
735           getNextToken();
736           if (token == TT_ARGOPEN) {
737             getNextToken();
738           } else {
739             throwSyntaxError("'(' expected after 'foreach' keyword.");
740           }
741           expression();
742           if (token == TT_as) {
743             getNextToken();
744           } else {
745             throwSyntaxError("'as' expected after 'foreach' exxpression.");
746           }
747           variable();
748           if (token == TT_FOREACH) {
749             getNextToken();
750             variable();
751           }
752           if (token == TT_ARGCLOSE) {
753             getNextToken();
754           } else {
755             throwSyntaxError("')' expected after 'foreach' expression.");
756           }
757           foreachStatement();
758           return;
759
760         } else if (token == TT_continue || token == TT_break || token == TT_return) {
761           getNextToken();
762           if (token != TT_SEMICOLON) {
763             expression();
764           }
765           if (token == TT_SEMICOLON) {
766             getNextToken();
767           } else {
768             throwSyntaxError("';' expected after 'continue', 'break' or 'return'.");
769           }
770           return;
771
772         } else if (token == TT_echo) {
773           getNextToken();
774           expressionList();
775           if (token == TT_SEMICOLON) {
776             getNextToken();
777           } else {
778             throwSyntaxError("';' expected after 'echo' statement.");
779           }
780           return;
781
782         } else if (token == TT_print) {
783           getNextToken();
784           expression();
785           if (token == TT_SEMICOLON) {
786             getNextToken();
787           } else {
788             throwSyntaxError("';' expected after 'print' statement.");
789           }
790           return;
791
792         } else if (token == TT_global || token == TT_static) {
793           getNextToken();
794           variableList();
795           if (token == TT_SEMICOLON) {
796             getNextToken();
797           } else {
798             throwSyntaxError("';' expected after 'global' or 'static' statement.");
799           }
800           return;
801
802         } else if (token == TT_unset) {
803           getNextToken();
804           if (token == TT_ARGOPEN) {
805             getNextToken();
806           } else {
807             throwSyntaxError("'(' expected after 'unset' keyword.");
808           }
809           variableList();
810           if (token == TT_ARGCLOSE) {
811             getNextToken();
812           } else {
813             throwSyntaxError("')' expected after 'unset' statement.");
814           }
815           if (token == TT_SEMICOLON) {
816             getNextToken();
817           } else {
818             throwSyntaxError("';' expected after 'unset' statement.");
819           }
820           return;
821
822         } else if (token == TT_exit || token == TT_die) {
823           getNextToken();
824           if (token != TT_SEMICOLON) {
825             exitStatus();
826           }
827           if (token == TT_SEMICOLON) {
828             getNextToken();
829           } else {
830             throwSyntaxError("';' expected after 'exit' or 'die' statement.");
831           }
832           return;
833
834         } else if (token == TT_define) {
835           getNextToken();
836           if (token == TT_ARGOPEN) {
837             getNextToken();
838           } else {
839             throwSyntaxError("'(' expected after 'define' keyword.");
840           }
841           constant();
842           if (token == TT_COMMA) {
843             getNextToken();
844           } else {
845             throwSyntaxError("',' expected after first 'define' constant.");
846           }
847           constant();
848           if (token == TT_ARGCLOSE) {
849             getNextToken();
850           } else {
851             throwSyntaxError("')' expected after 'define' statement.");
852           }
853           if (token == TT_SEMICOLON) {
854             getNextToken();
855           } else {
856             throwSyntaxError("';' expected after 'define' statement.");
857           }
858           return;
859
860         }
861
862       } else {
863         if (token == TT_LISTOPEN) {
864           getNextToken();
865           statementList();
866           if (token == TT_LISTCLOSE) {
867             getNextToken();
868           } else {
869             throwSyntaxError("'}' expected.");
870           }
871         }
872       }
873       expression();
874       if (token == TT_SEMICOLON) {
875         getNextToken();
876       } else {
877         throwSyntaxError("';' expected after expression.");
878       }
879     }
880   }
881
882   public void labeledStatement() {
883   }
884
885   public void expressionStatement() {
886   }
887
888   public void inclusionStatement() {
889   }
890
891   public void compoundStatement() {
892   }
893
894   public void selectionStatement() {
895   }
896
897   public void iterationStatement() {
898   }
899
900   public void jumpStatement() {
901   }
902
903   public void outputStatement() {
904   }
905
906   public void scopeStatement() {
907   }
908
909   public void flowStatement() {
910   }
911
912   public void definitionStatement() {
913   }
914
915   public void ifStatement() {
916   }
917
918   public void switchStatement() {
919   }
920
921   public void forStatement() {
922   }
923
924   public void whileStatement() {
925   }
926
927   public void foreachStatement() {
928   }
929
930   public void exitStatus() {
931     if (token == TT_ARGOPEN) {
932       getNextToken();
933     } else {
934       throwSyntaxError("'(' expected in 'exit-status'.");
935     }
936     if (token != TT_ARGCLOSE) {
937       expression();
938     }
939     if (token == TT_ARGCLOSE) {
940       getNextToken();
941     } else {
942       throwSyntaxError("')' expected after 'exit-status'.");
943     }
944   }
945
946   public void expressionList() {
947     do {
948       expression();
949       if (token == TT_COMMA) {
950         getNextToken();
951       } else {
952         break;
953       }
954     } while (true);
955   }
956
957   public void expression() {
958     if (token == TT_STRING_CONSTANT || token == TT_INTERPOLATED_STRING) {
959       getNextToken();
960     } else {
961       postfixExpression();
962       //      while (token != TT_SEMICOLON) {
963       //        getNextToken();
964       //      }
965     }
966   }
967
968   public void postfixExpression() {
969
970   }
971
972   public void variableList() {
973     do {
974       variable();
975       if (token == TT_COMMA) {
976         getNextToken();
977       } else {
978         break;
979       }
980     } while (true);
981   }
982
983   public void variable() {
984     if (token == TT_VARIABLE) {
985       getNextToken();
986     } else {
987       throwSyntaxError("$-variable expected in variable-list.");
988     }
989   }
990
991   public void constant() {
992
993   }
994 }