1/*
2 * [The "BSD license"]
3 *  Copyright (c) 2010 Terence Parr
4 *  All rights reserved.
5 *
6 *  Redistribution and use in source and binary forms, with or without
7 *  modification, are permitted provided that the following conditions
8 *  are met:
9 *  1. Redistributions of source code must retain the above copyright
10 *      notice, this list of conditions and the following disclaimer.
11 *  2. Redistributions in binary form must reproduce the above copyright
12 *      notice, this list of conditions and the following disclaimer in the
13 *      documentation and/or other materials provided with the distribution.
14 *  3. The name of the author may not be used to endorse or promote products
15 *      derived from this software without specific prior written permission.
16 *
17 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30
31Please excuse my obvious lack of Java experience. The code here is probably
32full of WTFs - though IMHO Java is the Real WTF(TM) here...
33
34 */
35
36package org.antlr.codegen;
37
38import org.antlr.runtime.Token;
39import org.antlr.tool.Grammar;
40
41import java.util.ArrayList;
42import java.util.List;
43
44public class PythonTarget extends Target {
45    /** Target must be able to override the labels used for token types */
46    public String getTokenTypeAsTargetLabel(CodeGenerator generator,
47					    int ttype) {
48	// use ints for predefined types;
49	// <invalid> <EOR> <DOWN> <UP>
50	if ( ttype >= 0 && ttype <= 3 ) {
51	    return String.valueOf(ttype);
52	}
53
54	String name = generator.grammar.getTokenDisplayName(ttype);
55
56	// If name is a literal, return the token type instead
57	if ( name.charAt(0)=='\'' ) {
58	    return String.valueOf(ttype);
59	}
60
61	return name;
62    }
63
64    public String getTargetCharLiteralFromANTLRCharLiteral(
65            CodeGenerator generator,
66            String literal) {
67	int c = Grammar.getCharValueFromGrammarCharLiteral(literal);
68	return String.valueOf(c);
69    }
70
71    private List splitLines(String text) {
72		ArrayList l = new ArrayList();
73		int idx = 0;
74
75		while ( true ) {
76			int eol = text.indexOf("\n", idx);
77			if ( eol == -1 ) {
78				l.add(text.substring(idx));
79				break;
80			}
81			else {
82				l.add(text.substring(idx, eol+1));
83				idx = eol+1;
84			}
85		}
86
87		return l;
88    }
89
90    public List postProcessAction(List chunks, Token actionToken) {
91		/* TODO
92		   - check for and report TAB usage
93		 */
94
95		//System.out.println("\n*** Action at " + actionToken.getLine() + ":" + actionToken.getColumn());
96
97		/* First I create a new list of chunks. String chunks are splitted into
98		   lines and some whitespace my be added at the beginning.
99
100		   As a result I get a list of chunks
101		   - where the first line starts at column 0
102		   - where every LF is at the end of a string chunk
103		*/
104
105		List nChunks = new ArrayList();
106		for (int i = 0; i < chunks.size(); i++) {
107			Object chunk = chunks.get(i);
108
109			if ( chunk instanceof String ) {
110				String text = (String)chunks.get(i);
111				if ( nChunks.size() == 0 && actionToken.getCharPositionInLine() >= 0 ) {
112					// first chunk and some 'virtual' WS at beginning
113					// prepend to this chunk
114
115					String ws = "";
116					for ( int j = 0 ; j < actionToken.getCharPositionInLine() ; j++ ) {
117						ws += " ";
118					}
119					text = ws + text;
120				}
121
122				List parts = splitLines(text);
123				for ( int j = 0 ; j < parts.size() ; j++ ) {
124					chunk = parts.get(j);
125					nChunks.add(chunk);
126				}
127			}
128			else {
129				if ( nChunks.size() == 0 && actionToken.getCharPositionInLine() >= 0 ) {
130					// first chunk and some 'virtual' WS at beginning
131					// add as a chunk of its own
132
133					String ws = "";
134					for ( int j = 0 ; j <= actionToken.getCharPositionInLine() ; j++ ) {
135						ws += " ";
136					}
137					nChunks.add(ws);
138				}
139
140				nChunks.add(chunk);
141			}
142		}
143
144		int lineNo = actionToken.getLine();
145		int col = 0;
146
147		// strip trailing empty lines
148		int lastChunk = nChunks.size() - 1;
149		while ( lastChunk > 0
150				&& nChunks.get(lastChunk) instanceof String
151				&& ((String)nChunks.get(lastChunk)).trim().length() == 0 )
152			lastChunk--;
153
154		// string leading empty lines
155		int firstChunk = 0;
156		while ( firstChunk <= lastChunk
157				&& nChunks.get(firstChunk) instanceof String
158				&& ((String)nChunks.get(firstChunk)).trim().length() == 0
159				&& ((String)nChunks.get(firstChunk)).endsWith("\n") ) {
160			lineNo++;
161			firstChunk++;
162		}
163
164		int indent = -1;
165		for ( int i = firstChunk ; i <= lastChunk ; i++ ) {
166			Object chunk = nChunks.get(i);
167
168			//System.out.println(lineNo + ":" + col + " " + quote(chunk.toString()));
169
170			if ( chunk instanceof String ) {
171				String text = (String)chunk;
172
173				if ( col == 0 ) {
174					if ( indent == -1 ) {
175						// first non-blank line
176						// count number of leading whitespaces
177
178						indent = 0;
179						for ( int j = 0; j < text.length(); j++ ) {
180							if ( !Character.isWhitespace(text.charAt(j)) )
181								break;
182
183							indent++;
184						}
185					}
186
187					if ( text.length() >= indent ) {
188						int j;
189						for ( j = 0; j < indent ; j++ ) {
190							if ( !Character.isWhitespace(text.charAt(j)) ) {
191								// should do real error reporting here...
192								System.err.println("Warning: badly indented line " + lineNo + " in action:");
193								System.err.println(text);
194								break;
195							}
196						}
197
198						nChunks.set(i, text.substring(j));
199					}
200					else if ( text.trim().length() > 0 ) {
201						// should do real error reporting here...
202						System.err.println("Warning: badly indented line " + lineNo + " in action:");
203						System.err.println(text);
204					}
205				}
206
207				if ( text.endsWith("\n") ) {
208					lineNo++;
209					col = 0;
210				}
211				else {
212					col += text.length();
213				}
214			}
215			else {
216				// not really correct, but all I need is col to increment...
217				col += 1;
218			}
219		}
220
221		return nChunks;
222    }
223}
224