J'ai essayé d'ajouter un post-incrément à l'implémentation CPython

Liste de liens

Les trois fois plus une édition supplémentaire. Présentation et résumé de l'ajout de post-incrémentation à CPython [J'ai essayé d'ajouter un post-incrément à CPython Implémentation Liste de toutes les modifications lors de l'ajout de post-incrémentation à CPython Édition supplémentaire de l'ajout de post-incrémentation à CPython

la mise en oeuvre

C'est finalement le stade de sa mise en œuvre.

Construire

environnement

Télécharger Python 3.5.0

Téléchargez Python 3.5.0 (dernière version du 22 octobre 2015 au début de cette expérience) à partir du Site Web officiel et placez-le dans un répertoire approprié.

Construire

$ cd Python-3.5.0/
$ CFLAGS="-O0" ./configure --prefix=$HOME/local

$ ./configure      
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
...
creating Modules/Setup.local
creating Makefile

$ make -j 8 && make install
gcc -pthread -c -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes    -Werror=declaration-after-statement   -I. -IInclude -I./Include    -DPy_BUILD_CORE -o Programs/python.o ./Programs/python.c
gcc -pthread -c -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes    -Werror=declaration-after-statement   -I. -IInclude -I./Include    -DPy_BUILD_CORE -o Parser/acceler.o Parser/acceler.c
...

$ ./local/bin/python3
Python 3.5.0 (default, Nov 12 2015, 16:48:32)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

...

Il n'y a pas eu d'erreur particulière et cela ne s'est pas arrêté. Dans Création et utilisation d'une version de débogage de la documentation Python-Python Extension Patterns 0.1.0,

$ ../configure CFLAGS='-DPy_DEBUG -DPy_TRACE_REFS' --with-pydebug

Il semble que diverses informations soient générées afin qu'il soit plus facile de déboguer si vous ajoutez des options comme, et j'ai trouvé que de telles macros sont définies dans le code source, mais à la fin je n'ai pas compris comment l'utiliser.

Outils utilisés pour jouer avec

En écrivant ceci, j'ai trouvé une page appelée Techniques pour lire le code source.

Fumbling!

Couper en jetons

Pour l'instant, suivez le mouvement avec gdb pour voir comment fonctionne Python. Démarrez gdb en tant que M-x gud-gdb dans Emacs et lisez le script Python suivant pour vérifier le comportement.

test.py


a = 0
a += 1
a++

Si vous essayez de le faire docilement

  File "test.py", line 3
    a++
      ^
SyntaxError: invalid syntax

Est affiché.

Par conséquent, définissez un point d'arrêt dans la fonction principale et suivez l'opération.

