Ich habe versucht, der CPython-Implementierung ein Post-Inkrement hinzuzufügen

Linkliste

Alle drei Male plus zusätzliche Ausgabe. Übersicht und Zusammenfassung des Hinzufügens von Post-Inkrement zu CPython [Ich habe versucht, CPython [Implementierung] ein Post-Inkrement hinzuzufügen (http://qiita.com/DOSS_INCREMENT/items/8b050847145f635211e2) Liste aller Änderungen, die durch Hinzufügen eines Post-Inkrements zu CPython vorgenommen wurden Zusätzliche Ausgabe zum Hinzufügen von Post-Inkrement zu CPython

Implementierung

Es ist endlich die Bühne, um es tatsächlich umzusetzen.

Bauen

Umgebung

Laden Sie Python 3.5.0 herunter

Laden Sie Python 3.5.0 (neueste Version vom 22. Oktober 2015 zu Beginn dieses Experiments) von der offiziellen Website herunter und legen Sie es in einem geeigneten Verzeichnis ab.

Bauen

$ 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.
>>>

...

Es gab keinen besonderen Fehler und es hörte nicht auf. In Erstellen und Verwenden einer Debug-Version der Dokumentation zu Python-Python-Erweiterungsmustern 0.1.0,

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

Es scheint, dass verschiedene Informationen ausgegeben werden, so dass das Debuggen einfacher ist, wenn Sie Optionen wie hinzufügen, und ich habe festgestellt, dass solche Makros im Quellcode definiert sind, aber am Ende habe ich nicht verstanden, wie man sie verwendet.

Werkzeuge, mit denen man sich anlegen kann

Ich habe beim Schreiben eine Seite namens Techniken zum Lesen des Quellcodes gefunden.

Fummelei!

In Token schneiden

Folgen Sie vorerst der Bewegung mit gdb, um zu sehen, wie Python funktioniert. Starten Sie gdb als M-x gud-gdb in Emacs und lesen Sie das folgende Python-Skript, um das Verhalten zu überprüfen.

test.py


a = 0
a += 1
a++

Wenn Sie dies gehorsam versuchen

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

Wird angezeigt.

Setzen Sie daher in der Hauptfunktion einen Haltepunkt und folgen Sie der Operation.

Die Hauptfunktion befindet sich in Programs / python.c. Am Anfang ist die Variable "argv_copy" weiterhin so, aber da sie das Argument nur aus der 69. Zeile verarbeitet, wenn Sie es überspringen

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;

Kann gefunden werden. Offensichtlich verdächtig, schauen wir uns also die Funktion "Py_Main" an. Es ist in Modules / main.c.

Wenn Sie ihm folgen, finden Sie in Zeile 768 "sts = run_file (fp, filename, & cf)". Wenn Sie also diesmal in Zeile 318 eintreten, wird "run = PyRun_AnyFileExFlags" (fp, filename_str, filename! = NULL, p_cf) angezeigt ); `Wird gefunden.

Wenn Sie der Bewegung folgen und den Namen auf diese Weise als Hinweis verwenden,

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);

Wenn Sie PyRun_FileExFlags folgen, finden Sie darin mod = PyParser_ASTFromStringObject (str, Dateiname, Start, Flags, Arena); (Zeile 916 von Python / pythonrun.c).

Zusätzlich zu "Parser" im Funktionsnamen wird "NULL" hier "mod" zugewiesen, und "go to exit" wird in der if-Anweisung unmittelbar danach ausgeführt. Es ist hässlich. Einblick.

Wenn Sie Python / pythonrun.c: PyParser_ASTFromStringObject folgen → Parser / parsetok.c: PyParser_ParseFileObject → Parser / parsetok.c: parsetok, finden Sie den folgenden Teil aus der 201. Zeile.

    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) {
         
...

Da es sich um eine Endlosschleife handelt, wird die Eingabe vermutlich von einem Ende aus verarbeitet. Wenn ich die Schleife drehe, während der Wert von str als Versuch angezeigt wird, ist str der Reihe nach "a", "=", "0". , "", "A", "+ =", "1", "", "a", "+", "+", "" und der Rückgabewert, der einen Fehler in Zeile 263 "PyParser_AddToken" anzeigt Sie können sehen, dass es zurückkehrt.

Sobald Sie PyParser_AddToken eingeben, finden Sie die Zeile ilabel = classify (ps, type, str);, und danach scheinen Sie an die Stelle zu springen, um Fehler gemäß dem Wert von ilabel zu behandeln.

Innerhalb von "klassifizieren"


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;
}

Es ist genauso wie das.

Es sieht aus wie ein kurzer Blick auf die Liste der Token, von denen Sie nach einem passenden Token suchen.

Verbessern Sie also zunächst das Inkrement "++" als ein Token. Wenn ich dieser Liste irgendwie "++" hinzufügen kann, denke ich, dass ich es löschen kann.

Zurück von type = PyTokenizer_Get (tok, & a, & b); in der Parsetok-Funktion Parser / tokenizer.c: int PyTokenizer_Get (struct tok_state * tok, char ** p_start, char ** p_end) → Es kommt mit Parser / tokenizer.c: static int tok_get (Struktur tok_state * tok, char ** p_start, char ** p_end).

Wenn Sie den Teil überspringen, der die Zeilenumbrüche und Zahlen in Bezug auf den Kommentar zu trennen scheint, werden Funktionen wie "PyToken_TwoChars" und "PyToken_ThreeChars" im Block aufgerufen, beginnend mit dem Kommentar "/ * Auf zweistelliges Token prüfen * /" Sie können sehen, dass es veröffentlicht wurde. Ich schaue in diese Typen hinein

Parser/tokenizer.c


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

Die switch-Anweisung wird so fortgesetzt, und wenn Sie sie hier hinzufügen, erkennt sie "++" als Token.

Fügen Sie zuerst "INCREMENT" am Ende von "Include / token.h" hinzu, wo dieses zurückgebende "EQEQUAL" wie folgt definiert ist.

Include/token.h


#define ERRORTOKEN    56
#define N_TOKENS    57

#ifdef DOSS_INCREMENT
#define INCREMENT 58
#endif

Im Folgenden werden die Änderungen angezeigt, indem sie in "#ifdef DOSS_INCREMENT" und "# endif" eingeschlossen werden.

Wenn Sie "grep" EQEQUAL "-r -I" versuchen, gibt es in "Parser / tokenizer.c" ein Array von Token-Namen. Ändern Sie es daher wie folgt.

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;
    
(Weggelassen)

Fügen Sie das definierte "INCREMENT" an der Stelle der switch-Anweisung hinzu.

Wenn Sie es bisher neu erstellen und mit gdb verfolgen, können Sie sehen, dass ++ sicherlich als ein Token gelesen wird. Um "#ifdef DOSS_INCREMNET" ~ "# endif" zu aktivieren, wurde "-DDOSS_INCREMENT = 1" zu CFLAGS hinzugefügt und make wurde ausgeführt.

Natürlich führt diese Änderung allein immer noch dazu, dass die Funktion "PyParser_AddToken" einen Fehlerrückgabewert zurückgibt.

Große Lieblingsgrammatik

Übrigens, als ich mit Python-Grammatik gegoogelt habe, stellte sich heraus, dass eine Datei namens Grammatik existiert. Die Site ist hier. Es heißt "die vollständige Python-Grammatik, wie sie vom Parser-Generator gelesen und zum Parsen von Python-Quelldateien verwendet wird". Ich muss es lesen

Wenn Sie es öffnen, finden Sie eine grammatikalische Definition, die einem regulären Ausdruck sehr ähnlich ist. Es scheint eine erweiterte BNF-Notation zu sein. Ich konnte nicht glauben, dass die Grammatik in einer solchen Nicht-C-Sprachdatei geschrieben war, also habe ich diese Datei gelöscht und erstellt. Dann konnte ich nicht bauen. Anscheinend wird es wirklich benutzt. Als ich in die Nähe der Kraft ** schaute, die dem Inkrement ähnelt, gab es etwas Nahes, also beschloss ich, es nachzuahmen und zu ändern.

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 ['++']
#######################################

Die ersten beiden Zeilen,

Grammar


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

Ursprünglich waren die beiden Zeilen unten,

Grammar


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

Ist nach der Änderung. Bitte beachten Sie, dass das Kommentarsymbol # ist. Solange Inkremente implementiert sind, dachte ich, dass es schlecht wäre, wenn mehrere Codes hinzugefügt würden, also habe ich es möglich gemacht, höchstens einen Code hinzuzufügen. Außerdem ist die Priorität des ++ - Operators höher als **.

Diese Grammatikdatei wird Ihnen später viel helfen. Im auskommentierten Teil wurde ein grober Weg für die Zukunft geschrieben. Der erste ist der folgende Kommentar am Ende des Satzes.

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.

Es ist lang, also scheint es, dass Sie auch mit ast.c. spielen sollten. Auch zu Beginn der Grammatik gibt es so etwas.

Grammar


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

Außerdem wird eine [nützliche Website] eingeführt (https://docs.python.org/devguide/grammar.html). Diese Seite, die offizielle Seite von Python, enthält Anweisungen zum Hinzufügen neuer Grammatik. Es ist jedoch sehr einfach.

Einführung in den abstrakten Syntaxbaum

Ich beschloss, ast.c neu zu schreiben, wie es die Grammatik sagt. Es scheint, dass es eine Baumstruktur erstellt, aber wenn Sie sehen, dass der Rückgabewert vom Typ int ist, suchen Sie anscheinend nur nach Grammatikfehlern. Die Holzkonstruktion wurde möglicherweise bereits hergestellt. Ich bin mir nicht sicher, also werde ich anderen Code imitieren und etwas zum Inkrementieren in einer Atmosphäre implementieren. Hier gibt es ein Händchen für einfaches Nachahmen. Da es Faktor und Leistung waren, die in der Grammatik geändert wurden, scheint es besser zu sein, diese beiden zu ändern oder zu löschen und inc einzugeben. Danach werden wir untersuchen, um welche Art von Verhalten es sich bei +, -, ~ desselben mononomischen Operators wie ++ handelt. Siehe hier für spezifische Änderungen.

Lassen Sie uns sofort "machen".

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]Fehler 1

Ich erhalte einen Fehler in ast.c. Natürlich erinnere ich mich nicht daran, UInc definiert zu haben. Danach werde ich mir die nützliche Seite ansehen, die ich zuvor in Tekito gesehen habe. Auszug aus der Checkliste

  • 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.

Ich habe es oben gemacht. Der zweite macht nicht viel Sinn. Brauchst du es? ast.c wurde umgeschrieben. Ich schaue auf pgen und symbol, aber ich kann nichts finden, was ich ändern könnte. Ich werde es vorerst überspringen. Es scheint so etwas wie compile.c zu geben. Kompilieren Sie mit Python, welches eine Interpretersprache sein sollte ...? Ich habe eine nützliche Site gefunden, als ich sie gegoogelt habe. Es scheint zu sein, wie man einen Compiler macht. Es sieht so aus, als würde es wirklich kompilieren. Sie können auch Bytecode sehen. Aufgrund verschiedener Untersuchungen scheint es eine virtuelle Python-Maschine zu geben, und es scheint sich um einen Bytecode dafür zu handeln. Es wird eine niedrigere Schicht.

Compile.c umschreiben

Sicherlich hat compile.c

compile.c


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

Was geschrieben steht. Suchen Sie nach Wörtern wie Unary, UAdd und power und fügen Sie natürlich Inkremente hinzu, um sie anzupassen.

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

Ich habe es so geändert. Übrigens ist die Verwendung von Makros in compile.c sehr interessant. Wenn Sie es sich leisten können, werde ich es in hoge erklären. Jetzt lass uns machen.

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]Fehler 1
make: ***Warten auf einen unvollständigen Job....
make: *** [Python/compile.o]Fehler 1

Ich war wütend, dass UInc und UNARY_INCREMENT nicht definiert wurden. Natürlich habe ich kein definiertes Gedächtnis.

Definieren Sie UNARY_INCREMENT

Lösen wir es zuerst aus UNARY_INCREMENT. Um herauszufinden, wo definiert werden muss

$ grep UNARY_INCREMENT . -r -I

Dann wurde "./Include/opcode.h:#define UNARY_POSITIVE 10" gefunden. Fügen wir es Ad Hock hinzu.

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

Definieren Sie UInc

Um die Datei zu finden, die UInc definieren soll, überprüfen Sie zunächst, wie das (vorzeichenbehaftete) + des monomorphen Operators in ast.c. aufgerufen wird.

ast.c


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

Es sieht aus wie das. Als ich mit einem symbolischen Link zur Definitionsquelle von "UnaryOp ()" sprang, war es in Python-ast.h. Wenn Sie einen neuen Quellcode finden, gibt es am Anfang oder am Ende eine Erklärung zum Quellcode. Schauen Sie sich diesen also ausnahmslos an.

Python-ast.h


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

Dies steht in der ersten Zeile. Diese Datei selbst scheint automatisch generiert zu werden. In asdl_c.py wird der Code geschrieben, der scheinbar automatisch generiert wird. Werfen wir einen Blick auf die eigentliche Website zur automatischen Generierung. Der Befehl, der ausgeführt werden soll, wenn make fertig ist, befindet sich in Makefile.

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

Es wird sicherlich verwendet. Nächstes Mal,

$ 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)

Zeigen Sie die Produktionsstätte als an. Es scheint, dass es AST_ASDL ist, das die Generation tatsächlich antreibt

$ 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)

Dann finden Sie Python.asdl. Lass uns einen Blick darauf werfen. Wenn Sie es langsam betrachten, werden Sie Wörter finden, wie Sie sie in der Grammatik gesehen haben. Wenn Sie nach Wörtern wie power und UAdd suchen, finden Sie die nächste Zeile.

Python.asdl


    unaryop = Invert | Not | UAdd | USub

Es scheint den Typ des monomorphen Operators anzugeben. Fügen wir also UInc hinzu.

Python.asdl


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

Der Kommentar scheint zu sein--. Lass uns machen. Ich konnte es bauen! Ich habe jedoch das Gefühl, einen Blick auf einen Fehler zu werfen ... Führen Sie es also mit "make | grep error" aus.

/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:
                ^

Es sieht so aus, als müsste parsermodule.c ebenfalls geändert werden.

Änderungen an parsermodule.c

Es gibt viele Funktionen mit dem Namen validate, und es scheint, dass sie prüfen, ob die Anzahl und Art der in Grammatik definierten Grammatikargumente korrekt sind. Ich habe mit Grammatik eine neue Inc gemacht und den Strom abgeschaltet, daher muss ich auch hier die entsprechenden Änderungen vornehmen. Es gibt viele Änderungen, bitte überprüfen Sie hier.

Wenn Sie es bisher ändern, werden sowohl "make" als auch "make install" bestanden. Versuchen Sie daher, es vorerst auszuführen.

$ ./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

Wie "i ++" ist jetzt "grammatikalisch" akzeptabel. Es wurde jedoch abgespielt, wenn ein solcher Operationscode zum Zeitpunkt der Ausführung nicht registriert war. Sicher nicht registriert. Andererseits wurde "++++ + 1" zu einem Syntaxfehler. Ursprünglich ein akzeptabler Ausdruck in Python, war es in der Grammatik verboten, mit der inkrementellen Implementierung übereinzustimmen. Da es sich um einen Grammatikfehler handelt, ist das Umschreiben der Grammatik erfolgreich.

Implementierung von "Inhalten"

Alles was bleibt ist der "Inhalt" von ++. Die Richtlinie besteht darin, herauszufinden, wo der unbekannte Opcode ausgegeben wird, und das Inkrementverhalten in der Definitionsquelle zu definieren. Behalten Sie den Überblick mit gdb. Zu diesem Zeitpunkt wurde es als "run test.py" festgelegt, aber der Inhalt von test.py ist wie folgt.

i=0
i++

Die Fehlermeldung scheint von pythonrun.c: 401, PyErr_Print () ausgegeben zu werden.

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);

Da es eine Fehlermeldung gibt, weil v == NULL, werde ich in PyRun_FileExFlags () eintauchen. Der Rückgabewert dieser Funktion ist pythonrun.c: 961, run_mod (), und es geht durch ... Dann kommen wir zu ceval.c und können sehen, dass die Fehlermeldung in Zeile 3429 als "PyErr_SetString (PyExc_SystemError," unbekannter Opcode ");" gesetzt ist. Die Erklärung von ceval.c ist sehr einfach,

ceval.c


/* Execute compiled code */

Es ist geworden. Das Herzstück von Python Virtual Machine, der ALU von VM. Es scheint also, dass Sie Inkremente implementieren können, indem Sie damit herumspielen. Ich konnte jedoch keine Ahnung bekommen, selbst wenn ich es beiläufig betrachtete.

Ein kleiner Rückschritt

Trotzdem habe ich den Operationscode in opcode.h definiert, mich aber gefragt, was es bedeutet, unbekannt zu sein, und opcode.h erneut geöffnet. Erste Linie

opcode.h


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

Was wurde geschrieben. Es machte keinen Sinn, es direkt zu ändern. Suche nach 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)
#
##########################################################################

Es scheint, dass opcode.py generiert wird. Ich habe es so hinzugefügt.

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)

Fehlende Funktion

make hat die Fehlermeldung weiterentwickelt.

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]Fehler 1
make: ***Warten auf einen unvollständigen Job....

Gemäß dem Fehler wurde TARGET_UNARY_INCREMENT automatisch in opcode_targets.h generiert. Das Problem scheint zu sein, dass die Definition nicht in ceval.c ist. Aber wenn ich mit TARGET_UNARY_POSITIVE nach ceval.c suche, gibt es keine Übereinstimmung.

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

Nur hier verwendet ...! Das ist merkwürdig. Wenn Sie die Bedingungen eingrenzen und in ceval.c nach "UNARY_POSITIVE" suchen

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();

So etwas war ein Hit. Dies ist keine Funktion ... Es ist ein Makro! Sie finden die Definition des bösen Makros am Anfang. Eine solche Formulierung.

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).

Soweit ich die Erklärung gelesen habe, scheint es schneller zu sein, die goto-Anweisung mithilfe von Makros vollständig zu nutzen, als den Operationscode mit der switch-Anweisung zu klassifizieren. Ein Blick auf den Einfallsreichtum zur Beschleunigung der Dolmetschersprache. Dies beeinträchtigt zwar die Lesbarkeit des Codes erheblich.

Erstellen Sie also "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 und SET_TOP scheinen Zugriff auf den Stapel zu sein. Es scheint auch, dass das Makro Py_DECREF () häufig aufgerufen wird, aber es scheint, dass es wahrscheinlich eine Speicherbereinigung durchführt.

Feige Definition

Sie sehen oben "PyNumber_Invert ()" und "PyNumber_Negative ()". Natürlich kann es besser sein, eine Funktion namens "PyNumber_Increment ()" zu erstellen, um das Inkrement legitim zu implementieren. Oder vielleicht hätte ich ein Objekt erstellen sollen, das 1 darstellt, und es mit einer Add-Anweisung realisiert. Ich habe jedoch die Definitionsquelle der Funktion herausgefunden und verschiedene Dinge ausprobiert, aber es war kompliziert und unklar, und es gab eine zeitliche Beschränkung. Daher gestehe ich, dass ich mich entschlossen habe, die vorhandene Funktion gut zu nutzen.

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

Mit dieser Tatsache ist es uns gelungen, x + 1 aus x nur mit vorhandenen Funktionen zu generieren. Wenn Sie dies "machen" und verschieben,

$ ./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

Ich erkannte es und begann mich zu bewegen. Der Wert wird jetzt ordnungsgemäß für "i ++" zurückgegeben. Der Wert von "i" wurde jedoch nicht umgeschrieben. Bis jetzt haben wir Inkremente "natürlich" definiert, aber es scheint, dass das Umschreiben von Variablen irgendwo aus dieser Natürlichkeit herausgelaufen ist.

eval und exec

Ich fand den folgenden aussagekräftigen Kommentar in 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.  ;-)
 */

Das heißt, es scheint zwei Arten zu geben, eval und exec. Dies wird durch "i + 1" und "i + = 1" dargestellt. Das Problem ist jetzt, dass ++ i beide Eigenschaften hat.

Das Umschreiben von Variablen erfolgt mit "i + = 1". Wenn Sie sich ansehen, wie dies in der Grammatik definiert ist, handelt es sich um eine Aug-Zuweisung, die sich an einer völlig anderen Stelle befindet als das + und-, das Sie bisher gesehen haben. Von der Grammatikstufe an existierte es an einem Ort, der sich völlig vom variablen Umschreibungsteil unterschied. Rückblickend mit dem Debugger wurde die STORE-Anweisung in ceval.c in AugAssign ausgeführt. Aus zeitlichen Gründen möchte ich eine Überprüfung aus der Grammatikphase vermeiden. Wenn ich mich auf + = konzentriere, werden die Variablen möglicherweise neu geschrieben, aber es kann kein Wert zurückgegeben werden.

Übersicht wieder

Hier ist der Fluss von Python.

  1. Teilen Sie die Eingabezeichenfolge mit dem Tokenizer in Token
  2. Erstellen Sie einen konkreten Syntaxbaum gemäß Grammatik
  3. Erstellen Sie mit ast.c einen abstrakten Syntaxbaum
  4. Konvertieren Sie den abstrakten Syntaxbaum mit compile.c in Bytecode
  5. Führen Sie den Bytecode auf der VM mit ceval.c aus

Letzte Klaue

Da eval und exec Inkremente implementieren, die dem Python-Designkonzept widersprechen, können sie nicht sauber implementiert werden. Wenn Sie jedoch eine der oben genannten Optionen zum Ändern auswählen, scheint es am orthodoxesten zu sein, compile.c neu zu schreiben. Bisher wird nur ein Operationscode ausgegeben, der explizit einen Wert von +1 für den Syntaxbaum für Inkremente zurückgibt. Andererseits wird angenommen, dass die gewünschte Operation durch Hinzufügen des STORE-Befehls erhalten werden kann.

Aus der Notation "i ++" sollte der Operand von ++ i sein. Ich konnte dies ausführen, indem ich den STORE-Befehl an die Position kopierte, die AugAssign entsprach, die Operation "+ 1" ausführte und ihn dann hinzufügte.

Recommended Posts

Ich habe versucht, der CPython-Implementierung ein Post-Inkrement hinzuzufügen
Ich habe versucht, der CPython Extra Edition ein Post-Inkrement hinzuzufügen
Ich habe versucht, CPython ein Post-Inkrement hinzuzufügen. Übersicht und Zusammenfassung
Ich habe versucht, CPython ein Post-Inkrement hinzuzufügen. Liste aller Änderungen
Ich habe versucht, einen Linebot zu erstellen (Implementierung)
Ich habe versucht, der ConoHa ~ SSH-Verbindung VPS hinzuzufügen
Ich habe versucht, PredNet zu lernen
Ich habe versucht, SVM zu organisieren.
Ich habe versucht, PCANet zu implementieren
Ich habe versucht, Linux wieder einzuführen
Ich habe versucht, Pylint vorzustellen
Ich habe versucht, SparseMatrix zusammenzufassen
jupyter ich habe es berührt
Ich habe versucht, StarGAN (1) zu implementieren.
Ich habe versucht, Deep VQE zu implementieren
Ich habe versucht, eine Quip-API zu erstellen
Ich habe versucht, eine kontroverse Validierung zu implementieren
Ich habe versucht, Pytorchs Datensatz zu erklären
Ich habe Watson Voice Authentication (Speech to Text) ausprobiert.
Ich habe Teslas API berührt
Ich habe versucht, mich über MCMC zu organisieren.
Ich habe versucht, Realness GAN zu implementieren
Ich habe versucht, den Ball zu bewegen
Ich habe versucht, den Abschnitt zu schätzen.
Ich habe versucht, die häufig verwendete Implementierungsmethode von pytest-mock zusammenzufassen
Ich habe versucht, die Behandlung von Python-Ausnahmen zusammenzufassen
Ich habe versucht, PLSA in Python zu implementieren
Ich habe versucht, Azure Speech to Text zu verwenden.
Ich habe versucht, Autoencoder mit TensorFlow zu implementieren
Ich habe versucht, den Befehl umask zusammenzufassen
Ich habe versucht, Permutation in Python zu implementieren
Ich habe versucht, einen Linebot zu erstellen (Vorbereitung)
Ich habe versucht, AutoEncoder mit TensorFlow zu visualisieren
Ich versuchte das Weckwort zu erkennen
Ich habe versucht, mit Hy anzufangen
Ich habe versucht, PLSA in Python 2 zu implementieren
Python3-Standardeingabe habe ich versucht zusammenzufassen
Ich habe versucht, Text mit TensorFlow zu klassifizieren
Ich habe versucht, die grafische Modellierung zusammenzufassen.
Ich habe versucht, ADALINE in Python zu implementieren
Ich habe versucht, Optuna die Nummer lösen zu lassen
Ich habe versucht, das Umfangsverhältnis π probabilistisch abzuschätzen
Ich habe versucht, die COTOHA-API zu berühren
Ich habe versucht, PPO in Python zu implementieren
Ich habe versucht, CVAE mit PyTorch zu implementieren
Ich habe eine Web-API erstellt
Ich habe versucht, TSP mit QAOA zu lösen
[Python] Ich habe versucht, TF-IDF stetig zu berechnen
Ich habe versucht, Python zu berühren (grundlegende Syntax)
Ich versuchte mein Bestes, um zu Lasso zurückzukehren
Ich habe versucht, Ansibles Module-Linux-Edition zusammenzufassen
(Maschinelles Lernen) Ich habe versucht, die Bayes'sche lineare Regression bei der Implementierung sorgfältig zu verstehen
Ich habe versucht, eine Super-Resolution-Methode / ESPCN zu erstellen
Ich habe versucht, die Blasensortierung nach Sprache zu programmieren
Ich habe Web Scraping versucht, um die Texte zu analysieren.
Ich habe versucht, das Lesen von Dataset mit PyTorch zu implementieren
Ich habe versucht, lightGBM, xg Boost mit Boruta zu verwenden