mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Handle 'from __future__ import print_function' in Python 2.6 (PY-314)
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
set JAVA_HOME="C:\Program Files\Java\jdk1.5.0_12"
|
||||
call C:\Src\jflex-1.4.1\bin\jflex.bat --table --skel idea-skeleton Python.flex
|
||||
python FixLexer.py
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import os, os.path
|
||||
if os.path.exists("_PythonLexerBad.java"): os.unlink("_PythonLexerBad.java")
|
||||
os.rename("_PythonLexer.java", "_PythonLexerBad.java")
|
||||
f = open("_PythonLexerBad.java", "r")
|
||||
out = open("_PythonLexer.java", "w")
|
||||
for line in f.readlines():
|
||||
i = line.find("zzBufferL[")
|
||||
if i >= 0:
|
||||
line = line [0:i-1] + "zzBufferL.charAt(" + line [i+10:-3] + ");\n"
|
||||
out.write(line)
|
||||
f.close()
|
||||
out.close()
|
||||
os.unlink("_PythonLexerBad.java")
|
||||
@@ -3,6 +3,7 @@ package com.jetbrains.python.lexer;
|
||||
|
||||
import com.intellij.lexer.FlexLexer;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
|
||||
%%
|
||||
|
||||
@@ -100,7 +101,6 @@ TRIPLE_APOS_LITERAL = {THREE_APOS} {STRING_3CHAR_APOS}* {THREE_APOS}
|
||||
"not" { return PyTokenTypes.NOT_KEYWORD; }
|
||||
"or" { return PyTokenTypes.OR_KEYWORD; }
|
||||
"pass" { return PyTokenTypes.PASS_KEYWORD; }
|
||||
"print" { return PyTokenTypes.PRINT_KEYWORD; }
|
||||
"raise" { return PyTokenTypes.RAISE_KEYWORD; }
|
||||
"return" { return PyTokenTypes.RETURN_KEYWORD; }
|
||||
"try" { return PyTokenTypes.TRY_KEYWORD; }
|
||||
|
||||
@@ -21,6 +21,10 @@ public class PythonHighlightingLexer extends PythonLexer {
|
||||
if (tokenText.equals("with")) return PyTokenTypes.WITH_KEYWORD;
|
||||
if (tokenText.equals("as")) return PyTokenTypes.AS_KEYWORD;
|
||||
}
|
||||
if (myLanguageLevel.hasPrintStatement()) {
|
||||
final String tokenText = getTokenText();
|
||||
if (tokenText.equals("print")) return PyTokenTypes.PRINT_KEYWORD;
|
||||
}
|
||||
return super.getTokenType();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,256 +0,0 @@
|
||||
/** initial size of the lookahead buffer */
|
||||
--- private static final int ZZ_BUFFERSIZE = ...;
|
||||
|
||||
/** lexical states */
|
||||
--- lexical states, charmap
|
||||
|
||||
/* error codes */
|
||||
private static final int ZZ_UNKNOWN_ERROR = 0;
|
||||
private static final int ZZ_NO_MATCH = 1;
|
||||
private static final int ZZ_PUSHBACK_2BIG = 2;
|
||||
private static final char[] EMPTY_BUFFER = new char[0];
|
||||
private static final int YYEOF = -1;
|
||||
private static java.io.Reader zzReader = null; // Fake
|
||||
|
||||
/* error messages for the codes above */
|
||||
private static final String ZZ_ERROR_MSG[] = {
|
||||
"Unkown internal scanner error",
|
||||
"Error: could not match input",
|
||||
"Error: pushback value was too large"
|
||||
};
|
||||
|
||||
--- isFinal list
|
||||
/** the current state of the DFA */
|
||||
private int zzState;
|
||||
|
||||
/** the current lexical state */
|
||||
private int zzLexicalState = YYINITIAL;
|
||||
|
||||
/** this buffer contains the current text to be matched and is
|
||||
the source of the yytext() string */
|
||||
private CharSequence zzBuffer = "";
|
||||
|
||||
/** this buffer may contains the current text array to be matched when it is cheap to acquire it */
|
||||
private char[] zzBufferArray;
|
||||
|
||||
/** the textposition at the last accepting state */
|
||||
private int zzMarkedPos;
|
||||
|
||||
/** the textposition at the last state to be included in yytext */
|
||||
private int zzPushbackPos;
|
||||
|
||||
/** the current text position in the buffer */
|
||||
private int zzCurrentPos;
|
||||
|
||||
/** startRead marks the beginning of the yytext() string in the buffer */
|
||||
private int zzStartRead;
|
||||
|
||||
/** endRead marks the last character in the buffer, that has been read
|
||||
from input */
|
||||
private int zzEndRead;
|
||||
|
||||
/**
|
||||
* zzAtBOL == true <=> the scanner is currently at the beginning of a line
|
||||
*/
|
||||
private boolean zzAtBOL = true;
|
||||
|
||||
/** zzAtEOF == true <=> the scanner is at the EOF */
|
||||
private boolean zzAtEOF;
|
||||
|
||||
--- user class code
|
||||
|
||||
--- constructor declaration
|
||||
|
||||
public final int getTokenStart(){
|
||||
return zzStartRead;
|
||||
}
|
||||
|
||||
public final int getTokenEnd(){
|
||||
return getTokenStart() + yylength();
|
||||
}
|
||||
|
||||
public void reset(CharSequence buffer, int start, int end,int initialState){
|
||||
zzBuffer = buffer;
|
||||
zzBufferArray = com.intellij.util.text.CharArrayUtil.fromSequenceWithoutCopying(buffer);
|
||||
zzCurrentPos = zzMarkedPos = zzStartRead = start;
|
||||
zzPushbackPos = 0;
|
||||
zzAtEOF = false;
|
||||
zzAtBOL = true;
|
||||
zzEndRead = end;
|
||||
yybegin(initialState);
|
||||
}
|
||||
|
||||
// For Demetra compatibility
|
||||
public void reset(CharSequence buffer, int initialState){
|
||||
reset(buffer, 0, buffer.length(), initialState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refills the input buffer.
|
||||
*
|
||||
* @return <code>false</code>, iff there was new input.
|
||||
*
|
||||
* @exception java.io.IOException if any I/O-Error occurs
|
||||
*/
|
||||
private boolean zzRefill() throws java.io.IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the current lexical state.
|
||||
*/
|
||||
public final int yystate() {
|
||||
return zzLexicalState;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enters a new lexical state
|
||||
*
|
||||
* @param newState the new lexical state
|
||||
*/
|
||||
public final void yybegin(int newState) {
|
||||
zzLexicalState = newState;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the text matched by the current regular expression.
|
||||
*/
|
||||
public final CharSequence yytext() {
|
||||
return zzBuffer.subSequence(zzStartRead, zzMarkedPos);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the character at position <tt>pos</tt> from the
|
||||
* matched text.
|
||||
*
|
||||
* It is equivalent to yytext().charAt(pos), but faster
|
||||
*
|
||||
* @param pos the position of the character to fetch.
|
||||
* A value from 0 to yylength()-1.
|
||||
*
|
||||
* @return the character at position pos
|
||||
*/
|
||||
public final char yycharat(int pos) {
|
||||
return zzBufferArray != null ? zzBufferArray[zzStartRead+pos]:zzBuffer.charAt(zzStartRead+pos);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the length of the matched text region.
|
||||
*/
|
||||
public final int yylength() {
|
||||
return zzMarkedPos-zzStartRead;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reports an error that occured while scanning.
|
||||
*
|
||||
* In a wellformed scanner (no or only correct usage of
|
||||
* yypushback(int) and a match-all fallback rule) this method
|
||||
* will only be called with things that "Can't Possibly Happen".
|
||||
* If this method is called, something is seriously wrong
|
||||
* (e.g. a JFlex bug producing a faulty scanner etc.).
|
||||
*
|
||||
* Usual syntax/scanner level error handling should be done
|
||||
* in error fallback rules.
|
||||
*
|
||||
* @param errorCode the code of the errormessage to display
|
||||
*/
|
||||
--- zzScanError declaration
|
||||
String message;
|
||||
try {
|
||||
message = ZZ_ERROR_MSG[errorCode];
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {
|
||||
message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR];
|
||||
}
|
||||
|
||||
--- throws clause
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pushes the specified amount of characters back into the input stream.
|
||||
*
|
||||
* They will be read again by then next call of the scanning method
|
||||
*
|
||||
* @param number the number of characters to be read again.
|
||||
* This number must not be greater than yylength()!
|
||||
*/
|
||||
--- yypushback decl (contains zzScanError exception)
|
||||
if ( number > yylength() )
|
||||
zzScanError(ZZ_PUSHBACK_2BIG);
|
||||
|
||||
zzMarkedPos -= number;
|
||||
}
|
||||
|
||||
|
||||
--- zzDoEOF
|
||||
/**
|
||||
* Resumes scanning until the next regular expression is matched,
|
||||
* the end of input is encountered or an I/O-Error occurs.
|
||||
*
|
||||
* @return the next token
|
||||
* @exception java.io.IOException if any I/O-Error occurs
|
||||
*/
|
||||
--- yylex declaration
|
||||
int zzInput;
|
||||
int zzAction;
|
||||
|
||||
// cached fields:
|
||||
int zzCurrentPosL;
|
||||
int zzMarkedPosL;
|
||||
int zzEndReadL = zzEndRead;
|
||||
CharSequence zzBufferL = zzBuffer;
|
||||
char[] zzBufferArrayL = zzBufferArray;
|
||||
char [] zzCMapL = ZZ_CMAP;
|
||||
|
||||
--- local declarations
|
||||
|
||||
while (true) {
|
||||
zzMarkedPosL = zzMarkedPos;
|
||||
|
||||
--- start admin (line, char, col count)
|
||||
zzAction = -1;
|
||||
|
||||
zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
|
||||
|
||||
--- start admin (lexstate etc)
|
||||
|
||||
zzForAction: {
|
||||
while (true) {
|
||||
|
||||
--- next input, line, col, char count, next transition, isFinal action
|
||||
zzAction = zzState;
|
||||
zzMarkedPosL = zzCurrentPosL;
|
||||
--- line count update
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// store back cached position
|
||||
zzMarkedPos = zzMarkedPosL;
|
||||
--- char count update
|
||||
|
||||
--- actions
|
||||
default:
|
||||
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
|
||||
zzAtEOF = true;
|
||||
--- eofvalue
|
||||
}
|
||||
else {
|
||||
--- no match
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
--- main
|
||||
|
||||
}
|
||||
@@ -25,10 +25,10 @@ public class ParsingContext {
|
||||
private final PsiBuilder myBuilder;
|
||||
private final LanguageLevel myLanguageLevel;
|
||||
|
||||
public ParsingContext(final PsiBuilder builder, LanguageLevel languageLevel) {
|
||||
public ParsingContext(final PsiBuilder builder, LanguageLevel languageLevel, StatementParsing.FUTURE futureFlag) {
|
||||
myBuilder = builder;
|
||||
myLanguageLevel = languageLevel;
|
||||
stmtParser = new StatementParsing(this);
|
||||
stmtParser = new StatementParsing(this, futureFlag);
|
||||
expressionParser = new ExpressionParsing(this);
|
||||
functionParser = new FunctionParsing(this);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public class PyParser implements PsiParser {
|
||||
private static final Logger LOGGER = Logger.getInstance(PyParser.class.getName());
|
||||
|
||||
private final LanguageLevel myLanguageLevel;
|
||||
private StatementParsing.FUTURE myFutureFlag;
|
||||
|
||||
public PyParser() {
|
||||
myLanguageLevel = LanguageLevel.getDefault();
|
||||
@@ -29,7 +30,7 @@ public class PyParser implements PsiParser {
|
||||
builder.setDebugMode(false);
|
||||
long start = System.currentTimeMillis();
|
||||
final PsiBuilder.Marker rootMarker = builder.mark();
|
||||
ParsingContext context = new ParsingContext(builder, myLanguageLevel);
|
||||
ParsingContext context = new ParsingContext(builder, myLanguageLevel, myFutureFlag);
|
||||
StatementParsing stmt_parser = context.getStatementParser();
|
||||
builder.setTokenTypeRemapper(stmt_parser); // must be done before touching the caching lexer with eof() call.
|
||||
while (!builder.eof()) {
|
||||
@@ -42,4 +43,8 @@ public class PyParser implements PsiParser {
|
||||
LOGGER.debug("Parsed " + String.format("%.1f", kb) + "K file in " + diff + "ms");
|
||||
return ast;
|
||||
}
|
||||
|
||||
public void setFutureFlag(StatementParsing.FUTURE future) {
|
||||
myFutureFlag = future;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,18 +22,23 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
@NonNls protected static final String TOK_FUTURE_IMPORT = "__future__";
|
||||
@NonNls protected static final String TOK_WITH_STATEMENT = "with_statement";
|
||||
@NonNls protected static final String TOK_NESTED_SCOPES = "nested_scopes";
|
||||
@NonNls protected static final String TOK_PRINT_FUNCTION = "print_function";
|
||||
@NonNls protected static final String TOK_WITH = "with";
|
||||
@NonNls protected static final String TOK_AS = "as";
|
||||
@NonNls protected static final String TOK_PRINT = "print";
|
||||
|
||||
protected enum Phase {NONE, FROM, FUTURE, IMPORT} // 'from __future__ import' phase
|
||||
private Phase myFutureImportPhase = Phase.NONE;
|
||||
private boolean myExpectAsKeyword = false;
|
||||
|
||||
protected enum FUTURE {ABSOLUTE_IMPORT, DIVISION, GENERATORS, NESTED_SCOPES, WITH_STATEMENT}
|
||||
public enum FUTURE {ABSOLUTE_IMPORT, DIVISION, GENERATORS, NESTED_SCOPES, WITH_STATEMENT, PRINT_FUNCTION}
|
||||
protected Set<FUTURE> myFutureFlags = EnumSet.noneOf(FUTURE.class);
|
||||
|
||||
protected StatementParsing(ParsingContext context) {
|
||||
protected StatementParsing(ParsingContext context, @Nullable FUTURE futureFlag) {
|
||||
super(context);
|
||||
if (futureFlag != null) {
|
||||
myFutureFlags.add(futureFlag);
|
||||
}
|
||||
}
|
||||
|
||||
public void parseStatement() {
|
||||
@@ -89,7 +94,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
if (firstToken == null) {
|
||||
return;
|
||||
}
|
||||
if (firstToken == PyTokenTypes.PRINT_KEYWORD) {
|
||||
if (firstToken == PyTokenTypes.PRINT_KEYWORD && hasPrintStatement()) {
|
||||
parsePrintStatement(builder, inSuite);
|
||||
return;
|
||||
}
|
||||
@@ -193,6 +198,10 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
builder.error("statement expected, found " + firstToken.toString());
|
||||
}
|
||||
|
||||
private boolean hasPrintStatement() {
|
||||
return myContext.getLanguageLevel().hasPrintStatement() && !myFutureFlags.contains(FUTURE.PRINT_FUNCTION);
|
||||
}
|
||||
|
||||
private void checkEndOfStatement(boolean inSuite) {
|
||||
PsiBuilder builder = myContext.getBuilder();
|
||||
if (builder.getTokenType() == PyTokenTypes.STATEMENT_BREAK) {
|
||||
@@ -398,6 +407,9 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
else if (TOK_NESTED_SCOPES.equals(token_text)) {
|
||||
myFutureFlags.add(FUTURE.NESTED_SCOPES);
|
||||
}
|
||||
else if (TOK_PRINT_FUNCTION.equals(token_text)) {
|
||||
myFutureFlags.add(FUTURE.PRINT_FUNCTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
myExpectAsKeyword = true; // possible 'as' comes as an ident; reparse it as keyword if found
|
||||
@@ -723,6 +735,10 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
|
||||
) {
|
||||
return PyTokenTypes.WITH_KEYWORD;
|
||||
}
|
||||
else if (hasPrintStatement() && source == PyTokenTypes.IDENTIFIER &&
|
||||
isWordAtPosition(text, start, end, TOK_PRINT)) {
|
||||
return PyTokenTypes.PRINT_KEYWORD;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,22 +10,28 @@ import org.jetbrains.annotations.NotNull;
|
||||
* @author yole
|
||||
*/
|
||||
public enum LanguageLevel {
|
||||
PYTHON24(false), PYTHON25(false), PYTHON26(true);
|
||||
PYTHON24(false, true), PYTHON25(false, true), PYTHON26(true, true), PYTHON30(true, false);
|
||||
|
||||
public static LanguageLevel getDefault() {
|
||||
return PYTHON26;
|
||||
}
|
||||
|
||||
private boolean myHasWithStatement;
|
||||
private boolean myHasPrintStatement;
|
||||
|
||||
LanguageLevel(boolean hasWithStatement) {
|
||||
LanguageLevel(boolean hasWithStatement, boolean hasPrintStatement) {
|
||||
myHasWithStatement = hasWithStatement;
|
||||
myHasPrintStatement = hasPrintStatement;
|
||||
}
|
||||
|
||||
public boolean hasWithStatement() {
|
||||
return myHasWithStatement;
|
||||
}
|
||||
|
||||
public boolean hasPrintStatement() {
|
||||
return myHasPrintStatement;
|
||||
}
|
||||
|
||||
public static LanguageLevel fromPythonVersion(String pythonVersion) {
|
||||
if (pythonVersion.startsWith("2.6")) {
|
||||
return PYTHON26;
|
||||
@@ -33,6 +39,9 @@ public enum LanguageLevel {
|
||||
if (pythonVersion.startsWith("2.5")) {
|
||||
return PYTHON25;
|
||||
}
|
||||
if (pythonVersion.startsWith("3.0") || pythonVersion.startsWith("3.1")) {
|
||||
return PYTHON30;
|
||||
}
|
||||
return PYTHON24;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import com.intellij.lang.*;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.PsiBuilder;
|
||||
import com.intellij.lang.PsiBuilderFactory;
|
||||
import com.intellij.lexer.Lexer;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
@@ -9,6 +12,7 @@ import com.intellij.psi.impl.source.tree.FileElement;
|
||||
import com.intellij.psi.tree.IStubFileElementType;
|
||||
import com.jetbrains.python.lexer.PythonIndentingLexer;
|
||||
import com.jetbrains.python.parsing.PyParser;
|
||||
import com.jetbrains.python.parsing.StatementParsing;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -20,7 +24,7 @@ public class PyFileElementType extends IStubFileElementType {
|
||||
|
||||
@Override
|
||||
public int getStubVersion() {
|
||||
return 4;
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -34,7 +38,12 @@ public class PyFileElementType extends IStubFileElementType {
|
||||
|
||||
final PsiBuilder builder = factory.createBuilder(project, chameleon, lexer, getLanguage(), chameleon.getChars());
|
||||
|
||||
final PsiParser parser = new PyParser(languageLevel);
|
||||
final PyParser parser = new PyParser(languageLevel);
|
||||
if (languageLevel == LanguageLevel.PYTHON26 &&
|
||||
node.getPsi().getContainingFile().getName().equals("__builtin__.py")) {
|
||||
parser.setFutureFlag(StatementParsing.FUTURE.PRINT_FUNCTION);
|
||||
}
|
||||
|
||||
return parser.parse(this, builder).getFirstChildNode();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
from __future__ import print_function
|
||||
print('a')
|
||||
@@ -0,0 +1,22 @@
|
||||
PyFile:PrintAsFunction26.py
|
||||
PyFromImportStatement
|
||||
PsiElement(Py:FROM_KEYWORD)('from')
|
||||
PsiWhiteSpace(' ')
|
||||
PyReferenceExpression: __future__
|
||||
PsiElement(Py:IDENTIFIER)('__future__')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IMPORT_KEYWORD)('import')
|
||||
PsiWhiteSpace(' ')
|
||||
PyImportElement
|
||||
PyReferenceExpression: print_function
|
||||
PsiElement(Py:IDENTIFIER)('print_function')
|
||||
PsiWhiteSpace('\n')
|
||||
PyExpressionStatement
|
||||
PyCallExpression: print
|
||||
PyReferenceExpression: print
|
||||
PsiElement(Py:IDENTIFIER)('print')
|
||||
PyArgumentList
|
||||
PsiElement(Py:LPAR)('(')
|
||||
PyStringLiteralExpression: a
|
||||
PsiElement(Py:STRING_LITERAL)(''a'')
|
||||
PsiElement(Py:RPAR)(')')
|
||||
@@ -100,6 +100,10 @@ public class PythonParsingTest extends ParsingTestCase {
|
||||
doTest(LanguageLevel.PYTHON26);
|
||||
}
|
||||
|
||||
public void testPrintAsFunction26() throws Exception {
|
||||
doTest(LanguageLevel.PYTHON26);
|
||||
}
|
||||
|
||||
public void doTest() throws Exception {
|
||||
doTest(LanguageLevel.PYTHON25);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user