La fonction principale se trouve dans Programmes / python.c. Au début, la variable ʻargv_copy` continue d'être comme ça, mais comme elle ne traite que l'argument, si vous l'ignorez, à partir de la 69ème ligne

Programs/python.c


res = Py_Main(argc, argv_copy);
    for (i = 0; i < argc; i++) {
            PyMem_RawFree(argv_copy2[i]);
    }
    PyMem_RawFree(argv_copy);
    PyMem_RawFree(argv_copy2);
    return res;

Peut être trouvé. Evidemment suspect, regardons donc à l'intérieur de la fonction Py_Main. C'est dans Modules / main.c.

Si vous le suivez, vous trouverez sts = run_file (fp, filename, & cf); à la ligne 768, donc lorsque vous entrez, cette fois à la ligne 318, run = PyRun_AnyFileExFlags (fp, filename_str, filename! = NULL, p_cf ); Est trouvé.

Si vous suivez le mouvement en utilisant le nom comme indice de cette manière,

Python/pythonrun.c, 68: PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit, PyCompilerFlags *flags) Python/pythonrun.c, 339: PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit, PyCompilerFlags *flags) PyRun_FileExFlags(fp, filename, Py_file_input, d, d, closeit, flags); Python/pythonrun.c, 396: PyRun_FileExFlags(fp, filename, Py_file_input, d, d, closeit, flags);

Si vous suivez PyRun_FileExFlags, vous trouverez mod = PyParser_ASTFromStringObject (str, filename, start, flags, arena); dedans (ligne 916 de Python / pythonrun.c).

En plus d'avoir "Parser" dans le nom de la fonction, NULL est assigné à mod ici, etgo to exit;est fait dans l'instruction if immédiatement après. C'est moche. Regarde à l'intérieur.

Si vous suivez Python / pythonrun.c: PyParser_ASTFromStringObject Parser / parsetok.c: PyParser_ParseFileObjectParser / parsetok.c: parsetok, vous trouverez la partie suivante de la ligne 201.

    for (;;) {
            char *a, *b;
        int type;
            size_t len;
            char *str;
        int col_offset;

            type = PyTokenizer_Get(tok, &a, &b);
        if (type == ERRORTOKEN) {
                    err_ret->error = tok->done;
                    break;
            }

        ...

        len = b - a; /* XXX this may compute NULL - NULL */
            str = (char *) PyObject_MALLOC(len + 1);
        if (str == NULL) {
         
...

Puisqu'il s'agit d'une boucle infinie, je suppose qu'il traite l'entrée d'une extrémité, et lorsque je tourne la boucle tout en affichant la valeur de str comme un essai, str est "a", "=", "0" dans l'ordre. , "", "A", "+ =", "1", "", "a", "+", "+", "" et la valeur de retour indiquant une erreur à la ligne 263 PyParser_AddToken Vous pouvez voir qu'il revient.

Dès que vous entrez PyParser_AddToken, vous trouverez la ligne ʻilabel = classify (ps, type, str);, et après cela, il semble que vous sautiez à l'endroit où la gestion des erreurs est effectuée en fonction de la valeur de ʻilabel.

À l'intérieur de classify


static int
classify(parser_state *ps, int type, const char *str)
{
    grammar *g = ps->p_grammar;
    int n = g->g_ll.ll_nlabels;

    ...

    {
        label *l = g->g_ll.ll_label;
            int i;
            for (i = n; i > 0; i--, l++) {
                    if (l->lb_type == type && l->lb_str == NULL) {
                        D(printf("It's a token we know\n"));
                        return n - i;
                    }
            }
    }

    D(printf("Illegal token\n"));
    return -1;
}

C'est comme ça.

Cela ressemble à un rapide coup d'œil à la liste des jetons, à partir de laquelle vous recherchez un jeton correspondant.

Donc, tout d'abord, améliorez pour lire l'incrément "++" comme un jeton. Si je peux en quelque sorte ajouter "++" à cette liste, je pense que je peux l'effacer.

Pour revenir en arrière, de type = PyTokenizer_Get (tok, & a, & b); dans la fonction parsetok, Parser / tokenizer.c: int PyTokenizer_Get (struct tok_state * tok, char ** p_start, char ** p_end) → Il est livré avec Parser / tokenizer.c: static int tok_get (struct tok_state * tok, char ** p_start, char ** p_end).

Si vous ignorez la partie qui semble séparer les sauts de ligne et les numéros en référence au commentaire, des fonctions telles que PyToken_TwoChars et PyToken_ThreeChars sont appelées dans le bloc commençant par le commentaire/ * Vérifier le jeton à deux caractères * / Vous pouvez voir qu'il a été publié. Regardant à l'intérieur de ces gars

Parser/tokenizer.c


    switch (c1) {
    case '=':
                switch (c2) {
                    case '=':               return EQEQUAL;
                }
                break;

L'instruction switch continue comme ceci, et si vous l'ajoutez ici, elle reconnaîtra «++» comme un jeton.

Tout d'abord, ajoutez ʻINCREMENT à la fin de ʻInclude / token.h où ce ʻEQEQUAL` renvoyé est défini comme ceci.

Include/token.h


#define ERRORTOKEN    56
#define N_TOKENS    57

#ifdef DOSS_INCREMENT
#define INCREMENT 58
#endif

Ci-dessous, les modifications sont affichées en les insérant dans #ifdef DOSS_INCREMENT et # endif.

De plus, si vous essayez grep“ EQEQUAL ”-r -I, il y a un tableau de noms de jetons dans Parser / tokenizer.c, modifiez-le comme suit.

Parser/tokenizer.c


/* Token names */

const char *_PyParser_TokenNames[] = {
 ...
    "<ERRORTOKEN>",
    "<N_TOKENS>"
    #ifdef DOSS_INCREMENT
    ,"INCREMENT"
    #endif
 };

Parser/tokenizer.c



