PostgreSQL Source Code  git master
pl_scanner.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pl_scanner.c
4  * lexical scanning for PL/pgSQL
5  *
6  *
7  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  * src/pl/plpgsql/src/pl_scanner.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17 
18 #include "mb/pg_wchar.h"
19 #include "parser/scanner.h"
20 
21 #include "plpgsql.h"
22 #include "pl_gram.h" /* must be after parser/scanner.h */
23 
24 
25 /* Klugy flag to tell scanner how to look up identifiers */
27 
28 /*
29  * A word about keywords:
30  *
31  * We keep reserved and unreserved keywords in separate headers. Be careful
32  * not to put the same word in both headers. Also be sure that pl_gram.y's
33  * unreserved_keyword production agrees with the unreserved header. The
34  * reserved keywords are passed to the core scanner, so they will be
35  * recognized before (and instead of) any variable name. Unreserved words
36  * are checked for separately, usually after determining that the identifier
37  * isn't a known variable name. If plpgsql_IdentifierLookup is DECLARE then
38  * no variable names will be recognized, so the unreserved words always work.
39  * (Note in particular that this helps us avoid reserving keywords that are
40  * only needed in DECLARE sections.)
41  *
42  * In certain contexts it is desirable to prefer recognizing an unreserved
43  * keyword over recognizing a variable name. In particular, at the start
44  * of a statement we should prefer unreserved keywords unless the statement
45  * looks like an assignment (i.e., first token is followed by ':=' or '[').
46  * This rule allows most statement-introducing keywords to be kept unreserved.
47  * (We still have to reserve initial keywords that might follow a block
48  * label, unfortunately, since the method used to determine if we are at
49  * start of statement doesn't recognize such cases. We'd also have to
50  * reserve any keyword that could legitimately be followed by ':=' or '['.)
51  * Some additional cases are handled in pl_gram.y using tok_is_keyword().
52  *
53  * We try to avoid reserving more keywords than we have to; but there's
54  * little point in not reserving a word if it's reserved in the core grammar.
55  * Currently, the following words are reserved here but not in the core:
56  * BEGIN BY DECLARE EXECUTE FOREACH IF LOOP STRICT WHILE
57  */
58 
59 /* ScanKeywordList lookup data for PL/pgSQL keywords */
60 #include "pl_reserved_kwlist_d.h"
61 #include "pl_unreserved_kwlist_d.h"
62 
63 /* Token codes for PL/pgSQL keywords */
64 #define PG_KEYWORD(kwname, value) value,
65 
66 static const uint16 ReservedPLKeywordTokens[] = {
67 #include "pl_reserved_kwlist.h"
68 };
69 
71 #include "pl_unreserved_kwlist.h"
72 };
73 
74 #undef PG_KEYWORD
75 
76 /*
77  * This macro must recognize all tokens that can immediately precede a
78  * PL/pgSQL executable statement (that is, proc_sect or proc_stmt in the
79  * grammar). Fortunately, there are not very many, so hard-coding in this
80  * fashion seems sufficient.
81  */
82 #define AT_STMT_START(prev_token) \
83  ((prev_token) == ';' || \
84  (prev_token) == K_BEGIN || \
85  (prev_token) == K_THEN || \
86  (prev_token) == K_ELSE || \
87  (prev_token) == K_LOOP)
88 
89 
90 /* Auxiliary data about a token (other than the token type) */
91 typedef struct
92 {
93  YYSTYPE lval; /* semantic information */
94  YYLTYPE lloc; /* offset in scanbuf */
95  int leng; /* length in bytes */
96 } TokenAuxData;
97 
98 /*
99  * Scanner working state. At some point we might wish to fold all this
100  * into a YY_EXTRA struct. For the moment, there is no need for plpgsql's
101  * lexer to be re-entrant, and the notational burden of passing a yyscanner
102  * pointer around is great enough to not want to do it without need.
103  */
104 
105 /* The stuff the core lexer needs */
106 static core_yyscan_t yyscanner = NULL;
108 
109 /* The original input string */
110 static const char *scanorig;
111 
112 /* Current token's length (corresponds to plpgsql_yylval and plpgsql_yylloc) */
113 static int plpgsql_yyleng;
114 
115 /* Current token's code (corresponds to plpgsql_yylval and plpgsql_yylloc) */
116 static int plpgsql_yytoken;
117 
118 /* Token pushback stack */
119 #define MAX_PUSHBACKS 4
120 
121 static int num_pushbacks;
124 
125 /* State for plpgsql_location_to_lineno() */
126 static const char *cur_line_start;
127 static const char *cur_line_end;
128 static int cur_line_num;
129 
130 /* Internal functions */
131 static int internal_yylex(TokenAuxData *auxdata);
132 static void push_back_token(int token, TokenAuxData *auxdata);
133 static void location_lineno_init(void);
134 
135 
136 /*
137  * This is the yylex routine called from the PL/pgSQL grammar.
138  * It is a wrapper around the core lexer, with the ability to recognize
139  * PL/pgSQL variables and return them as special T_DATUM tokens. If a
140  * word or compound word does not match any variable name, or if matching
141  * is turned off by plpgsql_IdentifierLookup, it is returned as
142  * T_WORD or T_CWORD respectively, or as an unreserved keyword if it
143  * matches one of those.
144  */
145 int
147 {
148  int tok1;
149  TokenAuxData aux1;
150  int kwnum;
151 
152  tok1 = internal_yylex(&aux1);
153  if (tok1 == IDENT || tok1 == PARAM)
154  {
155  int tok2;
156  TokenAuxData aux2;
157 
158  tok2 = internal_yylex(&aux2);
159  if (tok2 == '.')
160  {
161  int tok3;
162  TokenAuxData aux3;
163 
164  tok3 = internal_yylex(&aux3);
165  if (tok3 == IDENT)
166  {
167  int tok4;
168  TokenAuxData aux4;
169 
170  tok4 = internal_yylex(&aux4);
171  if (tok4 == '.')
172  {
173  int tok5;
174  TokenAuxData aux5;
175 
176  tok5 = internal_yylex(&aux5);
177  if (tok5 == IDENT)
178  {
179  if (plpgsql_parse_tripword(aux1.lval.str,
180  aux3.lval.str,
181  aux5.lval.str,
182  &aux1.lval.wdatum,
183  &aux1.lval.cword))
184  tok1 = T_DATUM;
185  else
186  tok1 = T_CWORD;
187  /* Adjust token length to include A.B.C */
188  aux1.leng = aux5.lloc - aux1.lloc + aux5.leng;
189  }
190  else
191  {
192  /* not A.B.C, so just process A.B */
193  push_back_token(tok5, &aux5);
194  push_back_token(tok4, &aux4);
195  if (plpgsql_parse_dblword(aux1.lval.str,
196  aux3.lval.str,
197  &aux1.lval.wdatum,
198  &aux1.lval.cword))
199  tok1 = T_DATUM;
200  else
201  tok1 = T_CWORD;
202  /* Adjust token length to include A.B */
203  aux1.leng = aux3.lloc - aux1.lloc + aux3.leng;
204  }
205  }
206  else
207  {
208  /* not A.B.C, so just process A.B */
209  push_back_token(tok4, &aux4);
210  if (plpgsql_parse_dblword(aux1.lval.str,
211  aux3.lval.str,
212  &aux1.lval.wdatum,
213  &aux1.lval.cword))
214  tok1 = T_DATUM;
215  else
216  tok1 = T_CWORD;
217  /* Adjust token length to include A.B */
218  aux1.leng = aux3.lloc - aux1.lloc + aux3.leng;
219  }
220  }
221  else
222  {
223  /* not A.B, so just process A */
224  push_back_token(tok3, &aux3);
225  push_back_token(tok2, &aux2);
226  if (plpgsql_parse_word(aux1.lval.str,
227  core_yy.scanbuf + aux1.lloc,
228  true,
229  &aux1.lval.wdatum,
230  &aux1.lval.word))
231  tok1 = T_DATUM;
232  else if (!aux1.lval.word.quoted &&
233  (kwnum = ScanKeywordLookup(aux1.lval.word.ident,
234  &UnreservedPLKeywords)) >= 0)
235  {
236  aux1.lval.keyword = GetScanKeyword(kwnum,
237  &UnreservedPLKeywords);
238  tok1 = UnreservedPLKeywordTokens[kwnum];
239  }
240  else
241  tok1 = T_WORD;
242  }
243  }
244  else
245  {
246  /* not A.B, so just process A */
247  push_back_token(tok2, &aux2);
248 
249  /*
250  * See if it matches a variable name, except in the context where
251  * we are at start of statement and the next token isn't
252  * assignment or '['. In that case, it couldn't validly be a
253  * variable name, and skipping the lookup allows variable names to
254  * be used that would conflict with plpgsql or core keywords that
255  * introduce statements (e.g., "comment"). Without this special
256  * logic, every statement-introducing keyword would effectively be
257  * reserved in PL/pgSQL, which would be unpleasant.
258  *
259  * If it isn't a variable name, try to match against unreserved
260  * plpgsql keywords. If not one of those either, it's T_WORD.
261  *
262  * Note: we must call plpgsql_parse_word even if we don't want to
263  * do variable lookup, because it sets up aux1.lval.word for the
264  * non-variable cases.
265  */
266  if (plpgsql_parse_word(aux1.lval.str,
267  core_yy.scanbuf + aux1.lloc,
269  (tok2 == '=' || tok2 == COLON_EQUALS ||
270  tok2 == '[')),
271  &aux1.lval.wdatum,
272  &aux1.lval.word))
273  tok1 = T_DATUM;
274  else if (!aux1.lval.word.quoted &&
275  (kwnum = ScanKeywordLookup(aux1.lval.word.ident,
276  &UnreservedPLKeywords)) >= 0)
277  {
278  aux1.lval.keyword = GetScanKeyword(kwnum,
279  &UnreservedPLKeywords);
280  tok1 = UnreservedPLKeywordTokens[kwnum];
281  }
282  else
283  tok1 = T_WORD;
284  }
285  }
286  else
287  {
288  /*
289  * Not a potential plpgsql variable name, just return the data.
290  *
291  * Note that we also come through here if the grammar pushed back a
292  * T_DATUM, T_CWORD, T_WORD, or unreserved-keyword token returned by a
293  * previous lookup cycle; thus, pushbacks do not incur extra lookup
294  * work, since we'll never do the above code twice for the same token.
295  * This property also makes it safe to rely on the old value of
296  * plpgsql_yytoken in the is-this-start-of-statement test above.
297  */
298  }
299 
300  plpgsql_yylval = aux1.lval;
301  plpgsql_yylloc = aux1.lloc;
302  plpgsql_yyleng = aux1.leng;
303  plpgsql_yytoken = tok1;
304  return tok1;
305 }
306 
307 /*
308  * Return the length of the token last returned by plpgsql_yylex().
309  *
310  * In the case of compound tokens, the length includes all the parts.
311  */
312 int
314 {
315  return plpgsql_yyleng;
316 }
317 
318 /*
319  * Internal yylex function. This wraps the core lexer and adds one feature:
320  * a token pushback stack. We also make a couple of trivial single-token
321  * translations from what the core lexer does to what we want, in particular
322  * interfacing from the core_YYSTYPE to YYSTYPE union.
323  */
324 static int
326 {
327  int token;
328  const char *yytext;
329 
330  if (num_pushbacks > 0)
331  {
332  num_pushbacks--;
334  *auxdata = pushback_auxdata[num_pushbacks];
335  }
336  else
337  {
338  token = core_yylex(&auxdata->lval.core_yystype,
339  &auxdata->lloc,
340  yyscanner);
341 
342  /* remember the length of yytext before it gets changed */
343  yytext = core_yy.scanbuf + auxdata->lloc;
344  auxdata->leng = strlen(yytext);
345 
346  /* Check for << >> and #, which the core considers operators */
347  if (token == Op)
348  {
349  if (strcmp(auxdata->lval.str, "<<") == 0)
350  token = LESS_LESS;
351  else if (strcmp(auxdata->lval.str, ">>") == 0)
352  token = GREATER_GREATER;
353  else if (strcmp(auxdata->lval.str, "#") == 0)
354  token = '#';
355  }
356 
357  /* The core returns PARAM as ival, but we treat it like IDENT */
358  else if (token == PARAM)
359  {
360  auxdata->lval.str = pstrdup(yytext);
361  }
362  }
363 
364  return token;
365 }
366 
367 /*
368  * Push back a token to be re-read by next internal_yylex() call.
369  */
370 static void
372 {
374  elog(ERROR, "too many tokens pushed back");
376  pushback_auxdata[num_pushbacks] = *auxdata;
377  num_pushbacks++;
378 }
379 
380 /*
381  * Push back a single token to be re-read by next plpgsql_yylex() call.
382  *
383  * NOTE: this does not cause yylval or yylloc to "back up". Also, it
384  * is not a good idea to push back a token code other than what you read.
385  */
386 void
388 {
389  TokenAuxData auxdata;
390 
391  auxdata.lval = plpgsql_yylval;
392  auxdata.lloc = plpgsql_yylloc;
393  auxdata.leng = plpgsql_yyleng;
394  push_back_token(token, &auxdata);
395 }
396 
397 /*
398  * Tell whether a token is an unreserved keyword.
399  *
400  * (If it is, its lowercased form was returned as the token value, so we
401  * do not need to offer that data here.)
402  */
403 bool
405 {
406  int i;
407 
408  for (i = 0; i < lengthof(UnreservedPLKeywordTokens); i++)
409  {
411  return true;
412  }
413  return false;
414 }
415 
416 /*
417  * Append the function text starting at startlocation and extending to
418  * (not including) endlocation onto the existing contents of "buf".
419  */
420 void
422  int startlocation, int endlocation)
423 {
424  Assert(startlocation <= endlocation);
425  appendBinaryStringInfo(buf, scanorig + startlocation,
426  endlocation - startlocation);
427 }
428 
429 /*
430  * Peek one token ahead in the input stream. Only the token code is
431  * made available, not any of the auxiliary info such as location.
432  *
433  * NB: no variable or unreserved keyword lookup is performed here, they will
434  * be returned as IDENT. Reserved keywords are resolved as usual.
435  */
436 int
438 {
439  int tok1;
440  TokenAuxData aux1;
441 
442  tok1 = internal_yylex(&aux1);
443  push_back_token(tok1, &aux1);
444  return tok1;
445 }
446 
447 /*
448  * Peek two tokens ahead in the input stream. The first token and its
449  * location in the query are returned in *tok1_p and *tok1_loc, second token
450  * and its location in *tok2_p and *tok2_loc.
451  *
452  * NB: no variable or unreserved keyword lookup is performed here, they will
453  * be returned as IDENT. Reserved keywords are resolved as usual.
454  */
455 void
456 plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc)
457 {
458  int tok1,
459  tok2;
460  TokenAuxData aux1,
461  aux2;
462 
463  tok1 = internal_yylex(&aux1);
464  tok2 = internal_yylex(&aux2);
465 
466  *tok1_p = tok1;
467  if (tok1_loc)
468  *tok1_loc = aux1.lloc;
469  *tok2_p = tok2;
470  if (tok2_loc)
471  *tok2_loc = aux2.lloc;
472 
473  push_back_token(tok2, &aux2);
474  push_back_token(tok1, &aux1);
475 }
476 
477 /*
478  * plpgsql_scanner_errposition
479  * Report an error cursor position, if possible.
480  *
481  * This is expected to be used within an ereport() call. The return value
482  * is a dummy (always 0, in fact).
483  *
484  * Note that this can only be used for messages emitted during initial
485  * parsing of a plpgsql function, since it requires the scanorig string
486  * to still be available.
487  */
488 int
490 {
491  int pos;
492 
493  if (location < 0 || scanorig == NULL)
494  return 0; /* no-op if location is unknown */
495 
496  /* Convert byte offset to character number */
497  pos = pg_mbstrlen_with_len(scanorig, location) + 1;
498  /* And pass it to the ereport mechanism */
499  (void) internalerrposition(pos);
500  /* Also pass the function body string */
501  return internalerrquery(scanorig);
502 }
503 
504 /*
505  * plpgsql_yyerror
506  * Report a lexer or grammar error.
507  *
508  * The message's cursor position refers to the current token (the one
509  * last returned by plpgsql_yylex()).
510  * This is OK for syntax error messages from the Bison parser, because Bison
511  * parsers report error as soon as the first unparsable token is reached.
512  * Beware of using yyerror for other purposes, as the cursor position might
513  * be misleading!
514  */
515 void
516 plpgsql_yyerror(const char *message)
517 {
518  char *yytext = core_yy.scanbuf + plpgsql_yylloc;
519 
520  if (*yytext == '\0')
521  {
522  ereport(ERROR,
523  (errcode(ERRCODE_SYNTAX_ERROR),
524  /* translator: %s is typically the translation of "syntax error" */
525  errmsg("%s at end of input", _(message)),
526  plpgsql_scanner_errposition(plpgsql_yylloc)));
527  }
528  else
529  {
530  /*
531  * If we have done any lookahead then flex will have restored the
532  * character after the end-of-token. Zap it again so that we report
533  * only the single token here. This modifies scanbuf but we no longer
534  * care about that.
535  */
536  yytext[plpgsql_yyleng] = '\0';
537 
538  ereport(ERROR,
539  (errcode(ERRCODE_SYNTAX_ERROR),
540  /* translator: first %s is typically the translation of "syntax error" */
541  errmsg("%s at or near \"%s\"", _(message), yytext),
542  plpgsql_scanner_errposition(plpgsql_yylloc)));
543  }
544 }
545 
546 /*
547  * Given a location (a byte offset in the function source text),
548  * return a line number.
549  *
550  * We expect that this is typically called for a sequence of increasing
551  * location values, so optimize accordingly by tracking the endpoints
552  * of the "current" line.
553  */
554 int
556 {
557  const char *loc;
558 
559  if (location < 0 || scanorig == NULL)
560  return 0; /* garbage in, garbage out */
561  loc = scanorig + location;
562 
563  /* be correct, but not fast, if input location goes backwards */
564  if (loc < cur_line_start)
566 
567  while (cur_line_end != NULL && loc > cur_line_end)
568  {
570  cur_line_num++;
571  cur_line_end = strchr(cur_line_start, '\n');
572  }
573 
574  return cur_line_num;
575 }
576 
577 /* initialize or reset the state for plpgsql_location_to_lineno */
578 static void
580 {
582  cur_line_num = 1;
583 
584  cur_line_end = strchr(cur_line_start, '\n');
585 }
586 
587 /* return the most recently computed lineno */
588 int
590 {
591  return cur_line_num;
592 }
593 
594 
595 /*
596  * Called before any actual parsing is done
597  *
598  * Note: the passed "str" must remain valid until plpgsql_scanner_finish().
599  * Although it is not fed directly to flex, we need the original string
600  * to cite in error messages.
601  */
602 void
604 {
605  /* Start up the core scanner */
607  &ReservedPLKeywords, ReservedPLKeywordTokens);
608 
609  /*
610  * scanorig points to the original string, which unlike the scanner's
611  * scanbuf won't be modified on-the-fly by flex. Notice that although
612  * yytext points into scanbuf, we rely on being able to apply locations
613  * (offsets from string start) to scanorig as well.
614  */
615  scanorig = str;
616 
617  /* Other setup */
619  plpgsql_yytoken = 0;
620 
621  num_pushbacks = 0;
622 
624 }
625 
626 /*
627  * Called after parsing is done to clean up after plpgsql_scanner_init()
628  */
629 void
631 {
632  /* release storage */
634  /* avoid leaving any dangling pointers */
635  yyscanner = NULL;
636  scanorig = NULL;
637 }
unsigned short uint16
Definition: c.h:508
#define Assert(condition)
Definition: c.h:861
#define lengthof(array)
Definition: c.h:791
int internalerrquery(const char *query)
Definition: elog.c:1482
int internalerrposition(int cursorpos)
Definition: elog.c:1462
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define _(x)
Definition: elog.c:90
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
const char * str
#define token
Definition: indent_globs.h:126
int i
Definition: isn.c:73
int ScanKeywordLookup(const char *str, const ScanKeywordList *keywords)
Definition: kwlookup.c:38
static const char * GetScanKeyword(int n, const ScanKeywordList *keywords)
Definition: kwlookup.h:39
int pg_mbstrlen_with_len(const char *mbstr, int limit)
Definition: mbutils.c:1057
char * pstrdup(const char *in)
Definition: mcxt.c:1696
static char * buf
Definition: pg_test_fsync.c:73
bool plpgsql_parse_dblword(char *word1, char *word2, PLwdatum *wdatum, PLcword *cword)
Definition: pl_comp.c:1441
bool plpgsql_parse_word(char *word1, const char *yytxt, bool lookup, PLwdatum *wdatum, PLword *word)
Definition: pl_comp.c:1386
bool plpgsql_parse_tripword(char *word1, char *word2, char *word3, PLwdatum *wdatum, PLcword *cword)
Definition: pl_comp.c:1522
void plpgsql_scanner_finish(void)
Definition: pl_scanner.c:630
static TokenAuxData pushback_auxdata[MAX_PUSHBACKS]
Definition: pl_scanner.c:123
int plpgsql_scanner_errposition(int location)
Definition: pl_scanner.c:489
#define AT_STMT_START(prev_token)
Definition: pl_scanner.c:82
static void push_back_token(int token, TokenAuxData *auxdata)
Definition: pl_scanner.c:371
static const char * scanorig
Definition: pl_scanner.c:110
static core_yyscan_t yyscanner
Definition: pl_scanner.c:106
static int num_pushbacks
Definition: pl_scanner.c:121
static int plpgsql_yytoken
Definition: pl_scanner.c:116
void plpgsql_yyerror(const char *message)
Definition: pl_scanner.c:516
static int plpgsql_yyleng
Definition: pl_scanner.c:113
int plpgsql_location_to_lineno(int location)
Definition: pl_scanner.c:555
static int cur_line_num
Definition: pl_scanner.c:128
static const uint16 UnreservedPLKeywordTokens[]
Definition: pl_scanner.c:70
IdentifierLookup plpgsql_IdentifierLookup
Definition: pl_scanner.c:26
bool plpgsql_token_is_unreserved_keyword(int token)
Definition: pl_scanner.c:404
void plpgsql_append_source_text(StringInfo buf, int startlocation, int endlocation)
Definition: pl_scanner.c:421
static const uint16 ReservedPLKeywordTokens[]
Definition: pl_scanner.c:66
static int pushback_token[MAX_PUSHBACKS]
Definition: pl_scanner.c:122
void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc, int *tok2_loc)
Definition: pl_scanner.c:456
int plpgsql_latest_lineno(void)
Definition: pl_scanner.c:589
int plpgsql_token_length(void)
Definition: pl_scanner.c:313
int plpgsql_yylex(void)
Definition: pl_scanner.c:146
static int internal_yylex(TokenAuxData *auxdata)
Definition: pl_scanner.c:325
static const char * cur_line_end
Definition: pl_scanner.c:127
static core_yy_extra_type core_yy
Definition: pl_scanner.c:107
int plpgsql_peek(void)
Definition: pl_scanner.c:437
static void location_lineno_init(void)
Definition: pl_scanner.c:579
void plpgsql_scanner_init(const char *str)
Definition: pl_scanner.c:603
void plpgsql_push_back_token(int token)
Definition: pl_scanner.c:387
static const char * cur_line_start
Definition: pl_scanner.c:126
#define MAX_PUSHBACKS
Definition: pl_scanner.c:119
IdentifierLookup
Definition: plpgsql.h:1188
@ IDENTIFIER_LOOKUP_NORMAL
Definition: plpgsql.h:1189
#define YYLTYPE
Definition: scanner.h:44
core_yyscan_t scanner_init(const char *str, core_yy_extra_type *yyext, const ScanKeywordList *keywordlist, const uint16 *keyword_tokens)
void scanner_finish(core_yyscan_t yyscanner)
void * core_yyscan_t
Definition: scanner.h:121
int core_yylex(core_YYSTYPE *yylval_param, YYLTYPE *yylloc_param, core_yyscan_t yyscanner)
void appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
Definition: stringinfo.c:233
YYLTYPE lloc
Definition: pl_scanner.c:94
YYSTYPE lval
Definition: pl_scanner.c:93
char * scanbuf
Definition: scanner.h:72