Пересмотренный вопрос
Значение в стеке Yacc контролируется YYSTYPE или %union
. Используйте YYSTYPE, когда информация о типе проста; используйте %union
, когда оно сложное.
Одна из моих грамматик содержит:
struct Token
{
int toktype;
char *start;
char *end;
};
typedef struct Token Token;
#define YYSTYPE Token
По разным причинам (не обязательно хорошим) моя грамматика использует лексический анализатор ручной работы вместо Lex.
В правилах грамматики вы указываете на элементы типа NAME в вашем примере как $1
(где фактическое число зависит от того, где токен появляется в списке токенов или терминалов, составляющих правило).
Например (та же грамматика):
disconnect
: K_DISCONNECT K_CURRENT
{ conn->ctype = CONN_CURRENT; }
| K_DISCONNECT K_ALL
{ conn->ctype = CONN_ALL; }
| K_DISCONNECT K_DEFAULT
{ conn->ctype = CONN_DEFAULT; }
| K_DISCONNECT string
{ conn->ctype = CONN_STRING;
set_connection(conn, $2.start, $2.end);
}
;
И
load
: K_LOAD K_FROM opt_file_pipe string load_opt_list K_INSERT
{
set_string("load file", load->file, sizeof(load->file),
$4.start, $4.end);
load->stmt = $6.start;
}
;
Я не знаю, помогает ли видеть контур ручной работы yylex()
; в грамматике это функция в том же файле, что и yyparse()
.
static const char *c_token; /* Where to start next token search */
static int yylex(void)
{
char buffer[MAX_LEXTOKENLENGTH];
const char *start;
if (c_token == 0)
abort();
if (bare_filename_ok)
start = scan_for_filename(c_token, &c_token);
else
start = sqltoken(c_token, &c_token);
yylval.start = CONST_CAST(char *, start);
yylval.end = CONST_CAST(char *, c_token);
if (*start == '\0')
{
yylval.toktype = 0;
return yylval.toktype;
}
set_token(buffer, sizeof(buffer), start, c_token);
#ifdef YYDEBUG
if (YYDEBUGVAR > 1)
printf("yylex(): token = %s\n", buffer);
#endif /* YYDEBUG */
/* printf("yylex(): token = %s\n", buffer); */
if (isalpha((unsigned char)buffer[0]) || buffer[0] == '_')
{
Keyword kw;
Keyword *p;
kw.keyword = buffer;
p = (Keyword *)bsearch(&kw, keylist, DIM(keylist), sizeof(Keyword),
kw_compare); /*=C++=*/
if (p == 0)
yylval.toktype = S_IDENTIFIER;
else
yylval.toktype = p->token;
}
else if (buffer[0] == '\'')
{
yylval.toktype = S_SQSTRING;
}
else if (buffer[0] == '"')
{
yylval.toktype = S_DQSTRING;
}
else if (isdigit((unsigned char)buffer[0]))
{
yylval.toktype = S_NUMBER;
}
else if (buffer[0] == '.' && isdigit((unsigned char)buffer[1]))
{
yylval.toktype = S_NUMBER;
}
... распознаются различные односимвольные символы ...
else if (buffer[0] == ':')
{
assert(buffer[1] == '\0');
yylval.toktype = C_COLON;
}
else
{
yylval.toktype = S_ERROR;
}
return yylval.toktype;
}
Оригинальный вопрос
Переменная обычно является глобальной переменной - ваш код Yacc использует одно из двух возможных объявлений:
extern char *yytext; /* Correct for Flex */
extern char yytext[]; /* Correct for traditional Lex */
То, что из этого является правильным, зависит от того, как это определяет ваша версия Lex.
Если вы хотите добавить длину (возможно, yytextlen
), то вы можете определить такую переменную, и каждый возврат из yylex()
гарантирует, что установлена yytextlen
. Кроме того, вы можете настроить грамматику для вызова wwlex()
, а ваш wwlex()
просто сделает:
int wwlex(void)
{
int rc = yylex();
yytextlen = strlen(yytext);
return rc;
}
Или вы можете организовать, чтобы Lex генерировал код с переименованием, и чтобы Yacc продолжал вызывать yylex()
, и вы предоставляете приведенный выше код как yylex()
и вызываете переименованную функцию Lex. В любом случае работает.