LCOV - code coverage report
Current view: top level - Parser - pegen.c (source / functions) Hit Total Coverage
Test: CPython 3.12 LCOV report [commit 5e6661bce9] Lines: 290 485 59.8 %
Date: 2023-03-20 08:15:36 Functions: 30 39 76.9 %
Branches: 149 288 51.7 %

           Branch data     Line data    Source code
       1                 :            : #include <Python.h>
       2                 :            : #include "pycore_ast.h"           // _PyAST_Validate(),
       3                 :            : #include "pycore_pystate.h"       // _PyThreadState_GET()
       4                 :            : #include <errcode.h>
       5                 :            : 
       6                 :            : #include "tokenizer.h"
       7                 :            : #include "pegen.h"
       8                 :            : 
       9                 :            : // Internal parser functions
      10                 :            : 
      11                 :            : asdl_stmt_seq*
      12                 :          0 : _PyPegen_interactive_exit(Parser *p)
      13                 :            : {
      14         [ #  # ]:          0 :     if (p->errcode) {
      15                 :          0 :         *(p->errcode) = E_EOF;
      16                 :            :     }
      17                 :          0 :     return NULL;
      18                 :            : }
      19                 :            : 
      20                 :            : Py_ssize_t
      21                 :          0 : _PyPegen_byte_offset_to_character_offset(PyObject *line, Py_ssize_t col_offset)
      22                 :            : {
      23                 :          0 :     const char *str = PyUnicode_AsUTF8(line);
      24         [ #  # ]:          0 :     if (!str) {
      25                 :          0 :         return -1;
      26                 :            :     }
      27                 :          0 :     Py_ssize_t len = strlen(str);
      28         [ #  # ]:          0 :     if (col_offset > len + 1) {
      29                 :          0 :         col_offset = len + 1;
      30                 :            :     }
      31                 :            :     assert(col_offset >= 0);
      32                 :          0 :     PyObject *text = PyUnicode_DecodeUTF8(str, col_offset, "replace");
      33         [ #  # ]:          0 :     if (!text) {
      34                 :          0 :         return -1;
      35                 :            :     }
      36                 :          0 :     Py_ssize_t size = PyUnicode_GET_LENGTH(text);
      37                 :          0 :     Py_DECREF(text);
      38                 :          0 :     return size;
      39                 :            : }
      40                 :            : 
      41                 :            : // Here, mark is the start of the node, while p->mark is the end.
      42                 :            : // If node==NULL, they should be the same.
      43                 :            : int
      44                 :    7835749 : _PyPegen_insert_memo(Parser *p, int mark, int type, void *node)
      45                 :            : {
      46                 :            :     // Insert in front
      47                 :    7835749 :     Memo *m = _PyArena_Malloc(p->arena, sizeof(Memo));
      48         [ -  + ]:    7835749 :     if (m == NULL) {
      49                 :          0 :         return -1;
      50                 :            :     }
      51                 :    7835749 :     m->type = type;
      52                 :    7835749 :     m->node = node;
      53                 :    7835749 :     m->mark = p->mark;
      54                 :    7835749 :     m->next = p->tokens[mark]->memo;
      55                 :    7835749 :     p->tokens[mark]->memo = m;
      56                 :    7835749 :     return 0;
      57                 :            : }
      58                 :            : 
      59                 :            : // Like _PyPegen_insert_memo(), but updates an existing node if found.
      60                 :            : int
      61                 :    7862743 : _PyPegen_update_memo(Parser *p, int mark, int type, void *node)
      62                 :            : {
      63         [ +  + ]:   38673908 :     for (Memo *m = p->tokens[mark]->memo; m != NULL; m = m->next) {
      64         [ +  + ]:   34601092 :         if (m->type == type) {
      65                 :            :             // Update existing node.
      66                 :    3789927 :             m->node = node;
      67                 :    3789927 :             m->mark = p->mark;
      68                 :    3789927 :             return 0;
      69                 :            :         }
      70                 :            :     }
      71                 :            :     // Insert new node.
      72                 :    4072816 :     return _PyPegen_insert_memo(p, mark, type, node);
      73                 :            : }
      74                 :            : 
      75                 :            : static int
      76                 :          0 : init_normalization(Parser *p)
      77                 :            : {
      78         [ #  # ]:          0 :     if (p->normalize) {
      79                 :          0 :         return 1;
      80                 :            :     }
      81                 :          0 :     p->normalize = _PyImport_GetModuleAttrString("unicodedata", "normalize");
      82         [ #  # ]:          0 :     if (!p->normalize)
      83                 :            :     {
      84                 :          0 :         return 0;
      85                 :            :     }
      86                 :          0 :     return 1;
      87                 :            : }
      88                 :            : 
      89                 :            : static int
      90                 :       1585 : growable_comment_array_init(growable_comment_array *arr, size_t initial_size) {
      91                 :            :     assert(initial_size > 0);
      92                 :       1585 :     arr->items = PyMem_Malloc(initial_size * sizeof(*arr->items));
      93                 :       1585 :     arr->size = initial_size;
      94                 :       1585 :     arr->num_items = 0;
      95                 :            : 
      96                 :       1585 :     return arr->items != NULL;
      97                 :            : }
      98                 :            : 
      99                 :            : static int
     100                 :          0 : growable_comment_array_add(growable_comment_array *arr, int lineno, char *comment) {
     101         [ #  # ]:          0 :     if (arr->num_items >= arr->size) {
     102                 :          0 :         size_t new_size = arr->size * 2;
     103                 :          0 :         void *new_items_array = PyMem_Realloc(arr->items, new_size * sizeof(*arr->items));
     104         [ #  # ]:          0 :         if (!new_items_array) {
     105                 :          0 :             return 0;
     106                 :            :         }
     107                 :          0 :         arr->items = new_items_array;
     108                 :          0 :         arr->size = new_size;
     109                 :            :     }
     110                 :            : 
     111                 :          0 :     arr->items[arr->num_items].lineno = lineno;
     112                 :          0 :     arr->items[arr->num_items].comment = comment;  // Take ownership
     113                 :          0 :     arr->num_items++;
     114                 :          0 :     return 1;
     115                 :            : }
     116                 :            : 
     117                 :            : static void
     118                 :       1585 : growable_comment_array_deallocate(growable_comment_array *arr) {
     119         [ -  + ]:       1585 :     for (unsigned i = 0; i < arr->num_items; i++) {
     120                 :          0 :         PyMem_Free(arr->items[i].comment);
     121                 :            :     }
     122                 :       1585 :     PyMem_Free(arr->items);
     123                 :       1585 : }
     124                 :            : 
     125                 :            : static int
     126                 :     208777 : _get_keyword_or_name_type(Parser *p, struct token *new_token)
     127                 :            : {
     128                 :     208777 :     int name_len = new_token->end_col_offset - new_token->col_offset;
     129                 :            :     assert(name_len > 0);
     130                 :            : 
     131         [ +  + ]:     208777 :     if (name_len >= p->n_keyword_lists ||
     132         [ +  - ]:     169658 :         p->keywords[name_len] == NULL ||
     133         [ +  + ]:     169658 :         p->keywords[name_len]->type == -1) {
     134                 :      46347 :         return NAME;
     135                 :            :     }
     136   [ +  -  +  + ]:     855931 :     for (KeywordToken *k = p->keywords[name_len]; k != NULL && k->type != -1; k++) {
     137         [ +  + ]:     746303 :         if (strncmp(k->str, new_token->start, name_len) == 0) {
     138                 :      52802 :             return k->type;
     139                 :            :         }
     140                 :            :     }
     141                 :     109628 :     return NAME;
     142                 :            : }
     143                 :            : 
     144                 :            : static int
     145                 :    1370610 : initialize_token(Parser *p, Token *parser_token, struct token *new_token, int token_type) {
     146                 :            :     assert(parser_token != NULL);
     147                 :            : 
     148         [ +  + ]:    1370610 :     parser_token->type = (token_type == NAME) ? _get_keyword_or_name_type(p, new_token) : token_type;
     149                 :    1370610 :     parser_token->bytes = PyBytes_FromStringAndSize(new_token->start, new_token->end - new_token->start);
     150         [ -  + ]:    1370610 :     if (parser_token->bytes == NULL) {
     151                 :          0 :         return -1;
     152                 :            :     }
     153         [ -  + ]:    1370610 :     if (_PyArena_AddPyObject(p->arena, parser_token->bytes) < 0) {
     154                 :          0 :         Py_DECREF(parser_token->bytes);
     155                 :          0 :         return -1;
     156                 :            :     }
     157                 :            : 
     158                 :    1370610 :     parser_token->level = new_token->level;
     159                 :    1370610 :     parser_token->lineno = new_token->lineno;
     160                 :    2747735 :     parser_token->col_offset = p->tok->lineno == p->starting_lineno ? p->starting_col_offset + new_token->col_offset
     161         [ +  + ]:    1370610 :                                                                     : new_token->col_offset;
     162                 :    1370610 :     parser_token->end_lineno = new_token->end_lineno;
     163                 :    2747735 :     parser_token->end_col_offset = p->tok->lineno == p->starting_lineno ? p->starting_col_offset + new_token->end_col_offset
     164         [ +  + ]:    1370610 :                                                                  : new_token->end_col_offset;
     165                 :            : 
     166                 :    1370610 :     p->fill += 1;
     167                 :            : 
     168   [ -  +  -  - ]:    1370610 :     if (token_type == ERRORTOKEN && p->tok->done == E_DECODE) {
     169                 :          0 :         return _Pypegen_raise_decode_error(p);
     170                 :            :     }
     171                 :            : 
     172         [ -  + ]:    1370610 :     return (token_type == ERRORTOKEN ? _Pypegen_tokenizer_error(p) : 0);
     173                 :            : }
     174                 :            : 
     175                 :            : static int
     176                 :       5970 : _resize_tokens_array(Parser *p) {
     177                 :       5970 :     int newsize = p->size * 2;
     178                 :       5970 :     Token **new_tokens = PyMem_Realloc(p->tokens, newsize * sizeof(Token *));
     179         [ -  + ]:       5970 :     if (new_tokens == NULL) {
     180                 :          0 :         PyErr_NoMemory();
     181                 :          0 :         return -1;
     182                 :            :     }
     183                 :       5970 :     p->tokens = new_tokens;
     184                 :            : 
     185         [ +  + ]:    2008557 :     for (int i = p->size; i < newsize; i++) {
     186                 :    2002587 :         p->tokens[i] = PyMem_Calloc(1, sizeof(Token));
     187         [ -  + ]:    2002587 :         if (p->tokens[i] == NULL) {
     188                 :          0 :             p->size = i; // Needed, in order to cleanup correctly after parser fails
     189                 :          0 :             PyErr_NoMemory();
     190                 :          0 :             return -1;
     191                 :            :         }
     192                 :            :     }
     193                 :       5970 :     p->size = newsize;
     194                 :       5970 :     return 0;
     195                 :            : }
     196                 :            : 
     197                 :            : int
     198                 :    1370610 : _PyPegen_fill_token(Parser *p)
     199                 :            : {
     200                 :            :     struct token new_token;
     201                 :    1370610 :     int type = _PyTokenizer_Get(p->tok, &new_token);
     202                 :            : 
     203                 :            :     // Record and skip '# type: ignore' comments
     204         [ -  + ]:    1370610 :     while (type == TYPE_IGNORE) {
     205                 :          0 :         Py_ssize_t len = new_token.end_col_offset - new_token.col_offset;
     206                 :          0 :         char *tag = PyMem_Malloc(len + 1);
     207         [ #  # ]:          0 :         if (tag == NULL) {
     208                 :          0 :             PyErr_NoMemory();
     209                 :          0 :             return -1;
     210                 :            :         }
     211                 :          0 :         strncpy(tag, new_token.start, len);
     212                 :          0 :         tag[len] = '\0';
     213                 :            :         // Ownership of tag passes to the growable array
     214         [ #  # ]:          0 :         if (!growable_comment_array_add(&p->type_ignore_comments, p->tok->lineno, tag)) {
     215                 :          0 :             PyErr_NoMemory();
     216                 :          0 :             return -1;
     217                 :            :         }
     218                 :          0 :         type = _PyTokenizer_Get(p->tok, &new_token);
     219                 :            :     }
     220                 :            : 
     221                 :            :     // If we have reached the end and we are in single input mode we need to insert a newline and reset the parsing
     222   [ +  +  +  +  :    1370610 :     if (p->start_rule == Py_single_input && type == ENDMARKER && p->parsing_started) {
                   +  - ]
     223                 :          1 :         type = NEWLINE; /* Add an extra newline */
     224                 :          1 :         p->parsing_started = 0;
     225                 :            : 
     226   [ -  +  -  - ]:          1 :         if (p->tok->indent && !(p->flags & PyPARSE_DONT_IMPLY_DEDENT)) {
     227                 :          0 :             p->tok->pendin = -p->tok->indent;
     228                 :          0 :             p->tok->indent = 0;
     229                 :            :         }
     230                 :            :     }
     231                 :            :     else {
     232                 :    1370609 :         p->parsing_started = 1;
     233                 :            :     }
     234                 :            : 
     235                 :            :     // Check if we are at the limit of the token array capacity and resize if needed
     236   [ +  +  -  + ]:    1370610 :     if ((p->fill == p->size) && (_resize_tokens_array(p) != 0)) {
     237                 :          0 :         return -1;
     238                 :            :     }
     239                 :            : 
     240                 :    1370610 :     Token *t = p->tokens[p->fill];
     241                 :    1370610 :     return initialize_token(p, t, &new_token, type);
     242                 :            : }
     243                 :            : 
     244                 :            : #if defined(Py_DEBUG)
     245                 :            : // Instrumentation to count the effectiveness of memoization.
     246                 :            : // The array counts the number of tokens skipped by memoization,
     247                 :            : // indexed by type.
     248                 :            : 
     249                 :            : #define NSTATISTICS _PYPEGEN_NSTATISTICS
     250                 :            : #define memo_statistics _PyRuntime.parser.memo_statistics
     251                 :            : 
     252                 :            : void
     253                 :            : _PyPegen_clear_memo_statistics()
     254                 :            : {
     255                 :            :     for (int i = 0; i < NSTATISTICS; i++) {
     256                 :            :         memo_statistics[i] = 0;
     257                 :            :     }
     258                 :            : }
     259                 :            : 
     260                 :            : PyObject *
     261                 :            : _PyPegen_get_memo_statistics()
     262                 :            : {
     263                 :            :     PyObject *ret = PyList_New(NSTATISTICS);
     264                 :            :     if (ret == NULL) {
     265                 :            :         return NULL;
     266                 :            :     }
     267                 :            :     for (int i = 0; i < NSTATISTICS; i++) {
     268                 :            :         PyObject *value = PyLong_FromLong(memo_statistics[i]);
     269                 :            :         if (value == NULL) {
     270                 :            :             Py_DECREF(ret);
     271                 :            :             return NULL;
     272                 :            :         }
     273                 :            :         // PyList_SetItem borrows a reference to value.
     274                 :            :         if (PyList_SetItem(ret, i, value) < 0) {
     275                 :            :             Py_DECREF(ret);
     276                 :            :             return NULL;
     277                 :            :         }
     278                 :            :     }
     279                 :            :     return ret;
     280                 :            : }
     281                 :            : #endif
     282                 :            : 
     283                 :            : int  // bool
     284                 :   32609153 : _PyPegen_is_memoized(Parser *p, int type, void *pres)
     285                 :            : {
     286         [ +  + ]:   32609153 :     if (p->mark == p->fill) {
     287         [ -  + ]:     452199 :         if (_PyPegen_fill_token(p) < 0) {
     288                 :          0 :             p->error_indicator = 1;
     289                 :          0 :             return -1;
     290                 :            :         }
     291                 :            :     }
     292                 :            : 
     293                 :   32609153 :     Token *t = p->tokens[p->mark];
     294                 :            : 
     295         [ +  + ]:  101487050 :     for (Memo *m = t->memo; m != NULL; m = m->next) {
     296         [ +  + ]:   93651301 :         if (m->type == type) {
     297                 :            : #if defined(PY_DEBUG)
     298                 :            :             if (0 <= type && type < NSTATISTICS) {
     299                 :            :                 long count = m->mark - p->mark;
     300                 :            :                 // A memoized negative result counts for one.
     301                 :            :                 if (count <= 0) {
     302                 :            :                     count = 1;
     303                 :            :                 }
     304                 :            :                 memo_statistics[type] += count;
     305                 :            :             }
     306                 :            : #endif
     307                 :   24773404 :             p->mark = m->mark;
     308                 :   24773404 :             *(void **)(pres) = m->node;
     309                 :   24773404 :             return 1;
     310                 :            :         }
     311                 :            :     }
     312                 :    7835749 :     return 0;
     313                 :            : }
     314                 :            : 
     315                 :            : int
     316                 :          0 : _PyPegen_lookahead_with_name(int positive, expr_ty (func)(Parser *), Parser *p)
     317                 :            : {
     318                 :          0 :     int mark = p->mark;
     319                 :          0 :     void *res = func(p);
     320                 :          0 :     p->mark = mark;
     321                 :          0 :     return (res != NULL) == positive;
     322                 :            : }
     323                 :            : 
     324                 :            : int
     325                 :         14 : _PyPegen_lookahead_with_string(int positive, expr_ty (func)(Parser *, const char*), Parser *p, const char* arg)
     326                 :            : {
     327                 :         14 :     int mark = p->mark;
     328                 :         14 :     void *res = func(p, arg);
     329                 :         14 :     p->mark = mark;
     330                 :         14 :     return (res != NULL) == positive;
     331                 :            : }
     332                 :            : 
     333                 :            : int
     334                 :     941615 : _PyPegen_lookahead_with_int(int positive, Token *(func)(Parser *, int), Parser *p, int arg)
     335                 :            : {
     336                 :     941615 :     int mark = p->mark;
     337                 :     941615 :     void *res = func(p, arg);
     338                 :     941615 :     p->mark = mark;
     339                 :     941615 :     return (res != NULL) == positive;
     340                 :            : }
     341                 :            : 
     342                 :            : int
     343                 :    1386959 : _PyPegen_lookahead(int positive, void *(func)(Parser *), Parser *p)
     344                 :            : {
     345                 :    1386959 :     int mark = p->mark;
     346                 :    1386959 :     void *res = (void*)func(p);
     347                 :    1386959 :     p->mark = mark;
     348                 :    1386959 :     return (res != NULL) == positive;
     349                 :            : }
     350                 :            : 
     351                 :            : Token *
     352                 :   29837980 : _PyPegen_expect_token(Parser *p, int type)
     353                 :            : {
     354         [ +  + ]:   29837980 :     if (p->mark == p->fill) {
     355         [ -  + ]:     795901 :         if (_PyPegen_fill_token(p) < 0) {
     356                 :          0 :             p->error_indicator = 1;
     357                 :          0 :             return NULL;
     358                 :            :         }
     359                 :            :     }
     360                 :   29837980 :     Token *t = p->tokens[p->mark];
     361         [ +  + ]:   29837980 :     if (t->type != type) {
     362                 :   27279260 :         return NULL;
     363                 :            :     }
     364                 :    2558720 :     p->mark += 1;
     365                 :    2558720 :     return t;
     366                 :            : }
     367                 :            : 
     368                 :            : void*
     369                 :          0 : _PyPegen_expect_forced_result(Parser *p, void* result, const char* expected) {
     370                 :            : 
     371         [ #  # ]:          0 :     if (p->error_indicator == 1) {
     372                 :          0 :         return NULL;
     373                 :            :     }
     374         [ #  # ]:          0 :     if (result == NULL) {
     375                 :          0 :         RAISE_SYNTAX_ERROR("expected (%s)", expected);
     376                 :          0 :         return NULL;
     377                 :            :     }
     378                 :          0 :     return result;
     379                 :            : }
     380                 :            : 
     381                 :            : Token *
     382                 :      17608 : _PyPegen_expect_forced_token(Parser *p, int type, const char* expected) {
     383                 :            : 
     384         [ -  + ]:      17608 :     if (p->error_indicator == 1) {
     385                 :          0 :         return NULL;
     386                 :            :     }
     387                 :            : 
     388         [ +  + ]:      17608 :     if (p->mark == p->fill) {
     389         [ -  + ]:       9921 :         if (_PyPegen_fill_token(p) < 0) {
     390                 :          0 :             p->error_indicator = 1;
     391                 :          0 :             return NULL;
     392                 :            :         }
     393                 :            :     }
     394                 :      17608 :     Token *t = p->tokens[p->mark];
     395         [ -  + ]:      17608 :     if (t->type != type) {
     396                 :          0 :         RAISE_SYNTAX_ERROR_KNOWN_LOCATION(t, "expected '%s'", expected);
     397                 :          0 :         return NULL;
     398                 :            :     }
     399                 :      17608 :     p->mark += 1;
     400                 :      17608 :     return t;
     401                 :            : }
     402                 :            : 
     403                 :            : expr_ty
     404                 :      61940 : _PyPegen_expect_soft_keyword(Parser *p, const char *keyword)
     405                 :            : {
     406         [ +  + ]:      61940 :     if (p->mark == p->fill) {
     407         [ -  + ]:         17 :         if (_PyPegen_fill_token(p) < 0) {
     408                 :          0 :             p->error_indicator = 1;
     409                 :          0 :             return NULL;
     410                 :            :         }
     411                 :            :     }
     412                 :      61940 :     Token *t = p->tokens[p->mark];
     413         [ +  + ]:      61940 :     if (t->type != NAME) {
     414                 :      37207 :         return NULL;
     415                 :            :     }
     416                 :      24733 :     const char *s = PyBytes_AsString(t->bytes);
     417         [ -  + ]:      24733 :     if (!s) {
     418                 :          0 :         p->error_indicator = 1;
     419                 :          0 :         return NULL;
     420                 :            :     }
     421         [ +  + ]:      24733 :     if (strcmp(s, keyword) != 0) {
     422                 :      24695 :         return NULL;
     423                 :            :     }
     424                 :         38 :     return _PyPegen_name_token(p);
     425                 :            : }
     426                 :            : 
     427                 :            : Token *
     428                 :     306539 : _PyPegen_get_last_nonnwhitespace_token(Parser *p)
     429                 :            : {
     430                 :            :     assert(p->mark >= 0);
     431                 :     306539 :     Token *token = NULL;
     432         [ +  - ]:     353253 :     for (int m = p->mark - 1; m >= 0; m--) {
     433                 :     353253 :         token = p->tokens[m];
     434   [ +  -  +  +  :     353253 :         if (token->type != ENDMARKER && (token->type < NEWLINE || token->type > DEDENT)) {
                   +  + ]
     435                 :            :             break;
     436                 :            :         }
     437                 :            :     }
     438                 :     306539 :     return token;
     439                 :            : }
     440                 :            : 
     441                 :            : PyObject *
     442                 :     534374 : _PyPegen_new_identifier(Parser *p, const char *n)
     443                 :            : {
     444                 :     534374 :     PyObject *id = PyUnicode_DecodeUTF8(n, strlen(n), NULL);
     445         [ -  + ]:     534374 :     if (!id) {
     446                 :          0 :         goto error;
     447                 :            :     }
     448                 :            :     /* PyUnicode_DecodeUTF8 should always return a ready string. */
     449                 :            :     assert(PyUnicode_IS_READY(id));
     450                 :            :     /* Check whether there are non-ASCII characters in the
     451                 :            :        identifier; if so, normalize to NFKC. */
     452         [ -  + ]:     534374 :     if (!PyUnicode_IS_ASCII(id))
     453                 :            :     {
     454                 :            :         PyObject *id2;
     455         [ #  # ]:          0 :         if (!init_normalization(p))
     456                 :            :         {
     457                 :          0 :             Py_DECREF(id);
     458                 :          0 :             goto error;
     459                 :            :         }
     460                 :          0 :         PyObject *form = PyUnicode_InternFromString("NFKC");
     461         [ #  # ]:          0 :         if (form == NULL)
     462                 :            :         {
     463                 :          0 :             Py_DECREF(id);
     464                 :          0 :             goto error;
     465                 :            :         }
     466                 :          0 :         PyObject *args[2] = {form, id};
     467                 :          0 :         id2 = _PyObject_FastCall(p->normalize, args, 2);
     468                 :          0 :         Py_DECREF(id);
     469                 :          0 :         Py_DECREF(form);
     470         [ #  # ]:          0 :         if (!id2) {
     471                 :          0 :             goto error;
     472                 :            :         }
     473         [ #  # ]:          0 :         if (!PyUnicode_Check(id2))
     474                 :            :         {
     475                 :          0 :             PyErr_Format(PyExc_TypeError,
     476                 :            :                          "unicodedata.normalize() must return a string, not "
     477                 :            :                          "%.200s",
     478                 :            :                          _PyType_Name(Py_TYPE(id2)));
     479                 :          0 :             Py_DECREF(id2);
     480                 :          0 :             goto error;
     481                 :            :         }
     482                 :          0 :         id = id2;
     483                 :            :     }
     484                 :     534374 :     PyUnicode_InternInPlace(&id);
     485         [ -  + ]:     534374 :     if (_PyArena_AddPyObject(p->arena, id) < 0)
     486                 :            :     {
     487                 :          0 :         Py_DECREF(id);
     488                 :          0 :         goto error;
     489                 :            :     }
     490                 :     534374 :     return id;
     491                 :            : 
     492                 :          0 : error:
     493                 :          0 :     p->error_indicator = 1;
     494                 :          0 :     return NULL;
     495                 :            : }
     496                 :            : 
     497                 :            : static expr_ty
     498                 :    1799794 : _PyPegen_name_from_token(Parser *p, Token* t)
     499                 :            : {
     500         [ +  + ]:    1799794 :     if (t == NULL) {
     501                 :    1265420 :         return NULL;
     502                 :            :     }
     503                 :     534374 :     const char *s = PyBytes_AsString(t->bytes);
     504         [ -  + ]:     534374 :     if (!s) {
     505                 :          0 :         p->error_indicator = 1;
     506                 :          0 :         return NULL;
     507                 :            :     }
     508                 :     534374 :     PyObject *id = _PyPegen_new_identifier(p, s);
     509         [ -  + ]:     534374 :     if (id == NULL) {
     510                 :          0 :         p->error_indicator = 1;
     511                 :          0 :         return NULL;
     512                 :            :     }
     513                 :     534374 :     return _PyAST_Name(id, Load, t->lineno, t->col_offset, t->end_lineno,
     514                 :            :                        t->end_col_offset, p->arena);
     515                 :            : }
     516                 :            : 
     517                 :            : expr_ty
     518                 :    1799794 : _PyPegen_name_token(Parser *p)
     519                 :            : {
     520                 :    1799794 :     Token *t = _PyPegen_expect_token(p, NAME);
     521                 :    1799794 :     return _PyPegen_name_from_token(p, t);
     522                 :            : }
     523                 :            : 
     524                 :            : void *
     525                 :    1013729 : _PyPegen_string_token(Parser *p)
     526                 :            : {
     527                 :    1013729 :     return _PyPegen_expect_token(p, STRING);
     528                 :            : }
     529                 :            : 
     530                 :          0 : expr_ty _PyPegen_soft_keyword_token(Parser *p) {
     531                 :          0 :     Token *t = _PyPegen_expect_token(p, NAME);
     532         [ #  # ]:          0 :     if (t == NULL) {
     533                 :          0 :         return NULL;
     534                 :            :     }
     535                 :            :     char *the_token;
     536                 :            :     Py_ssize_t size;
     537                 :          0 :     PyBytes_AsStringAndSize(t->bytes, &the_token, &size);
     538         [ #  # ]:          0 :     for (char **keyword = p->soft_keywords; *keyword != NULL; keyword++) {
     539         [ #  # ]:          0 :         if (strncmp(*keyword, the_token, size) == 0) {
     540                 :          0 :             return _PyPegen_name_from_token(p, t);
     541                 :            :         }
     542                 :            :     }
     543                 :          0 :     return NULL;
     544                 :            : }
     545                 :            : 
     546                 :            : static PyObject *
     547                 :     832141 : parsenumber_raw(const char *s)
     548                 :            : {
     549                 :            :     const char *end;
     550                 :            :     long x;
     551                 :            :     double dx;
     552                 :            :     Py_complex compl;
     553                 :            :     int imflag;
     554                 :            : 
     555                 :            :     assert(s != NULL);
     556                 :     832141 :     errno = 0;
     557                 :     832141 :     end = s + strlen(s) - 1;
     558   [ +  +  -  + ]:     832141 :     imflag = *end == 'j' || *end == 'J';
     559         [ +  + ]:     832141 :     if (s[0] == '0') {
     560                 :     266553 :         x = (long)PyOS_strtoul(s, (char **)&end, 0);
     561   [ +  +  +  - ]:     266553 :         if (x < 0 && errno == 0) {
     562                 :          2 :             return PyLong_FromString(s, (char **)0, 0);
     563                 :            :         }
     564                 :            :     }
     565                 :            :     else {
     566                 :     565588 :         x = PyOS_strtol(s, (char **)&end, 0);
     567                 :            :     }
     568         [ +  + ]:     832139 :     if (*end == '\0') {
     569         [ +  + ]:     830013 :         if (errno != 0) {
     570                 :         26 :             return PyLong_FromString(s, (char **)0, 0);
     571                 :            :         }
     572                 :     829987 :         return PyLong_FromLong(x);
     573                 :            :     }
     574                 :            :     /* XXX Huge floats may silently fail */
     575         [ +  + ]:       2126 :     if (imflag) {
     576                 :          6 :         compl.real = 0.;
     577                 :          6 :         compl.imag = PyOS_string_to_double(s, (char **)&end, NULL);
     578   [ -  +  -  - ]:          6 :         if (compl.imag == -1.0 && PyErr_Occurred()) {
     579                 :          0 :             return NULL;
     580                 :            :         }
     581                 :          6 :         return PyComplex_FromCComplex(compl);
     582                 :            :     }
     583                 :       2120 :     dx = PyOS_string_to_double(s, NULL, NULL);
     584   [ -  +  -  - ]:       2120 :     if (dx == -1.0 && PyErr_Occurred()) {
     585                 :          0 :         return NULL;
     586                 :            :     }
     587                 :       2120 :     return PyFloat_FromDouble(dx);
     588                 :            : }
     589                 :            : 
     590                 :            : static PyObject *
     591                 :     832141 : parsenumber(const char *s)
     592                 :            : {
     593                 :            :     char *dup;
     594                 :            :     char *end;
     595                 :     832141 :     PyObject *res = NULL;
     596                 :            : 
     597                 :            :     assert(s != NULL);
     598                 :            : 
     599         [ +  + ]:     832141 :     if (strchr(s, '_') == NULL) {
     600                 :     832135 :         return parsenumber_raw(s);
     601                 :            :     }
     602                 :            :     /* Create a duplicate without underscores. */
     603                 :          6 :     dup = PyMem_Malloc(strlen(s) + 1);
     604         [ -  + ]:          6 :     if (dup == NULL) {
     605                 :          0 :         return PyErr_NoMemory();
     606                 :            :     }
     607                 :          6 :     end = dup;
     608         [ +  + ]:         50 :     for (; *s; s++) {
     609         [ +  + ]:         44 :         if (*s != '_') {
     610                 :         36 :             *end++ = *s;
     611                 :            :         }
     612                 :            :     }
     613                 :          6 :     *end = '\0';
     614                 :          6 :     res = parsenumber_raw(dup);
     615                 :          6 :     PyMem_Free(dup);
     616                 :          6 :     return res;
     617                 :            : }
     618                 :            : 
     619                 :            : expr_ty
     620                 :     922774 : _PyPegen_number_token(Parser *p)
     621                 :            : {
     622                 :     922774 :     Token *t = _PyPegen_expect_token(p, NUMBER);
     623         [ +  + ]:     922774 :     if (t == NULL) {
     624                 :      90633 :         return NULL;
     625                 :            :     }
     626                 :            : 
     627                 :     832141 :     const char *num_raw = PyBytes_AsString(t->bytes);
     628         [ -  + ]:     832141 :     if (num_raw == NULL) {
     629                 :          0 :         p->error_indicator = 1;
     630                 :          0 :         return NULL;
     631                 :            :     }
     632                 :            : 
     633   [ -  +  -  - ]:     832141 :     if (p->feature_version < 6 && strchr(num_raw, '_') != NULL) {
     634                 :          0 :         p->error_indicator = 1;
     635                 :          0 :         return RAISE_SYNTAX_ERROR("Underscores in numeric literals are only supported "
     636                 :            :                                   "in Python 3.6 and greater");
     637                 :            :     }
     638                 :            : 
     639                 :     832141 :     PyObject *c = parsenumber(num_raw);
     640                 :            : 
     641         [ -  + ]:     832141 :     if (c == NULL) {
     642                 :          0 :         p->error_indicator = 1;
     643                 :          0 :         PyThreadState *tstate = _PyThreadState_GET();
     644                 :            :         // The only way a ValueError should happen in _this_ code is via
     645                 :            :         // PyLong_FromString hitting a length limit.
     646         [ #  # ]:          0 :         if (tstate->current_exception != NULL &&
     647         [ #  # ]:          0 :             Py_TYPE(tstate->current_exception) == (PyTypeObject *)PyExc_ValueError
     648                 :            :         ) {
     649                 :          0 :             PyObject *exc = PyErr_GetRaisedException();
     650                 :            :             /* Intentionally omitting columns to avoid a wall of 1000s of '^'s
     651                 :            :              * on the error message. Nobody is going to overlook their huge
     652                 :            :              * numeric literal once given the line. */
     653                 :          0 :             RAISE_ERROR_KNOWN_LOCATION(
     654                 :            :                 p, PyExc_SyntaxError,
     655                 :          0 :                 t->lineno, -1 /* col_offset */,
     656                 :          0 :                 t->end_lineno, -1 /* end_col_offset */,
     657                 :            :                 "%S - Consider hexadecimal for huge integer literals "
     658                 :            :                 "to avoid decimal conversion limits.",
     659                 :            :                 exc);
     660                 :          0 :             Py_DECREF(exc);
     661                 :            :         }
     662                 :          0 :         return NULL;
     663                 :            :     }
     664                 :            : 
     665         [ -  + ]:     832141 :     if (_PyArena_AddPyObject(p->arena, c) < 0) {
     666                 :          0 :         Py_DECREF(c);
     667                 :          0 :         p->error_indicator = 1;
     668                 :          0 :         return NULL;
     669                 :            :     }
     670                 :            : 
     671                 :     832141 :     return _PyAST_Constant(c, NULL, t->lineno, t->col_offset, t->end_lineno,
     672                 :            :                            t->end_col_offset, p->arena);
     673                 :            : }
     674                 :            : 
     675                 :            : /* Check that the source for a single input statement really is a single
     676                 :            :    statement by looking at what is left in the buffer after parsing.
     677                 :            :    Trailing whitespace and comments are OK. */
     678                 :            : static int // bool
     679                 :         56 : bad_single_statement(Parser *p)
     680                 :            : {
     681                 :         56 :     char *cur = p->tok->cur;
     682                 :         56 :     char c = *cur;
     683                 :            : 
     684                 :            :     for (;;) {
     685   [ -  +  -  +  :         56 :         while (c == ' ' || c == '\t' || c == '\n' || c == '\014') {
             -  +  -  + ]
     686                 :          0 :             c = *++cur;
     687                 :            :         }
     688                 :            : 
     689         [ +  - ]:         56 :         if (!c) {
     690                 :         56 :             return 0;
     691                 :            :         }
     692                 :            : 
     693         [ #  # ]:          0 :         if (c != '#') {
     694                 :          0 :             return 1;
     695                 :            :         }
     696                 :            : 
     697                 :            :         /* Suck up comment. */
     698   [ #  #  #  # ]:          0 :         while (c && c != '\n') {
     699                 :          0 :             c = *++cur;
     700                 :            :         }
     701                 :            :     }
     702                 :            : }
     703                 :            : 
     704                 :            : static int
     705                 :        351 : compute_parser_flags(PyCompilerFlags *flags)
     706                 :            : {
     707                 :        351 :     int parser_flags = 0;
     708         [ +  + ]:        351 :     if (!flags) {
     709                 :          4 :         return 0;
     710                 :            :     }
     711         [ -  + ]:        347 :     if (flags->cf_flags & PyCF_DONT_IMPLY_DEDENT) {
     712                 :          0 :         parser_flags |= PyPARSE_DONT_IMPLY_DEDENT;
     713                 :            :     }
     714         [ +  + ]:        347 :     if (flags->cf_flags & PyCF_IGNORE_COOKIE) {
     715                 :        146 :         parser_flags |= PyPARSE_IGNORE_COOKIE;
     716                 :            :     }
     717         [ -  + ]:        347 :     if (flags->cf_flags & CO_FUTURE_BARRY_AS_BDFL) {
     718                 :          0 :         parser_flags |= PyPARSE_BARRY_AS_BDFL;
     719                 :            :     }
     720         [ -  + ]:        347 :     if (flags->cf_flags & PyCF_TYPE_COMMENTS) {
     721                 :          0 :         parser_flags |= PyPARSE_TYPE_COMMENTS;
     722                 :            :     }
     723   [ +  +  -  + ]:        347 :     if ((flags->cf_flags & PyCF_ONLY_AST) && flags->cf_feature_version < 7) {
     724                 :          0 :         parser_flags |= PyPARSE_ASYNC_HACKS;
     725                 :            :     }
     726         [ -  + ]:        347 :     if (flags->cf_flags & PyCF_ALLOW_INCOMPLETE_INPUT) {
     727                 :          0 :         parser_flags |= PyPARSE_ALLOW_INCOMPLETE_INPUT;
     728                 :            :     }
     729                 :        347 :     return parser_flags;
     730                 :            : }
     731                 :            : 
     732                 :            : // Parser API
     733                 :            : 
     734                 :            : Parser *
     735                 :       1585 : _PyPegen_Parser_New(struct tok_state *tok, int start_rule, int flags,
     736                 :            :                     int feature_version, int *errcode, PyArena *arena)
     737                 :            : {
     738                 :       1585 :     Parser *p = PyMem_Malloc(sizeof(Parser));
     739         [ -  + ]:       1585 :     if (p == NULL) {
     740                 :          0 :         return (Parser *) PyErr_NoMemory();
     741                 :            :     }
     742                 :            :     assert(tok != NULL);
     743                 :       1585 :     tok->type_comments = (flags & PyPARSE_TYPE_COMMENTS) > 0;
     744                 :       1585 :     tok->async_hacks = (flags & PyPARSE_ASYNC_HACKS) > 0;
     745                 :       1585 :     p->tok = tok;
     746                 :       1585 :     p->keywords = NULL;
     747                 :       1585 :     p->n_keyword_lists = -1;
     748                 :       1585 :     p->soft_keywords = NULL;
     749                 :       1585 :     p->tokens = PyMem_Malloc(sizeof(Token *));
     750         [ -  + ]:       1585 :     if (!p->tokens) {
     751                 :          0 :         PyMem_Free(p);
     752                 :          0 :         return (Parser *) PyErr_NoMemory();
     753                 :            :     }
     754                 :       1585 :     p->tokens[0] = PyMem_Calloc(1, sizeof(Token));
     755         [ -  + ]:       1585 :     if (!p->tokens[0]) {
     756                 :          0 :         PyMem_Free(p->tokens);
     757                 :          0 :         PyMem_Free(p);
     758                 :          0 :         return (Parser *) PyErr_NoMemory();
     759                 :            :     }
     760         [ -  + ]:       1585 :     if (!growable_comment_array_init(&p->type_ignore_comments, 10)) {
     761                 :          0 :         PyMem_Free(p->tokens[0]);
     762                 :          0 :         PyMem_Free(p->tokens);
     763                 :          0 :         PyMem_Free(p);
     764                 :          0 :         return (Parser *) PyErr_NoMemory();
     765                 :            :     }
     766                 :            : 
     767                 :       1585 :     p->mark = 0;
     768                 :       1585 :     p->fill = 0;
     769                 :       1585 :     p->size = 1;
     770                 :            : 
     771                 :       1585 :     p->errcode = errcode;
     772                 :       1585 :     p->arena = arena;
     773                 :       1585 :     p->start_rule = start_rule;
     774                 :       1585 :     p->parsing_started = 0;
     775                 :       1585 :     p->normalize = NULL;
     776                 :       1585 :     p->error_indicator = 0;
     777                 :            : 
     778                 :       1585 :     p->starting_lineno = 0;
     779                 :       1585 :     p->starting_col_offset = 0;
     780                 :       1585 :     p->flags = flags;
     781                 :       1585 :     p->feature_version = feature_version;
     782                 :       1585 :     p->known_err_token = NULL;
     783                 :       1585 :     p->level = 0;
     784                 :       1585 :     p->call_invalid_rules = 0;
     785                 :            : #ifdef Py_DEBUG
     786                 :            :     p->debug = _Py_GetConfig()->parser_debug;
     787                 :            : #endif
     788                 :       1585 :     return p;
     789                 :            : }
     790                 :            : 
     791                 :            : void
     792                 :       1585 : _PyPegen_Parser_Free(Parser *p)
     793                 :            : {
     794                 :       1585 :     Py_XDECREF(p->normalize);
     795         [ +  + ]:    2005757 :     for (int i = 0; i < p->size; i++) {
     796                 :    2004172 :         PyMem_Free(p->tokens[i]);
     797                 :            :     }
     798                 :       1585 :     PyMem_Free(p->tokens);
     799                 :       1585 :     growable_comment_array_deallocate(&p->type_ignore_comments);
     800                 :       1585 :     PyMem_Free(p);
     801                 :       1585 : }
     802                 :            : 
     803                 :            : static void
     804                 :          0 : reset_parser_state_for_error_pass(Parser *p)
     805                 :            : {
     806         [ #  # ]:          0 :     for (int i = 0; i < p->fill; i++) {
     807                 :          0 :         p->tokens[i]->memo = NULL;
     808                 :            :     }
     809                 :          0 :     p->mark = 0;
     810                 :          0 :     p->call_invalid_rules = 1;
     811                 :            :     // Don't try to get extra tokens in interactive mode when trying to
     812                 :            :     // raise specialized errors in the second pass.
     813                 :          0 :     p->tok->interactive_underflow = IUNDERFLOW_STOP;
     814                 :          0 : }
     815                 :            : 
     816                 :            : static inline int
     817                 :          0 : _is_end_of_source(Parser *p) {
     818                 :          0 :     int err = p->tok->done;
     819   [ #  #  #  #  :          0 :     return err == E_EOF || err == E_EOFS || err == E_EOLS;
                   #  # ]
     820                 :            : }
     821                 :            : 
     822                 :            : void *
     823                 :       1585 : _PyPegen_run_parser(Parser *p)
     824                 :            : {
     825                 :       1585 :     void *res = _PyPegen_parse(p);
     826                 :            :     assert(p->level == 0);
     827         [ -  + ]:       1585 :     if (res == NULL) {
     828   [ #  #  #  # ]:          0 :         if ((p->flags & PyPARSE_ALLOW_INCOMPLETE_INPUT) &&  _is_end_of_source(p)) {
     829                 :          0 :             PyErr_Clear();
     830                 :          0 :             return RAISE_SYNTAX_ERROR("incomplete input");
     831                 :            :         }
     832   [ #  #  #  # ]:          0 :         if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_SyntaxError)) {
     833                 :          0 :             return NULL;
     834                 :            :         }
     835                 :            :        // Make a second parser pass. In this pass we activate heavier and slower checks
     836                 :            :         // to produce better error messages and more complete diagnostics. Extra "invalid_*"
     837                 :            :         // rules will be active during parsing.
     838                 :          0 :         Token *last_token = p->tokens[p->fill - 1];
     839                 :          0 :         reset_parser_state_for_error_pass(p);
     840                 :          0 :         _PyPegen_parse(p);
     841                 :            : 
     842                 :            :         // Set SyntaxErrors accordingly depending on the parser/tokenizer status at the failure
     843                 :            :         // point.
     844                 :          0 :         _Pypegen_set_syntax_error(p, last_token);
     845                 :          0 :        return NULL;
     846                 :            :     }
     847                 :            : 
     848   [ +  +  -  + ]:       1585 :     if (p->start_rule == Py_single_input && bad_single_statement(p)) {
     849                 :          0 :         p->tok->done = E_BADSINGLE; // This is not necessary for now, but might be in the future
     850                 :          0 :         return RAISE_SYNTAX_ERROR("multiple statements found while compiling a single statement");
     851                 :            :     }
     852                 :            : 
     853                 :            :     // test_peg_generator defines _Py_TEST_PEGEN to not call PyAST_Validate()
     854                 :            : #if defined(Py_DEBUG) && !defined(_Py_TEST_PEGEN)
     855                 :            :     if (p->start_rule == Py_single_input ||
     856                 :            :         p->start_rule == Py_file_input ||
     857                 :            :         p->start_rule == Py_eval_input)
     858                 :            :     {
     859                 :            :         if (!_PyAST_Validate(res)) {
     860                 :            :             return NULL;
     861                 :            :         }
     862                 :            :     }
     863                 :            : #endif
     864                 :       1585 :     return res;
     865                 :            : }
     866                 :            : 
     867                 :            : mod_ty
     868                 :         22 : _PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filename_ob,
     869                 :            :                              const char *enc, const char *ps1, const char *ps2,
     870                 :            :                              PyCompilerFlags *flags, int *errcode, PyArena *arena)
     871                 :            : {
     872                 :         22 :     struct tok_state *tok = _PyTokenizer_FromFile(fp, enc, ps1, ps2);
     873         [ -  + ]:         22 :     if (tok == NULL) {
     874         [ #  # ]:          0 :         if (PyErr_Occurred()) {
     875                 :          0 :             _PyPegen_raise_tokenizer_init_error(filename_ob);
     876                 :          0 :             return NULL;
     877                 :            :         }
     878                 :          0 :         return NULL;
     879                 :            :     }
     880   [ +  -  +  -  :         44 :     if (!tok->fp || ps1 != NULL || ps2 != NULL ||
             +  -  -  + ]
     881                 :         22 :         PyUnicode_CompareWithASCIIString(filename_ob, "<stdin>") == 0) {
     882                 :          0 :         tok->fp_interactive = 1;
     883                 :            :     }
     884                 :            :     // This transfers the ownership to the tokenizer
     885                 :         22 :     tok->filename = Py_NewRef(filename_ob);
     886                 :            : 
     887                 :            :     // From here on we need to clean up even if there's an error
     888                 :         22 :     mod_ty result = NULL;
     889                 :            : 
     890                 :         22 :     int parser_flags = compute_parser_flags(flags);
     891                 :         22 :     Parser *p = _PyPegen_Parser_New(tok, start_rule, parser_flags, PY_MINOR_VERSION,
     892                 :            :                                     errcode, arena);
     893         [ -  + ]:         22 :     if (p == NULL) {
     894                 :          0 :         goto error;
     895                 :            :     }
     896                 :            : 
     897                 :         22 :     result = _PyPegen_run_parser(p);
     898                 :         22 :     _PyPegen_Parser_Free(p);
     899                 :            : 
     900                 :         22 : error:
     901                 :         22 :     _PyTokenizer_Free(tok);
     902                 :         22 :     return result;
     903                 :            : }
     904                 :            : 
     905                 :            : mod_ty
     906                 :        329 : _PyPegen_run_parser_from_string(const char *str, int start_rule, PyObject *filename_ob,
     907                 :            :                        PyCompilerFlags *flags, PyArena *arena)
     908                 :            : {
     909                 :        329 :     int exec_input = start_rule == Py_file_input;
     910                 :            : 
     911                 :            :     struct tok_state *tok;
     912   [ +  +  +  + ]:        329 :     if (flags != NULL && flags->cf_flags & PyCF_IGNORE_COOKIE) {
     913                 :        146 :         tok = _PyTokenizer_FromUTF8(str, exec_input);
     914                 :            :     } else {
     915                 :        183 :         tok = _PyTokenizer_FromString(str, exec_input);
     916                 :            :     }
     917         [ -  + ]:        329 :     if (tok == NULL) {
     918         [ #  # ]:          0 :         if (PyErr_Occurred()) {
     919                 :          0 :             _PyPegen_raise_tokenizer_init_error(filename_ob);
     920                 :            :         }
     921                 :          0 :         return NULL;
     922                 :            :     }
     923                 :            :     // This transfers the ownership to the tokenizer
     924                 :        329 :     tok->filename = Py_NewRef(filename_ob);
     925                 :            : 
     926                 :            :     // We need to clear up from here on
     927                 :        329 :     mod_ty result = NULL;
     928                 :            : 
     929                 :        329 :     int parser_flags = compute_parser_flags(flags);
     930         [ +  + ]:        325 :     int feature_version = flags && (flags->cf_flags & PyCF_ONLY_AST) ?
     931         [ +  + ]:        654 :         flags->cf_feature_version : PY_MINOR_VERSION;
     932                 :        329 :     Parser *p = _PyPegen_Parser_New(tok, start_rule, parser_flags, feature_version,
     933                 :            :                                     NULL, arena);
     934         [ -  + ]:        329 :     if (p == NULL) {
     935                 :          0 :         goto error;
     936                 :            :     }
     937                 :            : 
     938                 :        329 :     result = _PyPegen_run_parser(p);
     939                 :        329 :     _PyPegen_Parser_Free(p);
     940                 :            : 
     941                 :        329 : error:
     942                 :        329 :     _PyTokenizer_Free(tok);
     943                 :        329 :     return result;
     944                 :            : }

Generated by: LCOV version 1.14