📄 scan.l
字号:
}<IN_STRING>[^'\\]+ { }<IN_STRING><<EOF>> { plpgsql_error_lineno = start_lineno; ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("unterminated string"))); }{dolqdelim} { start_lineno = plpgsql_scanner_lineno(); start_charpos = yytext; dolqstart = pstrdup(yytext); BEGIN(IN_DOLLARQUOTE); }<IN_DOLLARQUOTE>{dolqdelim} { if (strcmp(yytext, dolqstart) == 0) { pfree(dolqstart); /* tell plpgsql_get_string_value it is a dollar quote */ dolqlen = yyleng; /* adjust yytext/yyleng to describe whole string token */ yyleng += (yytext - start_charpos); yytext = start_charpos; BEGIN(INITIAL); return T_STRING; } else { /* * When we fail to match $...$ to dolqstart, transfer * the $... part to the output, but put back the final * $ for rescanning. Consider $delim$...$junk$delim$ */ yyless(yyleng-1); } }<IN_DOLLARQUOTE>{dolqinside} { }<IN_DOLLARQUOTE>. { /* needed for $ inside the quoted text */ }<IN_DOLLARQUOTE><<EOF>> { plpgsql_error_lineno = start_lineno; ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("unterminated dollar-quoted string"))); } /* ---------- * Any unmatched character is returned as is * ---------- */. { return yytext[0]; }%%/* * This is the yylex routine called from outside. It exists to provide * a pushback facility, as well as to allow us to parse syntax that * requires more than one token of lookahead. */intplpgsql_yylex(void){ int cur_token; if (have_pushback_token) { have_pushback_token = false; cur_token = pushback_token; } else if (have_lookahead_token) { have_lookahead_token = false; cur_token = lookahead_token; } else cur_token = yylex(); /* Do we need to look ahead for a possible multiword token? */ switch (cur_token) { /* RETURN NEXT must be reduced to a single token */ case K_RETURN: if (!have_lookahead_token) { lookahead_token = yylex(); have_lookahead_token = true; } if (lookahead_token == K_NEXT) { have_lookahead_token = false; cur_token = K_RETURN_NEXT; } break; default: break; } return cur_token;}/* * Push back a single token to be re-read by next plpgsql_yylex() call. */voidplpgsql_push_back_token(int token){ if (have_pushback_token) elog(ERROR, "cannot push back multiple tokens"); pushback_token = token; have_pushback_token = true;}/* * Report a syntax error. */voidplpgsql_yyerror(const char *message){ const char *loc = yytext; int cursorpos; plpgsql_error_lineno = plpgsql_scanner_lineno(); /* in multibyte encodings, return index in characters not bytes */ cursorpos = pg_mbstrlen_with_len(scanbuf, loc - scanbuf) + 1; if (*loc == YY_END_OF_BUFFER_CHAR) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), /* translator: %s is typically "syntax error" */ errmsg("%s at end of input", message), internalerrposition(cursorpos), internalerrquery(scanstr))); } else { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), /* translator: first %s is typically "syntax error" */ errmsg("%s at or near \"%s\"", message, loc), internalerrposition(cursorpos), internalerrquery(scanstr))); }}/* * Get the line number at which the current token ends. This substitutes * for flex's very poorly implemented yylineno facility. * * We assume that flex has written a '\0' over the character following the * current token in scanbuf. So, we just have to count the '\n' characters * before that. We optimize this a little by keeping track of the last * '\n' seen so far. */intplpgsql_scanner_lineno(void){ const char *c; while ((c = strchr(cur_line_start, '\n')) != NULL) { cur_line_start = c + 1; cur_line_num++; } return cur_line_num;}/* * Called before any actual parsing is done * * Note: the passed "str" must remain valid until plpgsql_scanner_finish(). * Although it is not fed directly to flex, we need the original string * to cite in error messages. */voidplpgsql_scanner_init(const char *str, int functype){ Size slen; slen = strlen(str); /* * Might be left over after ereport() */ if (YY_CURRENT_BUFFER) yy_delete_buffer(YY_CURRENT_BUFFER); /* * Make a scan buffer with special termination needed by flex. */ scanbuf = palloc(slen + 2); memcpy(scanbuf, str, slen); scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR; scanbufhandle = yy_scan_buffer(scanbuf, slen + 2); /* Other setup */ scanstr = str; scanner_functype = functype; scanner_typereported = false; have_pushback_token = false; have_lookahead_token = false; cur_line_start = scanbuf; cur_line_num = 1; /*---------- * Hack: skip any initial newline, so that in the common coding layout * CREATE FUNCTION ... AS ' * code body * ' LANGUAGE 'plpgsql'; * we will think "line 1" is what the programmer thinks of as line 1. *---------- */ if (*cur_line_start == '\r') cur_line_start++; if (*cur_line_start == '\n') cur_line_start++; BEGIN(INITIAL);}/* * Called after parsing is done to clean up after plpgsql_scanner_init() */voidplpgsql_scanner_finish(void){ yy_delete_buffer(scanbufhandle); pfree(scanbuf);}/* * Called after a T_STRING token is read to get the string literal's value * as a palloc'd string. (We make this a separate call because in many * scenarios there's no need to get the decoded value.) * * Note: we expect the literal to be the most recently lexed token. This * would not work well if we supported multiple-token pushback or if * plpgsql_yylex() wanted to read ahead beyond a T_STRING token. */char *plpgsql_get_string_value(void){ char *result; const char *cp; int len; if (dolqlen > 0) { /* Token is a $foo$...$foo$ string */ len = yyleng - 2 * dolqlen; Assert(len >= 0); result = (char *) palloc(len + 1); memcpy(result, yytext + dolqlen, len); result[len] = '\0'; } else if (*yytext == 'E' || *yytext == 'e') { /* Token is an E'...' string */ result = (char *) palloc(yyleng + 1); /* more than enough room */ len = 0; for (cp = yytext + 2; *cp; cp++) { if (*cp == '\'') { if (cp[1] == '\'') result[len++] = *cp++; /* else it must be string end quote */ } else if (*cp == '\\') { if (cp[1] != '\0') /* just a paranoid check */ result[len++] = *(++cp); } else result[len++] = *cp; } result[len] = '\0'; } else { /* Token is a '...' string */ result = (char *) palloc(yyleng + 1); /* more than enough room */ len = 0; for (cp = yytext + 1; *cp; cp++) { if (*cp == '\'') { if (cp[1] == '\'') result[len++] = *cp++; /* else it must be string end quote */ } else if (*cp == '\\') { if (cp[1] != '\0') /* just a paranoid check */ result[len++] = *(++cp); } else result[len++] = *cp; } result[len] = '\0'; } return result;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -