时间:2021-07-01 10:21:17 帮助过:39人阅读
token [NAME]: func
token [NAME]: fab
token [LPAR]: (
token [NAME]: number
token [RPAR]: )
token [COLON]: :
token [NEWLINE]:
token [INDENT]:
token [NAME]: if
token [LPAR]: (
token [NAME]: number
token [EQEQUAL]: ==
token [NUMBER]: 1
token [RPAR]: )
token [COLON]: :
token [NEWLINE]:
token [INDENT]:
token [NAME]: return
token [NUMBER]: 1
token [NEWLINE]:
token [DEDENT]:
token [NAME]: if
token [LPAR]: (
token [NAME]: number
token [EQEQUAL]: ==
token [NUMBER]: 2
token [RPAR]: )
token [COLON]: :
token [NEWLINE]:
token [INDENT]:
token [NAME]: return
token [NUMBER]: 2
token [NEWLINE]:
token [DEDENT]:
token [NAME]: return
token [NAME]: fab
token [LPAR]: (
token [NAME]: number
token [MINUS]: -
token [NUMBER]: 1
token [RPAR]: )
token [PLUS]: +
token [NAME]: fab
token [LPAR]: (
token [NAME]: number
token [MINUS]: -
token [NUMBER]: 2
token [RPAR]: )
token [NEWLINE]:
token [DEDENT]:
关键词: layout sensitive parsing, off-side rulefuncdef: 'def' NAME parameters ':' funcdef_body {
$5.line > $1.line && $5.col > $1.col
}?
;
我有一个想法。
首先确定源代码的缩进是由空格控制还是制表符控制,并在之后使用一致的方式进行处理。为了便于说明,下面不妨假设我们需要处理的代码都是由制表符控制缩进的。
在词法解析的阶段,需要把制表符输出为一个token,比如叫tabToken。然后对于每个语句,就有这样一个特点:在换行符的后边的tabToken数目就是代码的层级数(特殊情况另说)。根据这个原理,处理的时候就可以在层级数变化的时候,插入花括号token:当层级数变大的时候,插入左花括号token;变小的时候,插入右花括号token。
那么,我们就可以使用处理C代码的方法来处理Python代码了。
我思考过这个问题: Cirru 解析缩进的方案Leading whitespace (spaces and tabs) at the beginning of a logical line is used
to compute the indentation level of the line, which in turn is used to determine
the grouping of statements.
First, tabs are replaced (from left to right) by one to eight spaces such that
the total number of characters up to and including the replacement is a multiple
of eight (this is intended to be the same rule as used by Unix). The total
number of spaces preceding the first non-blank character then determines the
line’s indentation. Indentation cannot be split over multiple physical lines
using backslashes; the whitespace up to the first backslash determines the
indentation.
/* Get next token, after space stripping etc. */
static int
tok_get(register struct tok_state *tok, char **p_start, char **p_end)
{
register int c;
int blankline;
*p_start = *p_end = NULL;
nextline:
tok->start = NULL;
blankline = 0;
/* Get indentation level */
if (tok->atbol) {
register int col = 0;
register int altcol = 0;
tok->atbol = 0;
for (;;) {
c = tok_nextc(tok);
if (c == ' ')
col++, altcol++;
else if (c == '\t') {
col = (col/tok->tabsize + 1) * tok->tabsize;
altcol = (altcol/tok->alttabsize + 1)
* tok->alttabsize;
}
else if (c == '\014') /* Control-L (formfeed) */
col = altcol = 0; /* For Emacs users */
else
break;
}
tok_backup(tok, c);
if (c == '#' || c == '\n') {
/* Lines with only whitespace and/or comments
shouldn't affect the indentation and are
not passed to the parser as NEWLINE tokens,
except *totally* empty lines in interactive
mode, which signal the end of a command group. */
if (col == 0 && c == '\n' && tok->prompt != NULL)
blankline = 0; /* Let it through */
else
blankline = 1; /* Ignore completely */
/* We can't jump back right here since we still
may need to skip to the end of a comment */
}
if (!blankline && tok->level == 0) {
if (col == tok->indstack[tok->indent]) {
/* No change */
if (altcol != tok->altindstack[tok->indent]) {
if (indenterror(tok))
return ERRORTOKEN;
}
}
else if (col > tok->indstack[tok->indent]) {
/* Indent -- always one */
if (tok->indent+1 >= MAXINDENT) {
tok->done = E_TOODEEP;
tok->cur = tok->inp;
return ERRORTOKEN;
}
if (altcol <= tok->altindstack[tok->indent]) {
if (indenterror(tok))
return ERRORTOKEN;
}
tok->pendin++;
tok->indstack[++tok->indent] = col;
tok->altindstack[tok->indent] = altcol;
}
else /* col < tok->indstack[tok->indent] */ {
/* Dedent -- any number, must be consistent */
while (tok->indent > 0 &&
col < tok->indstack[tok->indent]) {
tok->pendin--;
tok->indent--;
}
if (col != tok->indstack[tok->indent]) {
tok->done = E_DEDENT;
tok->cur = tok->inp;
return ERRORTOKEN;
}
if (altcol != tok->altindstack[tok->indent]) {
if (indenterror(tok))
return ERRORTOKEN;
}
}
}
}
tok->start = tok->cur;
/* Return pending indents/dedents */
if (tok->pendin != 0) {
if (tok->pendin < 0) {
tok->pendin++;
return DEDENT;
}
else {
tok->pendin--;
return INDENT;
}
}
again:
tok->start = NULL;
/* Skip spaces */
do {
c = tok_nextc(tok);
} while (c == ' ' || c == '\t' || c == '\014');
/* Set start of current token */
tok->start = tok->cur - 1;
/* Skip comment, while looking for tab-setting magic */
if (c == '#') {
static char *tabforms[] = {
"tab-width:", /* Emacs */
":tabstop=", /* vim, full form */
":ts=", /* vim, abbreviated form */
"set tabsize=", /* will vi never die? */
/* more templates can be added here to support other editors */
};
char cbuf[80];
char *tp, **cp;
tp = cbuf;
do {
*tp++ = c = tok_nextc(tok);
} while (c != EOF && c != '\n' &&
(size_t)(tp - cbuf + 1) < sizeof(cbuf));
*tp = '\0';
for (cp = tabforms;
cp < tabforms + sizeof(tabforms)/sizeof(tabforms[0]);
cp++) {
if ((tp = strstr(cbuf, *cp))) {
int newsize = atoi(tp + strlen(*cp));
if (newsize >= 1 && newsize <= 40) {
tok->tabsize = newsize;
if (Py_VerboseFlag)
PySys_WriteStderr(
"Tab size set to %d\n",
newsize);
}
}
}
while (c != EOF && c != '\n')
c = tok_nextc(tok);
}
个人感觉可以把tab和换行符当成token处理。\n\t* 这样,记录\t的数量,这样处理每一个statement都可以方便地知道在哪一层缩进了。