001    /*
002      Copyright (C) 2001-2003 Laurent Martelli <laurent@aopsys.com>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public
015      License along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
017      USA */
018    
019    package org.objectweb.jac.core.parsers.acc;
020    
021    
022    import java.io.FileInputStream;
023    import java.io.FileNotFoundException;
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.io.InputStreamReader;
027    import java.io.Reader;
028    import java.io.StringWriter;
029    import java.net.MalformedURLException;
030    import java.net.URL;
031    import java.util.Set;
032    import java_cup.runtime.Scanner;
033    import java_cup.runtime.Symbol;
034    import org.apache.log4j.Logger;
035    import org.objectweb.jac.util.PushbackReader;
036    
037    public class AccScanner implements Scanner {
038        static Logger logger = Logger.getLogger("acc.scanner");
039        static Logger loggerParser = Logger.getLogger("acc.parser");
040    
041        PushbackReader input;
042        String streamName;
043        int line;
044        boolean bol; // beginning of line
045        boolean previousBol; // beginning of line
046    
047        AccScanner include = null;
048        Set blockKeywords;
049    
050        public AccScanner(Reader input, String streamName, Set blockKeywords) {
051            logger.debug("FileInputStream("+streamName+")");
052            this.input = new PushbackReader(input,2);
053            this.streamName = streamName;
054            this.line = 1;
055            this.bol = true;
056            this.blockKeywords = blockKeywords;
057            //System.out.println(streamName+":INIT");
058        }
059    
060        public AccScanner(String streamName, Set blockKeywords) 
061            throws IOException
062        {
063            InputStream inputStream = null;
064            try {
065                logger.debug("FileInputStream("+streamName+")");
066                inputStream = new FileInputStream(streamName);
067            } catch ( FileNotFoundException e ) {
068                try {
069                    logger.debug("getResourceAsStream("+streamName+")");
070                    inputStream = getClass().getClassLoader().getResourceAsStream(streamName);
071                } catch (Exception e2 ) {
072                    logger.debug("new URL("+streamName+")");
073                    try {
074                        URL url = new URL(streamName);
075                        inputStream = url.openStream();
076                    } catch (MalformedURLException e3) {
077                        e3.printStackTrace();
078                    }
079                }
080            }
081    
082            if (inputStream==null)
083                throw new FileNotFoundException("file not found : "+streamName);
084    
085            this.input = new PushbackReader(new InputStreamReader(inputStream),2);
086            this.streamName = streamName;
087            this.line = 1;
088            this.bol = true;
089            this.blockKeywords = blockKeywords;
090    
091            //      System.out.println(streamName+":INIT");
092        }
093    
094        public void printState() {
095            logger.error(getLine());
096            if (include!=null)
097                include.printState();
098        }
099    
100        protected boolean isEof(int c) {
101            return c==-1 || c==65535;
102        }
103    
104        public Symbol next_token() throws java.lang.Exception
105        {
106            if (include!=null) {
107                Symbol token = include.next_token();
108                if (token.sym != -1) {
109                    return token;
110                }
111                include = null;
112            }
113    
114            boolean quoted = false;
115            int c = input.read();
116            while (true) {
117                // skip white spaces and CR
118                while (c==' ' || c=='\t' || c=='\n' || c=='\r') {
119                    //System.out.println(streamName+":"+line+":WHITESPACE");
120                    if (c=='\n' || c=='\r') {
121                        //System.out.println(streamName+":"+line+":NEWLINE1");
122                        line++;
123                        bol = true;
124                        if (c=='\r') {
125                            c = input.read();
126                            if (c != '\n')
127                                input.unread(c);
128                        }
129                    }
130                    c = input.read();
131                }
132                // skip comments
133                if (c=='/') {
134                    int ahead = input.read();
135                    if (ahead=='/') {
136                        // c++-style comments
137                        //System.out.println(streamName+":"+line+":C++ COMMENT");
138                        c = input.read();
139                        while (c!='\n' && c!='\r' && c!=-1) {
140                            c = input.read();
141                        }
142                        continue;
143                    } if (ahead=='*') {
144                        // c-style comments
145                        //System.out.println(streamName+":"+line+":C COMMENT");
146                        int prev = c;
147                        c = input.read();
148                        boolean state = false;
149                        while (!(state && c=='/') && c!=-1) {
150                            if ((c=='\n' && prev!='\r') || c=='\r') line++;
151                            state = (c=='*');
152                            prev = c;
153                            c = input.read();
154                        }
155                        if (c==-1) {
156                            logger.error("unclosed comment");
157                            printState();
158                            break;
159                        }
160                        c = input.read();
161                        continue;
162                   
163                    } else {
164                        input.unread(ahead);
165                        break;
166                    }
167                }
168                break;
169            }
170    
171            int start = input.getPosition()-1;
172            Symbol token = null;/*new Symbol(AccSymbols.error);*/
173    
174            //System.out.println("Testing char `"+c+"' = `"+(char)c+"'");
175            switch (c) {
176                case -1: 
177                case 65535:
178                    return null;
179                    //token = new Symbol(AccSymbols.EOF);
180                    //System.out.println(streamName+":TOKEN "+token.sym+"["+token.value+"]");
181                    //break;
182                case '{':
183                    token = new Symbol(AccSymbols.LBRACE,start,start,"{");
184                    break;
185                case '}':
186                    token = new Symbol(AccSymbols.RBRACE,start,start,"}");
187                    break;
188                case ',':
189                    token = new Symbol(AccSymbols.COMMA,start,start,",");
190                    break;
191                    // when -1 is unread(), it becomes \uFFFF
192                case ';':
193                    token = new Symbol(AccSymbols.EOL,start,start,";");
194                    break;
195                case '/':
196                    token = new Symbol(AccSymbols.EOL,start,start,"/");
197                    break;
198                case '"':
199                    // quoted string
200                    {
201                        quoted = true;
202                        StringWriter value = new StringWriter();
203                        c = input.read();
204                        boolean escaped = false;
205                        if (c=='\\') {
206                            escaped = true;
207                            c=readEscaped(input.read());
208                        }
209                        while ((c!='"' || escaped) && !isEof(c)) {
210                            value.write(c);
211                            c = input.read();
212                            if (c=='\n')
213                                line++;
214                            escaped = false;
215                            if (c=='\\') {
216                                c=readEscaped(input.read());
217                                escaped = true;
218                            }
219                        }
220                        //System.out.println("value="+value);
221                        String str = value.toString();
222                        token = new Symbol(AccSymbols.ATOMIC_VALUE,
223                                           start,start+str.length()-1,str);
224                    }
225                    break;
226                default:
227                    {
228                        StringWriter value = new StringWriter();
229                        String delim="/{},; \t\n\r";
230                        while (delim.indexOf(c)==-1 && c!=-1) {
231                            value.write(c);
232                            c = input.read();
233                        }
234                        input.unread(c);
235                        //System.out.println("value="+value);
236                        String str = value.toString();
237                        token = new Symbol(AccSymbols.ATOMIC_VALUE,
238                                           start,start+str.length()-1,str);
239                    }
240            }
241    
242            previousBol = bol;
243    
244            if (bol && token.sym == AccSymbols.ATOMIC_VALUE && token.value.equals("include")) {
245                bol = false;
246                String filename = (String)next_token().value;
247                //System.out.println(streamName+":INCLUDE("+filename+")");
248                include = new AccScanner(filename,blockKeywords);
249                return next_token();
250            }
251            if (!quoted && token.sym == AccSymbols.ATOMIC_VALUE && 
252                ( token.value.equals("class") || 
253                  token.value.equals("member") || 
254                  token.value.equals("method") || 
255                  token.value.equals("attribute") || 
256                  token.value.equals("block") ||
257                  blockKeywords.contains(token.value) ) )
258            {
259                token.sym = AccSymbols.CLASS;
260                //System.out.println(streamName+":TOKEN block");
261            }
262            if (!quoted && token.sym == AccSymbols.ATOMIC_VALUE && 
263                token.value.equals("import")) {
264                token.sym = AccSymbols.IMPORT;
265            }
266            if (!quoted && token.sym == AccSymbols.ATOMIC_VALUE && 
267                token.value.equals("null")) {
268                token.value = null;
269            }
270            token.left = start;
271            if (token.value instanceof String)
272                token.right = start + ((String)token.value).length() - 1;
273            if (quoted)
274                token.right += 2;
275    
276            //System.out.println(streamName+":TOKEN "+token.sym+"["+token.value+"]");
277            loggerParser.debug("read token "+token+"  = "+token.value);
278            bol = false;
279            return token;
280        }
281    
282        /**
283         * Read a char from input, possibly escaped by '\'
284         */
285        int readEscaped(int c) throws IOException 
286        {
287            switch (c) {
288                case 'n':
289                    return '\n';
290                case 'r':
291                    return '\r';
292                case 't':
293                    return '\t';
294                default:
295                    return c;
296            }
297        }
298    
299        /**
300         * Return the current line number
301         */
302        public String getLine() {
303            if (include!=null)
304                return include.getLine();
305            else
306                return streamName+":"+(previousBol?line-1:line);
307        }
308    
309    }