int
PyToken_TwoChars(int c1, int c2)
{
    switch (c1) {
        ...
    case '+':
            switch (c2) {
            case '=':               return PLUSEQUAL;
            #ifdef DOSS_INCREMENT
            case '+':               return INCREMENT;
            #endif
        }
        break;
    
(Omis)

Ajoutez l'INCREMENT défini à l'emplacement de l'instruction switch.

Si vous faites cela et le refaites et le poursuivez avec gdb, vous pouvez voir que ++ est certainement lu comme un jeton. De plus, afin d'activer #ifdef DOSS_INCREMNET ~ # endif, -DDOSS_INCREMENT = 1 a été ajouté à CFLAGS et make a été effectué.

Bien sûr, ce seul changement fait toujours que la fonction PyParser_AddToken renvoie une valeur de retour d'erreur.

Grammaire préférée

Au fait, lorsque j'ai cherché sur Google la grammaire python, il s'est avéré qu'un fichier appelé Grammar existait. Le site est ici. Il dit "la grammaire Python complète, telle qu'elle est lue par le générateur d'analyseur et utilisée pour analyser les fichiers source Python". Je dois le lire.

Lorsque vous l'ouvrez, vous trouverez une définition grammaticale très similaire à une expression régulière. Cela semble être une notation BNF étendue. Je ne pouvais pas croire que la grammaire était écrite dans un tel fichier de langage non-C, j'ai donc supprimé ce fichier et l'ai créé. Alors je n'ai pas pu construire. Apparemment, il est vraiment utilisé. Quand j'ai regardé près de la puissance **, qui est similaire à l'incrément, il y avait quelque chose de proche, alors j'ai décidé de l'imiter et de le changer.

Grammar


#######################################
# #ifdef DOSS_INCREMENT
# Tips: We should not use Japanese here!!
#######################################
#factor: ('+'|'-'|'~') factor | power
#power: atom_expr ['**' factor]
# factor: power
factor: ['+'|'-'|'~'] inc ['**' factor]
inc: atom_expr ['++']
#######################################

Les deux premières lignes,

Grammar


factor: ('+'|'-'|'~') factor | power
power: atom_expr ['**' factor]

À l'origine, les deux lignes ci-dessous,

Grammar


factor: ['+'|'-'|'~'] inc ['**' factor]
inc: atom_expr ['++']

Est après le changement. Veuillez noter que le symbole de commentaire est #. Tant que l'incrément est implémenté, j'ai pensé que ce serait mauvais si plusieurs codes étaient ajoutés, j'ai donc rendu possible l'ajout d'au plus un code. De plus, la priorité de l'opérateur ++ est supérieure à **.

Ce fichier de grammaire vous aidera beaucoup plus tard. Dans la partie commentée, un itinéraire approximatif pour l'avenir a été écrit. Le premier est le commentaire suivant à la fin de la phrase.

Grammar


# The reason that keywords are test nodes instead of NAME is that using NAME
# results in an ambiguity. ast.c makes sure it's a NAME.
# "test '=' test" is really "keyword '=' test", but we have no such token.
# These need to be in a single rule to avoid grammar that is ambiguous
# to our LL(1) parser. Even though 'test' includes '*expr' in star_expr,
# we explicitly match '*' here, too, to give it proper precedence.
# Illegal combinations and orderings are blocked in ast.c:
# multiple (test comp_for) arguements are blocked; keyword unpackings
# that precede iterable unpackings are blocked; etc.

C'est long, donc en résumé, il semble que vous devriez également jouer avec ast.c. De plus, au début de la grammaire, il y a quelque chose comme ça.

Grammar


# NOTE WELL: You should also follow all the steps listed at
# https://docs.python.org/devguide/grammar.html

Il présente également un site utile. Ce site, la page officielle de Python, contient des instructions pour ajouter une nouvelle grammaire. Cependant, c'est très simple.

Présentation de l'arbre de syntaxe abstraite

J'ai décidé de réécrire ast.c comme indiqué par Grammar. Il semble qu'il crée une structure arborescente, mais vu que la valeur de retour est de type int, il semble qu'il ne fait que vérifier les erreurs grammaticales. La structure en bois a peut-être déjà été réalisée. Je ne suis pas sûr, donc je vais imiter un autre code et implémenter quelque chose pour incrémenter dans une atmosphère. Il y a un talent pour imiter simplement ici. Comme ce sont les facteurs et la puissance qui ont été modifiés dans la grammaire, il semble qu'il serait préférable de modifier ou de supprimer ces deux et de taper inc. Après cela, nous étudierons le type de comportement autour de +, -, ~ du même opérateur mononomial que ++. Voir ici pour des changements spécifiques.

Faisons "faire" immédiatement.

In file included from Python/ast.c:7:0:
Python/ast.c: In function ‘ast_for_inc’:
Python/ast.c:2492:22: error: ‘UInc’ undeclared (first use in this function)
       return UnaryOp(UInc, expression, LINENO(n), n->n_col_offset, c->c_arena);
                      ^
Include/Python-ast.h:503:49: note: in definition of macro ‘UnaryOp’
 #define UnaryOp(a0, a1, a2, a3, a4) _Py_UnaryOp(a0, a1, a2, a3, a4)
                                                 ^
Python/ast.c:2492:22: note: each undeclared identifier is reported only once for each function it appears in
       return UnaryOp(UInc, expression, LINENO(n), n->n_col_offset, c->c_arena);
                      ^
Include/Python-ast.h:503:49: note: in definition of macro ‘UnaryOp’
 #define UnaryOp(a0, a1, a2, a3, a4) _Py_UnaryOp(a0, a1, a2, a3, a4)
                                                 ^
make: *** [Python/ast.o]Erreur 1

J'obtiens une erreur dans ast.c. Certes, je ne me souviens pas avoir défini UInc. Après cela, je vais jeter un œil au site utile que j'ai vu auparavant dans Tekito. Extrait de la liste de contrôle

  • Grammar/Grammar: OK, you’d probably worked this one out :)
  • Parser/Python.asdl may need changes to match the Grammar. Run make to regenerate Include/Python-ast.h and Python/Python-ast.c.
  • Python/ast.c will need changes to create the AST objects involved with the Grammar change.
  • Parser/pgen needs to be rerun to regenerate Include/graminit.h and Python/graminit.c. (make should handle this for you.)
  • Python/symtable.c: This handles the symbol collection pass that happens immediately before the compilation pass.
  • Python/compile.c: You will need to create or modify the compiler_* functions to generate opcodes for your productions.

Je l'ai fait au sommet. Le second n'a pas beaucoup de sens. En as-tu besoin? ast.c a été réécrit. Je regarde pgen et symbole, mais je ne trouve rien à changer. Je vais l'oublier pour le moment. Il semble y avoir quelque chose comme compile.c. Compilez avec Python, qui devrait être un langage d'interprétation ...? J'ai trouvé un [site] utile (https://docs.python.org/devguide/compiler.html) lorsque je l'ai googlé. Il semble que ce soit comment faire un compilateur. On dirait que c'est vraiment une compilation. Vous pouvez également voir le bytecode. À la suite de diverses enquêtes, il semble qu'il existe une machine virtuelle Python, et cela semble être un code d'octet pour cela. Cela devient une couche inférieure.

Réécriture de compile.c

Compile.c a certainement

compile.c


/*
 * This file compiles an abstract syntax tree (AST) into Python bytecode.
 *

Qu'est-ce qui est écrit. Recherchez des mots tels que Unary, UAdd et power, et ajoutez naturellement des incréments pour s'adapter.

compile.c


    case UNARY_POSITIVE:
    case UNARY_NEGATIVE:
    case UNARY_NOT:
    case UNARY_INVERT:
#ifdef DOSS_INCREMENT
    case UNARY_INCREMENT:
#endif
        return 0;

compile.c


    case UAdd:
        return UNARY_POSITIVE;
    case USub:
        return UNARY_NEGATIVE;
#ifdef DOSS_INCREMENT
    case UInc:
        return UNARY_INCREMENT;
#endif

Je l'ai changé comme ça. Au fait, comment utiliser les macros dans compile.c est très intéressant, donc si vous pouvez vous le permettre, je vous l'expliquerai dans hoge. Maintenant, faisons "make".

In file included from Python/ast.c:7:0:
Python/ast.c: In function ‘ast_for_inc’:
Python/ast.c:2492:22: error: ‘UInc’ undeclared (first use in this function)
       return UnaryOp(UInc, expression, LINENO(n), n->n_col_offset, c->c_arena);
                      ^
Include/Python-ast.h:503:49: note: in definition of macro ‘UnaryOp’
 #define UnaryOp(a0, a1, a2, a3, a4) _Py_UnaryOp(a0, a1, a2, a3, a4)
                                                 ^
Python/ast.c:2492:22: note: each undeclared identifier is reported only once for each function it appears in
       return UnaryOp(UInc, expression, LINENO(n), n->n_col_offset, c->c_arena);
                      ^
Include/Python-ast.h:503:49: note: in definition of macro ‘UnaryOp’
 #define UnaryOp(a0, a1, a2, a3, a4) _Py_UnaryOp(a0, a1, a2, a3, a4)
                                                 ^
Python/compile.c: In function ‘PyCompile_OpcodeStackEffect’:
Python/compile.c:877:7: error: ‘UNARY_INCREMENT’ undeclared (first use in this function)
  case UNARY_INCREMENT:
       ^
Python/compile.c:877:7: note: each undeclared identifier is reported only once for each function it appears in
Python/compile.c: In function ‘unaryop’:
Python/compile.c:2755:7: error: ‘UInc’ undeclared (first use in this function)
  case UInc:
       ^
Python/compile.c:2756:10: error: ‘UNARY_INCREMENT’ undeclared (first use in this function)
   return UNARY_INCREMENT;
          ^
make: *** [Python/ast.o]Erreur 1
make: ***En attente d'un travail incomplet....
make: *** [Python/compile.o]Erreur 1

J'étais en colère que UInc et UNARY_INCREMENT ne soient pas définis. Je n'ai certainement pas de mémoire définie.

Définir UNARY_INCREMENT

Résolvons-le d'abord à partir de UNARY_INCREMENT. Pour savoir où définir

$ grep UNARY_INCREMENT . -r -I

Ensuite, ./Include/opcode.h:#define UNARY_POSITIVE 10 a été trouvé. Ajoutons-le à Ad Hock.

opcode.h


#define UNARY_POSITIVE           10
#define UNARY_NEGATIVE           11
#define UNARY_NOT                12
#ifdef DOSS_INCREMENT
#define UNARY_INCREMENT          13
#endif
#define UNARY_INVERT             15

Définir UInc

Ensuite, afin de trouver le fichier qui devrait définir UInc, vérifiez d'abord comment le (signé) + de l'opérateur monomorphe est appelé dans ast.c.

ast.c


case PLUS:
            return UnaryOp(UAdd, expression, LINENO(n), n->n_col_offset,
                           c->c_arena);

Ça ressemble à ça. Quand je suis passé à la source de définition de ʻUnaryOp () `avec un lien symbolique, c'était dans Python-ast.h. En gros, lorsque vous trouvez un nouveau code source, il y a une explication sur le code source au début ou à la fin, alors regardez-le sans exception.

Python-ast.h


/* File automatically generated by Parser/asdl_c.py. */

Ceci est écrit sur la première ligne. Ce fichier lui-même semble être généré automatiquement. Dans asdl_c.py, le code qui semble être généré automatiquement est écrit. Jetons un coup d'œil au site de génération automatique réel. La commande à exécuter lorsque make est terminé est dans Makefile.

$ grep asdl_c.py Makefile
ASDLGEN_FILES=    $(srcdir)/Parser/asdl.py $(srcdir)/Parser/asdl_c.py
ASDLGEN=    python3 $(srcdir)/Parser/asdl_c.py

Il est certainement utilisé. La prochaine fois,

$ grep ASDLGEN Makefile
ASDLGEN_FILES=    $(srcdir)/Parser/asdl.py $(srcdir)/Parser/asdl_c.py
ASDLGEN=    python3 $(srcdir)/Parser/asdl_c.py
$(AST_H): $(AST_ASDL) $(ASDLGEN_FILES)
    $(ASDLGEN) -h $(AST_H_DIR) $(AST_ASDL)
$(AST_C): $(AST_H) $(AST_ASDL) $(ASDLGEN_FILES)
    $(ASDLGEN) -c $(AST_C_DIR) $(AST_ASDL)

Affichez le site de production sous la forme. Il semble que ce soit AST_ASDL qui pilote réellement la génération, donc

$ grep AST_ASDL Makefile
AST_ASDL=    $(srcdir)/Parser/Python.asdl
$(AST_H): $(AST_ASDL) $(ASDLGEN_FILES)
    $(ASDLGEN) -h $(AST_H_DIR) $(AST_ASDL)
$(AST_C): $(AST_H) $(AST_ASDL) $(ASDLGEN_FILES)
    $(ASDLGEN) -c $(AST_C_DIR) $(AST_ASDL)

Ensuite, vous trouverez Python.asdl. Nous allons jeter un coup d'oeil. Si vous le regardez lentement, vous trouverez des mots comme ceux que vous avez vus dans Grammar. Si vous recherchez des mots tels que power et UAdd, vous trouverez la ligne suivante.

Python.asdl


    unaryop = Invert | Not | UAdd | USub

Il semble spécifier le type d'opérateur monomorphe. Alors ajoutons UInc à cela.

Python.asdl


    -- #ifdef DOSS_INCREMENT
    -- unaryop = Invert | Not | UAdd | USub
    unaryop = Invert | Not | UAdd | USub | UInc
    -- #endif

Le commentaire semble être .... Faisons "faire". J'ai pu le construire! Cependant, j'ai l'impression d'avoir jeté un coup d'œil sur une erreur ... Alors lancez-le avec make | grep error.

/Modules/parsermodule.c: In function ‘validate_power’:
/Modules/parsermodule.c:2501:37: error: ‘power’ undeclared (first use in this function)
     int res = (validate_ntype(tree, power) && (nch >= 1)
                                     ^
/Modules/parsermodule.c:2501:37: note: each undeclared identifier is reported only once for each function it appears in
/Modules/parsermodule.c: In function ‘validate_node’:
/Modules/parsermodule.c:3390:16: error: ‘power’ undeclared (first use in this function)
           case power:
                ^

Il semble que parsermodule.c doit également être modifié.

Modifications apportées à parsermodule.c

Il existe de nombreuses fonctions nommées validate, et il semble qu'elles vérifient si le nombre et les types d'arguments de grammaire définis dans Grammar sont corrects. J'ai fait une nouvelle inc avec Grammar et mis le courant, donc je dois faire les changements correspondants ici aussi. Il y a de nombreux changements, veuillez donc vérifier ici.

Si vous le modifiez jusqu'à présent, «make» et «make install» passeront tous les deux, alors essayez de l'exécuter pour le moment.

$ ./python3
Python 3.5.0 (default, Nov 16 2015, 17:03:57)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> i=0
>>> i++
XXX lineno: 1, opcode: 101
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
SystemError: unknown opcode
>>> +++++1
  File "<stdin>", line 1
    +++++1
     ^
SyntaxError: invalid syntax

Comment ʻi ++ est maintenant" grammaticalement "acceptable. Cependant, il était joué si un tel code d'opération n'était pas enregistré au moment de l'exécution. Certainement pas enregistré. D'un autre côté, ++++ + 1` est devenu une erreur de syntaxe. À l'origine une expression acceptable en Python, il était interdit dans Grammar de correspondre à l'implémentation incrémentielle. Puisqu'il s'agit d'une erreur de grammaire, la réécriture de la grammaire est réussie.

Mise en œuvre de "contenus"

Il ne reste plus que le "contenu" de ++. La politique consiste à découvrir où l'opcode inconnu est émis et à définir le comportement d'incrémentation dans la source de définition. Gardez une trace en utilisant gdb. À ce moment, il a été défini comme run test.py, mais le contenu de test.py est le suivant.

i=0
i++

Le message d'erreur semble être émis par pythonrun.c: 401, PyErr_Print ().

pythonrun.c


    } else {
        /* When running from stdin, leave __main__.__loader__ alone */
        if (strcmp(filename, "<stdin>") != 0 &&
            set_main_loader(d, filename, "SourceFileLoader") < 0) {
            fprintf(stderr, "python: failed to set __main__.__loader__\n");
            ret = -1;
            goto done;
        }
        v = PyRun_FileExFlags(fp, filename, Py_file_input, d, d,
                              closeit, flags);
    }
    flush_io();
    if (v == NULL) {
        PyErr_Print();
        goto done;
    }
    Py_DECREF(v);

Puisqu'il y a un message d'erreur parce que v == NULL, je vais plonger dans PyRun_FileExFlags (). La valeur de retour de cette fonction est pythonrun.c: 961, run_mod (), et elle passe par ... Ensuite, nous arrivons à ceval.c, et nous pouvons voir que le message d'erreur est défini comme PyErr_SetString (PyExc_SystemError," unknown opcode "); à la ligne 3429. L'explication de ceval.c est très simple,

ceval.c


/* Execute compiled code */

Il est devenu. Le cœur de Python Virtual Machine, l'ALU de VM. Il semble donc que vous puissiez implémenter des incréments en jouant avec. Cependant, je n'arrivais pas à avoir un indice même si je le regardais avec désinvolture.

Un petit retour en arrière

Même ainsi, j'ai défini le code d'opération dans opcode.h, mais en me demandant ce que signifie être inconnu, et ouvrez à nouveau opcode.h. Première ligne

opcode.h


/* Auto-generated by Tools/scripts/generate_opcode_h.py */

Ce qui a été écrit. Cela n'avait pas de sens de le changer directement. Recherchez Makefile.

$ grep opcode.h Makefile -3
PGENOBJS=    $(POBJS) $(PGOBJS)

##########################################################################
# opcode.h generation
OPCODE_H_DIR=     $(srcdir)/Include
OPCODE_H_SCRIPT= $(srcdir)/Tools/scripts/generate_opcode_h.py
OPCODE_H=    $(OPCODE_H_DIR)/opcode.h
OPCODE_H_GEN=    python3  $(OPCODE_H_SCRIPT) $(srcdir)/Lib/opcode.py $(OPCODE_H)
#
##########################################################################

Il semble que opcode.py soit généré. Je l'ai ajouté comme ça.

opcode.py


def_op('NOP', 9)
def_op('UNARY_POSITIVE', 10)
def_op('UNARY_NEGATIVE', 11)
def_op('UNARY_NOT', 12)
#ifdef DOSS_INCREMENT
def_op('UNARY_INCREMENT',13)
#endif
def_op('UNARY_INVERT', 15)

Fonction manquante

make a fait évoluer le message d'erreur.

In file included from Python/ceval.c:891:0:
Python/ceval.c: In function ‘PyEval_EvalFrameEx’:
Python/opcode_targets.h:15:5: error: label ‘TARGET_UNARY_INCREMENT’ used but not defined
     &&TARGET_UNARY_INCREMENT,
     ^
gcc -pthread -c -Wno-unused-result -Wsign-compare -Wunreachable-code -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -O0 -g -DDOSS_INCREMENT=1   -Werror=declaration-after-statement   -I. -IInclude -I./Include    -DPy_BUILD_CORE -o Python/future.o Python/future.c
make: *** [Python/ceval.o]Erreur 1
make: ***En attente d'un travail incomplet....

Selon l'erreur, TARGET_UNARY_INCREMENT a été automatiquement généré dans opcode_targets.h. Le problème semble être que la définition n'est pas dans ceval.c. Mais quand je recherche ceval.c avec TARGET_UNARY_POSITIVE, il n'y a pas de correspondance.

$ grep TARGET_UNARY_POSITIVE . -r -I
./Python/opcode_targets.h:    &&TARGET_UNARY_POSITIVE,

Seulement utilisé ici ...! Cela est étrange. Si vous affinez les conditions et recherchez ʻUNARY_POSITIVE` dans ceval.c

ceval.c


            TARGET(UNARY_POSITIVE) {
                PyObject *value = TOP();
                PyObject *res = PyNumber_Positive(value);
                Py_DECREF(value);
                SET_TOP(res);
                if (res == NULL)
                    goto error;
                DISPATCH();

Une telle chose était un succès. Ce n'est pas une fonction ... C'est une macro! Vous pouvez trouver la définition de la macro maléfique vers le début. Une telle formulation.

ceval.c


/* Computed GOTOs, or
       the-optimization-commonly-but-improperly-known-as-"threaded code"
   using gcc's labels-as-values extension
   (http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html).

Autant que j'ai lu l'explication, il semble qu'il soit plus rapide d'utiliser pleinement l'instruction goto en utilisant des macros que de classer le code d'opération avec l'instruction switch. Un aperçu de l'ingéniosité pour accélérer le langage de l'interprète. Bien que cela nuit considérablement à la lisibilité du code.

Créez donc TARGET (UNARY_INCREMENT).

ceval.c


    #ifdef DOSS_INCREMENT
            TARGET(UNARY_INCREMENT) {
                PyObject *left = TOP();
                PyObject *inv, *sum;
                // note that -(~x) == x+1 for all x
                inv = PyNumber_Invert(left);
                //Py_DECREF(left);
                if (inv == NULL)
                    goto error;
                sum = PyNumber_Negative(inv);
                Py_DECREF(inv);
                if (sum == NULL)
                    goto error;

                SET_TOP(sum);

                DISPATCH();
            }
    #endif

TOP et SET_TOP semblent être l'accès à la pile. De plus, il semble que la macro Py_DECREF () soit souvent appelée, mais il semble qu'elle fasse probablement un garbage collection.

Définition lâche

Vous verrez «PyNumber_Invert ()» et «PyNumber_Negative ()» ci-dessus. Certes, pour implémenter légitimement des incréments, il peut être préférable de créer une fonction appelée PyNumber_Increment (). Ou peut-être aurais-je dû créer un objet qui représente 1 et le réaliser avec une instruction d'ajout. Cependant, je suis allé chercher la source de définition de la fonction et j'ai essayé diverses choses, mais c'était compliqué et peu clair, et il y avait une contrainte de temps, alors j'avoue que j'ai décidé de faire bon usage de la fonction existante.

-(~x) == x+1 for all x

En utilisant ce fait, nous avons réussi à générer x + 1 à partir de x en utilisant uniquement les fonctions existantes. Si vous faites et déplacez ceci,

$ ./python3
Python 3.5.0 (default, Nov 16 2015, 18:50:21)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> i=0
>>> print(i++)
1
>>> i
0

Je l'ai reconnu et j'ai commencé à bouger. La valeur est maintenant renvoyée correctement pour ʻi ++ `. Cependant, la valeur de «i» n'a pas été réécrite. Jusqu'à présent, nous avons défini des incréments "naturellement", mais il semble que la réécriture des variables ait été quelque part en dehors de ce naturel.

évaluation et exécution

J'ai trouvé le commentaire significatif suivant dans parsermodule.c.

parsermodule.c


/*  There are two types of intermediate objects we're interested in:
 *  'eval' and 'exec' types.  These constants can be used in the st_type
 *  field of the object type to identify which any given object represents.
 *  These should probably go in an external header to allow other extensions
 *  to use them, but then, we really should be using C++ too.  ;-)
 */

Autrement dit, il semble qu'il existe deux types, eval et exec. Ceci est représenté par «i + 1» et «i + = 1». Le problème maintenant est que «++ i» possède ces deux propriétés.

La réécriture des variables se fait avec ʻi + = 1`. Si vous regardez comment cela est défini dans Grammar, il s'agit d'une attribution d'août, qui se trouve à un endroit complètement différent du + et du-que vous avez vu jusqu'à présent. Dès le stade de la grammaire, il existait dans un endroit complètement différent de la partie réécriture variable. En regardant en arrière avec le débogueur, l'instruction STORE a été exécutée dans ceval.c dans AugAssign. En raison de contraintes de temps, je voudrais éviter de revoir à partir de la grammaire, et surtout, si je me concentre sur + =, les variables peuvent être réécrites mais aucune valeur ne peut être retournée.

aperçu encore

Voici le flux de Python.

  1. Divisez la chaîne d'entrée en jetons avec le tokenizer
  2. Créez un arbre de syntaxe concret selon la grammaire
  3. Créez un arbre de syntaxe abstrait avec ast.c
  4. Convertissez l'arbre de syntaxe abstraite en bytecode avec compile.c
  5. Exécutez le bytecode sur la VM avec ceval.c

Dernière griffe

Étant donné que eval et exec implémentent des incréments contrairement au concept de conception Python, ils ne peuvent pas être mis en œuvre proprement. Cependant, si vous choisissez de changer l'une des options ci-dessus, la chose la plus orthodoxe semble être de réécrire compile.c. Jusqu'à présent, il ne produisait qu'un code d'opération qui renvoie explicitement une valeur +1 pour l'arborescence de syntaxe pour les incréments. Par contre, on considère que l'opération souhaitée peut être obtenue en ajoutant l'instruction STORE.

A partir de la notation «i ++», l'opérande de ++ devrait être i. J'ai pu l'exécuter en copiant l'instruction STORE à l'emplacement correspondant à AugAssign, en effectuant l'opération + 1, puis en l'ajoutant.

Recommended Posts

J'ai essayé d'ajouter un post-incrément à l'implémentation CPython
J'ai essayé d'ajouter un post-incrément à l'édition CPython Extra
J'ai essayé d'ajouter un post-incrément à CPython. Présentation et résumé
J'ai essayé d'ajouter un post-incrément à CPython. Liste de toutes les modifications
J'ai essayé de créer un linebot (implémentation)
J'ai essayé d'ajouter VPS à la connexion ConoHa ~ SSH
J'ai essayé d'apprendre PredNet
J'ai essayé d'organiser SVM.
J'ai essayé d'implémenter PCANet
J'ai essayé de réintroduire Linux
J'ai essayé de présenter Pylint
J'ai essayé de résumer SparseMatrix
jupyter je l'ai touché
J'ai essayé d'implémenter StarGAN (1)
J'ai essayé d'implémenter Deep VQE
J'ai essayé de créer l'API Quip
J'ai essayé de mettre en place une validation contradictoire
J'ai essayé d'expliquer l'ensemble de données de Pytorch
J'ai essayé l'authentification vocale Watson (Speech to Text)
J'ai touché l'API de Tesla
J'ai essayé de m'organiser à propos de MCMC.
J'ai essayé d'implémenter Realness GAN
J'ai essayé de déplacer le ballon
J'ai essayé d'estimer la section.
J'ai essayé de résumer la méthode de mise en œuvre fréquemment utilisée de pytest-mock
J'ai essayé de résumer la gestion des exceptions Python
J'ai essayé d'implémenter PLSA en Python
J'ai essayé d'utiliser Azure Speech to Text.
J'ai essayé d'implémenter Autoencoder avec TensorFlow
J'ai essayé de résumer la commande umask
J'ai essayé d'implémenter la permutation en Python
J'ai essayé de créer un linebot (préparation)
J'ai essayé de visualiser AutoEncoder avec TensorFlow
J'ai essayé de reconnaître le mot de réveil
J'ai essayé de commencer avec Hy
J'ai essayé d'implémenter PLSA dans Python 2
Entrée standard Python3 que j'ai essayé de résumer
J'ai essayé de classer le texte en utilisant TensorFlow
J'ai essayé de résumer la modélisation graphique.
J'ai essayé d'implémenter ADALINE en Python
J'ai essayé de laisser optuna résoudre le nombre
J'ai essayé d'estimer le rapport de circonférence π de manière probabiliste
J'ai essayé de toucher l'API COTOHA
J'ai essayé d'implémenter PPO en Python
J'ai essayé d'implémenter CVAE avec PyTorch
J'ai créé une API Web
J'ai essayé de résoudre TSP avec QAOA
[Python] J'ai essayé de calculer TF-IDF régulièrement
J'ai essayé de toucher Python (syntaxe de base)
J'ai fait de mon mieux pour retourner au Lasso
J'ai essayé de résumer les modules d'Ansible - l'édition Linux
(Apprentissage automatique) J'ai essayé de comprendre attentivement la régression linéaire bayésienne avec l'implémentation
J'ai essayé de créer une méthode de super résolution / ESPCN
J'ai essayé de programmer la bulle de tri par langue
J'ai essayé Web Scraping pour analyser les paroles.
J'ai essayé d'implémenter la lecture de Dataset avec PyTorch
J'ai essayé d'utiliser lightGBM, xg boost avec Boruta