変更手順まとめ
本記事は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_op
にPre/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
をしていて,STORE
,RETURN_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