python等縮排語言的詞法分析實現:
定義兩個虛擬Token:
tokens {
INDENT;
DEDENT;
}
還有一個縮排棧,用於確定是縮排一行,還是回退一行:
Stack<Integer> _indentStack = new Stack<Integer>();
在開始做詞法分析之前,壓入一個預設的Indent,這一步其實沒什麼必要,只是用來避免寫判斷棧頂是否為空白的冗餘判斷:
_indentStack = new Stack<Integer>();
_indentStack.push(new Integer(0));
針對每一個新行,首先判斷行首是否是空格,如果是空格,則空格計1、Tab鍵計8個空格,如果僅僅是空行,跳過。如果在碰到行尾之前碰有非Null 字元,則將空格數與棧頂的空格對比,如果大於,壓入當前行的空格數,並產生一個虛擬Indent Token,如果小於,將所有空格數大於當前行的出棧,並產生一個虛擬Dedent Token:
NEWLINE
@init {
int spaces = 0;
}
: ((('\u000C')?('\r')? '\n' ) | '\t' | ' ')* (('\u000C')?('\r')? '\n')
leading_space = (' ' { spaces++; } | '\t' { spaces += 8; spaces -= (spaces \% 8); })*
{
if ( !_inATable &&
(getCharPositionInLine() == 0 ||
implicitLineJoiningLevel > 0) ) {
emit(new ClassicToken(NEWLINE, this.getText(), HIDDEN));
} else {
emit(new ClassicToken(NEWLINE, this.getText(), HIDDEN));
}
if ( implicitLineJoiningLevel == 0 &&
_indentStack.size() > 0) {
if (spaces > _indentStack.peek().intValue() ) {
_indentStack.push(new Integer(spaces));
emit(new ClassicToken(INDENT, ">"));
}
else {
while ( spaces < _indentStack.peek().intValue() ) {
_indentStack.pop();
emit(new ClassicToken(DEDENT, "<"));
}
}
}
}
| ((('\u000C')?('\r')? '\n' ) | '\t' | ' ')* (('\u000C')?('\r')? '\n')
(' ' | '\t')* '#' (~'\n')*
{
$channel = HIDDEN;
}
;
當然還要考慮純注釋行,和空格後僅跟有注釋的情形。
這樣詞法分析過程中,縮排的詞法分析過程就完了,在文法分析中,Indent是透明的,例如:
compound_stmt
: IF compound_condition (suite)+ elif_clause* else_clause?
| assignment
;
suite
: INDENT (compound_stmt)+ (DEDENT | EOF)
;
上面的文法中,INDENT和DEDENT就跟括弧的處理沒什麼區別,只不過DEDENT可選,主要是考慮直接在代碼後就EOF的情況。