0
0

More than 1 year has passed since last update.

CPython - インクリメントの追加 全変更点まとめ

Posted at

変更手順まとめ

本記事はPythonに前置・後置インクリメントを追加してみたの参考ページです。

基本的に以下の順番で変更しました。
Parser/tokenizer.c, Python/ast.cなど、公式サイトでは変更が必要とされていたものでも変更していないものもあります。

Grammar/Tokens

++というものが存在することを明記

INCREMENT               '++'

$ make regen-tokenを実行する必要がある

Grammar/python.gram

実際に++というトークンはどのような使用法(文法)で用いられるのか,そしてどのような解釈をするのか
つまりa = primary(変数,配列,構造体)に対して++a, a++という文法を許容・追加し,それぞれ前置・後置インクリメントとして解釈すると記述した

factor[expr_ty] (memo):
    | '+' a=factor { _PyAST_UnaryOp(UAdd, a, EXTRA) }
    | '-' a=factor { _PyAST_UnaryOp(USub, a, EXTRA) }
    | '~' a=factor { _PyAST_UnaryOp(Invert, a, EXTRA) }
    | '++' a=primary { _PyAST_UnaryOp(PreIncr, a, EXTRA) }
    | a=primary '++' { _PyAST_UnaryOp(PostIncr, a, EXTRA) }
    | power

$ make regen-pegenを実行する必要がある

Parser/Python.asdl

unary_opPre/PostIncrを追加

unaryop = Invert | Not | UAdd | USub | PreIncr | PostIncr

$ make regen-astを実行する必要がある

Python/ast_unparse.c

static int
append_ast_unaryop(_PyUnicodeWriter *writer, expr_ty e, int level)
{
    const char *op;
    int pr;

    switch (e->v.UnaryOp.op) {
    case Invert: op = "~"; pr = PR_FACTOR; break;
    case Not: op = "not "; pr = PR_NOT; break;
    case UAdd: op = "+"; pr = PR_FACTOR; break;
    case USub: op = "-"; pr = PR_FACTOR; break;
    case PreIncr: op = "++"; pr = PR_FACTOR; break;
    case PostIncr: op = "++"; pr = PR_FACTOR; break;
    default:
        PyErr_SetString(PyExc_SystemError,
                        "unknown unary operator");
        return -1;
    }

    APPEND_STR_IF(level > pr, "(");
    APPEND_STR(op);
    APPEND_EXPR(e->v.UnaryOp.operand, pr);
    APPEND_STR_IF(level > pr, ")");
    return 0;
}

Lib/opcode.py

オペコードの定義

def_op('UNARY_PREINCREMENT', 13)
def_op('UNARY_POSTINCREMENT', 14)

Doc/Library/dis.rst

**Unary operations**
.. opcode:: UNARY_PREINCREMENT

   Implements ``TOS = TOS + 1``.

.. opcode:: UNARY_POSTINCREMENT

   Implements ``TOS = TOS + 1``.

$make regen-opcode regen-opcode-targetsを実行する必要がある

Python/ceval.c

オペコードにしたがって実行されるTARGETを記述
ここでは+1をしていて,STORERETURN_VALUEのためにスタックをセットしている.

TARGET(UNARY_PREINCREMENT) {
    PyObject *value = TOP();
    PyObject *res = PyNumber_Add(value, _PyLong_GetOne());
    Py_DECREF(value);
    SET_TOP(res);
    PUSH(res);
    if (res == NULL)
        goto error;
    DISPATCH();
}

TARGET(UNARY_POSTINCREMENT) {
    PyObject *value = TOP();
    PyObject *res = PyNumber_Add(value, _PyLong_GetOne());
    PUSH(res);
    if (res == NULL)
        goto error;
    DISPATCH();
}

$ make regen-importlibを実行する必要がある

Python/compile.c

コンパイラの動作の記述
Pre/PostIncrを受け取ると,UNARY_PRE/POSTINCREMENTと型にあった適切なSTOREを実行する

static int
compiler_visit_expr1(struct compiler *c, expr_ty e)
{
    switch (e->kind) {
    // 省略
    case UnaryOp_kind:
        VISIT(c, expr, e->v.UnaryOp.operand);
        ADDOP(c, unaryop(e->v.UnaryOp.op));
        if (e->v.UnaryOp.op == PreIncr || e->v.UnaryOp.op == PostIncr) {
            // increment op
            expr_ty operand = e->v.UnaryOp.operand;
            if (e->v.UnaryOp.operand->kind == Name_kind) {
                // store name
                compiler_nameop(c, operand->v.Name.id, Store);
            }
            else if (e->v.UnaryOp.operand->kind == Subscript_kind) {
                // store subscript
                operand->v.Attribute.ctx = Store;
                VISIT(c, expr, operand);
            }
            else if (e->v.UnaryOp.operand->kind == Attribute_kind) {
                // store attribute
                operand->v.Attribute.ctx = Store;
                VISIT(c, expr, operand);
            }
            else {
                // type error
                PyErr_Format(PyExc_SystemError,
                "invalid node type (%d) for increment",
                e->kind);
                return 0;
            }
        }
        break;
    // 省略
    return 1;
}

Lib/ast.py

unop, unop_precedenceに++を追加

unop = {"Invert": "~", "Not": "not", "UAdd": "+", "USub": "-", "PreIncr": "++", "PostIncr": "++"}
unop_precedence = {
    "not": _Precedence.NOT,
    "~": _Precedence.FACTOR,
    "+": _Precedence.FACTOR,
    "-": _Precedence.FACTOR,
    "++": _Precedence.FACTOR,
}

Doc/Library/ast.rst

.. class:: UAdd
           USub
           Not
           Invert
           PreIncr
           PostIncr
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0