Parser: parse a PHP expression after <?= ; statements aren't allowed
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpeclipse / phpeditor / php / PHPPartitionScanner.java
1 /**********************************************************************
2  Copyright (c) 2002  Widespace, OU  and others.
3  All rights reserved.   This program and the accompanying materials
4  are made available under the terms of the Common Public License v1.0
5  which accompanies this distribution, and is available at
6  http://solareclipse.sourceforge.net/legal/cpl-v10.html
7
8  Contributors:
9  Igor Malinin - initial contribution
10
11  $Id: PHPPartitionScanner.java,v 1.27 2005-05-06 00:57:28 stefanbjarni Exp $
12  **********************************************************************/
13 package net.sourceforge.phpeclipse.phpeditor.php;
14
15 import java.util.HashMap;
16 import java.util.Map;
17
18 import net.sourceforge.phpeclipse.ui.text.rules.AbstractPartitioner;
19
20 import org.eclipse.jface.text.Assert;
21 import org.eclipse.jface.text.BadLocationException;
22 import org.eclipse.jface.text.IDocument;
23 import org.eclipse.jface.text.rules.ICharacterScanner;
24 import org.eclipse.jface.text.rules.IPartitionTokenScanner;
25 import org.eclipse.jface.text.rules.IToken;
26 import org.eclipse.jface.text.rules.Token;
27
28 /**
29  * 
30  * 
31  * @author Igor Malinin
32  */
33 public class PHPPartitionScanner implements IPartitionTokenScanner {
34   //    public static final String JSP_DIRECTIVE = "__jsp_directive";
35   //    public static final String JSP_COMMENT = "__jsp_comment";
36   //// public static final String JSP_TAG = "__jsp_tag";
37   //    public static final String JSP_DECLARATION = "__jsp_declaration";
38   public static final String PHP_SCRIPTING_AREA = "__php_scripting_area ";
39
40   //    public static final String JSP_EXPRESSION = "__jsp_expression";
41
42   public static final int STATE_DEFAULT = 0;
43
44   //    public static final int STATE_TAG = 1;
45   //    public static final int STATE_SCRIPT = 2;
46
47   private IDocument document;
48
49   private int begin;
50
51   private int end;
52
53   private int offset;
54
55   private int length;
56
57   private int position;
58
59   private int state;
60
61   private Map tokens = new HashMap();
62
63   public PHPPartitionScanner() {
64   }
65
66   /*
67    * @see org.eclipse.jface.text.rules.ITokenScanner#nextToken()
68    */
69   public IToken nextToken() {
70     offset += length;
71
72     /*
73      * switch (state) { case STATE_TAG: return nextTagToken(); }
74      */
75
76     switch (read()) {
77     case ICharacterScanner.EOF:
78       state = STATE_DEFAULT;
79       return getToken(null);
80
81     case '<':
82       switch (read()) {
83       case ICharacterScanner.EOF:
84         state = STATE_DEFAULT;
85         return getToken(null);
86
87       case '?': // <%SCRIPLET <%@DIRECTIVE <%!DECLARATION <%=EXPRESSION <%--COMMENT
88         int ch = read();
89         //                                               if (Character.isWhitespace((char)ch)) {
90         //                                                 return nextJSPToken(PHP_SCRIPTING_AREA);
91         //                                               }
92         switch (ch) {
93         case ICharacterScanner.EOF:
94           state = STATE_DEFAULT;
95           return getToken(PHP_SCRIPTING_AREA);
96
97         //                                                      case '-': // <%- <%--COMMENT
98         //                                                              switch (read()) {
99         //                                                                      case ICharacterScanner.EOF:
100         //                                                                      case '-': // <%--
101         //                                                                              return nextCommentToken();
102         //                                                              }
103         //
104         //                                                              break;
105         }
106
107         return scanUntilPHPEndToken(PHP_SCRIPTING_AREA);
108       }
109
110       unread();
111     }
112
113     loop: while (true) {
114       switch (read()) {
115       case ICharacterScanner.EOF:
116         state = STATE_DEFAULT;
117         return getToken(null);
118
119       case '<':
120         switch (read()) {
121         case ICharacterScanner.EOF:
122           state = STATE_DEFAULT;
123           return getToken(null);
124
125         case '?':
126           unread();
127           break;
128
129         case '<':
130           unread();
131
132         default:
133           continue loop;
134         }
135
136         unread();
137
138         state = STATE_DEFAULT;
139         return getToken(null);
140       }
141     }
142   }
143
144   private IToken scanUntilPHPEndToken(String token) {
145     int ch = read();
146     while (true) {
147       switch (ch) {
148       case ICharacterScanner.EOF:
149         state = STATE_DEFAULT;
150         return getToken(token);
151       case '"': // double quoted string
152         // read until end of double quoted string
153         if (!readUntilEscapedDQ()) {
154           state = STATE_DEFAULT;
155           return getToken(token);
156         }
157         break;
158       case '\'': // single quoted string
159         // read until end of single quoted string
160         if (!readUntilEscapedSQ()) {
161           state = STATE_DEFAULT;
162           return getToken(token);
163         }
164         break;
165       case '/': // comment start?
166         ch = read();
167         switch (ch) {
168         case ICharacterScanner.EOF:
169           break;
170         case '/':
171           // read until end of line
172           readSingleLine();
173           break;
174         case '*':
175           // read until end of comment
176           readMultiLineComment();
177           break;
178         default:
179           continue;
180         }
181         break;
182       case '#': // line comment
183         // read until end of line
184         readSingleLine();
185         break;
186       case '?':
187         ch = read();
188         switch (ch) {
189         case ICharacterScanner.EOF:
190         case '>':
191           state = STATE_DEFAULT;
192           return getToken(token);
193
194         case '?':
195           continue;
196         }
197       }
198
199       ch = read();
200     }
201   }
202
203   //    private IToken nextCommentToken() {
204   //            int ch = read();
205   //            loop: while (true) {
206   //                    switch (ch) {
207   //                            case ICharacterScanner.EOF:
208   //                                    break loop;
209   //
210   //                            case '-': // - --%>
211   //                                    ch = read();
212   //                                    switch (ch) {
213   //                                            case ICharacterScanner.EOF:
214   //                                                    break loop;
215   //
216   //                                            case '-': // -- --%>
217   //                                                    ch = read();
218   //                                                    switch (ch) {
219   //                                                            case ICharacterScanner.EOF:
220   //                                                                    break loop;
221   //
222   //                                                            case '%': // --% --%>
223   //                                                                    ch = read();
224   //                                                                    switch (ch) {
225   //                                                                            case ICharacterScanner.EOF:
226   //                                                                            case '>':
227   //                                                                                    break loop;
228   //                                                                    }
229   //
230   //                                                                    continue loop;
231   //
232   //                                                            case '-': // --- ---%>
233   //                                                                    unread();
234   //                                                                    continue loop;
235   //                                                    }
236   //
237   //                                                    ch = read();
238   //                                                    continue loop;
239   //                                    }
240   //                    }
241   //
242   //                    ch = read();
243   //            }
244   //
245   //            return getToken(JSP_COMMENT);
246   //    }
247
248   private IToken getToken(String type) {
249     length = position - offset;
250
251     if (length == 0) {
252       return Token.EOF;
253     }
254
255     if (type == null) {
256       return Token.UNDEFINED;
257     }
258
259     IToken token = (IToken) tokens.get(type);
260     if (token == null) {
261       token = new Token(type);
262       tokens.put(type, token);
263     }
264
265     return token;
266   }
267
268   private int read() {
269     if (position >= end) {
270       return ICharacterScanner.EOF;
271     }
272
273     try {
274       return document.getChar(position++);
275     } catch (BadLocationException e) {
276       --position;
277       return ICharacterScanner.EOF;
278     }
279   }
280
281   private boolean readUntilEscapedDQ() {
282     // search last double quoted character
283     if (position >= end) {
284       return false;
285     }
286     try {
287       char ch;
288       while (true) {
289         ch = document.getChar(position++);
290         if (ch == '\\') {
291           ch = document.getChar(position++); // ignore escaped character
292         } else if (ch == '"') {
293           return true;
294         }
295       }
296     } catch (BadLocationException e) {
297       --position;
298     }
299     return false;
300   }
301   
302   private boolean readUntilEscapedSQ() {
303     // search last single quoted character
304     if (position >= end) {
305       return false;
306     }
307     try {  
308       char ch;
309       while (true) {
310         ch = document.getChar(position++); 
311         if (ch == '\\') {
312           ch = document.getChar(position++); // ignore escaped character
313         } else if (ch == '\'') {
314           return true;
315         }
316       }
317     } catch (BadLocationException e) {
318       --position;
319     }
320     return false;
321   }
322
323   private void readSingleLine() {
324     if (position >= end) {
325       return;
326     }
327     try {
328       while (document.getChar(position++) != '\n') {
329
330       }
331     } catch (BadLocationException e) {
332       --position;
333       return;
334     }
335   }
336
337   private void readMultiLineComment() {
338     if (position >= end) {
339       return;
340     }
341     try {
342       char ch;
343       while (true) {
344         ch = document.getChar(position++);
345         if (ch == '*') {
346           if (document.getChar(position) == '/') {
347             position++;
348             break;
349           }
350         }
351       }
352     } catch (BadLocationException e) {
353       --position;
354       return;
355     }
356   }
357
358   private void unread() {
359     --position;
360   }
361
362   /*
363    * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenOffset()
364    */
365   public int getTokenOffset() {
366     if (AbstractPartitioner.DEBUG) {
367       Assert.isTrue(offset >= 0, Integer.toString(offset));
368     }
369     return offset;
370   }
371
372   /*
373    * @see org.eclipse.jface.text.rules.ITokenScanner#getTokenLength()
374    */
375   public int getTokenLength() {
376     return length;
377   }
378
379   /*
380    * @see org.eclipse.jface.text.rules.ITokenScanner#setRange(IDocument, int, int)
381    */
382   public void setRange(IDocument document, int offset, int length) {
383     this.document = document;
384     this.begin = offset;
385     this.end = offset + length;
386
387     this.offset = offset;
388     this.position = offset;
389     this.length = 0;
390   }
391
392   /*
393    * @see org.eclipse.jface.text.rules.IPartitionTokenScanner
394    */
395   public void setPartialRange(IDocument document, int offset, int length, String contentType, int partitionOffset) {
396     state = STATE_DEFAULT;
397     //    if (partitionOffset > -1) {
398     //          int delta= offset - partitionOffset;
399     //          if (delta > 0) {
400     //                  this.setRange(document, partitionOffset, length + delta);
401     //                  return;
402     //          }
403     //  }
404     setRange(document, partitionOffset, length);
405   }
406
407   //    private boolean isContinuationPartition(IDocument document, int offset) {
408   //            try {
409   //                    String type = document.getContentType(offset - 1);
410   //
411   //                    if (type != IDocument.DEFAULT_CONTENT_TYPE) {
412   //                            return true;
413   //                    }
414   //            } catch (BadLocationException e) {}
415   //
416   //            return false;
417   //    }
418 }